Repository: wemake-services/wemake-python-styleguide

Issue:
Useless % and ^ operators
# Bug report

<!--
Hi, thanks for submitting a bug. We appreciate that.

But, we will need some information about what's wrong to help you.
-->

## What's wrong

<!-- Describe what is not working. Please, attach a traceback. -->

This code does not raise any violations:`

```python
3 ^ 1
3 ^ 0
3 % 1
3 % 0
```

## How is that should be

<!-- Describe how it should work. -->

It should raise:
- useless operations for `3 ^ 1`, `3 ^ 0` and `3 % 1`
- zero division for `3 % 0`

<!--

  Love wemake-python-styleguide? ❤️
  Do you want your bugs to be solved faster?

  Please consider supporting our collective:
👉  https://opencollective.com/wemake-python-styleguide/donate

-->



Discussion:
hello @sobolevn 
I am new to all this, and would very much like to work on this issue
can you please guide me and tell how to get started
I would love to! @SHITIZ-AGGARWAL 

Start with reading our contribution guide: https://github.com/wemake-services/wemake-python-styleguide/blob/master/CONTRIBUTING.md

We need to add these new cases here: https://github.com/wemake-services/wemake-python-styleguide/blob/master/wemake_python_styleguide/visitors/ast/operators.py#L40

And test them here: https://github.com/wemake-services/wemake-python-styleguide/blob/master/tests/test_visitors/test_ast/test_operators/test_useless_math.py
Hey, can I take this one? Thanks.
Yes, sure!

Resolving patch:
diff --git a/wemake_python_styleguide/violations/consistency.py b/wemake_python_styleguide/violations/consistency.py
--- a/wemake_python_styleguide/violations/consistency.py
+++ b/wemake_python_styleguide/violations/consistency.py
@@ -1718,12 +1718,12 @@ class BadComplexNumberSuffixViolation(TokenizeViolation):
 @final
 class ZeroDivisionViolation(ASTViolation):
     """
-    Forbids to explicitly divide by zero.
+    Forbids to explicitly divide(or take modulo) by zero.
 
     Reasoning:
         This will just throw ``ZeroDivisionError``
         in case that's what you need: just throw it.
-        No need to use undefined meth behaviours.
+        No need to use undefined math behaviours.
         Or it might be just a typo / mistake, then fix it.
 
     Solution:
@@ -1736,8 +1736,10 @@ class ZeroDivisionViolation(ASTViolation):
 
         # Wrong:
         1 / 0
+        1 % 0
 
     .. versionadded:: 0.12.0
+    .. versionchanged: 0.15.0
 
     """
 
@@ -1756,9 +1758,11 @@ class MeaninglessNumberOperationViolation(ASTViolation):
         Multipling by zero is also redundant:
         it can be replaced with explicit ``0`` assign.
         Multiplying and dividing by ``1`` is also meaningless.
+        Likewise, using ``|`` or ``^`` with ``0``, and using
+        the ``%`` operator with ``1`` are unnecessary.
 
     Solution:
-        Remove useless zero operations.
+        Remove useless operations.
 
     Example::
 
@@ -1766,13 +1770,18 @@ class MeaninglessNumberOperationViolation(ASTViolation):
         number = 1
         zero = 0
         one = 1
+        three = 3
 
         # Wrong:
         number = 1 + 0 * 1
         zero = some * 0 / 1
         one = some ** 0 ** 1
+        three = 3 ^ 0
+        three = 3 | 0
+        three = 3 % 1
 
     .. versionadded:: 0.12.0
+    .. versionchanged:: 0.15.0
 
     """
 
diff --git a/wemake_python_styleguide/visitors/ast/operators.py b/wemake_python_styleguide/visitors/ast/operators.py
--- a/wemake_python_styleguide/visitors/ast/operators.py
+++ b/wemake_python_styleguide/visitors/ast/operators.py
@@ -38,14 +38,13 @@ class UselessOperatorsVisitor(base.BaseNodeVisitor):
     }
 
     _meaningless_operations: ClassVar[_MeaninglessOperators] = {
-        # ast.Div is not in the list,
+        # ast.Div and ast.Mod is not in the list,
         # since we have a special violation for it.
         0: (
             ast.Mult,
             ast.Add,
             ast.Sub,
             ast.Pow,
-            ast.Mod,
 
             ast.BitAnd,
             ast.BitOr,
@@ -71,6 +70,7 @@ class UselessOperatorsVisitor(base.BaseNodeVisitor):
     _zero_divisors: ClassVar[AnyNodes] = (
         ast.Div,
         ast.FloorDiv,
+        ast.Mod,
     )
 
     def visit_numbers_and_constants(self, node: _NumbersAndConstants) -> None:


Test patch:
diff --git a/tests/test_visitors/test_ast/test_operators/test_useless_math.py b/tests/test_visitors/test_ast/test_operators/test_useless_math.py
--- a/tests/test_visitors/test_ast/test_operators/test_useless_math.py
+++ b/tests/test_visitors/test_ast/test_operators/test_useless_math.py
@@ -2,7 +2,6 @@
 
 from wemake_python_styleguide.violations.consistency import (
     MeaninglessNumberOperationViolation,
-    ZeroDivisionViolation,
 )
 from wemake_python_styleguide.visitors.ast.operators import (
     UselessOperatorsVisitor,
@@ -11,60 +10,12 @@
 usage_template = 'constant {0}'
 
 
-@pytest.mark.parametrize('expression', [
-    '/= 0',
-    '/= 0.0',
-    '/= -0',
-    '/= -0.0',
-    '/= -0b0',
-    '/= 0b0',
-    '/= 0x0',
-    '/= -0x0',
-    '/= 0o0',
-    '/= -0o0',
-    '/= 0e0',
-    '/= -0e0',
-    '//= 0',
-
-    '/ 0',
-    '/ 0.0',
-    '/ -0',
-    '/ -0.0',
-    '/ -0b0',
-    '/ 0b0',
-    '/ 0x0',
-    '/ -0x0',
-    '/ 0o0',
-    '/ -0o0',
-    '/ 0e0',
-    '/ -0e0',
-    '// 0',
-
-    '* other / 0',
-    '/ 0 * other',
-])
-def test_zero_div(
-    assert_errors,
-    parse_ast_tree,
-    expression,
-    default_options,
-):
-    """Testing that there is no useless zero div."""
-    tree = parse_ast_tree(usage_template.format(expression))
-
-    visitor = UselessOperatorsVisitor(default_options, tree=tree)
-    visitor.run()
-
-    assert_errors(visitor, [ZeroDivisionViolation])
-
-
 @pytest.mark.parametrize('expression', [
     # Math ops:
     '*= 0',
     '**= -0.0',
     '+= 0e0',
     '-= -0b0',
-    '%= 0',
 
     '* 0',
     '** -0.0',
@@ -80,6 +31,7 @@ def test_zero_div(
     '* 0b1',
     '** 1',
     '/ 1.0',
+    '% 0o1',
 
     '* other / 1.0',
     '* 1 * other',
@@ -103,7 +55,7 @@ def test_meaningless_math(
     expression,
     default_options,
 ):
-    """Testing that there is no useless zero div."""
+    """Testing that meaningless number operations are forbidden."""
     tree = parse_ast_tree(usage_template.format(expression))
 
     visitor = UselessOperatorsVisitor(default_options, tree=tree)
@@ -112,47 +64,6 @@ def test_meaningless_math(
     assert_errors(visitor, [MeaninglessNumberOperationViolation])
 
 
-@pytest.mark.parametrize('expression', [
-    # Div, but not with zero:
-    '/= 0.1',
-    '/= -1.0',
-    '/= -10',
-    '/= -100.0',
-    '/= 0b101',
-    '/= -1e0',
-    '//= 9',
-
-    '/ 10',
-    '/ 0.01',
-    '/ -0.2',
-    '/ -0x11',
-
-    # variables:
-    '* var',
-    '- var',
-    '/ var',
-    '+ var',
-    '*= var',
-    '-= var',
-    '/= var',
-    '+= var',
-    '%= var',
-])
-def test_correct_zero_div(
-    assert_errors,
-    parse_ast_tree,
-    expression,
-    default_options,
-):
-    """Testing that there is no useless zero div."""
-    tree = parse_ast_tree(usage_template.format(expression))
-
-    visitor = UselessOperatorsVisitor(default_options, tree=tree)
-    visitor.run()
-
-    assert_errors(visitor, [])
-
-
 @pytest.mark.parametrize('expression', [
     # Math ops:
     '*= -1',
@@ -176,12 +87,12 @@ def test_correct_zero_div(
     '<< 1',
     '| 0b1',
     '^ 2',
+    '^= 0x1',
     '& -1',
 
     '>>= 10',
     '<<= 1',
     '|= 0b1',
-    '^= 0x1',
     '&= -0o1',
 ])
 def test_useful_math(
@@ -190,7 +101,7 @@ def test_useful_math(
     expression,
     default_options,
 ):
-    """Testing that there is no useless zero div."""
+    """Testing that useful math operations are allowed."""
     tree = parse_ast_tree(usage_template.format(expression))
 
     visitor = UselessOperatorsVisitor(default_options, tree=tree)
diff --git a/tests/test_visitors/test_ast/test_operators/test_zero_div.py b/tests/test_visitors/test_ast/test_operators/test_zero_div.py
new file mode 100644
--- /dev/null
+++ b/tests/test_visitors/test_ast/test_operators/test_zero_div.py
@@ -0,0 +1,173 @@
+import pytest
+
+from wemake_python_styleguide.violations.consistency import (
+    ZeroDivisionViolation,
+)
+from wemake_python_styleguide.visitors.ast.operators import (
+    UselessOperatorsVisitor,
+)
+
+usage_template = 'constant {0}'
+
+
+@pytest.mark.parametrize('expression', [
+    '/= 0',
+    '/= 0.0',
+    '/= -0',
+    '/= -0.0',
+    '/= -0b0',
+    '/= 0b0',
+    '/= 0x0',
+    '/= -0x0',
+    '/= 0o0',
+    '/= -0o0',
+    '/= 0e0',
+    '/= -0e0',
+    '//= 0',
+
+    '/ 0',
+    '/ 0.0',
+    '/ -0',
+    '/ -0.0',
+    '/ -0b0',
+    '/ 0b0',
+    '/ 0x0',
+    '/ -0x0',
+    '/ 0o0',
+    '/ -0o0',
+    '/ 0e0',
+    '/ -0e0',
+    '// 0',
+
+    '* other / 0',
+    '/ 0 * other',
+])
+def test_zero_div(
+    assert_errors,
+    parse_ast_tree,
+    expression,
+    default_options,
+):
+    """Testing that divisions by zero are forbidden."""
+    tree = parse_ast_tree(usage_template.format(expression))
+
+    visitor = UselessOperatorsVisitor(default_options, tree=tree)
+    visitor.run()
+
+    assert_errors(visitor, [ZeroDivisionViolation])
+
+
+@pytest.mark.parametrize('expression', [
+    '%= 0',
+    '%= 0.0',
+    '%= -0',
+    '%= -0.0',
+    '%= -0b0',
+    '%= 0b0',
+    '%= 0x0',
+    '%= -0x0',
+    '%= 0o0',
+    '%= -0o0',
+    '%= 0e0',
+    '%= -0e0',
+
+    '% 0',
+    '% 0.0',
+    '% -0',
+    '% -0.0',
+    '% -0b0',
+    '% 0b0',
+    '% 0x0',
+    '% -0x0',
+    '% 0o0',
+    '% -0o0',
+    '% 0e0',
+    '% -0e0',
+
+    '* other % 0',
+    '% 0 * other',
+])
+def test_zero_mod(
+    assert_errors,
+    parse_ast_tree,
+    expression,
+    default_options,
+):
+    """Testing that useless modulus with zero are forbidden."""
+    tree = parse_ast_tree(usage_template.format(expression))
+
+    visitor = UselessOperatorsVisitor(default_options, tree=tree)
+    visitor.run()
+
+    assert_errors(visitor, [ZeroDivisionViolation])
+
+
+@pytest.mark.parametrize('expression', [
+    # Div, but not with zero:
+    '/= 0.1',
+    '/= -1.0',
+    '/= -10',
+    '/= -100.0',
+    '/= 0b101',
+    '/= -1e0',
+    '//= 9',
+
+    '/ 10',
+    '/ 0.01',
+    '/ -0.2',
+    '/ -0x11',
+
+    # variables:
+    '* var',
+    '- var',
+    '/ var',
+    '+ var',
+    '% var',
+    '*= var',
+    '-= var',
+    '/= var',
+    '+= var',
+    '%= var',
+])
+def test_correct_zero_div(
+    assert_errors,
+    parse_ast_tree,
+    expression,
+    default_options,
+):
+    """Testing that non-zero divisions are allowed."""
+    tree = parse_ast_tree(usage_template.format(expression))
+
+    visitor = UselessOperatorsVisitor(default_options, tree=tree)
+    visitor.run()
+
+    assert_errors(visitor, [])
+
+
+@pytest.mark.parametrize('expression', [
+    # Mod, but not with zero:
+    '%= 0.1',
+    '%= -1.0',
+    '%= -10',
+    '%= -100.0',
+    '%= 0b101',
+    '%= -1e0',
+
+    '% 10',
+    '% 0.01',
+    '% -0.2',
+    '% -0x11',
+])
+def test_correct_zero_mod(
+    assert_errors,
+    parse_ast_tree,
+    expression,
+    default_options,
+):
+    """Testing that non-zero modulus operations are allowed."""
+    tree = parse_ast_tree(usage_template.format(expression))
+
+    visitor = UselessOperatorsVisitor(default_options, tree=tree)
+    visitor.run()
+
+    assert_errors(visitor, [])