Repository: wemake-services/wemake-python-styleguide

Issue:
Disallow to ignore some violations on line level
# Rule request

<!-- Thanks for coming up with a proposal! -->

## Thesis

<!-- What exactly do you propose? Please, provide code examples. -->
Sometimes developers inline ignore violations that should not be ignored. Like complexity violations, best practices, or any others that are important for this specific project.

So, we need to give users an option to provide a configuration hook with violations names (both short like `WPS` and full ones like `WPS102`) that cannot be ignored.

And when we find inline ignore of this violations - we should raise a new one on `0` line.

We do not check `per-file-ignores` and `ignored` with this check.

Related: https://github.com/wemake-services/wemake-python-styleguide/pull/1246/files#diff-2c42a0740c36d757ff4d489010b3138eR4

This is a best practices violation.

Discussion:
edytagarbarz: Hi @sobolevn I need your help here with logistic. I added this new violation  under MagicCommentVisitor but doing so I exceeded allowed number of methods. I thought about moving noqa comments checks to a separate visitor but wanted to hear your thoughts first. Thanks!
edytagarbarz: This is a kind reminder. @sobolevn  could you take a look please? 
edytagarbarz: > Hi @sobolevn I need your help here with logistic. I added this new violation under MagicCommentVisitor but doing so I exceeded allowed number of methods. I thought about moving noqa comments checks to a separate visitor but wanted to hear your thoughts first. Thanks!

