Skip to content

Commit 65efb67

Browse files
[fix] Better approach in 'unnecessary-list-index-lookup' to avoid crashes (#10511) (#10512)
Closes #10510
1 parent 78444bb commit 65efb67

File tree

3 files changed

+26
-13
lines changed

3 files changed

+26
-13
lines changed
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 & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2452,18 +2452,20 @@ def _enumerate_with_start(
24522452
return False, confidence
24532453

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