Skip to content

Commit

Permalink
Fix use-sequence-for-iteration when unpacking a set with * (#7975) (
Browse files Browse the repository at this point in the history
#7988)

Co-authored-by: Dani Alcala <[email protected]>
  • Loading branch information
jacobtylerwalls and clavedeluna authored Dec 26, 2022
1 parent c3d7b39 commit e907020
Show file tree
Hide file tree
Showing 7 changed files with 46 additions and 11 deletions.
3 changes: 3 additions & 0 deletions doc/whatsnew/fragments/5788.false_positive
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Fix ``use-sequence-for-iteration`` when unpacking a set with ``*``.

Closes #5788
14 changes: 11 additions & 3 deletions pylint/checkers/refactoring/recommendation_checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

from pylint import checkers
from pylint.checkers import utils
from pylint.interfaces import HIGH


class RecommendationChecker(checkers.BaseChecker):
Expand Down Expand Up @@ -326,9 +327,16 @@ def _check_consider_using_dict_items_comprehension(
def _check_use_sequence_for_iteration(
self, node: nodes.For | nodes.Comprehension
) -> None:
"""Check if code iterates over an in-place defined set."""
if isinstance(node.iter, nodes.Set):
self.add_message("use-sequence-for-iteration", node=node.iter)
"""Check if code iterates over an in-place defined set.
Sets using `*` are not considered in-place.
"""
if isinstance(node.iter, nodes.Set) and not any(
utils.has_starred_node_recursive(node)
):
self.add_message(
"use-sequence-for-iteration", node=node.iter, confidence=HIGH
)

@utils.only_required_for_messages("consider-using-f-string")
def visit_const(self, node: nodes.Const) -> None:
Expand Down
14 changes: 14 additions & 0 deletions pylint/checkers/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -1961,6 +1961,20 @@ def find_assigned_names_recursive(
yield from find_assigned_names_recursive(elt)


def has_starred_node_recursive(
node: nodes.For | nodes.Comprehension | nodes.Set,
) -> Iterator[bool]:
"""Yield ``True`` if a Starred node is found recursively."""
if isinstance(node, nodes.Starred):
yield True
elif isinstance(node, nodes.Set):
for elt in node.elts:
yield from has_starred_node_recursive(elt)
elif isinstance(node, (nodes.For, nodes.Comprehension)):
for elt in node.iter.elts:
yield from has_starred_node_recursive(elt)


def is_hashable(node: nodes.NodeNG) -> bool:
"""Return whether any inferred value of `node` is hashable.
Expand Down
2 changes: 1 addition & 1 deletion tests/functional/ext/code_style/cs_consider_using_tuple.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,4 @@

# Don't emit warning for sets as this is handled by builtin checker
(x for x in {1, 2, 3}) # [use-sequence-for-iteration]
[x for x in {*var, 2}] # [use-sequence-for-iteration]
[x for x in {*var, 2}]
3 changes: 1 addition & 2 deletions tests/functional/ext/code_style/cs_consider_using_tuple.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,4 @@ consider-using-tuple:18:12:18:21::Consider using an in-place tuple instead of li
consider-using-tuple:21:9:21:15::Consider using an in-place tuple instead of list:UNDEFINED
consider-using-tuple:23:9:23:18::Consider using an in-place tuple instead of list:UNDEFINED
consider-using-tuple:26:12:26:21::Consider using an in-place tuple instead of list:UNDEFINED
use-sequence-for-iteration:30:12:30:21::Use a sequence type when iterating over values:UNDEFINED
use-sequence-for-iteration:31:12:31:21::Use a sequence type when iterating over values:UNDEFINED
use-sequence-for-iteration:30:12:30:21::Use a sequence type when iterating over values:HIGH
14 changes: 13 additions & 1 deletion tests/functional/u/use/use_sequence_for_iteration.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,16 @@
[x for x in var]
[x for x in {1, 2, 3}] # [use-sequence-for-iteration]

[x for x in {*var, 4}] # [use-sequence-for-iteration]
[x for x in {*var, 4}]

def deduplicate(list_in):
for thing in {*list_in}:
print(thing)

def deduplicate_two_lists(input1, input2):
for thing in {*input1, *input2}:
print(thing)

def deduplicate_nested_sets(input1, input2, input3, input4):
for thing in {{*input1, *input2}, {*input3, *input4}}:
print(thing)
7 changes: 3 additions & 4 deletions tests/functional/u/use/use_sequence_for_iteration.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
use-sequence-for-iteration:7:9:7:18::Use a sequence type when iterating over values:UNDEFINED
use-sequence-for-iteration:11:12:11:21::Use a sequence type when iterating over values:UNDEFINED
use-sequence-for-iteration:14:12:14:21::Use a sequence type when iterating over values:UNDEFINED
use-sequence-for-iteration:16:12:16:21::Use a sequence type when iterating over values:UNDEFINED
use-sequence-for-iteration:7:9:7:18::Use a sequence type when iterating over values:HIGH
use-sequence-for-iteration:11:12:11:21::Use a sequence type when iterating over values:HIGH
use-sequence-for-iteration:14:12:14:21::Use a sequence type when iterating over values:HIGH

0 comments on commit e907020

Please sign in to comment.