Skip to content

Commit

Permalink
Fix potential-index-error false positive when iterable contains sta…
Browse files Browse the repository at this point in the history
…rred element (#10097)
  • Loading branch information
zenlyj authored Nov 24, 2024
1 parent 68cb5b3 commit 3e9e613
Show file tree
Hide file tree
Showing 4 changed files with 46 additions and 1 deletion.
4 changes: 4 additions & 0 deletions doc/whatsnew/fragments/10076.false_positive
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Fix a false positive for `potential-index-error` when an indexed iterable
contains a starred element that evaluates to more than one item.

Closes #10076
15 changes: 14 additions & 1 deletion pylint/checkers/variables.py
Original file line number Diff line number Diff line change
Expand Up @@ -3366,6 +3366,19 @@ def visit_subscript(self, node: nodes.Subscript) -> None:

self._check_potential_index_error(node, inferred_slice)

def _inferred_iterable_length(self, iterable: nodes.Tuple | nodes.List) -> int:
length = 0
for elt in iterable.elts:
if not isinstance(elt, nodes.Starred):
length += 1
continue
unpacked = utils.safe_infer(elt.value)
if isinstance(unpacked, nodes.BaseContainer):
length += len(unpacked.elts)
else:
length += 1
return length

def _check_potential_index_error(
self,
node: nodes.Subscript,
Expand All @@ -3381,7 +3394,7 @@ def _check_potential_index_error(
# If the node.value is a Tuple or List without inference it is defined in place
if isinstance(node.value, (nodes.Tuple, nodes.List)):
# Add 1 because iterables are 0-indexed
if len(node.value.elts) < inferred_slice.value + 1:
if self._inferred_iterable_length(node.value) < inferred_slice.value + 1:
self.add_message(
"potential-index-error", node=node, confidence=INFERENCE
)
Expand Down
23 changes: 23 additions & 0 deletions tests/functional/p/potential_index_error.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,26 @@ def my_func():
# Test that we don't crash on more complicated indices/slices
# We do not raise here (currently)
print([1, 2, 3][2:3])


# Test for cases with unpacking operation
my_list = ["foo", "bar"]
my_set = {"foo", "bar"}
my_tuple = ("foo", "bar")
my_iterable = (*my_list, *my_set, *my_tuple, *("foo", "bar"))
my_non_iterable = None

print([*my_list][1])
print([*my_list][2]) # [potential-index-error]

print([*my_set][1])
print([*my_set][2]) # [potential-index-error]

print((*my_tuple,)[1])
print((*my_tuple,)[2]) # [potential-index-error]

print((*my_iterable,)[7])
print((*my_iterable,)[8]) # [potential-index-error]

print((*my_non_iterable,)[0])
print((*my_non_iterable,)[1]) # [potential-index-error]
5 changes: 5 additions & 0 deletions tests/functional/p/potential_index_error.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
potential-index-error:6:6:6:18::Invalid index for iterable length:INFERENCE
potential-index-error:7:6:7:18::Invalid index for iterable length:INFERENCE
potential-index-error:8:6:8:22::Invalid index for iterable length:INFERENCE
potential-index-error:36:6:36:19::Invalid index for iterable length:INFERENCE
potential-index-error:39:6:39:18::Invalid index for iterable length:INFERENCE
potential-index-error:42:6:42:21::Invalid index for iterable length:INFERENCE
potential-index-error:45:6:45:24::Invalid index for iterable length:INFERENCE
potential-index-error:48:6:48:28::Invalid index for iterable length:INFERENCE

0 comments on commit 3e9e613

Please sign in to comment.