summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog5
-rw-r--r--pylint/checkers/classes.py26
-rw-r--r--pylint/extensions/check_elif.py38
-rw-r--r--tests/functional/ext/check_elif/check_elif.py23
-rw-r--r--tests/functional/ext/check_elif/check_elif.txt6
5 files changed, 57 insertions, 41 deletions
diff --git a/ChangeLog b/ChangeLog
index 6c81c2e6a..30b15d0ea 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -17,6 +17,11 @@ Release date: TBA
* Fix ``install graphiz`` message which isn't needed for puml output format.
+* Fix a crash in the ``check_elif`` extensions where an undetected if in a comprehension
+ with an if statement within a f-string resulted in an out of range error. The checker no
+ longer relies on counting if statements anymore and uses known if statements locations instead.
+ It should not crash on badly parsed if statements anymore.
+
* Fix ``simplify-boolean-expression`` when condition can be inferred as False.
Closes #5200
diff --git a/pylint/checkers/classes.py b/pylint/checkers/classes.py
index 319a37f73..37651cdfd 100644
--- a/pylint/checkers/classes.py
+++ b/pylint/checkers/classes.py
@@ -1848,20 +1848,18 @@ a metaclass class method.",
"bad-mcs-method-argument",
node.name,
)
- # regular class
- else: # pylint: disable=else-if-used
- # class method
- if node.type == "classmethod" or node.name == "__class_getitem__":
- self._check_first_arg_config(
- first,
- self.config.valid_classmethod_first_arg,
- node,
- "bad-classmethod-argument",
- node.name,
- )
- # regular method without self as argument
- elif first != "self":
- self.add_message("no-self-argument", node=node)
+ # regular class with class method
+ elif node.type == "classmethod" or node.name == "__class_getitem__":
+ self._check_first_arg_config(
+ first,
+ self.config.valid_classmethod_first_arg,
+ node,
+ "bad-classmethod-argument",
+ node.name,
+ )
+ # regular class with regular method without self as argument
+ elif first != "self":
+ self.add_message("no-self-argument", node=node)
def _check_first_arg_config(self, first, config, node, message, method_name):
if first not in config:
diff --git a/pylint/extensions/check_elif.py b/pylint/extensions/check_elif.py
index 8dcfb74b3..aa4457748 100644
--- a/pylint/extensions/check_elif.py
+++ b/pylint/extensions/check_elif.py
@@ -15,7 +15,7 @@ from astroid import nodes
from pylint.checkers import BaseTokenChecker
from pylint.checkers.utils import check_messages
-from pylint.interfaces import IAstroidChecker, ITokenChecker
+from pylint.interfaces import HIGH, IAstroidChecker, ITokenChecker
class ElseifUsedChecker(BaseTokenChecker):
@@ -38,37 +38,27 @@ class ElseifUsedChecker(BaseTokenChecker):
self._init()
def _init(self):
- self._elifs = []
- self._if_counter = 0
+ self._elifs = {}
def process_tokens(self, tokens):
- # Process tokens and look for 'if' or 'elif'
- for _, token, _, _, _ in tokens:
- if token == "elif":
- self._elifs.append(True)
- elif token == "if":
- self._elifs.append(False)
+ """Process tokens and look for 'if' or 'elif'"""
+ self._elifs = {
+ begin: token for _, token, begin, _, _ in tokens if token in {"elif", "if"}
+ }
def leave_module(self, _: nodes.Module) -> None:
self._init()
- def visit_ifexp(self, node: nodes.IfExp) -> None:
- if isinstance(node.parent, nodes.FormattedValue):
- return
- self._if_counter += 1
-
- def visit_comprehension(self, node: nodes.Comprehension) -> None:
- self._if_counter += len(node.ifs)
-
@check_messages("else-if-used")
def visit_if(self, node: nodes.If) -> None:
- if isinstance(node.parent, nodes.If):
- orelse = node.parent.orelse
- # current if node must directly follow an "else"
- if orelse and orelse == [node]:
- if not self._elifs[self._if_counter]:
- self.add_message("else-if-used", node=node)
- self._if_counter += 1
+ """Current if node must directly follow an 'else'"""
+ if (
+ isinstance(node.parent, nodes.If)
+ and node.parent.orelse == [node]
+ and (node.lineno, node.col_offset) in self._elifs
+ and self._elifs[(node.lineno, node.col_offset)] == "if"
+ ):
+ self.add_message("else-if-used", node=node, confidence=HIGH)
def register(linter):
diff --git a/tests/functional/ext/check_elif/check_elif.py b/tests/functional/ext/check_elif/check_elif.py
index b9722f349..f03c76839 100644
--- a/tests/functional/ext/check_elif/check_elif.py
+++ b/tests/functional/ext/check_elif/check_elif.py
@@ -1,4 +1,7 @@
+# pylint: disable=no-else-raise,unsupported-membership-test,using-constant-test
+
"""Checks use of "else if" triggers a refactor message"""
+from typing import Union, Sequence, Any, Mapping
def my_function():
@@ -7,7 +10,7 @@ def my_function():
if myint > 5:
pass
else:
- if myint <= 5: # [else-if-used]
+ if myint <= 5: # [else-if-used]
pass
else:
myint = 3
@@ -25,3 +28,21 @@ def my_function():
if myint:
pass
myint = 4
+
+
+def _if_in_fstring_comprehension_with_elif(
+ params: Union[Sequence[Any], Mapping[str, Any]]
+):
+ order = {}
+ if "z" not in "false":
+ raise TypeError(
+ f" {', '.join(sorted(i for i in order or () if i not in params))}"
+ )
+ elif "z" not in "true":
+ pass
+ else:
+ if "t" not in "false": # [else-if-used]
+ raise TypeError("d")
+ else:
+ if "y" in "life": # [else-if-used]
+ print("e")
diff --git a/tests/functional/ext/check_elif/check_elif.txt b/tests/functional/ext/check_elif/check_elif.txt
index 43d1e3b1e..88853eb95 100644
--- a/tests/functional/ext/check_elif/check_elif.txt
+++ b/tests/functional/ext/check_elif/check_elif.txt
@@ -1,2 +1,4 @@
-else-if-used:10:8:my_function:"Consider using ""elif"" instead of ""else if""":HIGH
-else-if-used:22:20:my_function:"Consider using ""elif"" instead of ""else if""":HIGH
+else-if-used:13:8:my_function:"Consider using ""elif"" instead of ""else if""":HIGH
+else-if-used:25:20:my_function:"Consider using ""elif"" instead of ""else if""":HIGH
+else-if-used:44:8:_if_in_fstring_comprehension_with_elif:"Consider using ""elif"" instead of ""else if""":HIGH
+else-if-used:47:12:_if_in_fstring_comprehension_with_elif:"Consider using ""elif"" instead of ""else if""":HIGH