Do not report a fix that --ignore-regex made impossible (#3994)
diff --git a/codespell_lib/_codespell.py b/codespell_lib/_codespell.py
index 994c5c4..e7d94aa 100644
--- a/codespell_lib/_codespell.py
+++ b/codespell_lib/_codespell.py
@@ -1093,11 +1093,15 @@
continue
if options.write_changes and fix:
- changed = True
- lines[i] = re.sub(rf"\b{word}\b", fixword, lines[i])
- fixed_words.add(word)
- changes_made.append((line_number + 1, word, fixword))
- continue
+ new_line = re.sub(rf"\b{word}\b", fixword, lines[i])
+ if new_line != lines[i]:
+ changed = True
+ lines[i] = new_line
+ fixed_words.add(word)
+ changes_made.append((line_number + 1, word, fixword))
+ continue
+ # Not found in the original line (e.g. --ignore-regex split
+ # it out of a larger word), so report it instead (GH-2056).
# otherwise warning was explicitly set by interactive mode
if (
diff --git a/codespell_lib/tests/test_basic.py b/codespell_lib/tests/test_basic.py
index 930f09f..c82d847 100644
--- a/codespell_lib/tests/test_basic.py
+++ b/codespell_lib/tests/test_basic.py
@@ -1054,6 +1054,34 @@
assert cs.main(fname, r"--ignore-regex=\bdonn\b") == 1
+def test_ignore_regex_with_write_changes(
+ tmp_path: Path,
+ capsys: pytest.CaptureFixture[str],
+) -> None:
+ """Test that -w never claims a fix it did not perform (gh-2056)."""
+ fname = tmp_path / "flag.txt"
+ content = "1nd\n1nd_2nd\n"
+
+ # Without -w, --ignore-regex=_ makes BOTH lines report a misspelling.
+ fname.write_text(content)
+ assert cs.main(fname, "--ignore-regex=_") == 2
+
+ fname.write_text(content)
+ result = cs.main("-w", "--ignore-regex=_", fname, std=True)
+ assert isinstance(result, tuple)
+ code, stdout, stderr = result
+ corrected = fname.read_text()
+
+ # Line 1 really is rewritten.
+ assert corrected == "1st\n1nd_2nd\n"
+ # Line 2 cannot be rewritten (\b1nd\b does not match "1nd_2nd"), so it must
+ # not be listed as fixed, must be reported as a misspelling instead ...
+ assert "flag.txt:2: 1nd ==> 1st" not in stderr
+ assert "flag.txt:2: 1nd ==> 1st" in stdout
+ # ... and must be counted, so that it affects the exit code.
+ assert code == 1
+
+
def test_ignore_multiline_regex_option(
tmp_path: Path,
capsys: pytest.CaptureFixture[str],