Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add B020 check to find for-loop control variable overiding iter set #220

Merged
merged 10 commits into from Jan 27, 2022
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Expand Up @@ -63,3 +63,6 @@ target/
.ipynb_checkpoints

.vscode

# JetBrains
.idea/
2 changes: 2 additions & 0 deletions README.rst
Expand Up @@ -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.
Korben11 marked this conversation as resolved.
Show resolved Hide resolved


Opinionated warnings
~~~~~~~~~~~~~~~~~~~~
Expand Down
18 changes: 18 additions & 0 deletions bugbear.py
Expand Up @@ -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):
Expand Down Expand Up @@ -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.

Expand Down Expand Up @@ -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."
Korben11 marked this conversation as resolved.
Show resolved Hide resolved
)

# Warnings disabled by default.
B901 = Error(
Expand Down
17 changes: 17 additions & 0 deletions tests/b020.py
@@ -0,0 +1,17 @@
items = [1, 2, 3]
Korben11 marked this conversation as resolved.
Show resolved Hide resolved

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=}")
Korben11 marked this conversation as resolved.
Show resolved Hide resolved
13 changes: 13 additions & 0 deletions tests/test_bugbear.py
Expand Up @@ -29,6 +29,7 @@
B016,
B017,
B018,
B020,
B901,
B902,
B903,
Expand Down Expand Up @@ -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",)),
Korben11 marked this conversation as resolved.
Show resolved Hide resolved
),
)

def test_b901(self):
filename = Path(__file__).absolute().parent / "b901.py"
bbc = BugBearChecker(filename=str(filename))
Expand Down