Repository: PyCQA/pyflakes (python)

Issue:
False Positive: walrus in a list comprehension
Minimal reproduction:

```python
def foo(x):
    fns = [lambda x: x + 1, lambda x: x + 2, lambda x: x + 3]
    return [(x := fn(x)) for fn in fns]
```

This raises F841 incorrectly asserting that the `x` variable assigned in the comprehension is never used

Pull request description:
allow assignment expressions to redefine outer names
resolves #800

Patch:
diff --git a/pyflakes/checker.py b/pyflakes/checker.py
index 754ab30..47a59ba 100644
--- a/pyflakes/checker.py
+++ b/pyflakes/checker.py
@@ -548,7 +548,7 @@ class FunctionScope(Scope):
     """
     usesLocals = False
     alwaysUsed = {'__tracebackhide__', '__traceback_info__',
-                  '__traceback_supplement__'}
+                  '__traceback_supplement__', '__debuggerskip__'}
 
     def __init__(self):
         super().__init__()
@@ -1003,14 +1003,17 @@ class Checker:
         # don't treat annotations as assignments if there is an existing value
         # in scope
         if value.name not in self.scope or not isinstance(value, Annotation):
-            cur_scope_pos = -1
-            # As per PEP 572, use scope in which outermost generator is defined
-            while (
-                isinstance(value, NamedExprAssignment) and
-                isinstance(self.scopeStack[cur_scope_pos], GeneratorScope)
-            ):
-                cur_scope_pos -= 1
-            self.scopeStack[cur_scope_pos][value.name] = value
+            if isinstance(value, NamedExprAssignment):
+                # PEP 572: use scope in which outermost generator is defined
+                scope = next(
+                    scope
+                    for scope in reversed(self.scopeStack)
+                    if not isinstance(scope, GeneratorScope)
+                )
+                # it may be a re-assignment to an already existing name
+                scope.setdefault(value.name, value)
+            else:
+                self.scope[value.name] = value
 
     def _unknown_handler(self, node):
         # this environment variable configures whether to error on unknown


Test patch:
diff --git a/pyflakes/test/test_other.py b/pyflakes/test/test_other.py
index aebdcea..0af87ec 100644
--- a/pyflakes/test/test_other.py
+++ b/pyflakes/test/test_other.py
@@ -1349,6 +1349,16 @@ class TestUnusedAssignment(TestCase):
                 __tracebackhide__ = True
         """)
 
+    def test_debuggerskipSpecialVariable(self):
+        """
+        Do not warn about unused local variable __debuggerskip__, which is
+        a special variable for IPython.
+        """
+        self.flakes("""
+            def helper():
+                __debuggerskip__ = True
+        """)
+
     def test_ifexp(self):
         """
         Test C{foo if bar else baz} statements.
@@ -1707,6 +1717,13 @@ class TestUnusedAssignment(TestCase):
             print(y)
         ''')
 
+    def test_assign_expr_generator_scope_reassigns_parameter(self):
+        self.flakes('''
+        def foo(x):
+            fns = [lambda x: x + 1, lambda x: x + 2, lambda x: x + 3]
+            return [(x := fn(x)) for fn in fns]
+        ''')
+
     def test_assign_expr_nested(self):
         """Test assignment expressions in nested expressions."""
         self.flakes('''