Skip to content

Commit e8fa469

Browse files
Fix a crash on psycopg2 for elif used (#5369)
* 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. specify the confidence of the message * Remove disable for else-if-used in pylint/checkers/classes.py Co-authored-by: Daniël van Noord <[email protected]>
1 parent e3a1e35 commit e8fa469

File tree

5 files changed

+57
-41
lines changed

5 files changed

+57
-41
lines changed

ChangeLog

+5
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@ Release date: TBA
1717

1818
* Fix ``install graphiz`` message which isn't needed for puml output format.
1919

20+
* Fix a crash in the ``check_elif`` extensions where an undetected if in a comprehension
21+
with an if statement within a f-string resulted in an out of range error. The checker no
22+
longer relies on counting if statements anymore and uses known if statements locations instead.
23+
It should not crash on badly parsed if statements anymore.
24+
2025
* Fix ``simplify-boolean-expression`` when condition can be inferred as False.
2126

2227
Closes #5200

pylint/checkers/classes.py

+12-14
Original file line numberDiff line numberDiff line change
@@ -1848,20 +1848,18 @@ def _check_first_arg_for_type(self, node, metaclass=0):
18481848
"bad-mcs-method-argument",
18491849
node.name,
18501850
)
1851-
# regular class
1852-
else: # pylint: disable=else-if-used
1853-
# class method
1854-
if node.type == "classmethod" or node.name == "__class_getitem__":
1855-
self._check_first_arg_config(
1856-
first,
1857-
self.config.valid_classmethod_first_arg,
1858-
node,
1859-
"bad-classmethod-argument",
1860-
node.name,
1861-
)
1862-
# regular method without self as argument
1863-
elif first != "self":
1864-
self.add_message("no-self-argument", node=node)
1851+
# regular class with class method
1852+
elif node.type == "classmethod" or node.name == "__class_getitem__":
1853+
self._check_first_arg_config(
1854+
first,
1855+
self.config.valid_classmethod_first_arg,
1856+
node,
1857+
"bad-classmethod-argument",
1858+
node.name,
1859+
)
1860+
# regular class with regular method without self as argument
1861+
elif first != "self":
1862+
self.add_message("no-self-argument", node=node)
18651863

18661864
def _check_first_arg_config(self, first, config, node, message, method_name):
18671865
if first not in config:

pylint/extensions/check_elif.py

+14-24
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515

1616
from pylint.checkers import BaseTokenChecker
1717
from pylint.checkers.utils import check_messages
18-
from pylint.interfaces import IAstroidChecker, ITokenChecker
18+
from pylint.interfaces import HIGH, IAstroidChecker, ITokenChecker
1919

2020

2121
class ElseifUsedChecker(BaseTokenChecker):
@@ -38,37 +38,27 @@ def __init__(self, linter=None):
3838
self._init()
3939

4040
def _init(self):
41-
self._elifs = []
42-
self._if_counter = 0
41+
self._elifs = {}
4342

4443
def process_tokens(self, tokens):
45-
# Process tokens and look for 'if' or 'elif'
46-
for _, token, _, _, _ in tokens:
47-
if token == "elif":
48-
self._elifs.append(True)
49-
elif token == "if":
50-
self._elifs.append(False)
44+
"""Process tokens and look for 'if' or 'elif'"""
45+
self._elifs = {
46+
begin: token for _, token, begin, _, _ in tokens if token in {"elif", "if"}
47+
}
5148

5249
def leave_module(self, _: nodes.Module) -> None:
5350
self._init()
5451

55-
def visit_ifexp(self, node: nodes.IfExp) -> None:
56-
if isinstance(node.parent, nodes.FormattedValue):
57-
return
58-
self._if_counter += 1
59-
60-
def visit_comprehension(self, node: nodes.Comprehension) -> None:
61-
self._if_counter += len(node.ifs)
62-
6352
@check_messages("else-if-used")
6453
def visit_if(self, node: nodes.If) -> None:
65-
if isinstance(node.parent, nodes.If):
66-
orelse = node.parent.orelse
67-
# current if node must directly follow an "else"
68-
if orelse and orelse == [node]:
69-
if not self._elifs[self._if_counter]:
70-
self.add_message("else-if-used", node=node)
71-
self._if_counter += 1
54+
"""Current if node must directly follow an 'else'"""
55+
if (
56+
isinstance(node.parent, nodes.If)
57+
and node.parent.orelse == [node]
58+
and (node.lineno, node.col_offset) in self._elifs
59+
and self._elifs[(node.lineno, node.col_offset)] == "if"
60+
):
61+
self.add_message("else-if-used", node=node, confidence=HIGH)
7262

7363

7464
def register(linter):

tests/functional/ext/check_elif/check_elif.py

+22-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1+
# pylint: disable=no-else-raise,unsupported-membership-test,using-constant-test
2+
13
"""Checks use of "else if" triggers a refactor message"""
4+
from typing import Union, Sequence, Any, Mapping
25

36

47
def my_function():
@@ -7,7 +10,7 @@ def my_function():
710
if myint > 5:
811
pass
912
else:
10-
if myint <= 5: # [else-if-used]
13+
if myint <= 5: # [else-if-used]
1114
pass
1215
else:
1316
myint = 3
@@ -25,3 +28,21 @@ def my_function():
2528
if myint:
2629
pass
2730
myint = 4
31+
32+
33+
def _if_in_fstring_comprehension_with_elif(
34+
params: Union[Sequence[Any], Mapping[str, Any]]
35+
):
36+
order = {}
37+
if "z" not in "false":
38+
raise TypeError(
39+
f" {', '.join(sorted(i for i in order or () if i not in params))}"
40+
)
41+
elif "z" not in "true":
42+
pass
43+
else:
44+
if "t" not in "false": # [else-if-used]
45+
raise TypeError("d")
46+
else:
47+
if "y" in "life": # [else-if-used]
48+
print("e")
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
1-
else-if-used:10:8:my_function:"Consider using ""elif"" instead of ""else if""":HIGH
2-
else-if-used:22:20:my_function:"Consider using ""elif"" instead of ""else if""":HIGH
1+
else-if-used:13:8:my_function:"Consider using ""elif"" instead of ""else if""":HIGH
2+
else-if-used:25:20:my_function:"Consider using ""elif"" instead of ""else if""":HIGH
3+
else-if-used:44:8:_if_in_fstring_comprehension_with_elif:"Consider using ""elif"" instead of ""else if""":HIGH
4+
else-if-used:47:12:_if_in_fstring_comprehension_with_elif:"Consider using ""elif"" instead of ""else if""":HIGH

0 commit comments

Comments
 (0)