44
55import mypy .types as types
66from mypy .types import TypeVisitor
7- from mypy .util import split_module_names
8-
9-
10- def extract_module_names (type_name : str | None ) -> list [str ]:
11- """Returns the module names of a fully qualified type name."""
12- if type_name is not None :
13- # Discard the first one, which is just the qualified name of the type
14- possible_module_names = split_module_names (type_name )
15- return possible_module_names [1 :]
16- else :
17- return []
187
198
209class TypeIndirectionVisitor (TypeVisitor [None ]):
@@ -23,50 +12,57 @@ class TypeIndirectionVisitor(TypeVisitor[None]):
2312 def __init__ (self ) -> None :
2413 # Module references are collected here
2514 self .modules : set [str ] = set ()
26- # User to avoid infinite recursion with recursive type aliases
27- self .seen_aliases : set [types .TypeAliasType ] = set ()
28- # Used to avoid redundant work
29- self .seen_fullnames : set [str ] = set ()
15+ # User to avoid infinite recursion with recursive types
16+ self .seen_types : set [types .TypeAliasType | types .Instance ] = set ()
3017
3118 def find_modules (self , typs : Iterable [types .Type ]) -> set [str ]:
3219 self .modules = set ()
33- self .seen_fullnames = set ()
34- self .seen_aliases = set ()
20+ self .seen_types = set ()
3521 for typ in typs :
3622 self ._visit (typ )
3723 return self .modules
3824
3925 def _visit (self , typ : types .Type ) -> None :
40- if isinstance (typ , types .TypeAliasType ):
41- # Avoid infinite recursion for recursive type aliases.
42- if typ not in self .seen_aliases :
43- self .seen_aliases .add (typ )
26+ # Note: instances are needed for `class str(Sequence[str]): ...`
27+ if (
28+ isinstance (typ , types .TypeAliasType )
29+ or isinstance (typ , types .ProperType )
30+ and isinstance (typ , types .Instance )
31+ ):
32+ # Avoid infinite recursion for recursive types.
33+ if typ in self .seen_types :
34+ return
35+ self .seen_types .add (typ )
4436 typ .accept (self )
4537
4638 def _visit_type_tuple (self , typs : tuple [types .Type , ...]) -> None :
4739 # Micro-optimization: Specialized version of _visit for lists
4840 for typ in typs :
49- if isinstance (typ , types .TypeAliasType ):
50- # Avoid infinite recursion for recursive type aliases.
51- if typ in self .seen_aliases :
41+ if (
42+ isinstance (typ , types .TypeAliasType )
43+ or isinstance (typ , types .ProperType )
44+ and isinstance (typ , types .Instance )
45+ ):
46+ # Avoid infinite recursion for recursive types.
47+ if typ in self .seen_types :
5248 continue
53- self .seen_aliases .add (typ )
49+ self .seen_types .add (typ )
5450 typ .accept (self )
5551
5652 def _visit_type_list (self , typs : list [types .Type ]) -> None :
5753 # Micro-optimization: Specialized version of _visit for tuples
5854 for typ in typs :
59- if isinstance (typ , types .TypeAliasType ):
60- # Avoid infinite recursion for recursive type aliases.
61- if typ in self .seen_aliases :
55+ if (
56+ isinstance (typ , types .TypeAliasType )
57+ or isinstance (typ , types .ProperType )
58+ and isinstance (typ , types .Instance )
59+ ):
60+ # Avoid infinite recursion for recursive types.
61+ if typ in self .seen_types :
6262 continue
63- self .seen_aliases .add (typ )
63+ self .seen_types .add (typ )
6464 typ .accept (self )
6565
66- def _visit_module_name (self , module_name : str ) -> None :
67- if module_name not in self .modules :
68- self .modules .update (split_module_names (module_name ))
69-
7066 def visit_unbound_type (self , t : types .UnboundType ) -> None :
7167 self ._visit_type_tuple (t .args )
7268
@@ -93,6 +89,7 @@ def visit_type_var(self, t: types.TypeVarType) -> None:
9389 def visit_param_spec (self , t : types .ParamSpecType ) -> None :
9490 self ._visit (t .upper_bound )
9591 self ._visit (t .default )
92+ self ._visit (t .prefix )
9693
9794 def visit_type_var_tuple (self , t : types .TypeVarTupleType ) -> None :
9895 self ._visit (t .upper_bound )
@@ -105,27 +102,36 @@ def visit_parameters(self, t: types.Parameters) -> None:
105102 self ._visit_type_list (t .arg_types )
106103
107104 def visit_instance (self , t : types .Instance ) -> None :
105+ # Instance is named, record its definition and continue digging into
106+ # components that constitute semantic meaning of this type: bases, metaclass,
107+ # tuple type, and typeddict type.
108+ # Note: we cannot simply record the MRO, in case an intermediate base contains
109+ # a reference to type alias, this affects meaning of map_instance_to_supertype(),
110+ # see e.g. testDoubleReexportGenericUpdated.
108111 self ._visit_type_tuple (t .args )
109112 if t .type :
110- # Uses of a class depend on everything in the MRO,
111- # as changes to classes in the MRO can add types to methods,
112- # change property types, change the MRO itself, etc.
113+ # Important optimization: instead of simply recording the definition and
114+ # recursing into bases, record the MRO and only traverse generic bases.
113115 for s in t .type .mro :
114- self ._visit_module_name (s .module_name )
115- if t .type .metaclass_type is not None :
116- self ._visit_module_name (t .type .metaclass_type .type .module_name )
116+ self .modules .add (s .module_name )
117+ for base in s .bases :
118+ if base .args :
119+ self ._visit_type_tuple (base .args )
120+ if t .type .metaclass_type :
121+ self ._visit (t .type .metaclass_type )
122+ if t .type .typeddict_type :
123+ self ._visit (t .type .typeddict_type )
124+ if t .type .tuple_type :
125+ self ._visit (t .type .tuple_type )
117126
118127 def visit_callable_type (self , t : types .CallableType ) -> None :
119128 self ._visit_type_list (t .arg_types )
120129 self ._visit (t .ret_type )
121- if t .definition is not None :
122- fullname = t .definition .fullname
123- if fullname not in self .seen_fullnames :
124- self .modules .update (extract_module_names (t .definition .fullname ))
125- self .seen_fullnames .add (fullname )
130+ self ._visit_type_tuple (t .variables )
126131
127132 def visit_overloaded (self , t : types .Overloaded ) -> None :
128- self ._visit_type_list (list (t .items ))
133+ for item in t .items :
134+ self ._visit (item )
129135 self ._visit (t .fallback )
130136
131137 def visit_tuple_type (self , t : types .TupleType ) -> None :
@@ -149,4 +155,9 @@ def visit_type_type(self, t: types.TypeType) -> None:
149155 self ._visit (t .item )
150156
151157 def visit_type_alias_type (self , t : types .TypeAliasType ) -> None :
152- self ._visit (types .get_proper_type (t ))
158+ # Type alias is named, record its definition and continue digging into
159+ # components that constitute semantic meaning of this type: target and args.
160+ if t .alias :
161+ self .modules .add (t .alias .module )
162+ self ._visit (t .alias .target )
163+ self ._visit_type_list (t .args )
0 commit comments