8585from mypy import join
8686from mypy .util import get_prefix , correct_relative_import
8787from mypy .semanal_shared import PRIORITY_FALLBACKS
88+ from mypy .scope import Scope
8889
8990
9091T = TypeVar ('T' )
@@ -255,6 +256,7 @@ def __init__(self,
255256 # If True, process function definitions. If False, don't. This is used
256257 # for processing module top levels in fine-grained incremental mode.
257258 self .recurse_into_functions = True
259+ self .scope = Scope ()
258260
259261 def visit_file (self , file_node : MypyFile , fnam : str , options : Options ,
260262 patches : List [Tuple [int , Callable [[], None ]]]) -> None :
@@ -287,8 +289,10 @@ def visit_file(self, file_node: MypyFile, fnam: str, options: Options,
287289 v .is_ready = True
288290
289291 defs = file_node .defs
292+ self .scope .enter_file (file_node .fullname ())
290293 for d in defs :
291294 self .accept (d )
295+ self .scope .leave ()
292296
293297 if self .cur_mod_id == 'builtins' :
294298 remove_imported_names_from_symtable (self .globals , 'builtins' )
@@ -305,11 +309,13 @@ def visit_file(self, file_node: MypyFile, fnam: str, options: Options,
305309
306310 def refresh_partial (self , node : Union [MypyFile , FuncItem , OverloadedFuncDef ]) -> None :
307311 """Refresh a stale target in fine-grained incremental mode."""
312+ self .scope .enter_file (self .cur_mod_id )
308313 if isinstance (node , MypyFile ):
309314 self .refresh_top_level (node )
310315 else :
311316 self .recurse_into_functions = True
312317 self .accept (node )
318+ self .scope .leave ()
313319
314320 def refresh_top_level (self , file_node : MypyFile ) -> None :
315321 """Reanalyze a stale module top-level in fine-grained incremental mode."""
@@ -591,15 +597,19 @@ def analyze_property_with_multi_part_definition(self, defn: OverloadedFuncDef) -
591597
592598 def analyze_function (self , defn : FuncItem ) -> None :
593599 is_method = self .is_class_scope ()
600+ self .scope .enter_function (defn )
594601 with self .tvar_scope_frame (self .tvar_scope .method_frame ()):
595602 if defn .type :
596603 self .check_classvar_in_signature (defn .type )
597604 assert isinstance (defn .type , CallableType )
598605 # Signature must be analyzed in the surrounding scope so that
599606 # class-level imported names and type variables are in scope.
600- defn .type = self .type_analyzer ().visit_callable_type (defn .type , nested = False )
607+ analyzer = self .type_analyzer ()
608+ defn .type = analyzer .visit_callable_type (defn .type , nested = False )
609+ self .add_type_alias_deps (analyzer .aliases_used )
601610 self .check_function_signature (defn )
602611 if isinstance (defn , FuncDef ):
612+ assert isinstance (defn .type , CallableType )
603613 defn .type = set_callable_name (defn .type , defn )
604614 for arg in defn .arguments :
605615 if arg .initializer :
@@ -633,6 +643,7 @@ def analyze_function(self, defn: FuncItem) -> None:
633643
634644 self .leave ()
635645 self .function_stack .pop ()
646+ self .scope .leave ()
636647
637648 def check_classvar_in_signature (self , typ : Type ) -> None :
638649 if isinstance (typ , Overloaded ):
@@ -660,10 +671,12 @@ def check_function_signature(self, fdef: FuncItem) -> None:
660671 self .fail ('Type signature has too many arguments' , fdef , blocker = True )
661672
662673 def visit_class_def (self , defn : ClassDef ) -> None :
674+ self .scope .enter_class (defn .info )
663675 with self .analyze_class_body (defn ) as should_continue :
664676 if should_continue :
665677 # Analyze class body.
666678 defn .defs .accept (self )
679+ self .scope .leave ()
667680
668681 @contextmanager
669682 def analyze_class_body (self , defn : ClassDef ) -> Iterator [bool ]:
@@ -1679,7 +1692,24 @@ def anal_type(self, t: Type, *,
16791692 aliasing = aliasing ,
16801693 allow_tuple_literal = allow_tuple_literal ,
16811694 third_pass = third_pass )
1682- return t .accept (a )
1695+ typ = t .accept (a )
1696+ self .add_type_alias_deps (a .aliases_used )
1697+ return typ
1698+
1699+ def add_type_alias_deps (self , aliases_used : Iterable [str ],
1700+ target : Optional [str ] = None ) -> None :
1701+ """Add full names of type aliases on which the current node depends.
1702+
1703+ This is used by fine-grained incremental mode to re-check the corresponding nodes.
1704+ If `target` is None, then the target node used will be the current scope.
1705+ """
1706+ if not aliases_used :
1707+ # A basic optimization to avoid adding targets with no dependencies to
1708+ # the `alias_deps` dict.
1709+ return
1710+ if target is None :
1711+ target = self .scope .current_target ()
1712+ self .cur_mod_node .alias_deps [target ].update (aliases_used )
16831713
16841714 def visit_assignment_stmt (self , s : AssignmentStmt ) -> None :
16851715 for lval in s .lvalues :
@@ -1755,10 +1785,17 @@ def alias_fallback(self, tp: Type) -> Instance:
17551785 return Instance (fb_info , [])
17561786
17571787 def analyze_alias (self , rvalue : Expression ,
1758- warn_bound_tvar : bool = False ) -> Tuple [Optional [Type ], List [str ]]:
1759- """Check if 'rvalue' represents a valid type allowed for aliasing
1760- (e.g. not a type variable). If yes, return the corresponding type and a list of
1761- qualified type variable names for generic aliases.
1788+ warn_bound_tvar : bool = False ) -> Tuple [Optional [Type ], List [str ],
1789+ Set [str ], List [str ]]:
1790+ """Check if 'rvalue' is a valid type allowed for aliasing (e.g. not a type variable).
1791+
1792+ If yes, return the corresponding type, a list of
1793+ qualified type variable names for generic aliases, a set of names the alias depends on,
1794+ and a list of type variables if the alias is generic.
1795+ An schematic example for the dependencies:
1796+ A = int
1797+ B = str
1798+ analyze_alias(Dict[A, B])[2] == {'__main__.A', '__main__.B'}
17621799 """
17631800 dynamic = bool (self .function_stack and self .function_stack [- 1 ].is_dynamic ())
17641801 global_scope = not self .type and not self .function_stack
@@ -1775,15 +1812,21 @@ def analyze_alias(self, rvalue: Expression,
17751812 in_dynamic_func = dynamic ,
17761813 global_scope = global_scope ,
17771814 warn_bound_tvar = warn_bound_tvar )
1815+ typ = None # type: Optional[Type]
17781816 if res :
1779- alias_tvars = [name for (name , _ ) in
1780- res .accept (TypeVariableQuery (self .lookup_qualified , self .tvar_scope ))]
1817+ typ , depends_on = res
1818+ found_type_vars = typ .accept (TypeVariableQuery (self .lookup_qualified , self .tvar_scope ))
1819+ alias_tvars = [name for (name , node ) in found_type_vars ]
1820+ qualified_tvars = [node .fullname () for (name , node ) in found_type_vars ]
17811821 else :
17821822 alias_tvars = []
1783- return res , alias_tvars
1823+ depends_on = set ()
1824+ qualified_tvars = []
1825+ return typ , alias_tvars , depends_on , qualified_tvars
17841826
17851827 def check_and_set_up_type_alias (self , s : AssignmentStmt ) -> None :
17861828 """Check if assignment creates a type alias and set it up as needed.
1829+
17871830 For simple aliases like L = List we use a simpler mechanism, just copying TypeInfo.
17881831 For subscripted (including generic) aliases the resulting types are stored
17891832 in rvalue.analyzed.
@@ -1809,11 +1852,20 @@ def check_and_set_up_type_alias(self, s: AssignmentStmt) -> None:
18091852 # annotations (see the second rule).
18101853 return
18111854 rvalue = s .rvalue
1812- res , alias_tvars = self .analyze_alias (rvalue , warn_bound_tvar = True )
1855+ res , alias_tvars , depends_on , qualified_tvars = self .analyze_alias (rvalue ,
1856+ warn_bound_tvar = True )
18131857 if not res :
18141858 return
1859+ s .is_alias_def = True
18151860 node = self .lookup (lvalue .name , lvalue )
18161861 assert node is not None
1862+ if lvalue .fullname is not None :
1863+ node .alias_name = lvalue .fullname
1864+ self .add_type_alias_deps (depends_on )
1865+ self .add_type_alias_deps (qualified_tvars )
1866+ # The above are only direct deps on other aliases.
1867+ # For subscripted aliases, type deps from expansion are added in deps.py
1868+ # (because the type is stored)
18171869 if not lvalue .is_inferred_def :
18181870 # Type aliases can't be re-defined.
18191871 if node and (node .kind == TYPE_ALIAS or isinstance (node .node , TypeInfo )):
@@ -1830,7 +1882,14 @@ def check_and_set_up_type_alias(self, s: AssignmentStmt) -> None:
18301882 # For simple (on-generic) aliases we use aliasing TypeInfo's
18311883 # to allow using them in runtime context where it makes sense.
18321884 node .node = res .type
1885+ node .is_aliasing = True
18331886 if isinstance (rvalue , RefExpr ):
1887+ # For non-subscripted aliases we add type deps right here
1888+ # (because the node is stored, not type)
1889+ # TODO: currently subscripted and unsubscripted aliases are processed differently
1890+ # This leads to duplication of most of the logic with small variations.
1891+ # Fix this.
1892+ self .add_type_alias_deps ({node .node .fullname ()})
18341893 sym = self .lookup_type_node (rvalue )
18351894 if sym :
18361895 node .normalized = sym .normalized
@@ -3445,12 +3504,15 @@ def visit_index_expr(self, expr: IndexExpr) -> None:
34453504 elif isinstance (expr .base , RefExpr ) and expr .base .kind == TYPE_ALIAS :
34463505 # Special form -- subscripting a generic type alias.
34473506 # Perform the type substitution and create a new alias.
3448- res , alias_tvars = self .analyze_alias (expr )
3507+ res , alias_tvars , depends_on , _ = self .analyze_alias (expr )
34493508 assert res is not None , "Failed analyzing already defined alias"
34503509 expr .analyzed = TypeAliasExpr (res , alias_tvars , fallback = self .alias_fallback (res ),
34513510 in_runtime = True )
34523511 expr .analyzed .line = expr .line
34533512 expr .analyzed .column = expr .column
3513+ # We also store fine-grained dependencies to correctly re-process nodes
3514+ # with situations like `L = LongGeneric; x = L[int]()`.
3515+ self .add_type_alias_deps (depends_on )
34543516 elif refers_to_class_or_function (expr .base ):
34553517 # Special form -- type application.
34563518 # Translate index to an unanalyzed type.
0 commit comments