Repository: wemake-services/wemake-python-styleguide

Issue:
False positive 434: toggling a boolean
### What's wrong

The following code triggers a WPS434 violation warning.

```python
in_code_block = not in_code_block
```

### How it should be

No warning. There's no better way to toggle a boolean.

### Flake8 version and plugins

```json
{
  "dependencies": [],
  "platform": {
    "python_implementation": "CPython",
    "python_version": "3.8.12",
    "system": "Linux"
  },
  "plugins": [
    {
      "is_local": false,
      "plugin": "black",
      "version": "0.2.3"
    },
    {
      "is_local": false,
      "plugin": "flake8-bandit",
      "version": "2.1.2"
    },
    {
      "is_local": false,
      "plugin": "flake8-bugbear",
      "version": "21.9.1"
    },
    {
      "is_local": false,
      "plugin": "flake8-comprehensions",
      "version": "3.6.1"
    },
    {
      "is_local": false,
      "plugin": "flake8-darglint",
      "version": "1.8.0"
    },
    {
      "is_local": false,
      "plugin": "flake8-docstrings",
      "version": "1.6.0, pydocstyle: 6.1.1"
    },
    {
      "is_local": false,
      "plugin": "flake8-pytest-style",
      "version": "1.5.0"
    },
    {
      "is_local": false,
      "plugin": "flake8-string-format",
      "version": "0.3.0"
    },
    {
      "is_local": false,
      "plugin": "flake8-tidy-imports",
      "version": "4.4.1"
    },
    {
      "is_local": false,
      "plugin": "flake8-variables-names",
      "version": "0.0.4"
    },
    {
      "is_local": false,
      "plugin": "flake8_builtins",
      "version": "1.5.2"
    },
    {
      "is_local": false,
      "plugin": "mccabe",
      "version": "0.6.1"
    },
    {
      "is_local": false,
      "plugin": "naming",
      "version": "0.12.1"
    },
    {
      "is_local": false,
      "plugin": "pycodestyle",
      "version": "2.7.0"
    },
    {
      "is_local": false,
      "plugin": "pyflakes",
      "version": "2.3.1"
    },
    {
      "is_local": false,
      "plugin": "wps-light",
      "version": "0.15.3"
    }
  ],
  "version": "3.9.2"
}
```

### pip information

pip 21.2.4 from /home/pawamoy/.local/lib/python3.8/site-packages/pip (python 3.8)

Not using pip to manage packages.

### OS information

GNU/Linux 5.14.6

