Skip to content

Commit 621ecf6

Browse files
Fix potential-index-error false positive when iterable contains starred element (#10097) (#10098)
(cherry picked from commit 3e9e613) Co-authored-by: Zen Lee <[email protected]>
1 parent 5597aae commit 621ecf6

File tree

4 files changed

+46
-1
lines changed

4 files changed

+46
-1
lines changed

Diff for: doc/whatsnew/fragments/10076.false_positive

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Fix a false positive for `potential-index-error` when an indexed iterable
2+
contains a starred element that evaluates to more than one item.
3+
4+
Closes #10076

Diff for: pylint/checkers/variables.py

+14-1
Original file line numberDiff line numberDiff line change
@@ -3356,6 +3356,19 @@ def visit_subscript(self, node: nodes.Subscript) -> None:
33563356

33573357
self._check_potential_index_error(node, inferred_slice)
33583358

3359+
def _inferred_iterable_length(self, iterable: nodes.Tuple | nodes.List) -> int:
3360+
length = 0
3361+
for elt in iterable.elts:
3362+
if not isinstance(elt, nodes.Starred):
3363+
length += 1
3364+
continue
3365+
unpacked = utils.safe_infer(elt.value)
3366+
if isinstance(unpacked, nodes.BaseContainer):
3367+
length += len(unpacked.elts)
3368+
else:
3369+
length += 1
3370+
return length
3371+
33593372
def _check_potential_index_error(
33603373
self, node: nodes.Subscript, inferred_slice: nodes.NodeNG | None
33613374
) -> None:
@@ -3369,7 +3382,7 @@ def _check_potential_index_error(
33693382
# If the node.value is a Tuple or List without inference it is defined in place
33703383
if isinstance(node.value, (nodes.Tuple, nodes.List)):
33713384
# Add 1 because iterables are 0-indexed
3372-
if len(node.value.elts) < inferred_slice.value + 1:
3385+
if self._inferred_iterable_length(node.value) < inferred_slice.value + 1:
33733386
self.add_message(
33743387
"potential-index-error", node=node, confidence=INFERENCE
33753388
)

Diff for: tests/functional/p/potential_index_error.py

+23
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,26 @@ def my_func():
2323
# Test that we don't crash on more complicated indices/slices
2424
# We do not raise here (currently)
2525
print([1, 2, 3][2:3])
26+
27+
28+
# Test for cases with unpacking operation
29+
my_list = ["foo", "bar"]
30+
my_set = {"foo", "bar"}
31+
my_tuple = ("foo", "bar")
32+
my_iterable = (*my_list, *my_set, *my_tuple, *("foo", "bar"))
33+
my_non_iterable = None
34+
35+
print([*my_list][1])
36+
print([*my_list][2]) # [potential-index-error]
37+
38+
print([*my_set][1])
39+
print([*my_set][2]) # [potential-index-error]
40+
41+
print((*my_tuple,)[1])
42+
print((*my_tuple,)[2]) # [potential-index-error]
43+
44+
print((*my_iterable,)[7])
45+
print((*my_iterable,)[8]) # [potential-index-error]
46+
47+
print((*my_non_iterable,)[0])
48+
print((*my_non_iterable,)[1]) # [potential-index-error]

Diff for: tests/functional/p/potential_index_error.txt

+5
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
11
potential-index-error:6:6:6:18::Invalid index for iterable length:INFERENCE
22
potential-index-error:7:6:7:18::Invalid index for iterable length:INFERENCE
33
potential-index-error:8:6:8:22::Invalid index for iterable length:INFERENCE
4+
potential-index-error:36:6:36:19::Invalid index for iterable length:INFERENCE
5+
potential-index-error:39:6:39:18::Invalid index for iterable length:INFERENCE
6+
potential-index-error:42:6:42:21::Invalid index for iterable length:INFERENCE
7+
potential-index-error:45:6:45:24::Invalid index for iterable length:INFERENCE
8+
potential-index-error:48:6:48:28::Invalid index for iterable length:INFERENCE

0 commit comments

Comments
 (0)