From b5aab35a6f20efad94ae8da07392fad6d1c28aad Mon Sep 17 00:00:00 2001 From: Pierre Sassoulas Date: Thu, 2 Mar 2023 21:18:40 +0100 Subject: [spelling] Ignore spelling in type/mypy type ignore comments (#8370) enchant does not understand class name well enough so it creates false positives, and mypy type ignore comments with additional text are a syntax error anyway, so raising a spelling mistakes for it is not really important. --- doc/whatsnew/fragments/8370.false_positive | 3 +++ pylint/checkers/spelling.py | 23 +++--------------- tests/checkers/unittest_spelling.py | 39 ++++++++++++++++-------------- 3 files changed, 28 insertions(+), 37 deletions(-) create mode 100644 doc/whatsnew/fragments/8370.false_positive diff --git a/doc/whatsnew/fragments/8370.false_positive b/doc/whatsnew/fragments/8370.false_positive new file mode 100644 index 000000000..a641ecbbf --- /dev/null +++ b/doc/whatsnew/fragments/8370.false_positive @@ -0,0 +1,3 @@ +Fix false positive for ``wrong-spelling-in-comment`` with class names in a python 2 type comment. + +Closes #8370 diff --git a/pylint/checkers/spelling.py b/pylint/checkers/spelling.py index d39229ad0..3fbd017f8 100644 --- a/pylint/checkers/spelling.py +++ b/pylint/checkers/spelling.py @@ -191,7 +191,6 @@ class ForwardSlashChunker(Chunker): # type: ignore[misc] CODE_FLANKED_IN_BACKTICK_REGEX = re.compile(r"(\s|^)(`{1,2})([^`]+)(\2)([^`]|$)") -MYPY_IGNORE_DIRECTIVE_RULE_REGEX = re.compile(r"(\s|^)(type\: ignore\[[^\]]+\])(.*)") def _strip_code_flanked_in_backticks(line: str) -> str: @@ -209,23 +208,6 @@ def _strip_code_flanked_in_backticks(line: str) -> str: ) -def _strip_mypy_ignore_directive_rule(line: str) -> str: - """Alter line so mypy rule name is ignored. - - Pyenchant parses anything flanked by spaces as an individual token, - so this cannot be done at the individual filter level. - """ - - def replace_rule_name_but_leave_surrounding_characters( - match_obj: re.Match[str], - ) -> str: - return match_obj.group(1) + match_obj.group(3) - - return MYPY_IGNORE_DIRECTIVE_RULE_REGEX.sub( - replace_rule_name_but_leave_surrounding_characters, line - ) - - class SpellingChecker(BaseTokenChecker): """Check spelling in comments and docstrings.""" @@ -380,7 +362,6 @@ class SpellingChecker(BaseTokenChecker): starts_with_comment = False line = _strip_code_flanked_in_backticks(line) - line = _strip_mypy_ignore_directive_rule(line) for word, word_start_at in self.tokenizer(line.strip()): word_start_at += initial_space @@ -454,6 +435,10 @@ class SpellingChecker(BaseTokenChecker): if token.startswith("# pylint:"): # Skip pylint enable/disable comments continue + if token.startswith("# type: "): + # Skip python 2 type comments and mypy type ignore comments + # mypy do not support additional text in type comments + continue self._check_spelling("wrong-spelling-in-comment", token, start_row) @only_required_for_messages("wrong-spelling-in-docstring") diff --git a/tests/checkers/unittest_spelling.py b/tests/checkers/unittest_spelling.py index d716def60..b02cf73a8 100644 --- a/tests/checkers/unittest_spelling.py +++ b/tests/checkers/unittest_spelling.py @@ -258,6 +258,27 @@ class TestSpellingChecker(CheckerTestCase): # pylint:disable=too-many-public-me self.checker.process_tokens(_tokenize_str("# https://github.com/rfk/pyenchant")) assert not self.linter.release_messages() + @skip_on_missing_package_or_dict + @set_config(spelling_dict=spell_dict) + @pytest.mark.parametrize( + "type_comment", + [ + "# type: (NotAWord) -> NotAWord", + "# type: List[NotAWord] -> List[NotAWord]", + "# type: Dict[NotAWord] -> Dict[NotAWord]", + "# type: NotAWord", + "# type: List[NotAWord]", + "# type: Dict[NotAWord]", + "# type: ImmutableList[Manager]", + # will result in error: Invalid "type: ignore" comment [syntax] + # when analyzed with mypy 1.02 + "# type: ignore[attr-defined] NotAWord", + ], + ) + def test_skip_type_comments(self, type_comment: str) -> None: + self.checker.process_tokens(_tokenize_str(type_comment)) + assert not self.linter.release_messages() + @skip_on_missing_package_or_dict @set_config(spelling_dict=spell_dict) def test_skip_sphinx_directives(self) -> None: @@ -363,24 +384,6 @@ class TestSpellingChecker(CheckerTestCase): # pylint:disable=too-many-public-me ): self.checker.process_tokens(_tokenize_str(full_comment)) - @skip_on_missing_package_or_dict - @set_config(spelling_dict=spell_dict) - def test_skip_mypy_ignore_directives(self) -> None: - full_comment = "# type: ignore[attr-defined] attr" - with self.assertAddsMessages( - MessageTest( - "wrong-spelling-in-comment", - line=1, - args=( - "attr", - full_comment, - " ^^^^", - self._get_msg_suggestions("attr"), - ), - ) - ): - self.checker.process_tokens(_tokenize_str(full_comment)) - @skip_on_missing_package_or_dict @set_config( spelling_dict=spell_dict, -- cgit v1.2.1