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
@@ -106,27 +102,36 @@ def visit_parameters(self, t: types.Parameters) -> None:
106102 self ._visit_type_list (t .arg_types )
107103
108104 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.
109111 self ._visit_type_tuple (t .args )
110112 if t .type :
111- # Uses of a class depend on everything in the MRO,
112- # as changes to classes in the MRO can add types to methods,
113- # 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.
114115 for s in t .type .mro :
115- self ._visit_module_name (s .module_name )
116- if t .type .metaclass_type is not None :
117- 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 )
118126
119127 def visit_callable_type (self , t : types .CallableType ) -> None :
120128 self ._visit_type_list (t .arg_types )
121129 self ._visit (t .ret_type )
122- if t .definition is not None :
123- fullname = t .definition .fullname
124- if fullname not in self .seen_fullnames :
125- self .modules .update (extract_module_names (t .definition .fullname ))
126- self .seen_fullnames .add (fullname )
130+ self ._visit_type_tuple (t .variables )
127131
128132 def visit_overloaded (self , t : types .Overloaded ) -> None :
129- self ._visit_type_list (list (t .items ))
133+ for item in t .items :
134+ self ._visit (item )
130135 self ._visit (t .fallback )
131136
132137 def visit_tuple_type (self , t : types .TupleType ) -> None :
@@ -150,4 +155,9 @@ def visit_type_type(self, t: types.TypeType) -> None:
150155 self ._visit (t .item )
151156
152157 def visit_type_alias_type (self , t : types .TypeAliasType ) -> None :
153- 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