Repository: PyCQA/flake8-bugbear

Issue:
Proposed Check: same variable name of list and interating item in for-loop
I've already placed a question [here](https://stackoverflow.com/questions/70827394/is-there-pep-rule-or-pre-commit-check-to-prevent-same-name-of-variable-and-list). Then found this project.

```
items = [1, 2, 3]
for items in items:
    continue
# items == 3
```
This typo accidentally happened to me and caused a bug.  
Would be nice to catch it with something like: `Bxxx for-loop with the same variable name of the list and interating item found.`

Comments:

Patch:
diff --git a/.gitignore b/.gitignore
index 1dc5815..fca188e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -63,3 +63,6 @@ target/
 .ipynb_checkpoints
 
 .vscode
+
+# JetBrains
+.idea/
diff --git a/README.rst b/README.rst
index 2ecc7b8..09e87df 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 iterable it iterates
+
 
 Opinionated warnings
 ~~~~~~~~~~~~~~~~~~~~
@@ -245,6 +247,11 @@ MIT
 Change Log
 ----------
 
+Unreleased
+~~~~~~~~~~
+
+* B020: ensure loop control variable doesn't overrides iterable it iterates (#220)
+
 22.1.11
 ~~~~~~~~~~
 
diff --git a/bugbear.py b/bugbear.py
index b4fcf89..177af11 100644
--- a/bugbear.py
+++ b/bugbear.py
@@ -333,6 +333,7 @@ class BugBearVisitor(ast.NodeVisitor):
 
     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 @@ class BugBearVisitor(ast.NodeVisitor):
         ):
             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,12 @@ B018 = Error(
         "B018 Found useless expression. Either assign it to a variable or remove it."
     )
 )
+B020 = Error(
+    message=(
+        "B020 Found for loop that reassigns the iterable it is iterating "
+        + "with each iterable value."
+    )
+)
 
 # Warnings disabled by default.
 B901 = Error(


Test patch:
diff --git a/tests/b020.py b/tests/b020.py
new file mode 100644
index 0000000..df30e75
--- /dev/null
+++ b/tests/b020.py
@@ -0,0 +1,22 @@
+"""
+Should emit:
+B020 - on lines 8 and 21
+"""
+
+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..7e25386 100644
--- a/tests/test_bugbear.py
+++ b/tests/test_bugbear.py
@@ -29,6 +29,7 @@ from bugbear import (
     B016,
     B017,
     B018,
+    B020,
     B901,
     B902,
     B903,
@@ -249,6 +250,18 @@ class BugbearTestCase(unittest.TestCase):
         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(8, 4, vars=("items",)),
+                B020(21, 9, vars=("values",)),
+            ),
+        )
+
     def test_b901(self):
         filename = Path(__file__).absolute().parent / "b901.py"
         bbc = BugBearChecker(filename=str(filename))