I still have a problem with this. Thanks!
sobolevn: Lets move it to a new one, please!
codecov[bot]: # [Codecov](https://codecov.io/gh/wemake-services/wemake-python-styleguide/pull/1347?src=pr&el=h1) Report
> Merging [#1347](https://codecov.io/gh/wemake-services/wemake-python-styleguide/pull/1347?src=pr&el=desc) into [master](https://codecov.io/gh/wemake-services/wemake-python-styleguide/commit/c1e7db222f813b23e640ca169e3f4a6bc5dab45a&el=desc) will **not change** coverage.
> The diff coverage is `100.00%`.

[![Impacted file tree graph](https://codecov.io/gh/wemake-services/wemake-python-styleguide/pull/1347/graphs/tree.svg?width=650&height=150&src=pr&token=yMZCKw0D02)](https://codecov.io/gh/wemake-services/wemake-python-styleguide/pull/1347?src=pr&el=tree)

```diff
@@            Coverage Diff            @@
##            master     #1347   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files          104       104           
  Lines         5343      5351    +8     
  Branches      1168      1172    +4     
=========================================
+ Hits          5343      5351    +8     
```


| [Impacted Files](https://codecov.io/gh/wemake-services/wemake-python-styleguide/pull/1347?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [...ake\_python\_styleguide/presets/types/file\_tokens.py](https://codecov.io/gh/wemake-services/wemake-python-styleguide/pull/1347/diff?src=pr&el=tree#diff-d2VtYWtlX3B5dGhvbl9zdHlsZWd1aWRlL3ByZXNldHMvdHlwZXMvZmlsZV90b2tlbnMucHk=) | `100.00% <ø> (ø)` | |
| [wemake\_python\_styleguide/options/config.py](https://codecov.io/gh/wemake-services/wemake-python-styleguide/pull/1347/diff?src=pr&el=tree#diff-d2VtYWtlX3B5dGhvbl9zdHlsZWd1aWRlL29wdGlvbnMvY29uZmlnLnB5) | `100.00% <100.00%> (ø)` | |
| [wemake\_python\_styleguide/options/defaults.py](https://codecov.io/gh/wemake-services/wemake-python-styleguide/pull/1347/diff?src=pr&el=tree#diff-d2VtYWtlX3B5dGhvbl9zdHlsZWd1aWRlL29wdGlvbnMvZGVmYXVsdHMucHk=) | `100.00% <100.00%> (ø)` | |
| [wemake\_python\_styleguide/options/validation.py](https://codecov.io/gh/wemake-services/wemake-python-styleguide/pull/1347/diff?src=pr&el=tree#diff-d2VtYWtlX3B5dGhvbl9zdHlsZWd1aWRlL29wdGlvbnMvdmFsaWRhdGlvbi5weQ==) | `100.00% <100.00%> (ø)` | |
| [...ake\_python\_styleguide/violations/best\_practices.py](https://codecov.io/gh/wemake-services/wemake-python-styleguide/pull/1347/diff?src=pr&el=tree#diff-d2VtYWtlX3B5dGhvbl9zdHlsZWd1aWRlL3Zpb2xhdGlvbnMvYmVzdF9wcmFjdGljZXMucHk=) | `100.00% <100.00%> (ø)` | |
| [...ke\_python\_styleguide/visitors/tokenize/comments.py](https://codecov.io/gh/wemake-services/wemake-python-styleguide/pull/1347/diff?src=pr&el=tree#diff-d2VtYWtlX3B5dGhvbl9zdHlsZWd1aWRlL3Zpc2l0b3JzL3Rva2VuaXplL2NvbW1lbnRzLnB5) | `100.00% <100.00%> (ø)` | |
| [wemake\_python\_styleguide/compat/nodes.py](https://codecov.io/gh/wemake-services/wemake-python-styleguide/pull/1347/diff?src=pr&el=tree#diff-d2VtYWtlX3B5dGhvbl9zdHlsZWd1aWRlL2NvbXBhdC9ub2Rlcy5weQ==) | `100.00% <0.00%> (ø)` | |
| [wemake\_python\_styleguide/logic/walrus.py](https://codecov.io/gh/wemake-services/wemake-python-styleguide/pull/1347/diff?src=pr&el=tree#diff-d2VtYWtlX3B5dGhvbl9zdHlsZWd1aWRlL2xvZ2ljL3dhbHJ1cy5weQ==) | `100.00% <0.00%> (ø)` | |
| [wemake\_python\_styleguide/compat/routing.py](https://codecov.io/gh/wemake-services/wemake-python-styleguide/pull/1347/diff?src=pr&el=tree#diff-d2VtYWtlX3B5dGhvbl9zdHlsZWd1aWRlL2NvbXBhdC9yb3V0aW5nLnB5) | `100.00% <0.00%> (ø)` | |
| [wemake\_python\_styleguide/logic/safe\_eval.py](https://codecov.io/gh/wemake-services/wemake-python-styleguide/pull/1347/diff?src=pr&el=tree#diff-d2VtYWtlX3B5dGhvbl9zdHlsZWd1aWRlL2xvZ2ljL3NhZmVfZXZhbC5weQ==) | `100.00% <0.00%> (ø)` | |
| ... and [3 more](https://codecov.io/gh/wemake-services/wemake-python-styleguide/pull/1347/diff?src=pr&el=tree-more) | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/wemake-services/wemake-python-styleguide/pull/1347?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/wemake-services/wemake-python-styleguide/pull/1347?src=pr&el=footer). Last update [c1e7db2...156482c](https://codecov.io/gh/wemake-services/wemake-python-styleguide/pull/1347?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

edytagarbarz: Done!
KumarGanesha1996: > * [] I have added my changes to the `CHANGELOG.md`

please fix checkbox in description

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f357984f..5c7598aa 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,36 +6,10 @@ We used to have incremental versioning before `0.1.0`.
 Semantic versioning in our case means:
 - Bugfixes do not bring new features, code that passes on `x.y.0` should pass on `x.y.1`. With the only exception that bugfix can raise old violations in new places, if they were hidden by a buggy behaviour.
 - Minor releases do bring new features and configuration options. New violations can be added. Code that passes `x.0.y` might not pass on `x.1.y` release.
-- Major releases inidicate significant milestones or serious breaking changes.
+- Major releases inidicate significant milestones or serious breaking changes..
 
-## 0.16.0
 
-### Features
-
-- Adds `SingleElementDestructuringViolation` as `WPS454`, because it's hard to read single
-element tuple or list unpacking.
-- Raises `WrongUnpackingViolation` for unpacking lists with side-effects.
-
-
-## 0.15.0 aka New runtime
-
-### Features
-
-- Forbids to use single `return None`
-- Add `__await__` to the list of priority magic methods
-
-### Bugfixes
-
-- Fixes how wrong variable names were checked case sensitive with `WPS110`
-- Fixes false positives DirectMagicAttributeAccessViolation with `__mro__`, `__subclasses__` and `__version__`
-
-### Misc
-
-- Updates lots of dependenices
-- Fixed documentation for TooManyPublicAttributesViolation
-
-
-## 0.15.0
+## 0.15.0 aka Hello, LibCST
 
 ### Features
 
@@ -56,6 +30,10 @@ element tuple or list unpacking.
 - Forbids to use too complex `f`-strings
 - Forbids to use too many `raise` statements inside a single function
 - Forbids to compare with `float` and `complex` values
+- Forbids single element destruct
+- Forbids to ignore some violations (configurable) on a line level
+- Forbids single element unpacking
+- Forbids to unpack lists with side-effects
 
 ### Bugfixes
 
@@ -65,8 +43,7 @@ element tuple or list unpacking.
 - Make `WPS326` work when there is comment between string literals
 - Allowed yield statements in call method
 - Allow to use `^` with `1`
-- Fixes false positives in WPS513
-- Fixes false positives in WPS323
+- Fixes false positives in WPS513 and WPS323
 
 ### Misc
 
diff --git a/wemake_python_styleguide/options/config.py b/wemake_python_styleguide/options/config.py
index d1f271c7..99deb5e9 100644
--- a/wemake_python_styleguide/options/config.py
+++ b/wemake_python_styleguide/options/config.py
@@ -56,6 +56,10 @@ You can also show all options that ``flake8`` supports by running:
     :str:`wemake_python_styleguide.options.defaults.ALLOWED_DOMAIN_NAMES`
 - ``forbidden-domain-names`` - list of forbidden domain names, defaults to
     :str:`wemake_python_styleguide.options.defaults.FORBIDDEN_DOMAIN_NAMES`
+- ``forbidden-inline-ignore`` - list of codes of violations or
+    class of violations that are forbidden to ignore inline, defaults to
+    :str:`wemake_python_styleguide.options.defaults.FORBIDDEN_NOQA`
+
 
 .. rubric:: Complexity options
 
@@ -151,6 +155,7 @@ from typing_extensions import final
 from wemake_python_styleguide.options import defaults
 
 ConfigValuesTypes = Union[str, int, bool, Sequence[str]]
+string = 'string'
 
 
 @final
@@ -232,20 +237,27 @@ class Configuration(object):
             '--nested-classes-whitelist',
             defaults.NESTED_CLASSES_WHITELIST,
             'List of nested classes names we allow to use.',
-            type='string',
+            type=string,
             comma_separated_list=True,
         ),
         _Option(
             '--allowed-domain-names',
             defaults.ALLOWED_DOMAIN_NAMES,
             "Domain names that are removed from variable names' blacklist.",
-            type='string',
+            type=string,
             comma_separated_list=True,
         ),
         _Option(
             '--forbidden-domain-names',
             defaults.FORBIDDEN_DOMAIN_NAMES,
             "Domain names that extends variable names' blacklist.",
+            type=string,
+            comma_separated_list=True,
+        ),
+        _Option(
+            '--forbidden-inline-ignore',
+            defaults.FORBIDDEN_INLINE_IGNORE,
+            'Codes of violations or class of violations forbidden to ignore.',
             type='string',
             comma_separated_list=True,
         ),
diff --git a/wemake_python_styleguide/options/defaults.py b/wemake_python_styleguide/options/defaults.py
index 07db0396..de13c491 100644
--- a/wemake_python_styleguide/options/defaults.py
+++ b/wemake_python_styleguide/options/defaults.py
@@ -42,6 +42,8 @@ ALLOWED_DOMAIN_NAMES: Final = ()
 #: Domain names that extends variable names' blacklist.
 FORBIDDEN_DOMAIN_NAMES: Final = ()
 
+#: Violation codes that are forbidden to use.
+FORBIDDEN_INLINE_IGNORE: Final = ()
 
 # ===========
 # Complexity:
diff --git a/wemake_python_styleguide/options/validation.py b/wemake_python_styleguide/options/validation.py
index e148dace..e128846c 100644
--- a/wemake_python_styleguide/options/validation.py
+++ b/wemake_python_styleguide/options/validation.py
@@ -65,6 +65,7 @@ class _ValidatedOptions(object):
     nested_classes_whitelist: Tuple[str, ...] = attr.ib(converter=tuple)
     allowed_domain_names: Tuple[str, ...] = attr.ib(converter=tuple)
     forbidden_domain_names: Tuple[str, ...] = attr.ib(converter=tuple)
+    forbidden_inline_ignore: Tuple[str, ...] = attr.ib(converter=tuple)
 
     # Complexity:
     max_arguments: int = attr.ib(validator=[_min_max(min=1)])
diff --git a/wemake_python_styleguide/presets/types/file_tokens.py b/wemake_python_styleguide/presets/types/file_tokens.py
index 049942cf..3cd705e9 100644
--- a/wemake_python_styleguide/presets/types/file_tokens.py
+++ b/wemake_python_styleguide/presets/types/file_tokens.py
@@ -12,6 +12,7 @@ from wemake_python_styleguide.visitors.tokenize import (
 PRESET: Final = (
     comments.WrongCommentVisitor,
     comments.ShebangVisitor,
+    comments.NoqaVisitor,
 
     syntax.WrongKeywordTokenVisitor,
 
diff --git a/wemake_python_styleguide/types.py b/wemake_python_styleguide/types.py
index 919a7dbc..5157040a 100644
--- a/wemake_python_styleguide/types.py
+++ b/wemake_python_styleguide/types.py
@@ -143,6 +143,10 @@ class ConfigurationOptions(Protocol):
     def nested_classes_whitelist(self) -> Tuple[str, ...]:
         ...
 
+    @property
+    def forbidden_inline_ignore(self) -> Tuple[str, ...]:
+        ...
+
     @property
     def allowed_domain_names(self) -> Tuple[str, ...]:
         ...
diff --git a/wemake_python_styleguide/violations/best_practices.py b/wemake_python_styleguide/violations/best_practices.py
index cf8e6cf1..f2b5a0f4 100644
--- a/wemake_python_styleguide/violations/best_practices.py
+++ b/wemake_python_styleguide/violations/best_practices.py
@@ -77,6 +77,7 @@ Summary
    ImportCollisionViolation
    FloatComplexCompareViolation
    SingleElementDestructuringViolation
+   ForbiddenInlineIgnoreViolation
 
 Best practices
 --------------
@@ -142,6 +143,7 @@ Best practices
 .. autoclass:: ImportCollisionViolation
 .. autoclass:: FloatComplexCompareViolation
 .. autoclass:: SingleElementDestructuringViolation
+.. autoclass:: ForbiddenInlineIgnoreViolation
 
 """
 
@@ -2360,3 +2362,31 @@ class SingleElementDestructuringViolation(ASTViolation):
 
     error_template = 'Found single element destructuring'
     code = 460
+
+
+@final
+class ForbiddenInlineIgnoreViolation(SimpleViolation):
+    """
+    Forbids to use specific inline ignore violations.
+
+    There can be forbidden a specific violation or whole
+    class of violations.
+
+    Reasoning:
+        There are violations important for specific project that must not
+        be ignored, e.g. complexity or best practices violations.
+
+    Solution:
+        Remove inline ignore for forbidden violations.
+
+    Configuration:
+        This rule is configurable with `--forbidden-inline-ignore``.
+        Default:
+        :str:`wemake_python_styleguide.options.defaults.FORBIDDEN_INLINE_IGNORE`
+
+    .. versionadded:: 0.15.0
+
+    """
+
+    error_template = 'Forbidden inline ignore: {0}'
+    code = 461
diff --git a/wemake_python_styleguide/visitors/tokenize/comments.py b/wemake_python_styleguide/visitors/tokenize/comments.py
index 766680e2..a3a82412 100644
--- a/wemake_python_styleguide/visitors/tokenize/comments.py
+++ b/wemake_python_styleguide/visitors/tokenize/comments.py
@@ -27,6 +27,7 @@ from wemake_python_styleguide.constants import MAX_NO_COVER_COMMENTS, STDIN
 from wemake_python_styleguide.logic.system import is_executable_file, is_windows
 from wemake_python_styleguide.logic.tokens import NEWLINES, get_comment_text
 from wemake_python_styleguide.violations.best_practices import (
+    ForbiddenInlineIgnoreViolation,
     OveruseOfNoCoverCommentViolation,
     OveruseOfNoqaCommentViolation,
     ShebangViolation,
@@ -41,7 +42,6 @@ class WrongCommentVisitor(BaseTokenVisitor):
     """Checks comment tokens."""
 
     _no_cover: ClassVar[Pattern] = re.compile(r'^pragma:\s+no\s+cover')
-    _noqa_check: ClassVar[Pattern] = re.compile(r'^(noqa:?)($|[A-Z\d\,\s]+)')
     _type_check: ClassVar[Pattern] = re.compile(
         r'^type:\s?([\w\d\[\]\'\"\.]+)$',
     )
@@ -49,7 +49,6 @@ class WrongCommentVisitor(BaseTokenVisitor):
     def __init__(self, *args, **kwargs) -> None:
         """Initializes a counter."""
         super().__init__(*args, **kwargs)
-        self._noqa_count = 0
         self._no_cover_count = 0
 
     def visit_comment(self, token: tokenize.TokenInfo) -> None:
@@ -57,31 +56,14 @@ class WrongCommentVisitor(BaseTokenVisitor):
         Performs comment checks.
 
         Raises:
-            OveruseOfNoqaCommentViolation
             WrongDocCommentViolation
             WrongMagicCommentViolation
 
         """
-        self._check_noqa(token)
         self._check_typed_ast(token)
         self._check_empty_doc_comment(token)
         self._check_cover_comments(token)
 
-    def _check_noqa(self, token: tokenize.TokenInfo) -> None:
-        comment_text = get_comment_text(token)
-        match = self._noqa_check.match(comment_text)
-        if not match:
-            return
-
-        self._noqa_count += 1
-        excludes = match.groups()[1].strip()
-        prefix = match.groups()[0].strip()
-
-        if not excludes or prefix[-1] != ':':
-            # We cannot pass the actual line here,
-            # since it will be ignored due to `# noqa` comment:
-            self.add_violation(WrongMagicCommentViolation(text=comment_text))
-
     def _check_typed_ast(self, token: tokenize.TokenInfo) -> None:
         comment_text = get_comment_text(token)
         match = self._type_check.match(comment_text)
@@ -107,10 +89,6 @@ class WrongCommentVisitor(BaseTokenVisitor):
         self._no_cover_count += 1
 
     def _post_visit(self) -> None:
-        if self._noqa_count > self.options.max_noqa_comments:
-            self.add_violation(
-                OveruseOfNoqaCommentViolation(text=str(self._noqa_count)),
-            )
         if self._no_cover_count > MAX_NO_COVER_COMMENTS:
             self.add_violation(
                 OveruseOfNoCoverCommentViolation(
@@ -208,3 +186,68 @@ class ShebangVisitor(BaseTokenVisitor):
 
     def _is_valid_shebang_line(self, token: tokenize.TokenInfo) -> bool:
         return self._shebang.match(token.line) is not None
+
+
+@final
+class NoqaVisitor(BaseTokenVisitor):
+    """Checks noqa comment tokens."""
+
+    _noqa_check: ClassVar[Pattern] = re.compile(r'^(noqa:?)($|[A-Z\d\,\s]+)')
+
+    def __init__(self, *args, **kwargs) -> None:
+        """Initializes a counter."""
+        super().__init__(*args, **kwargs)
+        self._noqa_count = 0
+
+    def visit_comment(self, token: tokenize.TokenInfo) -> None:
+        """
+        Performs comment checks.
+
+        Raises:
+            OveruseOfNoqaCommentViolation
+            ForbiddenInlineIgnoreViolation
+
+        """
+        self._check_noqa(token)
+
+    def _check_noqa(self, token: tokenize.TokenInfo) -> None:
+        comment_text = get_comment_text(token)
+        match = self._noqa_check.match(comment_text)
+        if not match:
+            return
+
+        self._noqa_count += 1
+        excludes = match.groups()[1].strip()
+        prefix = match.groups()[0].strip()
+
+        if not excludes or prefix[-1] != ':':
+            # We cannot pass the actual line here,
+            # since it will be ignored due to `# noqa` comment:
+            self.add_violation(WrongMagicCommentViolation(text=comment_text))
+            return
+        self._check_forbidden_noqa(excludes)
+
+    def _check_forbidden_noqa(self, noqa_excludes) -> None:
+        excludes_list = [ex.strip() for ex in noqa_excludes.split(',')]
+        forbidden_noqa = ''.join(self.options.forbidden_inline_ignore)
+        for noqa_code in forbidden_noqa.split(','):
+            noqa_code = noqa_code.strip()
+            if noqa_code in excludes_list:
+                self.add_violation(
+                    ForbiddenInlineIgnoreViolation(text=str(noqa_excludes)),
+                )
+                return
+            if not noqa_code.isalpha():
+                continue
+            for excluded in excludes_list:
+                if re.fullmatch(r'{0}($|\d+)'.format(noqa_code), excluded):
+                    self.add_violation(
+                        ForbiddenInlineIgnoreViolation(text=str(noqa_excludes)),
+                    )
+                    return
+
+    def _post_visit(self) -> None:
+        if self._noqa_count > self.options.max_noqa_comments:
+            self.add_violation(
+                OveruseOfNoqaCommentViolation(text=str(self._noqa_count)),
+            )


Test patch:
diff --git a/tests/test_checker/test_noqa.py b/tests/test_checker/test_noqa.py
index 1c3ddea2..21cc2bff 100644
--- a/tests/test_checker/test_noqa.py
+++ b/tests/test_checker/test_noqa.py
@@ -233,6 +233,7 @@ SHOULD_BE_RAISED = types.MappingProxyType({
     'WPS458': 1,
     'WPS459': 1,
     'WPS460': 1,
+    'WPS461': 0,  # logically unacceptable.
 
     'WPS500': 1,
     'WPS501': 1,
diff --git a/tests/test_visitors/test_tokenize/test_comments/test_forbidden_noqa.py b/tests/test_visitors/test_tokenize/test_comments/test_forbidden_noqa.py
new file mode 100644
index 00000000..5642a68c
--- /dev/null
+++ b/tests/test_visitors/test_tokenize/test_comments/test_forbidden_noqa.py
@@ -0,0 +1,47 @@
+import pytest
+
+from wemake_python_styleguide.violations.best_practices import (
+    ForbiddenInlineIgnoreViolation,
+)
+from wemake_python_styleguide.visitors.tokenize.comments import NoqaVisitor
+
+
+@pytest.mark.parametrize(('code', 'forbidden_inline_ignore'), [
+    ('x = 10_00  # noqa: WPS002,Z114', 'A, C,WPS002'),
+    ('x = 10_00  # noqa:W002, U114', 'W'),
+    ('x = 10_00  # noqa: J002, WPS114', 'J, WPS'),
+    ('x = 10_00  # noqa: J, WPS114', 'J'),
+    ('x = 10_00  # noqa: WPS114', 'WPS'),
+])
+def test_forbidden_noqa(
+    parse_tokens,
+    assert_errors,
+    options,
+    code,
+    forbidden_inline_ignore,
+):
+    """Ensure that noqa comments with forbidden violations raise a violation."""
+    file_tokens = parse_tokens(code)
+    options = options(forbidden_inline_ignore=forbidden_inline_ignore)
+    visitor = NoqaVisitor(options, file_tokens=file_tokens)
+    visitor.run()
+    assert_errors(visitor, [ForbiddenInlineIgnoreViolation])
+
+
+@pytest.mark.parametrize(('code', 'forbidden_inline_ignore'), [
+    ('x = 10_00  # noqa: WPS002,Z114', 'W'),
+    ('x = 10_00  # noqa: WPS002,Z114', 'Z1'),
+])
+def test_correct_noqa(
+    parse_tokens,
+    assert_errors,
+    options,
+    code,
+    forbidden_inline_ignore,
+):
+    """Ensure that proper noqa comments do not rise violations."""
+    file_tokens = parse_tokens(code)
+    options = options(forbidden_inline_ignore=forbidden_inline_ignore)
+    visitor = NoqaVisitor(options, file_tokens=file_tokens)
+    visitor.run()
+    assert_errors(visitor, [])
diff --git a/tests/test_visitors/test_tokenize/test_comments/test_noqa_comment.py b/tests/test_visitors/test_tokenize/test_comments/test_noqa_comment.py
index e1b77bcd..16d50f85 100644
--- a/tests/test_visitors/test_tokenize/test_comments/test_noqa_comment.py
+++ b/tests/test_visitors/test_tokenize/test_comments/test_noqa_comment.py
@@ -3,9 +3,7 @@ import pytest
 from wemake_python_styleguide.violations.best_practices import (
     WrongMagicCommentViolation,
 )
-from wemake_python_styleguide.visitors.tokenize.comments import (
-    WrongCommentVisitor,
-)
+from wemake_python_styleguide.visitors.tokenize.comments import NoqaVisitor
 
 
 @pytest.mark.parametrize('code', [
@@ -29,7 +27,7 @@ def test_correct_comments(
     """Ensures that correct comments do not raise a warning."""
     file_tokens = parse_tokens(code)
 
-    visitor = WrongCommentVisitor(default_options, file_tokens=file_tokens)
+    visitor = NoqaVisitor(default_options, file_tokens=file_tokens)
     visitor.run()
 
     assert_errors(visitor, [])
@@ -56,7 +54,7 @@ def test_incorrect_noqa_comment(
     """Ensures that incorrect `noqa` comments raise a warning."""
     file_tokens = parse_tokens(code)
 
-    visitor = WrongCommentVisitor(default_options, file_tokens=file_tokens)
+    visitor = NoqaVisitor(default_options, file_tokens=file_tokens)
     visitor.run()
 
     assert_errors(visitor, [WrongMagicCommentViolation])
diff --git a/tests/test_visitors/test_tokenize/test_comments/test_noqa_count.py b/tests/test_visitors/test_tokenize/test_comments/test_noqa_count.py
index f6331223..4cc47e8e 100644
--- a/tests/test_visitors/test_tokenize/test_comments/test_noqa_count.py
+++ b/tests/test_visitors/test_tokenize/test_comments/test_noqa_count.py
@@ -4,9 +4,7 @@ from wemake_python_styleguide.options import defaults
 from wemake_python_styleguide.violations.best_practices import (
     OveruseOfNoqaCommentViolation,
 )
-from wemake_python_styleguide.visitors.tokenize.comments import (
-    WrongCommentVisitor,
-)
+from wemake_python_styleguide.visitors.tokenize.comments import NoqaVisitor
 
 
 @pytest.mark.parametrize('code', [
@@ -26,7 +24,7 @@ def test_noqa_overuse(
     """Ensures that `noqa` overuse raises a warning."""
     file_tokens = parse_tokens('{0}\n'.format(code) * (10 + 1))
 
-    visitor = WrongCommentVisitor(default_options, file_tokens=file_tokens)
+    visitor = NoqaVisitor(default_options, file_tokens=file_tokens)
     visitor.run()
 
     assert_errors(visitor, [OveruseOfNoqaCommentViolation])
@@ -43,7 +41,7 @@ def test_noqa_overuse_is_configurable(
     )
 
     options = options(max_noqa_comments=defaults.MAX_NOQA_COMMENTS - 1)
-    visitor = WrongCommentVisitor(options, file_tokens=file_tokens)
+    visitor = NoqaVisitor(options, file_tokens=file_tokens)
     visitor.run()
 
     assert_errors(visitor, [OveruseOfNoqaCommentViolation])
@@ -58,7 +56,7 @@ def test_noqa_comments_can_be_forbidden(
     file_tokens = parse_tokens('wallet = 10  # noqa: WPS002, WPS114')
 
     options = options(max_noqa_comments=0)
-    visitor = WrongCommentVisitor(options, file_tokens=file_tokens)
+    visitor = NoqaVisitor(options, file_tokens=file_tokens)
     visitor.run()
 
     assert_errors(visitor, [OveruseOfNoqaCommentViolation])