Repository: codespell-project/codespell

Issue:
codespell ignores C/C++ tabs ('\t') in strings
I found out, that codespell gives wrong answers in case of a tab is used within a C/C++ string. For example:
test.c:

``` c++
int main()
{
    std::cout<< "\tTest\n";
}
```

dict.txt:

```
ttest->test
```

Scanning with test.c with codespell gives a wrong result:

```
$ ../codespell.py dict.txt test.c 
test.c:3: tTest  ==> test
```

Escaped characters in strings not ignored in 2.0 version
Hello! I use the latest version of codespell in CI. Yesterday everything was ok, but the new release gives errors about escaped characters.

A minimal reproducible example is simple:
```
cat hello.c
#include <stdio.h>

int main() {
  printf("CodeSpell do not ignore escaped chars\nin strings.");
  return 0;
}
```

codespell==1.17.1 sees no problem in that file.

codespell==2.0.0 gives this message:

```
hello.c:4: nin ==> inn, min, bin, nine
```

C escapes
A new command line option `--c-esacpes` is implemented which treats files as if they contain C-style character escapes. So for example "\nHello" is parsed as "hello" instead of "nhello".

What do you think?


Discussion:
`\n` should also be ignored.
`\t` issue might have been resolved but I keep hitting this eldest ever issue with `\n` once in a while, e.g. on `/tmp/1.c`:

```C
int main()
{
    std::cout<< "\tTest\nin here";
}
```

```shell
❯ codespell --version
2.2.2
❯ codespell /tmp/1.c
/tmp/1.c:3: nin ==> inn, min, bin, nine
```

codespell is language-agnostic. Handling escape characters would require to identify the type of each file and adapt `--regex` or `--ignore-regex`. A proof of concept could be a shell script identifying the type of each file and calling codespell with the proper `--regex` and `--ignore-regex`.
I think it would even **better** if codespell intelligently detected a `\` or `\n` at the beginning of a typo and skipped it.  Because it's common to make typos in strings.  Of course it should respect your configuration, and there shouldn't be a regression.  Just a thought.
An easy solution to this is something like:

```
[codespell]
ignore-regex = \\[fnrstv]
```
I also see this when working on ARM template (Azure; json files).

Above workaround seems to work well.

multiple related entries
https://github.com/codespell-project/codespell/pull/151
https://github.com/codespell-project/codespell/pull/174
https://github.com/codespell-project/codespell/issues/233
https://github.com/codespell-project/codespell/pull/1422
This might be widely useful enough to add it to the [README.rst](https://github.com/codespell-project/codespell/blob/master/README.rst) page, or to a new "recipes" page.
# [Codecov](https://codecov.io/gh/lucasdemarchi/codespell/pull/174?src=pr&el=h1) Report
> Merging [#174](https://codecov.io/gh/lucasdemarchi/codespell/pull/174?src=pr&el=desc) into [master](https://codecov.io/gh/lucasdemarchi/codespell/commit/e87625e56f887a9d31cf7403f10d2b81b7c8d3e1?src=pr&el=desc) will **increase** coverage by `0.59%`.
> The diff coverage is `100%`.

[![Impacted file tree graph](https://codecov.io/gh/lucasdemarchi/codespell/pull/174/graphs/tree.svg?token=Gw3LxZcNS4&src=pr&height=150&width=650)](https://codecov.io/gh/lucasdemarchi/codespell/pull/174?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master     #174      +/-   ##
==========================================
+ Coverage   87.81%   88.41%   +0.59%     
==========================================
  Files           2        3       +1     
  Lines         665      656       -9     
  Branches       93       94       +1     
==========================================
- Hits          584      580       -4     
+ Misses         62       58       -4     
+ Partials       19       18       -1
```


| [Impacted Files](https://codecov.io/gh/lucasdemarchi/codespell/pull/174?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [codespell\_lib/tests/test\_basic.py](https://codecov.io/gh/lucasdemarchi/codespell/pull/174/diff?src=pr&el=tree#diff-Y29kZXNwZWxsX2xpYi90ZXN0cy90ZXN0X2Jhc2ljLnB5) | `95.3% <100%> (-0.2%)` | :arrow_down: |
| [codespell\_lib/\_codespell.py](https://codecov.io/gh/lucasdemarchi/codespell/pull/174/diff?src=pr&el=tree#diff-Y29kZXNwZWxsX2xpYi9fY29kZXNwZWxsLnB5) | `83.33% <100%> (+1.41%)` | :arrow_up: |
| [codespell\_lib/tests/\_\_init\_\_.py](https://codecov.io/gh/lucasdemarchi/codespell/pull/174/diff?src=pr&el=tree#diff-Y29kZXNwZWxsX2xpYi90ZXN0cy9fX2luaXRfXy5weQ==) | `100% <0%> (ø)` | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/lucasdemarchi/codespell/pull/174?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/lucasdemarchi/codespell/pull/174?src=pr&el=footer). Last update [e87625e...87b94df](https://codecov.io/gh/lucasdemarchi/codespell/pull/174?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

I like the idea. 
Thank you @larsoner for the good comment.
The reason why I have not updated this PR so far is that I recognized that the new option doesn't work with the write-changes flag. See my comments for #196 

ping @thdot — any progress?  If not, we can close this PR.

Resolving patch:
diff --git a/codespell_lib/_codespell.py b/codespell_lib/_codespell.py
--- a/codespell_lib/_codespell.py
+++ b/codespell_lib/_codespell.py
@@ -897,6 +897,19 @@ def parse_file(
             word = match.group()
             lword = word.lower()
             if lword in misspellings:
+                # Sometimes we find a 'misspelling' which is actually a valid word
+                # preceded by a string escape sequence.  Ignore such cases as
+                # they're usually false alarms; see issue #17 among others.
+                char_before_idx = match.start() - 1
+                if (
+                    char_before_idx >= 0
+                    and line[char_before_idx] == "\\"
+                    # bell, backspace, formfeed, newline, carriage-return, tab, vtab.
+                    and word.startswith(("a", "b", "f", "n", "r", "t", "v"))
+                    and lword[1:] not in misspellings
+                ):
+                    continue
+
                 context_shown = False
                 fix = misspellings[lword].fix
                 fixword = fix_case(word, misspellings[lword].data)


Test patch:
diff --git a/codespell_lib/tests/test_basic.py b/codespell_lib/tests/test_basic.py
--- a/codespell_lib/tests/test_basic.py
+++ b/codespell_lib/tests/test_basic.py
@@ -97,6 +97,9 @@ def test_basic(
     with fname.open("a") as f:
         f.write("this is a test file\n")
     assert cs.main(fname) == 0, "good"
+    with fname.open("a") as f:
+        f.write("var = '\\nDoes not error on newline'\n")
+    assert cs.main(fname) == 0, "with string escape"
     with fname.open("a") as f:
         f.write("abandonned\n")
     assert cs.main(fname) == 1, "bad"