Repository: wemake-services/wemake-python-styleguide

Issue:
WPS000 on several code fragments
### What's wrong

If I run WPS on such code fragments, then the error WPS000 appears, although the code is correct.

**Fragment №1:**
```python
number = int(input())
if number == 1:
    print("1")
else:
    if number == 2: print("2")
```

**Output №1:**
```
Traceback (most recent call last):
  File "/home/ilya/Desktop/venv-wps-15.2/lib/python3.8/site-packages/wemake_python_styleguide/checker.py", line 154, in run
    visitor.run()
  File "/home/ilya/Desktop/venv-wps-15.2/lib/python3.8/site-packages/wemake_python_styleguide/visitors/base.py", line 285, in run
    self.visit(token)
  File "/home/ilya/Desktop/venv-wps-15.2/lib/python3.8/site-packages/wemake_python_styleguide/visitors/base.py", line 279, in visit
    method(token)
  File "/home/ilya/Desktop/venv-wps-15.2/lib/python3.8/site-packages/wemake_python_styleguide/visitors/tokenize/conditions.py", line 56, in visit_name
    self._check_implicit_elif(token)
  File "/home/ilya/Desktop/venv-wps-15.2/lib/python3.8/site-packages/wemake_python_styleguide/visitors/tokenize/conditions.py", line 135, in _check_implicit_elif
    self._check_complex_else(next_tokens, next_token, index)
  File "/home/ilya/Desktop/venv-wps-15.2/lib/python3.8/site-packages/wemake_python_styleguide/visitors/tokenize/conditions.py", line 110, in _check_complex_else
    complex_else = self._if_has_code_below(tokens[index + 1:])
  File "/home/ilya/Desktop/venv-wps-15.2/lib/python3.8/site-packages/wemake_python_styleguide/visitors/tokenize/conditions.py", line 89, in _if_has_code_below
    while remaining_tokens[index - 1].exact_type != tokenize.INDENT:
IndexError: list index out of range

../df_test.py:0:1: WPS000 Internal error happened, see log. Please, take some time to report it
../df_test.py:1:1: D100 Missing docstring in public module
../df_test.py:1:14: WPS421 Found wrong function call: input
../df_test.py:3:5: WPS421 Found wrong function call: print
../df_test.py:3:11: Q000 Remove bad quotes
../df_test.py:5:19: E701 multiple statements on one line (colon)
../df_test.py:5:21: WPS421 Found wrong function call: print
../df_test.py:5:27: Q000 Remove bad quotes
```

**Fragment №2:**
```python
def is_prime(number: int):
    for i in range(2, number):
        if number % i == 0:
            return False
    else:
        if number != 1:
            return True
    return False
```

**Output №2:**
```
Traceback (most recent call last):
  File "/home/ilya/Desktop/venv-wps-15.2/lib/python3.8/site-packages/wemake_python_styleguide/checker.py", line 154, in run
    visitor.run()
  File "/home/ilya/Desktop/venv-wps-15.2/lib/python3.8/site-packages/wemake_python_styleguide/visitors/base.py", line 191, in run
    self.visit(self.tree)
  File "/home/ilya/Desktop/venv-wps-15.2/lib/python3.8/site-packages/wemake_python_styleguide/visitors/base.py", line 186, in visit
    return route_visit(self, tree)
  File "/home/ilya/Desktop/venv-wps-15.2/lib/python3.8/site-packages/wemake_python_styleguide/compat/routing.py", line 36, in route_visit
    return getattr(
  File "/usr/lib/python3.8/ast.py", line 379, in generic_visit
    self.visit(item)
  File "/home/ilya/Desktop/venv-wps-15.2/lib/python3.8/site-packages/wemake_python_styleguide/visitors/base.py", line 186, in visit
    return route_visit(self, tree)
  File "/home/ilya/Desktop/venv-wps-15.2/lib/python3.8/site-packages/wemake_python_styleguide/compat/routing.py", line 36, in route_visit
    return getattr(
  File "/usr/lib/python3.8/ast.py", line 379, in generic_visit
    self.visit(item)
  File "/home/ilya/Desktop/venv-wps-15.2/lib/python3.8/site-packages/wemake_python_styleguide/visitors/base.py", line 186, in visit
    return route_visit(self, tree)
  File "/home/ilya/Desktop/venv-wps-15.2/lib/python3.8/site-packages/wemake_python_styleguide/compat/routing.py", line 36, in route_visit
    return getattr(
  File "/usr/lib/python3.8/ast.py", line 379, in generic_visit
    self.visit(item)
  File "/home/ilya/Desktop/venv-wps-15.2/lib/python3.8/site-packages/wemake_python_styleguide/visitors/base.py", line 186, in visit
    return route_visit(self, tree)
  File "/home/ilya/Desktop/venv-wps-15.2/lib/python3.8/site-packages/wemake_python_styleguide/compat/routing.py", line 36, in route_visit
    return getattr(
  File "/home/ilya/Desktop/venv-wps-15.2/lib/python3.8/site-packages/wemake_python_styleguide/visitors/ast/conditions.py", line 77, in visit_any_if
    self._check_simplifiable_returning_if(node)
  File "/home/ilya/Desktop/venv-wps-15.2/lib/python3.8/site-packages/wemake_python_styleguide/visitors/ast/conditions.py", line 115, in _check_simplifiable_returning_if
    next_index_in_parent = body.index(node) + 1
ValueError: <_ast.If object at 0x7fa0e1305f40> is not in list

../df_test.py:0:1: WPS000 Internal error happened, see log. Please, take some time to report it
../df_test.py:1:1: D100 Missing docstring in public module
../df_test.py:1:1: D103 Missing docstring in public function
../df_test.py:2:5: WPS500 Found `else` in a loop without `break`
../df_test.py:2:5: WPS503 Found useless returning `else` statement
../df_test.py:2:9: WPS111 Found too short name: i < 2
```

