Skip to content

Commit a52dd58

Browse files
[fix] Better approach in 'unnecessary-list-index-lookup' to avoid crashes (#10511)
Closes #10510
1 parent a49e11a commit a52dd58

File tree

3 files changed

+26
-12
lines changed

3 files changed

+26
-12
lines changed

doc/whatsnew/fragments/10510.bugfix

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
Fixed crash in 'unnecessary-list-index-lookup' when starting an enumeration using
2+
minus the length of an iterable inside a dict comprehension when the len call was only
3+
made in this dict comprehension, and not elsewhere. Also changed the approach,
4+
to use inference in all cases but the simple ones, so we don't have to fix crashes
5+
one by one for arbitrarily complex expressions in enumerate.
6+
7+
Closes #10510

pylint/checkers/refactoring/refactoring_checker.py

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2450,17 +2450,20 @@ def _enumerate_with_start(
24502450
return False, confidence
24512451

24522452
def _get_start_value(self, node: nodes.NodeNG) -> tuple[int | None, Confidence]:
2453-
if isinstance(node, (nodes.Name, nodes.Call, nodes.Attribute)) or (
2454-
isinstance(node, nodes.UnaryOp)
2455-
and isinstance(node.operand, (nodes.Attribute, nodes.Name))
2456-
):
2457-
inferred = utils.safe_infer(node)
2458-
# inferred can be an astroid.base.Instance as in 'enumerate(x, int(y))' or
2459-
# not correctly inferred (None)
2460-
start_val = inferred.value if isinstance(inferred, nodes.Const) else None
2461-
return start_val, INFERENCE
2462-
if isinstance(node, nodes.UnaryOp):
2463-
return node.operand.value, HIGH
2453+
# Most common use cases are a constant integer or minus a constant integer. We
2454+
# don't need inference for that. If that's not the case, we assume arbitrary
2455+
# complexity and we use inference.
24642456
if isinstance(node, nodes.Const):
24652457
return node.value, HIGH
2466-
return None, HIGH
2458+
if isinstance(node, nodes.UnaryOp) and isinstance(node.operand, nodes.Const):
2459+
return node.operand.value, HIGH
2460+
inferred = utils.safe_infer(node)
2461+
if isinstance(inferred, nodes.Const):
2462+
return inferred.value, INFERENCE
2463+
# inferred can be an 'astroid.base.Instance' in 'enumerate(x, int(y))',
2464+
# for example. We're doing nothing in this case for now, as extracting
2465+
# the value is costly.
2466+
2467+
# At this point the most likely cases is that the node is uninferable
2468+
# But we don't have to check if it's actually uninferable.
2469+
return None, INFERENCE

tests/functional/u/unnecessary/unnecessary_list_index_lookup.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,3 +167,7 @@ def random_uninferrable_start(pears):
167167

168168
for _, _ in enumerate(pears, random.choice([5, 42])):
169169
...
170+
171+
# Regression test for https://github.com/pylint-dev/pylint/issues/10510
172+
xs = [1, 2, 3]
173+
test_dict = {j: i for i, j in enumerate(xs, -len(xs))}

0 commit comments

Comments
 (0)