fixup.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411
  1. """Fix up various things after deserialization."""
  2. from __future__ import annotations
  3. from typing import Any
  4. from typing_extensions import Final
  5. from mypy.lookup import lookup_fully_qualified
  6. from mypy.nodes import (
  7. Block,
  8. ClassDef,
  9. Decorator,
  10. FuncDef,
  11. MypyFile,
  12. OverloadedFuncDef,
  13. ParamSpecExpr,
  14. SymbolTable,
  15. TypeAlias,
  16. TypeInfo,
  17. TypeVarExpr,
  18. TypeVarTupleExpr,
  19. Var,
  20. )
  21. from mypy.types import (
  22. NOT_READY,
  23. AnyType,
  24. CallableType,
  25. Instance,
  26. LiteralType,
  27. Overloaded,
  28. Parameters,
  29. ParamSpecType,
  30. TupleType,
  31. TypeAliasType,
  32. TypedDictType,
  33. TypeOfAny,
  34. TypeType,
  35. TypeVarTupleType,
  36. TypeVarType,
  37. TypeVisitor,
  38. UnboundType,
  39. UnionType,
  40. UnpackType,
  41. )
  42. from mypy.visitor import NodeVisitor
  43. # N.B: we do a allow_missing fixup when fixing up a fine-grained
  44. # incremental cache load (since there may be cross-refs into deleted
  45. # modules)
  46. def fixup_module(tree: MypyFile, modules: dict[str, MypyFile], allow_missing: bool) -> None:
  47. node_fixer = NodeFixer(modules, allow_missing)
  48. node_fixer.visit_symbol_table(tree.names, tree.fullname)
  49. # TODO: Fix up .info when deserializing, i.e. much earlier.
  50. class NodeFixer(NodeVisitor[None]):
  51. current_info: TypeInfo | None = None
  52. def __init__(self, modules: dict[str, MypyFile], allow_missing: bool) -> None:
  53. self.modules = modules
  54. self.allow_missing = allow_missing
  55. self.type_fixer = TypeFixer(self.modules, allow_missing)
  56. # NOTE: This method isn't (yet) part of the NodeVisitor API.
  57. def visit_type_info(self, info: TypeInfo) -> None:
  58. save_info = self.current_info
  59. try:
  60. self.current_info = info
  61. if info.defn:
  62. info.defn.accept(self)
  63. if info.names:
  64. self.visit_symbol_table(info.names, info.fullname)
  65. if info.bases:
  66. for base in info.bases:
  67. base.accept(self.type_fixer)
  68. if info._promote:
  69. for p in info._promote:
  70. p.accept(self.type_fixer)
  71. if info.tuple_type:
  72. info.tuple_type.accept(self.type_fixer)
  73. info.update_tuple_type(info.tuple_type)
  74. if info.special_alias:
  75. info.special_alias.alias_tvars = list(info.defn.type_vars)
  76. if info.typeddict_type:
  77. info.typeddict_type.accept(self.type_fixer)
  78. info.update_typeddict_type(info.typeddict_type)
  79. if info.special_alias:
  80. info.special_alias.alias_tvars = list(info.defn.type_vars)
  81. if info.declared_metaclass:
  82. info.declared_metaclass.accept(self.type_fixer)
  83. if info.metaclass_type:
  84. info.metaclass_type.accept(self.type_fixer)
  85. if info.alt_promote:
  86. info.alt_promote.accept(self.type_fixer)
  87. instance = Instance(info, [])
  88. # Hack: We may also need to add a backwards promotion (from int to native int),
  89. # since it might not be serialized.
  90. if instance not in info.alt_promote.type._promote:
  91. info.alt_promote.type._promote.append(instance)
  92. if info._mro_refs:
  93. info.mro = [
  94. lookup_fully_qualified_typeinfo(
  95. self.modules, name, allow_missing=self.allow_missing
  96. )
  97. for name in info._mro_refs
  98. ]
  99. info._mro_refs = None
  100. finally:
  101. self.current_info = save_info
  102. # NOTE: This method *definitely* isn't part of the NodeVisitor API.
  103. def visit_symbol_table(self, symtab: SymbolTable, table_fullname: str) -> None:
  104. # Copy the items because we may mutate symtab.
  105. for key, value in list(symtab.items()):
  106. cross_ref = value.cross_ref
  107. if cross_ref is not None: # Fix up cross-reference.
  108. value.cross_ref = None
  109. if cross_ref in self.modules:
  110. value.node = self.modules[cross_ref]
  111. else:
  112. stnode = lookup_fully_qualified(
  113. cross_ref, self.modules, raise_on_missing=not self.allow_missing
  114. )
  115. if stnode is not None:
  116. assert stnode.node is not None, (table_fullname + "." + key, cross_ref)
  117. value.node = stnode.node
  118. elif not self.allow_missing:
  119. assert False, f"Could not find cross-ref {cross_ref}"
  120. else:
  121. # We have a missing crossref in allow missing mode, need to put something
  122. value.node = missing_info(self.modules)
  123. else:
  124. if isinstance(value.node, TypeInfo):
  125. # TypeInfo has no accept(). TODO: Add it?
  126. self.visit_type_info(value.node)
  127. elif value.node is not None:
  128. value.node.accept(self)
  129. else:
  130. assert False, f"Unexpected empty node {key!r}: {value}"
  131. def visit_func_def(self, func: FuncDef) -> None:
  132. if self.current_info is not None:
  133. func.info = self.current_info
  134. if func.type is not None:
  135. func.type.accept(self.type_fixer)
  136. def visit_overloaded_func_def(self, o: OverloadedFuncDef) -> None:
  137. if self.current_info is not None:
  138. o.info = self.current_info
  139. if o.type:
  140. o.type.accept(self.type_fixer)
  141. for item in o.items:
  142. item.accept(self)
  143. if o.impl:
  144. o.impl.accept(self)
  145. def visit_decorator(self, d: Decorator) -> None:
  146. if self.current_info is not None:
  147. d.var.info = self.current_info
  148. if d.func:
  149. d.func.accept(self)
  150. if d.var:
  151. d.var.accept(self)
  152. for node in d.decorators:
  153. node.accept(self)
  154. def visit_class_def(self, c: ClassDef) -> None:
  155. for v in c.type_vars:
  156. if isinstance(v, TypeVarType):
  157. for value in v.values:
  158. value.accept(self.type_fixer)
  159. v.upper_bound.accept(self.type_fixer)
  160. v.default.accept(self.type_fixer)
  161. def visit_type_var_expr(self, tv: TypeVarExpr) -> None:
  162. for value in tv.values:
  163. value.accept(self.type_fixer)
  164. tv.upper_bound.accept(self.type_fixer)
  165. tv.default.accept(self.type_fixer)
  166. def visit_paramspec_expr(self, p: ParamSpecExpr) -> None:
  167. p.upper_bound.accept(self.type_fixer)
  168. p.default.accept(self.type_fixer)
  169. def visit_type_var_tuple_expr(self, tv: TypeVarTupleExpr) -> None:
  170. tv.upper_bound.accept(self.type_fixer)
  171. tv.default.accept(self.type_fixer)
  172. def visit_var(self, v: Var) -> None:
  173. if self.current_info is not None:
  174. v.info = self.current_info
  175. if v.type is not None:
  176. v.type.accept(self.type_fixer)
  177. def visit_type_alias(self, a: TypeAlias) -> None:
  178. a.target.accept(self.type_fixer)
  179. for v in a.alias_tvars:
  180. v.accept(self.type_fixer)
  181. class TypeFixer(TypeVisitor[None]):
  182. def __init__(self, modules: dict[str, MypyFile], allow_missing: bool) -> None:
  183. self.modules = modules
  184. self.allow_missing = allow_missing
  185. def visit_instance(self, inst: Instance) -> None:
  186. # TODO: Combine Instances that are exactly the same?
  187. type_ref = inst.type_ref
  188. if type_ref is None:
  189. return # We've already been here.
  190. inst.type_ref = None
  191. inst.type = lookup_fully_qualified_typeinfo(
  192. self.modules, type_ref, allow_missing=self.allow_missing
  193. )
  194. # TODO: Is this needed or redundant?
  195. # Also fix up the bases, just in case.
  196. for base in inst.type.bases:
  197. if base.type is NOT_READY:
  198. base.accept(self)
  199. for a in inst.args:
  200. a.accept(self)
  201. if inst.last_known_value is not None:
  202. inst.last_known_value.accept(self)
  203. def visit_type_alias_type(self, t: TypeAliasType) -> None:
  204. type_ref = t.type_ref
  205. if type_ref is None:
  206. return # We've already been here.
  207. t.type_ref = None
  208. t.alias = lookup_fully_qualified_alias(
  209. self.modules, type_ref, allow_missing=self.allow_missing
  210. )
  211. for a in t.args:
  212. a.accept(self)
  213. def visit_any(self, o: Any) -> None:
  214. pass # Nothing to descend into.
  215. def visit_callable_type(self, ct: CallableType) -> None:
  216. if ct.fallback:
  217. ct.fallback.accept(self)
  218. for argt in ct.arg_types:
  219. # argt may be None, e.g. for __self in NamedTuple constructors.
  220. if argt is not None:
  221. argt.accept(self)
  222. if ct.ret_type is not None:
  223. ct.ret_type.accept(self)
  224. for v in ct.variables:
  225. v.accept(self)
  226. for arg in ct.bound_args:
  227. if arg:
  228. arg.accept(self)
  229. if ct.type_guard is not None:
  230. ct.type_guard.accept(self)
  231. def visit_overloaded(self, t: Overloaded) -> None:
  232. for ct in t.items:
  233. ct.accept(self)
  234. def visit_erased_type(self, o: Any) -> None:
  235. # This type should exist only temporarily during type inference
  236. raise RuntimeError("Shouldn't get here", o)
  237. def visit_deleted_type(self, o: Any) -> None:
  238. pass # Nothing to descend into.
  239. def visit_none_type(self, o: Any) -> None:
  240. pass # Nothing to descend into.
  241. def visit_uninhabited_type(self, o: Any) -> None:
  242. pass # Nothing to descend into.
  243. def visit_partial_type(self, o: Any) -> None:
  244. raise RuntimeError("Shouldn't get here", o)
  245. def visit_tuple_type(self, tt: TupleType) -> None:
  246. if tt.items:
  247. for it in tt.items:
  248. it.accept(self)
  249. if tt.partial_fallback is not None:
  250. tt.partial_fallback.accept(self)
  251. def visit_typeddict_type(self, tdt: TypedDictType) -> None:
  252. if tdt.items:
  253. for it in tdt.items.values():
  254. it.accept(self)
  255. if tdt.fallback is not None:
  256. if tdt.fallback.type_ref is not None:
  257. if (
  258. lookup_fully_qualified(
  259. tdt.fallback.type_ref,
  260. self.modules,
  261. raise_on_missing=not self.allow_missing,
  262. )
  263. is None
  264. ):
  265. # We reject fake TypeInfos for TypedDict fallbacks because
  266. # the latter are used in type checking and must be valid.
  267. tdt.fallback.type_ref = "typing._TypedDict"
  268. tdt.fallback.accept(self)
  269. def visit_literal_type(self, lt: LiteralType) -> None:
  270. lt.fallback.accept(self)
  271. def visit_type_var(self, tvt: TypeVarType) -> None:
  272. if tvt.values:
  273. for vt in tvt.values:
  274. vt.accept(self)
  275. tvt.upper_bound.accept(self)
  276. tvt.default.accept(self)
  277. def visit_param_spec(self, p: ParamSpecType) -> None:
  278. p.upper_bound.accept(self)
  279. p.default.accept(self)
  280. def visit_type_var_tuple(self, t: TypeVarTupleType) -> None:
  281. t.upper_bound.accept(self)
  282. t.default.accept(self)
  283. def visit_unpack_type(self, u: UnpackType) -> None:
  284. u.type.accept(self)
  285. def visit_parameters(self, p: Parameters) -> None:
  286. for argt in p.arg_types:
  287. if argt is not None:
  288. argt.accept(self)
  289. for var in p.variables:
  290. var.accept(self)
  291. def visit_unbound_type(self, o: UnboundType) -> None:
  292. for a in o.args:
  293. a.accept(self)
  294. def visit_union_type(self, ut: UnionType) -> None:
  295. if ut.items:
  296. for it in ut.items:
  297. it.accept(self)
  298. def visit_void(self, o: Any) -> None:
  299. pass # Nothing to descend into.
  300. def visit_type_type(self, t: TypeType) -> None:
  301. t.item.accept(self)
  302. def lookup_fully_qualified_typeinfo(
  303. modules: dict[str, MypyFile], name: str, *, allow_missing: bool
  304. ) -> TypeInfo:
  305. stnode = lookup_fully_qualified(name, modules, raise_on_missing=not allow_missing)
  306. node = stnode.node if stnode else None
  307. if isinstance(node, TypeInfo):
  308. return node
  309. else:
  310. # Looks like a missing TypeInfo during an initial daemon load, put something there
  311. assert (
  312. allow_missing
  313. ), "Should never get here in normal mode, got {}:{} instead of TypeInfo".format(
  314. type(node).__name__, node.fullname if node else ""
  315. )
  316. return missing_info(modules)
  317. def lookup_fully_qualified_alias(
  318. modules: dict[str, MypyFile], name: str, *, allow_missing: bool
  319. ) -> TypeAlias:
  320. stnode = lookup_fully_qualified(name, modules, raise_on_missing=not allow_missing)
  321. node = stnode.node if stnode else None
  322. if isinstance(node, TypeAlias):
  323. return node
  324. elif isinstance(node, TypeInfo):
  325. if node.special_alias:
  326. # Already fixed up.
  327. return node.special_alias
  328. if node.tuple_type:
  329. alias = TypeAlias.from_tuple_type(node)
  330. elif node.typeddict_type:
  331. alias = TypeAlias.from_typeddict_type(node)
  332. else:
  333. assert allow_missing
  334. return missing_alias()
  335. node.special_alias = alias
  336. return alias
  337. else:
  338. # Looks like a missing TypeAlias during an initial daemon load, put something there
  339. assert (
  340. allow_missing
  341. ), "Should never get here in normal mode, got {}:{} instead of TypeAlias".format(
  342. type(node).__name__, node.fullname if node else ""
  343. )
  344. return missing_alias()
  345. _SUGGESTION: Final = "<missing {}: *should* have gone away during fine-grained update>"
  346. def missing_info(modules: dict[str, MypyFile]) -> TypeInfo:
  347. suggestion = _SUGGESTION.format("info")
  348. dummy_def = ClassDef(suggestion, Block([]))
  349. dummy_def.fullname = suggestion
  350. info = TypeInfo(SymbolTable(), dummy_def, "<missing>")
  351. obj_type = lookup_fully_qualified_typeinfo(modules, "builtins.object", allow_missing=False)
  352. info.bases = [Instance(obj_type, [])]
  353. info.mro = [info, obj_type]
  354. return info
  355. def missing_alias() -> TypeAlias:
  356. suggestion = _SUGGESTION.format("alias")
  357. return TypeAlias(AnyType(TypeOfAny.special_form), suggestion, line=-1, column=-1)