Skip to content

Commit

Permalink
Fix scoping of list comprehensions within class bodies
Browse files Browse the repository at this point in the history
Fixes #1000.
  • Loading branch information
JukkaL committed Nov 25, 2015
1 parent 42e2583 commit 5c8122a
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 5 deletions.
25 changes: 20 additions & 5 deletions mypy/semanal.py
Original file line number Diff line number Diff line change
Expand Up @@ -1780,26 +1780,41 @@ def visit_dictionary_comprehension(self, expr: DictionaryComprehension) -> None:
expr.key.accept(self)
expr.value.accept(self)
self.leave()
self.analyze_comp_for_2(expr)

def visit_generator_expr(self, expr: GeneratorExpr) -> None:
self.enter()
self.analyze_comp_for(expr)
expr.left_expr.accept(self)
self.leave()
self.analyze_comp_for_2(expr)

def analyze_comp_for(self, expr: Union[GeneratorExpr,
DictionaryComprehension]) -> None:
"""Analyses the 'comp_for' part of comprehensions.
That is the part after 'for' in (x for x in l if p)
"""Analyses the 'comp_for' part of comprehensions (part 1).
That is the part after 'for' in (x for x in l if p). This analyzes
variables and conditions which are analyzed in a local scope.
"""
for index, sequence, conditions in zip(expr.indices, expr.sequences,
expr.condlists):
sequence.accept(self)
for i, (index, sequence, conditions) in enumerate(zip(expr.indices,
expr.sequences,
expr.condlists)):
if i > 0:
sequence.accept(self)
# Bind index variables.
self.analyze_lvalue(index)
for cond in conditions:
cond.accept(self)

def analyze_comp_for_2(self, expr: Union[GeneratorExpr,
DictionaryComprehension]) -> None:
"""Analyses the 'comp_for' part of comprehensions (part 2).
That is the part after 'for' in (x for x in l if p). This analyzes
the 'l' part which is analyzed in the surrounding scope.
"""
expr.sequences[0].accept(self)

def visit_func_expr(self, expr: FuncExpr) -> None:
self.analyze_function(expr)

Expand Down
11 changes: 11 additions & 0 deletions mypy/test/data/check-expressions.test
Original file line number Diff line number Diff line change
Expand Up @@ -1104,6 +1104,17 @@ o = [x for x in a] # type: List[object]
class A: pass
[builtins fixtures/for.py]

[case testSimpleListComprehensionInClassBody]
from typing import List
class A:
a = None # type: List[A]
a = [x for x in a]
b = [x for x in a] # type: List[B] # E: List comprehension has incompatible type List[A]
class B: pass
[builtins fixtures/for.py]
[out]
main: note: In class "A":


-- Set comprehension
-- -----------------
Expand Down
10 changes: 10 additions & 0 deletions mypy/test/data/semanal-errors.test
Original file line number Diff line number Diff line change
Expand Up @@ -1410,3 +1410,13 @@ import tkinter
main:1: error: No library stub file for standard library module 'tabnanny'
main:1: note: (Stub files are from https://github.com/python/typeshed)
main:2: error: No library stub file for standard library module 'tkinter'

[case testListComprehensionSpecialScoping]
class A:
x = 1
y = 1
z = 1
[x for i in z if y]
[out]
main:5: error: Name 'y' is not defined
main:5: error: Name 'x' is not defined

0 comments on commit 5c8122a

Please sign in to comment.