1414 OverloadedFuncDef ,
1515 ParamSpecExpr ,
1616 SymbolTable ,
17+ SymbolTableNode ,
1718 TypeAlias ,
1819 TypeInfo ,
1920 TypeVarExpr ,
4546from mypy .visitor import NodeVisitor
4647
4748
48- # N.B: we do a allow_missing fixup when fixing up a fine-grained
49- # incremental cache load (since there may be cross-refs into deleted
50- # modules)
51- def fixup_module (tree : MypyFile , modules : dict [str , MypyFile ], allow_missing : bool ) -> None :
52- node_fixer = NodeFixer (modules , allow_missing )
53- node_fixer .visit_symbol_table (tree .names , tree .fullname )
54-
55-
56- # TODO: Fix up .info when deserializing, i.e. much earlier.
5749class NodeFixer (NodeVisitor [None ]):
5850 current_info : TypeInfo | None = None
5951
6052 def __init__ (self , modules : dict [str , MypyFile ], allow_missing : bool ) -> None :
6153 self .modules = modules
54+ # N.B: we do an allow_missing fixup when fixing up a fine-grained
55+ # incremental cache load (since there may be cross-refs into deleted
56+ # modules)
6257 self .allow_missing = allow_missing
6358 self .type_fixer = TypeFixer (self .modules , allow_missing )
6459
@@ -70,7 +65,7 @@ def visit_type_info(self, info: TypeInfo) -> None:
7065 if info .defn :
7166 info .defn .accept (self )
7267 if info .names :
73- self .visit_symbol_table (info .names , info . fullname )
68+ self .visit_symbol_table (info .names )
7469 if info .bases :
7570 for base in info .bases :
7671 base .accept (self .type_fixer )
@@ -118,62 +113,66 @@ def visit_type_info(self, info: TypeInfo) -> None:
118113 self .current_info = save_info
119114
120115 # NOTE: This method *definitely* isn't part of the NodeVisitor API.
121- def visit_symbol_table (self , symtab : SymbolTable , table_fullname : str ) -> None :
122- # Copy the items because we may mutate symtab.
123- for key in list (symtab ):
116+ def visit_symbol_table (self , symtab : SymbolTable ) -> None :
117+ for key in symtab :
124118 value = symtab [key ]
125119 cross_ref = value .cross_ref
126- if cross_ref is not None : # Fix up cross-reference.
127- value . cross_ref = None
120+ # Fix up module cross-reference eagerly because it is very cheap .
121+ if cross_ref is not None :
128122 if cross_ref in self .modules :
129- value .node = self .modules [cross_ref ]
130- else :
131- stnode = lookup_fully_qualified (
132- cross_ref , self .modules , raise_on_missing = not self .allow_missing
133- )
134- if stnode is not None :
135- if stnode is value :
136- # The node seems to refer to itself, which can mean that
137- # the target is a deleted submodule of the current module,
138- # and thus lookup falls back to the symbol table of the parent
139- # package. Here's how this may happen:
140- #
141- # pkg/__init__.py:
142- # from pkg import sub
143- #
144- # Now if pkg.sub is deleted, the pkg.sub symbol table entry
145- # appears to refer to itself. Replace the entry with a
146- # placeholder to avoid a crash. We can't delete the entry,
147- # as it would stop dependency propagation.
148- value .node = Var (key + "@deleted" )
149- else :
150- assert stnode .node is not None , (table_fullname + "." + key , cross_ref )
151- value .node = stnode .node
152- elif not self .allow_missing :
153- assert False , f"Could not find cross-ref { cross_ref } "
154- else :
155- # We have a missing crossref in allow missing mode, need to put something
156- value .node = missing_info (self .modules )
123+ value .cross_ref = None
124+ value .unfixed = False
125+ value ._node = self .modules [cross_ref ]
126+ # TODO: this should not be needed, looks like a daemon bug.
127+ elif self .allow_missing :
128+ self .resolve_cross_ref (value )
129+ # Look at private attribute to avoid triggering fixup eagerly.
130+ elif isinstance (value ._node , TypeInfo ):
131+ self .visit_type_info (value ._node )
157132 else :
158- if isinstance (value .node , TypeInfo ):
159- # TypeInfo has no accept(). TODO: Add it?
160- self .visit_type_info (value .node )
161- elif value .node is not None :
162- value .node .accept (self )
163- else :
164- assert False , f"Unexpected empty node { key !r} : { value } "
133+ value .stored_info = self .current_info
134+
135+ def resolve_cross_ref (self , value : SymbolTableNode ) -> None :
136+ """Replace cross-reference with an actual referred node."""
137+ assert value .cross_ref is not None
138+ cross_ref = value .cross_ref
139+ value .cross_ref = None
140+ value .unfixed = False
141+ stnode = lookup_fully_qualified (
142+ cross_ref , self .modules , raise_on_missing = not self .allow_missing
143+ )
144+ if stnode is not None :
145+ if stnode is value :
146+ # The node seems to refer to itself, which can mean that
147+ # the target is a deleted submodule of the current module,
148+ # and thus lookup falls back to the symbol table of the parent
149+ # package. Here's how this may happen:
150+ #
151+ # pkg/__init__.py:
152+ # from pkg import sub
153+ #
154+ # Now if pkg.sub is deleted, the pkg.sub symbol table entry
155+ # appears to refer to itself. Replace the entry with a
156+ # placeholder to avoid a crash. We can't delete the entry,
157+ # as it would stop dependency propagation.
158+ short_name = cross_ref .rsplit ("." , maxsplit = 1 )[- 1 ]
159+ value ._node = Var (short_name + "@deleted" )
160+ else :
161+ assert stnode .node is not None , cross_ref
162+ value ._node = stnode .node
163+ elif not self .allow_missing :
164+ assert False , f"Could not find cross-ref { cross_ref } "
165+ else :
166+ # We have a missing crossref in allow missing mode, need to put something
167+ value ._node = missing_info (self .modules )
165168
166169 def visit_func_def (self , func : FuncDef ) -> None :
167- if self .current_info is not None :
168- func .info = self .current_info
169170 if func .type is not None :
170171 func .type .accept (self .type_fixer )
171172 if isinstance (func .type , CallableType ):
172173 func .type .definition = func
173174
174175 def visit_overloaded_func_def (self , o : OverloadedFuncDef ) -> None :
175- if self .current_info is not None :
176- o .info = self .current_info
177176 if o .type :
178177 o .type .accept (self .type_fixer )
179178 for item in o .items :
@@ -186,14 +185,10 @@ def visit_overloaded_func_def(self, o: OverloadedFuncDef) -> None:
186185 typ .definition = item
187186
188187 def visit_decorator (self , d : Decorator ) -> None :
189- if self .current_info is not None :
190- d .var .info = self .current_info
191188 if d .func :
192189 d .func .accept (self )
193190 if d .var :
194191 d .var .accept (self )
195- for node in d .decorators :
196- node .accept (self )
197192 typ = d .var .type
198193 if isinstance (typ , ProperType ) and isinstance (typ , CallableType ):
199194 typ .definition = d .func
@@ -218,8 +213,6 @@ def visit_type_var_tuple_expr(self, tv: TypeVarTupleExpr) -> None:
218213 tv .default .accept (self .type_fixer )
219214
220215 def visit_var (self , v : Var ) -> None :
221- if self .current_info is not None :
222- v .info = self .current_info
223216 if v .type is not None :
224217 v .type .accept (self .type_fixer )
225218 if v .setter_type is not None :
@@ -237,7 +230,6 @@ def __init__(self, modules: dict[str, MypyFile], allow_missing: bool) -> None:
237230 self .allow_missing = allow_missing
238231
239232 def visit_instance (self , inst : Instance ) -> None :
240- # TODO: Combine Instances that are exactly the same?
241233 type_ref = inst .type_ref
242234 if type_ref is None :
243235 return # We've already been here.
0 commit comments