@@ -247,6 +247,8 @@ def __init__(self,
247
247
using the Errors instance.
248
248
"""
249
249
self .locals = [None ]
250
+ # Whether each nested scope is a comprehension.
251
+ self .is_comprehension_stack = [False ] # type: List[bool]
250
252
self .imports = set ()
251
253
self .type = None
252
254
self .type_stack = []
@@ -931,6 +933,7 @@ def enter_class(self, info: TypeInfo) -> None:
931
933
# Remember previous active class
932
934
self .type_stack .append (self .type )
933
935
self .locals .append (None ) # Add class scope
936
+ self .is_comprehension_stack .append (False )
934
937
self .block_depth .append (- 1 ) # The class body increments this to 0
935
938
self .postpone_nested_functions_stack .append (FUNCTION_BOTH_PHASES )
936
939
self .type = info
@@ -940,6 +943,7 @@ def leave_class(self) -> None:
940
943
self .postpone_nested_functions_stack .pop ()
941
944
self .block_depth .pop ()
942
945
self .locals .pop ()
946
+ self .is_comprehension_stack .pop ()
943
947
self .type = self .type_stack .pop ()
944
948
945
949
def analyze_class_decorator (self , defn : ClassDef , decorator : Expression ) -> None :
@@ -1781,7 +1785,7 @@ def add_type_alias_deps(self, aliases_used: Iterable[str],
1781
1785
self .cur_mod_node .alias_deps [target ].update (aliases_used )
1782
1786
1783
1787
def visit_assignment_expr (self , e : AssignmentExpr ) -> None :
1784
- self .analyze_lvalue (e .target )
1788
+ self .analyze_lvalue (e .target , escape_comprehension = True )
1785
1789
e .value .accept (self )
1786
1790
1787
1791
def visit_assignment_stmt (self , s : AssignmentStmt ) -> None :
@@ -2101,7 +2105,8 @@ def check_and_set_up_type_alias(self, s: AssignmentStmt) -> None:
2101
2105
def analyze_lvalue (self , lval : Lvalue , nested : bool = False ,
2102
2106
add_global : bool = False ,
2103
2107
explicit_type : bool = False ,
2104
- is_final : bool = False ) -> None :
2108
+ is_final : bool = False ,
2109
+ escape_comprehension : bool = False ) -> None :
2105
2110
"""Analyze an lvalue or assignment target.
2106
2111
2107
2112
Note that this is used in both pass 1 and 2.
@@ -2111,9 +2116,14 @@ def analyze_lvalue(self, lval: Lvalue, nested: bool = False,
2111
2116
nested: If true, the lvalue is within a tuple or list lvalue expression
2112
2117
add_global: Add name to globals table only if this is true (used in first pass)
2113
2118
explicit_type: Assignment has type annotation
2119
+ escape_comprehension: If we are inside a comprehension, set the variable
2120
+ in the enclosing scope instead. This implements
2121
+ https://www.python.org/dev/peps/pep-0572/#scope-of-the-target
2114
2122
"""
2123
+ if escape_comprehension :
2124
+ assert isinstance (lval , NameExpr ), "assignment expression target must be NameExpr"
2115
2125
if isinstance (lval , NameExpr ):
2116
- self .analyze_name_lvalue (lval , add_global , explicit_type , is_final )
2126
+ self .analyze_name_lvalue (lval , add_global , explicit_type , is_final , escape_comprehension )
2117
2127
elif isinstance (lval , MemberExpr ):
2118
2128
if not add_global :
2119
2129
self .analyze_member_lvalue (lval , explicit_type , is_final )
@@ -2142,7 +2152,8 @@ def analyze_name_lvalue(self,
2142
2152
lval : NameExpr ,
2143
2153
add_global : bool ,
2144
2154
explicit_type : bool ,
2145
- is_final : bool ) -> None :
2155
+ is_final : bool ,
2156
+ escape_comprehension : bool ) -> None :
2146
2157
"""Analyze an lvalue that targets a name expression.
2147
2158
2148
2159
Arguments are similar to "analyze_lvalue".
@@ -2179,7 +2190,7 @@ def analyze_name_lvalue(self,
2179
2190
lval .name not in self .nonlocal_decls [- 1 ]):
2180
2191
# Define new local name.
2181
2192
v = self .make_name_lvalue_var (lval , LDEF , not explicit_type )
2182
- self .add_local (v , lval )
2193
+ self .add_local (v , lval , escape_comprehension = escape_comprehension )
2183
2194
if unmangle (lval .name ) == '_' :
2184
2195
# Special case for assignment to local named '_': always infer 'Any'.
2185
2196
typ = AnyType (TypeOfAny .special_form )
@@ -3344,15 +3355,15 @@ def visit_set_comprehension(self, expr: SetComprehension) -> None:
3344
3355
expr .generator .accept (self )
3345
3356
3346
3357
def visit_dictionary_comprehension (self , expr : DictionaryComprehension ) -> None :
3347
- self .enter ()
3358
+ self .enter (is_comprehension = True )
3348
3359
self .analyze_comp_for (expr )
3349
3360
expr .key .accept (self )
3350
3361
expr .value .accept (self )
3351
3362
self .leave ()
3352
3363
self .analyze_comp_for_2 (expr )
3353
3364
3354
3365
def visit_generator_expr (self , expr : GeneratorExpr ) -> None :
3355
- self .enter ()
3366
+ self .enter (is_comprehension = True )
3356
3367
self .analyze_comp_for (expr )
3357
3368
expr .left_expr .accept (self )
3358
3369
self .leave ()
@@ -3658,18 +3669,20 @@ def qualified_name(self, n: str) -> str:
3658
3669
base = self .cur_mod_id
3659
3670
return base + '.' + n
3660
3671
3661
- def enter (self ) -> None :
3672
+ def enter (self , is_comprehension : bool = False ) -> None :
3662
3673
self .locals .append (SymbolTable ())
3663
3674
self .global_decls .append (set ())
3664
3675
self .nonlocal_decls .append (set ())
3665
3676
# -1 since entering block will increment this to 0.
3666
3677
self .block_depth .append (- 1 )
3678
+ self .is_comprehension_stack .append (is_comprehension )
3667
3679
3668
3680
def leave (self ) -> None :
3669
3681
self .locals .pop ()
3670
3682
self .global_decls .pop ()
3671
3683
self .nonlocal_decls .pop ()
3672
3684
self .block_depth .pop ()
3685
+ self .is_comprehension_stack .pop ()
3673
3686
3674
3687
def is_func_scope (self ) -> bool :
3675
3688
return self .locals [- 1 ] is not None
@@ -3731,15 +3744,25 @@ def add_symbol(self, name: str, node: SymbolTableNode,
3731
3744
return
3732
3745
self .globals [name ] = node
3733
3746
3734
- def add_local (self , node : Union [Var , FuncDef , OverloadedFuncDef ], ctx : Context ) -> None :
3747
+ def add_local (self , node : Union [Var , FuncDef , OverloadedFuncDef ], ctx : Context ,
3748
+ escape_comprehension : bool = False ) -> None :
3735
3749
"""Add local variable or function."""
3736
3750
assert self .locals [- 1 ] is not None , "Should not add locals outside a function"
3751
+ if escape_comprehension :
3752
+ for i , is_comprehension in enumerate (reversed (self .is_comprehension_stack )):
3753
+ if not is_comprehension :
3754
+ scope = self .locals [- 1 - i ]
3755
+ break
3756
+ else :
3757
+ assert False , "Should have at least one non-comprehension scope"
3758
+ else :
3759
+ scope = self .locals [- 1 ]
3737
3760
name = node .name ()
3738
- if name in self . locals [ - 1 ] :
3739
- self .name_already_defined (name , ctx , self . locals [ - 1 ] [name ])
3761
+ if name in scope :
3762
+ self .name_already_defined (name , ctx , scope [name ])
3740
3763
return
3741
3764
node ._fullname = name
3742
- self . locals [ - 1 ] [name ] = SymbolTableNode (LDEF , node )
3765
+ scope [name ] = SymbolTableNode (LDEF , node )
3743
3766
3744
3767
def add_exports (self , exp_or_exps : Union [Iterable [Expression ], Expression ]) -> None :
3745
3768
exps = [exp_or_exps ] if isinstance (exp_or_exps , Expression ) else exp_or_exps
0 commit comments