I also checked this problem occurs on version 0.15.3 and in wps-light-0.15.2.

### How it should be

I expect there should be no WPS000 error on these fragments. :slightly_smiling_face:

### Flake8 version and plugins

```json
{
  "dependencies": [],
  "platform": {
    "python_implementation": "CPython",
    "python_version": "3.8.10",
    "system": "Linux"
  },
  "plugins": [
    {
      "is_local": false,
      "plugin": "flake8-bandit",
      "version": "2.1.2"
    },
    {
      "is_local": false,
      "plugin": "flake8-broken-line",
      "version": "0.3.0"
    },
    {
      "is_local": false,
      "plugin": "flake8-bugbear",
      "version": "20.11.1"
    },
    {
      "is_local": false,
      "plugin": "flake8-comprehensions",
      "version": "3.5.0"
    },
    {
      "is_local": false,
      "plugin": "flake8-darglint",
      "version": "1.8.0"
    },
    {
      "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.1.0"
    },
    {
      "is_local": false,
      "plugin": "flake8-string-format",
      "version": "0.3.0"
    },
    {
      "is_local": false,
      "plugin": "flake8_commas",
      "version": "2.0.0"
    },
    {
      "is_local": false,
      "plugin": "flake8_isort",
      "version": "4.0.0"
    },
    {
      "is_local": false,
      "plugin": "flake8_quotes",
      "version": "3.2.0"
    },
    {
      "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.7.0"
    },
    {
      "is_local": false,
      "plugin": "pyflakes",
      "version": "2.3.1"
    },
    {
      "is_local": false,
      "plugin": "rst-docstrings",
      "version": "0.0.14"
    },
    {
      "is_local": false,
      "plugin": "wemake_python_styleguide",
      "version": "0.15.2"
    }
  ],
  "version": "3.9.2"
}
```

### pip information

```
pip 20.0.2 from /home/ilya/Desktop/venv-wps-15.2/lib/python3.8/site-packages/pip (python 3.8)
astor==0.8.1
attrs==21.2.0
bandit==1.7.0
darglint==1.8.0
docutils==0.17.1
eradicate==2.0.0
flake8==3.9.2
flake8-bandit==2.1.2
flake8-broken-line==0.3.0
flake8-bugbear==20.11.1
flake8-commas==2.0.0
flake8-comprehensions==3.5.0
flake8-debugger==4.0.0
flake8-docstrings==1.6.0
flake8-eradicate==1.1.0
flake8-isort==4.0.0
flake8-polyfill==1.0.2
flake8-quotes==3.2.0
flake8-rst-docstrings==0.0.14
flake8-string-format==0.3.0
gitdb==4.0.7
GitPython==3.1.18
isort==5.9.3
mccabe==0.6.1
pbr==5.6.0
pep8-naming==0.11.1
pycodestyle==2.7.0
pydocstyle==6.1.1
pyflakes==2.3.1
Pygments==2.9.0
PyYAML==5.4.1
restructuredtext-lint==1.3.2
six==1.16.0
smmap==4.0.0
snowballstemmer==2.1.0
stevedore==3.3.0
testfixtures==6.18.0
typing-extensions==3.10.0.0
wemake-python-styleguide==0.15.2
```

### OS information

```
NAME="Ubuntu"
VERSION="20.04.2 LTS (Focal Fossa)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 20.04.2 LTS"
VERSION_ID="20.04"
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
VERSION_CODENAME=focal
UBUNTU_CODENAME=focal
```


Discussion:
Hi! I'm new here, but I would like to take a look at this issue. Would it be okay if I try to solve it?
@danielboghean sure! You can start with the very first code sample: copy it to some file, run `flake8` on it.
It will reproduce the bug. Then, solve it and write a regression test case. Done! 

