From 4a4220f1c60236d3afed751f9278b5311dcbc1e4 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Fri, 13 Aug 2021 01:21:21 +0200 Subject: [PATCH 1/3] Refactor visit_comprehension --- pylint/checkers/refactoring/recommendation_checker.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pylint/checkers/refactoring/recommendation_checker.py b/pylint/checkers/refactoring/recommendation_checker.py index 0cb724985e..252ae684a4 100644 --- a/pylint/checkers/refactoring/recommendation_checker.py +++ b/pylint/checkers/refactoring/recommendation_checker.py @@ -264,6 +264,12 @@ def _check_consider_using_dict_items(self, node: astroid.For) -> None: @utils.check_messages("consider-using-dict-items") def visit_comprehension(self, node: astroid.Comprehension) -> None: + self._check_consider_using_dict_items_comprehension(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 From ba9ba45f418f94da4c5444f7aec9dfcb869dd977 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Fri, 13 Aug 2021 01:34:41 +0200 Subject: [PATCH 2/3] Add new check - use-sequence-for-iteration --- ChangeLog | 2 ++ doc/whatsnew/2.10.rst | 2 ++ .../refactoring/recommendation_checker.py | 28 +++++++++++++++++-- .../u/use/use_sequence_for_iteration.py | 16 +++++++++++ .../u/use/use_sequence_for_iteration.txt | 4 +++ 5 files changed, 49 insertions(+), 3 deletions(-) create mode 100644 tests/functional/u/use/use_sequence_for_iteration.py create mode 100644 tests/functional/u/use/use_sequence_for_iteration.txt diff --git a/ChangeLog b/ChangeLog index fae693a3d4..ecfe42ff0c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -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? =========================== diff --git a/doc/whatsnew/2.10.rst b/doc/whatsnew/2.10.rst index 585ac8cc74..d13099989d 100644 --- a/doc/whatsnew/2.10.rst +++ b/doc/whatsnew/2.10.rst @@ -28,6 +28,8 @@ New checkers Closes #3692 +* Added ``use-sequence-for-iteration``: Emitted when iterating over an in-place defined ``set``. + Other Changes ============= diff --git a/pylint/checkers/refactoring/recommendation_checker.py b/pylint/checkers/refactoring/recommendation_checker.py index 252ae684a4..b7bda57548 100644 --- a/pylint/checkers/refactoring/recommendation_checker.py +++ b/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 @@ -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 @@ -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.""" @@ -262,9 +273,13 @@ 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 @@ -291,3 +306,10 @@ def _check_consider_using_dict_items_comprehension( 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) diff --git a/tests/functional/u/use/use_sequence_for_iteration.py b/tests/functional/u/use/use_sequence_for_iteration.py new file mode 100644 index 0000000000..2dd1feb187 --- /dev/null +++ b/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] diff --git a/tests/functional/u/use/use_sequence_for_iteration.txt b/tests/functional/u/use/use_sequence_for_iteration.txt new file mode 100644 index 0000000000..615df8a304 --- /dev/null +++ b/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 From 941dd78d91850471cea0cac51581cd51eaae4cd7 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Fri, 13 Aug 2021 01:49:49 +0200 Subject: [PATCH 3/3] Fix existing tests --- .../ext/code_style/code_style_consider_using_tuple.py | 4 ++-- .../ext/code_style/code_style_consider_using_tuple.txt | 2 ++ tests/functional/u/undefined/undefined_loop_variable.py | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/functional/ext/code_style/code_style_consider_using_tuple.py b/tests/functional/ext/code_style/code_style_consider_using_tuple.py index 679ef77d2d..1bf06d82c5 100644 --- a/tests/functional/ext/code_style/code_style_consider_using_tuple.py +++ b/tests/functional/ext/code_style/code_style_consider_using_tuple.py @@ -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)] @@ -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] diff --git a/tests/functional/ext/code_style/code_style_consider_using_tuple.txt b/tests/functional/ext/code_style/code_style_consider_using_tuple.txt index df796b683e..622aeb33eb 100644 --- a/tests/functional/ext/code_style/code_style_consider_using_tuple.txt +++ b/tests/functional/ext/code_style/code_style_consider_using_tuple.txt @@ -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 diff --git a/tests/functional/u/undefined/undefined_loop_variable.py b/tests/functional/u/undefined/undefined_loop_variable.py index 3173727491..3df17f7d15 100644 --- a/tests/functional/u/undefined/undefined_loop_variable.py +++ b/tests/functional/u/undefined/undefined_loop_variable.py @@ -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