Repository: wemake-services/wemake-python-styleguide

Issue:
WPS204: Overused expression should not count -1
### What's wrong

WPS complains about -1 being used to often as overused expression. Example: `position = (0, -1)`

### How it should be

No violation should be raised.

See https://github.com/wemake-services/wemake-python-styleguide/issues/1753

### Flake8 version and plugins

{
  "dependencies": [],
  "platform": {
    "python_implementation": "CPython",
    "python_version": "3.9.10",
    "system": "Linux"
  },
  "plugins": [
    {
      "is_local": false,
      "plugin": "flake8-bandit",
      "version": "2.1.2"
    },
    {
      "is_local": false,
      "plugin": "flake8-broken-line",
      "version": "0.4.0"
    },
    {
      "is_local": false,
      "plugin": "flake8-bugbear",
      "version": "21.9.2"
    },
    {
      "is_local": false,
      "plugin": "flake8-comprehensions",
      "version": "3.7.0"
    },
    {
      "is_local": false,
      "plugin": "flake8-darglint",
      "version": "1.8.1"
    },
    {
      "is_local": false,
      "plugin": "flake8-debugger",
      "version": "4.0.0"
    },
    {
      "is_local": false,
      "plugin": "flake8-docstrings",
      "version": "1.6.0, pydocstyle: 6.1.1"
    },
    {
      "is_local": false,
      "plugin": "flake8-eradicate",
      "version": "1.2.0"
    },
    {
      "is_local": false,
      "plugin": "flake8-string-format",
      "version": "0.3.0"
    },
    {
      "is_local": false,
      "plugin": "flake8_commas",
      "version": "2.1.0"
    },
    {
      "is_local": false,
      "plugin": "flake8_isort",
      "version": "4.1.1"
    },
       {
      "is_local": false,
      "plugin": "flake8_quotes",
      "version": "3.3.1"
    },
    {
      "is_local": false,
      "plugin": "mccabe",
      "version": "0.6.1"
    },
    {
      "is_local": false,
      "plugin": "naming",
      "version": "0.11.1"
    },
    {
      "is_local": false,
      "plugin": "pycodestyle",
      "version": "2.8.0"
    },
    {
      "is_local": false,
      "plugin": "pyflakes",
      "version": "2.4.0"
    },
    {
      "is_local": false,
      "plugin": "rst-docstrings",
      "version": "0.2.3"
    },
    {
      "is_local": false,
      "plugin": "wemake_python_styleguide",
      "version": "0.16.0"
    }
  ],
  "version": "4.0.1"
}


### pip information

please be more specific about package versions you actually need

### OS information

Ubuntu 20.04 LTS


Discussion:
Totally! Would you like to send a PR? 🙂 
I looked at it, and it looks doable. The easy solution would be to exclude -1 here: https://github.com/wemake-services/wemake-python-styleguide/pull/1766

This explicitely names -1 as something that should be named: https://github.com/wemake-services/wemake-python-styleguide/blob/master/wemake_python_styleguide/logic/complexity/overuses.py#L105 – but I don't think so. We often use -1 for indexing the last element (ie. exlude the last element of a list) and thus is okay to use, such as 0 and 1.

I would like to remove this line: https://github.com/wemake-services/wemake-python-styleguide/blob/master/wemake_python_styleguide/logic/complexity/overuses.py#L111 (disallowing constants). But I have the following question: I think -200 and other negative constants should be caught by a different check; that constants should be named, is that correct? If yes, then we can safely remove that additional check.

I just had a look, and indeed -200 is flagged as "Found magic number". Then I will create a PR to remove the constant checking. 

Resolving patch:
diff --git a/wemake_python_styleguide/logic/complexity/overuses.py b/wemake_python_styleguide/logic/complexity/overuses.py
--- a/wemake_python_styleguide/logic/complexity/overuses.py
+++ b/wemake_python_styleguide/logic/complexity/overuses.py
@@ -102,12 +102,12 @@ def is_unary_minus(node: ast.AST) -> bool:
     We use this predicate to allow values
     like ``-some_value`` to be overused.
 
-    Although negative constants like ``-1``
+    Although negative constants like ``-5``
     should raise violation to force naming them.
     """
-    if isinstance(node, ast.UnaryOp):
-        return (
-            isinstance(node.op, ast.USub) and
-            not isinstance(node.operand, (Constant, ast.Num))
-        )
+    if isinstance(node, ast.UnaryOp) and isinstance(node.op, ast.USub):
+        # We allow variables, attributes, subscripts, and `-1`
+        if isinstance(node.operand, (Constant, ast.Num)):
+            return node.operand.n == 1
+        return True
     return False


Test patch:
diff --git a/tests/test_visitors/test_ast/test_complexity/test_overuses/test_overused_expressions.py b/tests/test_visitors/test_ast/test_complexity/test_overuses/test_overused_expressions.py
--- a/tests/test_visitors/test_ast/test_complexity/test_overuses/test_overused_expressions.py
+++ b/tests/test_visitors/test_ast/test_complexity/test_overuses/test_overused_expressions.py
@@ -134,8 +134,8 @@ def method2(self, arg2: "List[int]") -> 'type.Any':
     '{a for a in other}',
 
     # Unary nodes:
-    '-1',  # unary minus operator is raising when used with literal
     '+some',  # unary plus always raises
+    '~other',
 
     # Special cases for self:
     'self.method([star])',  # `[star]` is raising
@@ -169,6 +169,8 @@ def method2(self, arg2: "List[int]") -> 'type.Any':
     'some.prop',
     # unary operator
     '-value',
+    '-value.attr',
+    '-1',
 )