Resolving patch:
diff --git a/wemake_python_styleguide/visitors/tokenize/conditions.py b/wemake_python_styleguide/visitors/tokenize/conditions.py
--- a/wemake_python_styleguide/visitors/tokenize/conditions.py
+++ b/wemake_python_styleguide/visitors/tokenize/conditions.py
@@ -38,6 +38,11 @@ class IfElseVisitor(BaseTokenVisitor):
 
     """
 
+    _idents: ClassVar[FrozenSet[int]] = frozenset((
+        tokenize.INDENT,
+        tokenize.DEDENT,
+    ))
+
     _allowed_token_types: ClassVar[FrozenSet[int]] = frozenset((
         tokenize.NEWLINE,
         tokenize.NL,
@@ -46,14 +51,23 @@ class IfElseVisitor(BaseTokenVisitor):
     ))
 
     def visit_name(self, token: tokenize.TokenInfo) -> None:
-        """
-        Checks that ``if`` nodes are defined correctly.
+        """Checks that ``if`` nodes are defined correctly."""
+        self._check_implicit_elif(token)
 
-        Raises:
-            ImplicitElifViolation
+    def _check_implicit_elif(self, token: tokenize.TokenInfo) -> None:
+        token_index = self.file_tokens.index(token)
 
-        """
-        self._check_implicit_elif(token)
+        if self._is_invalid_token(token_index, token):
+            return
+
+        # There's a bug in coverage, I am not sure how to make it work.
+        next_tokens = self.file_tokens[token_index + 1:]
+        for index, next_token in enumerate(next_tokens):  # pragma: no cover
+            if next_token.exact_type in self._allowed_token_types:
+                continue
+            elif next_token.string == 'if':
+                self._check_complex_else(next_tokens, next_token, index)
+            return
 
     def _does_else_belong_to_if(self, start_index: int) -> bool:
         previous_token = self.file_tokens[start_index - 1]
@@ -85,12 +99,13 @@ def _if_has_code_below(
         more code to be considered so as not to throw an incorrect violation.
         """
         index = 1
-
-        while remaining_tokens[index - 1].exact_type != tokenize.INDENT:
+        while remaining_tokens[index - 1].exact_type not in self._idents:
             index += 1
 
-        context_count = 1
+        if len(remaining_tokens) == index + 1:
+            return False
 
+        context_count = 1
         while context_count:
             next_token = remaining_tokens[index]
             if next_token.exact_type == tokenize.INDENT:
@@ -117,20 +132,4 @@ def _is_invalid_token(self, index: int, token: tokenize.TokenInfo) -> bool:
         # `else` token can belong also to `for` and `try/except` statement,
         # which can trigger false positive for that violation.
         belongs_to_if = self._does_else_belong_to_if(index)
-
         return is_not_else or not belongs_to_if
-
-    def _check_implicit_elif(self, token: tokenize.TokenInfo) -> None:
-        token_index = self.file_tokens.index(token)
-
-        if self._is_invalid_token(token_index, token):
-            return
-
-        # There's a bug in coverage, I am not sure how to make it work.
-        next_tokens = self.file_tokens[token_index + 1:]
-        for index, next_token in enumerate(next_tokens):  # pragma: no cover
-            if next_token.exact_type in self._allowed_token_types:
-                continue
-            elif next_token.string == 'if':
-                self._check_complex_else(next_tokens, next_token, index)
-            return


Test patch:
diff --git a/tests/test_visitors/test_tokenize/test_conditions/test_implict_elif_oneline.py b/tests/test_visitors/test_tokenize/test_conditions/test_implict_elif_oneline.py
new file mode 100644
--- /dev/null
+++ b/tests/test_visitors/test_tokenize/test_conditions/test_implict_elif_oneline.py
@@ -0,0 +1,48 @@
+from wemake_python_styleguide.violations.refactoring import (
+    ImplicitElifViolation,
+)
+from wemake_python_styleguide.visitors.tokenize.conditions import IfElseVisitor
+
+code_that_breaks = """
+number = int(input())
+if number == 1:
+    print("1")
+else:
+    if number == 2: print("2")
+"""
+
+code_implict_if_else = """
+if numbers:
+    my_print('first')
+else:
+    if numbers:
+        my_print('other')
+"""
+
+
+def test_if_else_one_line(
+    assert_errors,
+    default_options,
+    parse_tokens,
+):
+    """Testing to see if ``if`` statement code is on one line."""
+    tokens = parse_tokens(code_that_breaks)
+
+    visitor = IfElseVisitor(default_options, tokens)
+    visitor.run()
+
+    assert_errors(visitor, [ImplicitElifViolation])
+
+
+def test_if_else_implict(
+    assert_errors,
+    default_options,
+    parse_tokens,
+):
+    """Testing to make sure implicit if works."""
+    tokens = parse_tokens(code_implict_if_else)
+
+    visitor = IfElseVisitor(default_options, tokens)
+    visitor.run()
+
+    assert_errors(visitor, [ImplicitElifViolation])