From 89bdb6536deae0c94f4b7804395c554bbeecdb51 Mon Sep 17 00:00:00 2001 From: Juraj Date: Tue, 25 Jan 2022 17:11:52 +0100 Subject: [PATCH 01/10] ignore JetBrains files --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 1dc5815..fca188e 100644 --- a/.gitignore +++ b/.gitignore @@ -63,3 +63,6 @@ target/ .ipynb_checkpoints .vscode + +# JetBrains +.idea/ From 70e92c8745c3f0cfaaafa73cc42bc9eac406cdc5 Mon Sep 17 00:00:00 2001 From: Juraj Date: Tue, 25 Jan 2022 17:38:10 +0100 Subject: [PATCH 02/10] Implement rule B020: for-loop control variable overrides iter set --- README.rst | 2 ++ bugbear.py | 18 ++++++++++++++++++ tests/b020.py | 17 +++++++++++++++++ tests/test_bugbear.py | 13 +++++++++++++ 4 files changed, 50 insertions(+) create mode 100644 tests/b020.py diff --git a/README.rst b/README.rst index 2ecc7b8..22a1bdb 100644 --- a/README.rst +++ b/README.rst @@ -132,6 +132,8 @@ data available in ``ex``. **B018**: Found useless expression. Either assign it to a variable or remove it. +**B020**: Loop control variable overrides iter set. + Opinionated warnings ~~~~~~~~~~~~~~~~~~~~ diff --git a/bugbear.py b/bugbear.py index b4fcf89..d398279 100644 --- a/bugbear.py +++ b/bugbear.py @@ -333,6 +333,7 @@ def visit_Assign(self, node): def visit_For(self, node): self.check_for_b007(node) + self.check_for_b020(node) self.generic_visit(node) def visit_Assert(self, node): @@ -506,6 +507,20 @@ def check_for_b017(self, node): ): self.errors.append(B017(node.lineno, node.col_offset)) + def check_for_b020(self, node): + targets = NameFinder() + targets.visit(node.target) + ctrl_names = set(targets.names) + + iterset = NameFinder() + iterset.visit(node.iter) + iterset_names = set(iterset.names) + + for name in sorted(ctrl_names): + if name in iterset_names: + n = targets.names[name][0] + self.errors.append(B020(n.lineno, n.col_offset, vars=(name,))) + def check_for_b904(self, node): """Checks `raise` without `from` inside an `except` clause. @@ -871,6 +886,9 @@ def visit(self, node): "B018 Found useless expression. Either assign it to a variable or remove it." ) ) +B020 = Error( + message="B020 Found for-loop with each item variable reassigning set variable." +) # Warnings disabled by default. B901 = Error( diff --git a/tests/b020.py b/tests/b020.py new file mode 100644 index 0000000..1f6e6e5 --- /dev/null +++ b/tests/b020.py @@ -0,0 +1,17 @@ +items = [1, 2, 3] + +for items in items: + print(items) + +items = [1, 2, 3] + +for item in items: + print(item) + +values = {"secret": 123} + +for key, value in values.items(): + print(f"{key=}, {value=}") + +for key, values in values.items(): + print(f"{key=}, {values=}") diff --git a/tests/test_bugbear.py b/tests/test_bugbear.py index d02b637..699d3d3 100644 --- a/tests/test_bugbear.py +++ b/tests/test_bugbear.py @@ -29,6 +29,7 @@ B016, B017, B018, + B020, B901, B902, B903, @@ -249,6 +250,18 @@ def test_b018_classes(self): expected.append(B018(33, 4)) self.assertEqual(errors, self.errors(*expected)) + def test_b020(self): + filename = Path(__file__).absolute().parent / "b020.py" + bbc = BugBearChecker(filename=str(filename)) + errors = list(bbc.run()) + self.assertEqual( + errors, + self.errors( + B020(3, 4, vars=("items",)), + B020(16, 9, vars=("values",)), + ), + ) + def test_b901(self): filename = Path(__file__).absolute().parent / "b901.py" bbc = BugBearChecker(filename=str(filename)) From 5e01e13c016c2f6a6c89ba6d4ab7d4a139a047b1 Mon Sep 17 00:00:00 2001 From: Korben11 Date: Wed, 26 Jan 2022 11:35:12 +0100 Subject: [PATCH 03/10] Update tests/b020.py Co-authored-by: Cooper Lees --- tests/b020.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/b020.py b/tests/b020.py index 1f6e6e5..bbedb71 100644 --- a/tests/b020.py +++ b/tests/b020.py @@ -11,7 +11,7 @@ values = {"secret": 123} for key, value in values.items(): - print(f"{key=}, {value=}") + print(f"{key}, {value}") for key, values in values.items(): - print(f"{key=}, {values=}") + print(f"{key}, {values}") From d16bea26f73a87cc11e6af6713c0a10ba942f3dd Mon Sep 17 00:00:00 2001 From: Korben11 Date: Wed, 26 Jan 2022 11:35:32 +0100 Subject: [PATCH 04/10] Update README.rst Co-authored-by: Cooper Lees --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 22a1bdb..1aa03ef 100644 --- a/README.rst +++ b/README.rst @@ -132,7 +132,7 @@ data available in ``ex``. **B018**: Found useless expression. Either assign it to a variable or remove it. -**B020**: Loop control variable overrides iter set. +**B020**: Loop control variable overrides iterable it iterates Opinionated warnings From f0d36c83b588cf349b4fd15bbe8c78c5521df326 Mon Sep 17 00:00:00 2001 From: Korben11 Date: Wed, 26 Jan 2022 11:35:42 +0100 Subject: [PATCH 05/10] Update bugbear.py Co-authored-by: Cooper Lees --- bugbear.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bugbear.py b/bugbear.py index d398279..5d58a79 100644 --- a/bugbear.py +++ b/bugbear.py @@ -887,7 +887,7 @@ def visit(self, node): ) ) B020 = Error( - message="B020 Found for-loop with each item variable reassigning set variable." + message="B020 Found for loop that reassigns the iterable it is iterating with each iterable value." ) # Warnings disabled by default. From 14664dd6fb38252da494a08ca1080a99a59d50ee Mon Sep 17 00:00:00 2001 From: Korben11 Date: Wed, 26 Jan 2022 11:35:49 +0100 Subject: [PATCH 06/10] Update tests/b020.py Co-authored-by: Cooper Lees --- tests/b020.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/b020.py b/tests/b020.py index bbedb71..df30e75 100644 --- a/tests/b020.py +++ b/tests/b020.py @@ -1,3 +1,8 @@ +""" +Should emit: +B020 - on lines 8 and 21 +""" + items = [1, 2, 3] for items in items: From 2bc85989f94ef9edbc17ff144c3d5b16a5f60551 Mon Sep 17 00:00:00 2001 From: Korben11 Date: Wed, 26 Jan 2022 11:35:56 +0100 Subject: [PATCH 07/10] Update tests/test_bugbear.py Co-authored-by: Cooper Lees --- tests/test_bugbear.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_bugbear.py b/tests/test_bugbear.py index 699d3d3..7e25386 100644 --- a/tests/test_bugbear.py +++ b/tests/test_bugbear.py @@ -257,8 +257,8 @@ def test_b020(self): self.assertEqual( errors, self.errors( - B020(3, 4, vars=("items",)), - B020(16, 9, vars=("values",)), + B020(8, 4, vars=("items",)), + B020(21, 9, vars=("values",)), ), ) From b2004f06cc821fc7318c1cb14d8d42f589a2f8df Mon Sep 17 00:00:00 2001 From: Korben11 Date: Thu, 27 Jan 2022 10:54:58 +0100 Subject: [PATCH 08/10] Update bugbear.py Co-authored-by: Cooper Lees --- bugbear.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/bugbear.py b/bugbear.py index 5d58a79..177af11 100644 --- a/bugbear.py +++ b/bugbear.py @@ -887,7 +887,10 @@ def visit(self, node): ) ) B020 = Error( - message="B020 Found for loop that reassigns the iterable it is iterating with each iterable value." + message=( + "B020 Found for loop that reassigns the iterable it is iterating " + + "with each iterable value." + ) ) # Warnings disabled by default. From 13cb20322d3f1628a5783162217e3337338e055d Mon Sep 17 00:00:00 2001 From: Juraj Date: Thu, 27 Jan 2022 11:04:19 +0100 Subject: [PATCH 09/10] update change log --- README.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.rst b/README.rst index 1aa03ef..11cf368 100644 --- a/README.rst +++ b/README.rst @@ -247,6 +247,11 @@ MIT Change Log ---------- +22.1.27 +~~~~~~~~~~ + +* B020: ensure loop control variable doesn't overrides iterable it iterates (#220) + 22.1.11 ~~~~~~~~~~ From 5bbe8a9a695eede688cb24b70f62654f7ca2f718 Mon Sep 17 00:00:00 2001 From: Korben11 Date: Thu, 27 Jan 2022 22:58:47 +0100 Subject: [PATCH 10/10] Update README.rst Co-authored-by: Cooper Lees --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 11cf368..09e87df 100644 --- a/README.rst +++ b/README.rst @@ -247,7 +247,7 @@ MIT Change Log ---------- -22.1.27 +Unreleased ~~~~~~~~~~ * B020: ensure loop control variable doesn't overrides iterable it iterates (#220)