Skip to content

Commit

Permalink
Merge pull request #4835 from cdce8p/feature-sequence_for_iteration
Browse files Browse the repository at this point in the history
Add `use-sequence-for-iteration` checker
  • Loading branch information
cdce8p committed Aug 13, 2021
2 parents 4f57320 + 941dd78 commit 61b4db0
Show file tree
Hide file tree
Showing 8 changed files with 60 additions and 6 deletions.
2 changes: 2 additions & 0 deletions ChangeLog
Expand Up @@ -118,6 +118,8 @@ Release date: TBA

Closes #4828

* Added ``use-sequence-for-iteration``: Emitted when iterating over an in-place defined ``set``.


What's New in Pylint 2.9.6?
===========================
Expand Down
2 changes: 2 additions & 0 deletions doc/whatsnew/2.10.rst
Expand Up @@ -28,6 +28,8 @@ New checkers

Closes #3692

* Added ``use-sequence-for-iteration``: Emitted when iterating over an in-place defined ``set``.


Other Changes
=============
Expand Down
34 changes: 31 additions & 3 deletions pylint/checkers/refactoring/recommendation_checker.py
@@ -1,6 +1,6 @@
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/PyCQA/pylint/blob/main/LICENSE
from typing import cast
from typing import Union, cast

import astroid

Expand Down Expand Up @@ -43,6 +43,12 @@ class RecommendationChecker(checkers.BaseChecker):
"str.split(sep, maxsplit=1)[0] or str.rsplit(sep, maxsplit=1)[-1] "
"instead.",
),
"C0208": (
"Use a sequence type when iterating over values",
"use-sequence-for-iteration",
"When iterating over values, sequence types (e.g., ``lists``, ``tuples``, ``ranges``) "
"are more efficient than ``sets``.",
),
}

@staticmethod
Expand Down Expand Up @@ -134,10 +140,15 @@ def _check_use_maxsplit_arg(self, node: astroid.Call) -> None:
)
self.add_message("use-maxsplit-arg", node=node, args=(new_name,))

@utils.check_messages("consider-using-enumerate", "consider-using-dict-items")
@utils.check_messages(
"consider-using-enumerate",
"consider-using-dict-items",
"use-sequence-for-iteration",
)
def visit_for(self, node: astroid.For) -> None:
self._check_consider_using_enumerate(node)
self._check_consider_using_dict_items(node)
self._check_use_sequence_for_iteration(node)

def _check_consider_using_enumerate(self, node: astroid.For) -> None:
"""Emit a convention whenever range and len are used for indexing."""
Expand Down Expand Up @@ -262,8 +273,18 @@ def _check_consider_using_dict_items(self, node: astroid.For) -> None:
self.add_message("consider-using-dict-items", node=node)
return

@utils.check_messages("consider-using-dict-items")
@utils.check_messages(
"consider-using-dict-items",
"use-sequence-for-iteration",
)
def visit_comprehension(self, node: astroid.Comprehension) -> None:
self._check_consider_using_dict_items_comprehension(node)
self._check_use_sequence_for_iteration(node)

def _check_consider_using_dict_items_comprehension(
self, node: astroid.Comprehension
) -> None:
"""Add message when accessing dict values by index lookup."""
iterating_object_name = utils.get_iterating_dictionary_name(node)
if iterating_object_name is None:
return
Expand All @@ -285,3 +306,10 @@ def visit_comprehension(self, node: astroid.Comprehension) -> None:

self.add_message("consider-using-dict-items", node=node)
return

def _check_use_sequence_for_iteration(
self, node: Union[astroid.For, astroid.Comprehension]
) -> None:
"""Check if code iterates over an in-place defined set."""
if isinstance(node.iter, astroid.Set):
self.add_message("use-sequence-for-iteration", node=node.iter)
Expand Up @@ -12,7 +12,7 @@
(x for x in var)
(x for x in (1, 2, 3))
(x for x in [1, 2, 3]) # [consider-using-tuple]
(x for x in {1, 2, 3}) # [consider-using-tuple]
(x for x in {1, 2, 3}) # [consider-using-tuple,use-sequence-for-iteration]

[x for x in var]
[x for x in (1, 2, 3)]
Expand All @@ -26,4 +26,4 @@
pass

[x for x in [*var, 2]]
[x for x in {*var, 2}]
[x for x in {*var, 2}] # [use-sequence-for-iteration]
@@ -1,4 +1,6 @@
consider-using-tuple:9:9::Consider using an in-place tuple instead of list
consider-using-tuple:14:12::Consider using an in-place tuple instead of list
consider-using-tuple:15:12::Consider using an in-place tuple instead of set
use-sequence-for-iteration:15:12::Use a sequence type when iterating over values
consider-using-tuple:19:12::Consider using an in-place tuple instead of list
use-sequence-for-iteration:29:12::Use a sequence type when iterating over values
2 changes: 1 addition & 1 deletion tests/functional/u/undefined/undefined_loop_variable.py
Expand Up @@ -44,7 +44,7 @@ def do_stuff_with_a_list():


def do_stuff_with_a_set():
for var in {1, 2, 3}:
for var in {1, 2, 3}: # pylint: disable=use-sequence-for-iteration
pass
return var

Expand Down
16 changes: 16 additions & 0 deletions tests/functional/u/use/use_sequence_for_iteration.py
@@ -0,0 +1,16 @@
# pylint: disable=missing-docstring,pointless-statement,unnecessary-comprehension

var = {1, 2, 3}

for x in var:
pass
for x in {1, 2, 3}: # [use-sequence-for-iteration]
pass

(x for x in var)
(x for x in {1, 2, 3}) # [use-sequence-for-iteration]

[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]
4 changes: 4 additions & 0 deletions tests/functional/u/use/use_sequence_for_iteration.txt
@@ -0,0 +1,4 @@
use-sequence-for-iteration:7:9::Use a sequence type when iterating over values:HIGH
use-sequence-for-iteration:11:12::Use a sequence type when iterating over values:HIGH
use-sequence-for-iteration:14:12::Use a sequence type when iterating over values:HIGH
use-sequence-for-iteration:16:12::Use a sequence type when iterating over values:HIGH

0 comments on commit 61b4db0

Please sign in to comment.