Discussion:
codecov[bot]: # [Codecov](https://codecov.io/gh/wemake-services/wemake-python-styleguide/pull/2207?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=wemake-services) Report
> Merging [#2207](https://codecov.io/gh/wemake-services/wemake-python-styleguide/pull/2207?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=wemake-services) (69dc068) into [master](https://codecov.io/gh/wemake-services/wemake-python-styleguide/commit/6fa7d0036812050c86da9fb1f9bde6389481697e?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=wemake-services) (6fa7d00) will **not change** coverage.
> The diff coverage is `100.00%`.

[![Impacted file tree graph](https://codecov.io/gh/wemake-services/wemake-python-styleguide/pull/2207/graphs/tree.svg?width=650&height=150&src=pr&token=yMZCKw0D02&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=wemake-services)](https://codecov.io/gh/wemake-services/wemake-python-styleguide/pull/2207?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=wemake-services)

```diff
@@            Coverage Diff            @@
##            master     #2207   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files          118       118           
  Lines         6230      6209   -21     
  Branches      1386      1380    -6     
=========================================
- Hits          6230      6209   -21     
```


| [Impacted Files](https://codecov.io/gh/wemake-services/wemake-python-styleguide/pull/2207?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=wemake-services) | Coverage Δ | |
|---|---|---|
| [...ake\_python\_styleguide/violations/best\_practices.py](https://codecov.io/gh/wemake-services/wemake-python-styleguide/pull/2207/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=wemake-services#diff-d2VtYWtlX3B5dGhvbl9zdHlsZWd1aWRlL3Zpb2xhdGlvbnMvYmVzdF9wcmFjdGljZXMucHk=) | `100.00% <ø> (ø)` | |
| [...python\_styleguide/visitors/ast/naming/variables.py](https://codecov.io/gh/wemake-services/wemake-python-styleguide/pull/2207/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=wemake-services#diff-d2VtYWtlX3B5dGhvbl9zdHlsZWd1aWRlL3Zpc2l0b3JzL2FzdC9uYW1pbmcvdmFyaWFibGVzLnB5) | `100.00% <100.00%> (ø)` | |
| [wemake\_python\_styleguide/compat/nodes.py](https://codecov.io/gh/wemake-services/wemake-python-styleguide/pull/2207/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=wemake-services#diff-d2VtYWtlX3B5dGhvbl9zdHlsZWd1aWRlL2NvbXBhdC9ub2Rlcy5weQ==) | `100.00% <0.00%> (ø)` | |
| [wemake\_python\_styleguide/logic/walrus.py](https://codecov.io/gh/wemake-services/wemake-python-styleguide/pull/2207/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=wemake-services#diff-d2VtYWtlX3B5dGhvbl9zdHlsZWd1aWRlL2xvZ2ljL3dhbHJ1cy5weQ==) | `100.00% <0.00%> (ø)` | |
| [wemake\_python\_styleguide/compat/routing.py](https://codecov.io/gh/wemake-services/wemake-python-styleguide/pull/2207/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=wemake-services#diff-d2VtYWtlX3B5dGhvbl9zdHlsZWd1aWRlL2NvbXBhdC9yb3V0aW5nLnB5) | `100.00% <0.00%> (ø)` | |
| [wemake\_python\_styleguide/logic/safe\_eval.py](https://codecov.io/gh/wemake-services/wemake-python-styleguide/pull/2207/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=wemake-services#diff-d2VtYWtlX3B5dGhvbl9zdHlsZWd1aWRlL2xvZ2ljL3NhZmVfZXZhbC5weQ==) | `100.00% <0.00%> (ø)` | |
| [wemake\_python\_styleguide/compat/packaging.py](https://codecov.io/gh/wemake-services/wemake-python-styleguide/pull/2207/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=wemake-services#diff-d2VtYWtlX3B5dGhvbl9zdHlsZWd1aWRlL2NvbXBhdC9wYWNrYWdpbmcucHk=) | `100.00% <0.00%> (ø)` | |
| [wemake\_python\_styleguide/visitors/ast/functions.py](https://codecov.io/gh/wemake-services/wemake-python-styleguide/pull/2207/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=wemake-services#diff-d2VtYWtlX3B5dGhvbl9zdHlsZWd1aWRlL3Zpc2l0b3JzL2FzdC9mdW5jdGlvbnMucHk=) | `100.00% <0.00%> (ø)` | |
| [wemake\_python\_styleguide/visitors/ast/operators.py](https://codecov.io/gh/wemake-services/wemake-python-styleguide/pull/2207/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=wemake-services#diff-d2VtYWtlX3B5dGhvbl9zdHlsZWd1aWRlL3Zpc2l0b3JzL2FzdC9vcGVyYXRvcnMucHk=) | `100.00% <0.00%> (ø)` | |
| [...emake\_python\_styleguide/visitors/ast/decorators.py](https://codecov.io/gh/wemake-services/wemake-python-styleguide/pull/2207/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=wemake-services#diff-d2VtYWtlX3B5dGhvbl9zdHlsZWd1aWRlL3Zpc2l0b3JzL2FzdC9kZWNvcmF0b3JzLnB5) | `100.00% <0.00%> (ø)` | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/wemake-services/wemake-python-styleguide/pull/2207?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=wemake-services).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=wemake-services)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/wemake-services/wemake-python-styleguide/pull/2207?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=wemake-services). Last update [6fa7d00...69dc068](https://codecov.io/gh/wemake-services/wemake-python-styleguide/pull/2207?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=wemake-services). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=wemake-services).


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2aef9e81..d075cca6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -13,8 +13,8 @@ Semantic versioning in our case means:
   might not pass on `x.1.y` release because of the new checks.
 - Major releases inidicate significant milestones or serious breaking changes.
   There are no major releases right now: we are still at `0.x.y` version.
-  But, in the future we might change the configuration names / logic,
-  change the client facing API, change code conventions signigicantly, etc.
+  But, in the future we might change the configuration names/logic,
+  change the client facing API, change code conventions significantly, etc.
 
 
 ## {{ Next Version }}
@@ -27,8 +27,8 @@ Semantic versioning in our case means:
 
 ### Bugfixes
 
-- Fixes thah `InconsistentComprehensionViolation` was ignoring
-  misalligned `in` expressions #2075
+- Fixes that `InconsistentComprehensionViolation` was ignoring
+  misaligned `in` expressions #2075
 
 ### Misc
 
@@ -36,6 +36,7 @@ Semantic versioning in our case means:
 - Added example to README.md
 - Added `why strict is good`
 - Replaced all `python` with `Python` in README.md
+- Improve Docs: Fixed all typos and grammatical errors in CHANGELOG
 
 
 ## 0.15.3
@@ -81,7 +82,7 @@ Semantic versioning in our case means:
 
 - Fixes `dataclasses` import, it was failing on `python3.6`
 - Fixes `InconsistentComprehensionViolation` work with `async` comprehensions
-- Fixes nested compehensions support for `InconsistentComprehensionViolation`
+- Fixes nested comprehensions support for `InconsistentComprehensionViolation`
 - Fixes multiple `if` support for `InconsistentComprehensionViolation`
 - Fixes that `NestedTernaryViolation` was not reported for a comprehension
 - Fixes that `ConstantConditionViolation` was not reported for a comprehension
@@ -96,7 +97,7 @@ Semantic versioning in our case means:
 
 - Refactors how `tokenize` tests are executed,
   now we have an option to compile fixture code
-  to make sure it is syntatically valid
+  to make sure it is syntactically valid.
 
 
 ## 0.15.0 aka python3.9
@@ -108,7 +109,7 @@ Semantic versioning in our case means:
 - Changes how we treat own/foreign attributes,
   since now we only check assigned attribute names for `self`/`cls`/`mcs`,
   but not any other ones.
-  So, now writting `point.x = 1` will not trigger any violations.
+  So, now writing `point.x = 1` will not trigger any violations.
   Previously, it would raise "too short name".
 - Forbids using non-trivial expressions as an argument to `except`
 - Forbids using too many variables in a tuple unpacking
@@ -133,7 +134,7 @@ Semantic versioning in our case means:
 - Forbids to ignore some violations (configurable) on a line level
 - Forbids single element unpacking
 - Forbids to unpack lists with side-effects
-- Forbids to use miltiline strings except for assignments and docstrings
+- Forbids to use multiline strings except for assignments and docstrings
 - Forbids not returning anything in functions and methods starting with `get_`
 - Forbids to use empty comment
 - Forbids using bitwise operation with boolean operation
@@ -593,7 +594,7 @@ In this release we had a little focus on:
 - Adds `bellybutton` to the list of other linters
 - Documents how to use `nitpick` to sync the configuration
 - Documents how to use `flakehell` to create `baseline`s for legacy integrations
-- Improves tests for binary, octal, hex, and expanetional numbers
+- Improves tests for binary, octal, hex, and exponentional numbers
 - Adds new `xenon` CI check
 - Now handles exceptions in our own code, hope to never see them!
 - Now uses `coverage` checks in deepsource
@@ -663,7 +664,7 @@ We introduce a lot of new rules related to this topic improving:
 consistency, complexity, and general feel from your code.
 
 In this release we have ported a lot of existing `pylint` rules,
-big cudos to the developers of this wonderful tool.
+big kudos to the developers of this wonderful tool.
 
 ### Features
 
diff --git a/wemake_python_styleguide/options/config.py b/wemake_python_styleguide/options/config.py
index 99deb5e9..a6fc7c82 100644
--- a/wemake_python_styleguide/options/config.py
+++ b/wemake_python_styleguide/options/config.py
@@ -58,7 +58,7 @@ You can also show all options that ``flake8`` supports by running:
     :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`
+    :str:`wemake_python_styleguide.options.defaults.FORBIDDEN_INLINE_IGNORE`
 
 
 .. rubric:: Complexity options
diff --git a/wemake_python_styleguide/violations/best_practices.py b/wemake_python_styleguide/violations/best_practices.py
index 29cc5d7d..3b7b5b19 100644
--- a/wemake_python_styleguide/violations/best_practices.py
+++ b/wemake_python_styleguide/violations/best_practices.py
@@ -1424,13 +1424,14 @@ class ReassigningVariableToItselfViolation(ASTViolation):
         # Correct:
         some = some + 1
         x_coord, y_coord = y_coord, x_coord
+        flag = not flag
 
         # Wrong:
         some = some
         x_coord, y_coord = x_coord, y_coord
 
     .. versionadded:: 0.3.0
-    .. versionchanged:: 0.11.0
+    .. versionchanged:: 0.16.0
 
     """
 
diff --git a/wemake_python_styleguide/visitors/ast/naming/variables.py b/wemake_python_styleguide/visitors/ast/naming/variables.py
index 89543518..1870b9ce 100644
--- a/wemake_python_styleguide/visitors/ast/naming/variables.py
+++ b/wemake_python_styleguide/visitors/ast/naming/variables.py
@@ -67,6 +67,16 @@ class WrongVariableAssignmentVisitor(BaseNodeVisitor):
         self._check_unique_assignment(node, names)
         self.generic_visit(node)
 
+    def _is_reassignment_edge_case(self, node: AnyAssign) -> bool:
+        # This is not a variable, but a class property
+        if isinstance(nodes.get_context(node), ast.ClassDef):
+            return True
+
+        # It means that someone probably modifies original value
+        # of the variable using some unary operation, e.g. `a = not a`
+        # See #2189
+        return isinstance(node.value, ast.UnaryOp)
+
     def _check_reassignment(
         self,
         node: AnyAssign,
@@ -75,8 +85,8 @@ class WrongVariableAssignmentVisitor(BaseNodeVisitor):
         if not node.value:
             return
 
-        if isinstance(nodes.get_context(node), ast.ClassDef):
-            return  # This is not a variable, but a class property
+        if self._is_reassignment_edge_case(node):
+            return
 
         var_values = name_nodes.get_variables_from_node(node.value)
         if len(names) <= 1 < len(var_values):


Test patch:
diff --git a/tests/test_visitors/test_ast/test_naming/test_variable_self_reassignment.py b/tests/test_visitors/test_ast/test_naming/test_variable_self_reassignment.py
index 131e5f29..a89ceb6a 100644
--- a/tests/test_visitors/test_ast/test_naming/test_variable_self_reassignment.py
+++ b/tests/test_visitors/test_ast/test_naming/test_variable_self_reassignment.py
@@ -86,6 +86,8 @@ right_swap4 = 'dx, dy = +dy, -dx'
 right_swap5 = 'dx, dy = dy, dx'
 right_swap6 = 'dy, dx = dx, dy'
 
+right_unary1 = 'a = not a'
+right_unary2 = 'a = -a'
 
 # Wrong:
 
@@ -267,6 +269,8 @@ def test_self_variable_reassignment_triple(
     right_swap4,
     right_swap5,
     right_swap6,
+    right_unary1,
+    right_unary2,
 ])
 def test_correct_variable_reassignment(
     assert_errors,