74
74
PlaceholderNode , COVARIANT , CONTRAVARIANT , INVARIANT ,
75
75
nongen_builtins , get_member_expr_fullname , REVEAL_TYPE ,
76
76
REVEAL_LOCALS , is_final_node , TypedDictExpr , type_aliases_target_versions ,
77
- EnumCallExpr , RUNTIME_PROTOCOL_DECOS , FakeExpression , Statement
77
+ EnumCallExpr , RUNTIME_PROTOCOL_DECOS , FakeExpression , Statement , AssignmentExpr ,
78
78
)
79
79
from mypy .tvar_scope import TypeVarScope
80
80
from mypy .typevars import fill_typevars
@@ -177,6 +177,8 @@ class SemanticAnalyzer(NodeVisitor[None],
177
177
nonlocal_decls = None # type: List[Set[str]]
178
178
# Local names of function scopes; None for non-function scopes.
179
179
locals = None # type: List[Optional[SymbolTable]]
180
+ # Whether each scope is a comprehension scope.
181
+ is_comprehension_stack = None # type: List[bool]
180
182
# Nested block depths of scopes
181
183
block_depth = None # type: List[int]
182
184
# TypeInfo of directly enclosing class (or None)
@@ -242,6 +244,7 @@ def __init__(self,
242
244
errors: Report analysis errors using this instance
243
245
"""
244
246
self .locals = [None ]
247
+ self .is_comprehension_stack = [False ]
245
248
# Saved namespaces from previous iteration. Every top-level function/method body is
246
249
# analyzed in several iterations until all names are resolved. We need to save
247
250
# the local namespaces for the top level function and all nested functions between
@@ -519,6 +522,12 @@ def file_context(self,
519
522
520
523
def visit_func_def (self , defn : FuncDef ) -> None :
521
524
self .statement = defn
525
+
526
+ # Visit default values because they may contain assignment expressions.
527
+ for arg in defn .arguments :
528
+ if arg .initializer :
529
+ arg .initializer .accept (self )
530
+
522
531
defn .is_conditional = self .block_depth [- 1 ] > 0
523
532
524
533
# Set full names even for those definitions that aren't added
@@ -1148,13 +1157,15 @@ def enter_class(self, info: TypeInfo) -> None:
1148
1157
# Remember previous active class
1149
1158
self .type_stack .append (self .type )
1150
1159
self .locals .append (None ) # Add class scope
1160
+ self .is_comprehension_stack .append (False )
1151
1161
self .block_depth .append (- 1 ) # The class body increments this to 0
1152
1162
self .type = info
1153
1163
1154
1164
def leave_class (self ) -> None :
1155
1165
""" Restore analyzer state. """
1156
1166
self .block_depth .pop ()
1157
1167
self .locals .pop ()
1168
+ self .is_comprehension_stack .pop ()
1158
1169
self .type = self .type_stack .pop ()
1159
1170
1160
1171
def analyze_class_decorator (self , defn : ClassDef , decorator : Expression ) -> None :
@@ -1858,6 +1869,10 @@ def visit_import_all(self, i: ImportAll) -> None:
1858
1869
# Assignment
1859
1870
#
1860
1871
1872
+ def visit_assignment_expr (self , s : AssignmentExpr ) -> None :
1873
+ s .value .accept (self )
1874
+ self .analyze_lvalue (s .target , escape_comprehensions = True )
1875
+
1861
1876
def visit_assignment_stmt (self , s : AssignmentStmt ) -> None :
1862
1877
self .statement = s
1863
1878
@@ -2493,16 +2508,22 @@ def analyze_lvalue(self,
2493
2508
lval : Lvalue ,
2494
2509
nested : bool = False ,
2495
2510
explicit_type : bool = False ,
2496
- is_final : bool = False ) -> None :
2511
+ is_final : bool = False ,
2512
+ escape_comprehensions : bool = False ) -> None :
2497
2513
"""Analyze an lvalue or assignment target.
2498
2514
2499
2515
Args:
2500
2516
lval: The target lvalue
2501
2517
nested: If true, the lvalue is within a tuple or list lvalue expression
2502
2518
explicit_type: Assignment has type annotation
2519
+ escape_comprehensions: If we are inside a comprehension, set the variable
2520
+ in the enclosing scope instead. This implements
2521
+ https://www.python.org/dev/peps/pep-0572/#scope-of-the-target
2503
2522
"""
2523
+ if escape_comprehensions :
2524
+ assert isinstance (lval , NameExpr ), "assignment expression target must be NameExpr"
2504
2525
if isinstance (lval , NameExpr ):
2505
- self .analyze_name_lvalue (lval , explicit_type , is_final )
2526
+ self .analyze_name_lvalue (lval , explicit_type , is_final , escape_comprehensions )
2506
2527
elif isinstance (lval , MemberExpr ):
2507
2528
self .analyze_member_lvalue (lval , explicit_type , is_final )
2508
2529
if explicit_type and not self .is_self_member_ref (lval ):
@@ -2528,7 +2549,8 @@ def analyze_lvalue(self,
2528
2549
def analyze_name_lvalue (self ,
2529
2550
lvalue : NameExpr ,
2530
2551
explicit_type : bool ,
2531
- is_final : bool ) -> None :
2552
+ is_final : bool ,
2553
+ escape_comprehensions : bool ) -> None :
2532
2554
"""Analyze an lvalue that targets a name expression.
2533
2555
2534
2556
Arguments are similar to "analyze_lvalue".
@@ -2552,7 +2574,7 @@ def analyze_name_lvalue(self,
2552
2574
if (not existing or isinstance (existing .node , PlaceholderNode )) and not outer :
2553
2575
# Define new variable.
2554
2576
var = self .make_name_lvalue_var (lvalue , kind , not explicit_type )
2555
- added = self .add_symbol (name , var , lvalue )
2577
+ added = self .add_symbol (name , var , lvalue , escape_comprehensions = escape_comprehensions )
2556
2578
# Only bind expression if we successfully added name to symbol table.
2557
2579
if added :
2558
2580
lvalue .is_new_def = True
@@ -4082,7 +4104,8 @@ def add_symbol(self,
4082
4104
context : Context ,
4083
4105
module_public : bool = True ,
4084
4106
module_hidden : bool = False ,
4085
- can_defer : bool = True ) -> bool :
4107
+ can_defer : bool = True ,
4108
+ escape_comprehensions : bool = False ) -> bool :
4086
4109
"""Add symbol to the currently active symbol table.
4087
4110
4088
4111
Generally additions to symbol table should go through this method or
@@ -4104,7 +4127,7 @@ def add_symbol(self,
4104
4127
node ,
4105
4128
module_public = module_public ,
4106
4129
module_hidden = module_hidden )
4107
- return self .add_symbol_table_node (name , symbol , context , can_defer )
4130
+ return self .add_symbol_table_node (name , symbol , context , can_defer , escape_comprehensions )
4108
4131
4109
4132
def add_symbol_skip_local (self , name : str , node : SymbolNode ) -> None :
4110
4133
"""Same as above, but skipping the local namespace.
@@ -4132,7 +4155,8 @@ def add_symbol_table_node(self,
4132
4155
name : str ,
4133
4156
symbol : SymbolTableNode ,
4134
4157
context : Optional [Context ] = None ,
4135
- can_defer : bool = True ) -> bool :
4158
+ can_defer : bool = True ,
4159
+ escape_comprehensions : bool = False ) -> bool :
4136
4160
"""Add symbol table node to the currently active symbol table.
4137
4161
4138
4162
Return True if we actually added the symbol, or False if we refused
@@ -4151,7 +4175,7 @@ def add_symbol_table_node(self,
4151
4175
can_defer: if True, defer current target if adding a placeholder
4152
4176
context: error context (see above about None value)
4153
4177
"""
4154
- names = self .current_symbol_table ()
4178
+ names = self .current_symbol_table (escape_comprehensions = escape_comprehensions )
4155
4179
existing = names .get (name )
4156
4180
if isinstance (symbol .node , PlaceholderNode ) and can_defer :
4157
4181
self .defer (context )
@@ -4379,13 +4403,16 @@ def enter(self, function: Union[FuncItem, GeneratorExpr, DictionaryComprehension
4379
4403
"""Enter a function, generator or comprehension scope."""
4380
4404
names = self .saved_locals .setdefault (function , SymbolTable ())
4381
4405
self .locals .append (names )
4406
+ is_comprehension = isinstance (function , (GeneratorExpr , DictionaryComprehension ))
4407
+ self .is_comprehension_stack .append (is_comprehension )
4382
4408
self .global_decls .append (set ())
4383
4409
self .nonlocal_decls .append (set ())
4384
4410
# -1 since entering block will increment this to 0.
4385
4411
self .block_depth .append (- 1 )
4386
4412
4387
4413
def leave (self ) -> None :
4388
4414
self .locals .pop ()
4415
+ self .is_comprehension_stack .pop ()
4389
4416
self .global_decls .pop ()
4390
4417
self .nonlocal_decls .pop ()
4391
4418
self .block_depth .pop ()
@@ -4412,10 +4439,19 @@ def current_symbol_kind(self) -> int:
4412
4439
kind = GDEF
4413
4440
return kind
4414
4441
4415
- def current_symbol_table (self ) -> SymbolTable :
4442
+ def current_symbol_table (self , escape_comprehensions : bool = False ) -> SymbolTable :
4416
4443
if self .is_func_scope ():
4417
4444
assert self .locals [- 1 ] is not None
4418
- names = self .locals [- 1 ]
4445
+ if escape_comprehensions :
4446
+ for i , is_comprehension in enumerate (reversed (self .is_comprehension_stack )):
4447
+ if not is_comprehension :
4448
+ names = self .locals [- 1 - i ]
4449
+ break
4450
+ else :
4451
+ assert False , "Should have at least one non-comprehension scope"
4452
+ else :
4453
+ names = self .locals [- 1 ]
4454
+ assert names is not None
4419
4455
elif self .type is not None :
4420
4456
names = self .type .names
4421
4457
else :
0 commit comments