semanal_shared.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475
  1. """Shared definitions used by different parts of semantic analysis."""
  2. from __future__ import annotations
  3. from abc import abstractmethod
  4. from typing import Callable, Final, overload
  5. from typing_extensions import Literal, Protocol
  6. from mypy_extensions import trait
  7. from mypy import join
  8. from mypy.errorcodes import LITERAL_REQ, ErrorCode
  9. from mypy.nodes import (
  10. CallExpr,
  11. ClassDef,
  12. Context,
  13. DataclassTransformSpec,
  14. Decorator,
  15. Expression,
  16. FuncDef,
  17. NameExpr,
  18. Node,
  19. OverloadedFuncDef,
  20. RefExpr,
  21. SymbolNode,
  22. SymbolTable,
  23. SymbolTableNode,
  24. TypeInfo,
  25. )
  26. from mypy.plugin import SemanticAnalyzerPluginInterface
  27. from mypy.tvar_scope import TypeVarLikeScope
  28. from mypy.type_visitor import ANY_STRATEGY, BoolTypeQuery
  29. from mypy.types import (
  30. TPDICT_FB_NAMES,
  31. AnyType,
  32. FunctionLike,
  33. Instance,
  34. Parameters,
  35. ParamSpecFlavor,
  36. ParamSpecType,
  37. PlaceholderType,
  38. ProperType,
  39. TupleType,
  40. Type,
  41. TypeOfAny,
  42. TypeVarId,
  43. TypeVarLikeType,
  44. get_proper_type,
  45. )
  46. # Subclasses can override these Var attributes with incompatible types. This can also be
  47. # set for individual attributes using 'allow_incompatible_override' of Var.
  48. ALLOW_INCOMPATIBLE_OVERRIDE: Final = ("__slots__", "__deletable__", "__match_args__")
  49. # Priorities for ordering of patches within the "patch" phase of semantic analysis
  50. # (after the main pass):
  51. # Fix fallbacks (does joins)
  52. PRIORITY_FALLBACKS: Final = 1
  53. @trait
  54. class SemanticAnalyzerCoreInterface:
  55. """A core abstract interface to generic semantic analyzer functionality.
  56. This is implemented by both semantic analyzer passes 2 and 3.
  57. """
  58. @abstractmethod
  59. def lookup_qualified(
  60. self, name: str, ctx: Context, suppress_errors: bool = False
  61. ) -> SymbolTableNode | None:
  62. raise NotImplementedError
  63. @abstractmethod
  64. def lookup_fully_qualified(self, name: str) -> SymbolTableNode:
  65. raise NotImplementedError
  66. @abstractmethod
  67. def lookup_fully_qualified_or_none(self, name: str) -> SymbolTableNode | None:
  68. raise NotImplementedError
  69. @abstractmethod
  70. def fail(
  71. self,
  72. msg: str,
  73. ctx: Context,
  74. serious: bool = False,
  75. *,
  76. blocker: bool = False,
  77. code: ErrorCode | None = None,
  78. ) -> None:
  79. raise NotImplementedError
  80. @abstractmethod
  81. def note(self, msg: str, ctx: Context, *, code: ErrorCode | None = None) -> None:
  82. raise NotImplementedError
  83. @abstractmethod
  84. def incomplete_feature_enabled(self, feature: str, ctx: Context) -> bool:
  85. raise NotImplementedError
  86. @abstractmethod
  87. def record_incomplete_ref(self) -> None:
  88. raise NotImplementedError
  89. @abstractmethod
  90. def defer(self, debug_context: Context | None = None, force_progress: bool = False) -> None:
  91. raise NotImplementedError
  92. @abstractmethod
  93. def is_incomplete_namespace(self, fullname: str) -> bool:
  94. """Is a module or class namespace potentially missing some definitions?"""
  95. raise NotImplementedError
  96. @property
  97. @abstractmethod
  98. def final_iteration(self) -> bool:
  99. """Is this the final iteration of semantic analysis?"""
  100. raise NotImplementedError
  101. @abstractmethod
  102. def is_future_flag_set(self, flag: str) -> bool:
  103. """Is the specific __future__ feature imported"""
  104. raise NotImplementedError
  105. @property
  106. @abstractmethod
  107. def is_stub_file(self) -> bool:
  108. raise NotImplementedError
  109. @abstractmethod
  110. def is_func_scope(self) -> bool:
  111. raise NotImplementedError
  112. @property
  113. @abstractmethod
  114. def type(self) -> TypeInfo | None:
  115. raise NotImplementedError
  116. @trait
  117. class SemanticAnalyzerInterface(SemanticAnalyzerCoreInterface):
  118. """A limited abstract interface to some generic semantic analyzer pass 2 functionality.
  119. We use this interface for various reasons:
  120. * Looser coupling
  121. * Cleaner import graph
  122. * Less need to pass around callback functions
  123. """
  124. tvar_scope: TypeVarLikeScope
  125. @abstractmethod
  126. def lookup(
  127. self, name: str, ctx: Context, suppress_errors: bool = False
  128. ) -> SymbolTableNode | None:
  129. raise NotImplementedError
  130. @abstractmethod
  131. def named_type(self, fullname: str, args: list[Type] | None = None) -> Instance:
  132. raise NotImplementedError
  133. @abstractmethod
  134. def named_type_or_none(self, fullname: str, args: list[Type] | None = None) -> Instance | None:
  135. raise NotImplementedError
  136. @abstractmethod
  137. def accept(self, node: Node) -> None:
  138. raise NotImplementedError
  139. @abstractmethod
  140. def anal_type(
  141. self,
  142. t: Type,
  143. *,
  144. tvar_scope: TypeVarLikeScope | None = None,
  145. allow_tuple_literal: bool = False,
  146. allow_unbound_tvars: bool = False,
  147. allow_required: bool = False,
  148. allow_placeholder: bool = False,
  149. report_invalid_types: bool = True,
  150. prohibit_self_type: str | None = None,
  151. ) -> Type | None:
  152. raise NotImplementedError
  153. @abstractmethod
  154. def get_and_bind_all_tvars(self, type_exprs: list[Expression]) -> list[TypeVarLikeType]:
  155. raise NotImplementedError
  156. @abstractmethod
  157. def basic_new_typeinfo(self, name: str, basetype_or_fallback: Instance, line: int) -> TypeInfo:
  158. raise NotImplementedError
  159. @abstractmethod
  160. def schedule_patch(self, priority: int, fn: Callable[[], None]) -> None:
  161. raise NotImplementedError
  162. @abstractmethod
  163. def add_symbol_table_node(self, name: str, stnode: SymbolTableNode) -> bool:
  164. """Add node to the current symbol table."""
  165. raise NotImplementedError
  166. @abstractmethod
  167. def current_symbol_table(self) -> SymbolTable:
  168. """Get currently active symbol table.
  169. May be module, class, or local namespace.
  170. """
  171. raise NotImplementedError
  172. @abstractmethod
  173. def add_symbol(
  174. self,
  175. name: str,
  176. node: SymbolNode,
  177. context: Context,
  178. module_public: bool = True,
  179. module_hidden: bool = False,
  180. can_defer: bool = True,
  181. ) -> bool:
  182. """Add symbol to the current symbol table."""
  183. raise NotImplementedError
  184. @abstractmethod
  185. def add_symbol_skip_local(self, name: str, node: SymbolNode) -> None:
  186. """Add symbol to the current symbol table, skipping locals.
  187. This is used to store symbol nodes in a symbol table that
  188. is going to be serialized (local namespaces are not serialized).
  189. See implementation docstring for more details.
  190. """
  191. raise NotImplementedError
  192. @abstractmethod
  193. def parse_bool(self, expr: Expression) -> bool | None:
  194. raise NotImplementedError
  195. @abstractmethod
  196. def qualified_name(self, n: str) -> str:
  197. raise NotImplementedError
  198. @property
  199. @abstractmethod
  200. def is_typeshed_stub_file(self) -> bool:
  201. raise NotImplementedError
  202. @abstractmethod
  203. def process_placeholder(
  204. self, name: str | None, kind: str, ctx: Context, force_progress: bool = False
  205. ) -> None:
  206. raise NotImplementedError
  207. def set_callable_name(sig: Type, fdef: FuncDef) -> ProperType:
  208. sig = get_proper_type(sig)
  209. if isinstance(sig, FunctionLike):
  210. if fdef.info:
  211. if fdef.info.fullname in TPDICT_FB_NAMES:
  212. # Avoid exposing the internal _TypedDict name.
  213. class_name = "TypedDict"
  214. else:
  215. class_name = fdef.info.name
  216. return sig.with_name(f"{fdef.name} of {class_name}")
  217. else:
  218. return sig.with_name(fdef.name)
  219. else:
  220. return sig
  221. def calculate_tuple_fallback(typ: TupleType) -> None:
  222. """Calculate a precise item type for the fallback of a tuple type.
  223. This must be called only after the main semantic analysis pass, since joins
  224. aren't available before that.
  225. Note that there is an apparent chicken and egg problem with respect
  226. to verifying type arguments against bounds. Verifying bounds might
  227. require fallbacks, but we might use the bounds to calculate the
  228. fallbacks. In practice this is not a problem, since the worst that
  229. can happen is that we have invalid type argument values, and these
  230. can happen in later stages as well (they will generate errors, but
  231. we don't prevent their existence).
  232. """
  233. fallback = typ.partial_fallback
  234. assert fallback.type.fullname == "builtins.tuple"
  235. fallback.args = (join.join_type_list(list(typ.items)),) + fallback.args[1:]
  236. class _NamedTypeCallback(Protocol):
  237. def __call__(self, fully_qualified_name: str, args: list[Type] | None = None) -> Instance:
  238. ...
  239. def paramspec_args(
  240. name: str,
  241. fullname: str,
  242. id: TypeVarId | int,
  243. *,
  244. named_type_func: _NamedTypeCallback,
  245. line: int = -1,
  246. column: int = -1,
  247. prefix: Parameters | None = None,
  248. ) -> ParamSpecType:
  249. return ParamSpecType(
  250. name,
  251. fullname,
  252. id,
  253. flavor=ParamSpecFlavor.ARGS,
  254. upper_bound=named_type_func("builtins.tuple", [named_type_func("builtins.object")]),
  255. default=AnyType(TypeOfAny.from_omitted_generics),
  256. line=line,
  257. column=column,
  258. prefix=prefix,
  259. )
  260. def paramspec_kwargs(
  261. name: str,
  262. fullname: str,
  263. id: TypeVarId | int,
  264. *,
  265. named_type_func: _NamedTypeCallback,
  266. line: int = -1,
  267. column: int = -1,
  268. prefix: Parameters | None = None,
  269. ) -> ParamSpecType:
  270. return ParamSpecType(
  271. name,
  272. fullname,
  273. id,
  274. flavor=ParamSpecFlavor.KWARGS,
  275. upper_bound=named_type_func(
  276. "builtins.dict", [named_type_func("builtins.str"), named_type_func("builtins.object")]
  277. ),
  278. default=AnyType(TypeOfAny.from_omitted_generics),
  279. line=line,
  280. column=column,
  281. prefix=prefix,
  282. )
  283. class HasPlaceholders(BoolTypeQuery):
  284. def __init__(self) -> None:
  285. super().__init__(ANY_STRATEGY)
  286. def visit_placeholder_type(self, t: PlaceholderType) -> bool:
  287. return True
  288. def has_placeholder(typ: Type) -> bool:
  289. """Check if a type contains any placeholder types (recursively)."""
  290. return typ.accept(HasPlaceholders())
  291. def find_dataclass_transform_spec(node: Node | None) -> DataclassTransformSpec | None:
  292. """
  293. Find the dataclass transform spec for the given node, if any exists.
  294. Per PEP 681 (https://peps.python.org/pep-0681/#the-dataclass-transform-decorator), dataclass
  295. transforms can be specified in multiple ways, including decorator functions and
  296. metaclasses/base classes. This function resolves the spec from any of these variants.
  297. """
  298. # The spec only lives on the function/class definition itself, so we need to unwrap down to that
  299. # point
  300. if isinstance(node, CallExpr):
  301. # Like dataclasses.dataclass, transform-based decorators can be applied either with or
  302. # without parameters; ie, both of these forms are accepted:
  303. #
  304. # @typing.dataclass_transform
  305. # class Foo: ...
  306. # @typing.dataclass_transform(eq=True, order=True, ...)
  307. # class Bar: ...
  308. #
  309. # We need to unwrap the call for the second variant.
  310. node = node.callee
  311. if isinstance(node, RefExpr):
  312. node = node.node
  313. if isinstance(node, Decorator):
  314. # typing.dataclass_transform usage must always result in a Decorator; it always uses the
  315. # `@dataclass_transform(...)` syntax and never `@dataclass_transform`
  316. node = node.func
  317. if isinstance(node, OverloadedFuncDef):
  318. # The dataclass_transform decorator may be attached to any single overload, so we must
  319. # search them all.
  320. # Note that using more than one decorator is undefined behavior, so we can just take the
  321. # first that we find.
  322. for candidate in node.items:
  323. spec = find_dataclass_transform_spec(candidate)
  324. if spec is not None:
  325. return spec
  326. return find_dataclass_transform_spec(node.impl)
  327. # For functions, we can directly consult the AST field for the spec
  328. if isinstance(node, FuncDef):
  329. return node.dataclass_transform_spec
  330. if isinstance(node, ClassDef):
  331. node = node.info
  332. if isinstance(node, TypeInfo):
  333. # Search all parent classes to see if any are decorated with `typing.dataclass_transform`
  334. for base in node.mro[1:]:
  335. if base.dataclass_transform_spec is not None:
  336. return base.dataclass_transform_spec
  337. # Check if there is a metaclass that is decorated with `typing.dataclass_transform`
  338. #
  339. # Note that PEP 681 only discusses using a metaclass that is directly decorated with
  340. # `typing.dataclass_transform`; subclasses thereof should be treated with dataclass
  341. # semantics rather than as transforms:
  342. #
  343. # > If dataclass_transform is applied to a class, dataclass-like semantics will be assumed
  344. # > for any class that directly or indirectly derives from the decorated class or uses the
  345. # > decorated class as a metaclass.
  346. #
  347. # The wording doesn't make this entirely explicit, but Pyright (the reference
  348. # implementation for this PEP) only handles directly-decorated metaclasses.
  349. metaclass_type = node.metaclass_type
  350. if metaclass_type is not None and metaclass_type.type.dataclass_transform_spec is not None:
  351. return metaclass_type.type.dataclass_transform_spec
  352. return None
  353. # Never returns `None` if a default is given
  354. @overload
  355. def require_bool_literal_argument(
  356. api: SemanticAnalyzerInterface | SemanticAnalyzerPluginInterface,
  357. expression: Expression,
  358. name: str,
  359. default: Literal[True] | Literal[False],
  360. ) -> bool:
  361. ...
  362. @overload
  363. def require_bool_literal_argument(
  364. api: SemanticAnalyzerInterface | SemanticAnalyzerPluginInterface,
  365. expression: Expression,
  366. name: str,
  367. default: None = None,
  368. ) -> bool | None:
  369. ...
  370. def require_bool_literal_argument(
  371. api: SemanticAnalyzerInterface | SemanticAnalyzerPluginInterface,
  372. expression: Expression,
  373. name: str,
  374. default: bool | None = None,
  375. ) -> bool | None:
  376. """Attempt to interpret an expression as a boolean literal, and fail analysis if we can't."""
  377. value = parse_bool(expression)
  378. if value is None:
  379. api.fail(
  380. f'"{name}" argument must be a True or False literal', expression, code=LITERAL_REQ
  381. )
  382. return default
  383. return value
  384. def parse_bool(expr: Expression) -> bool | None:
  385. if isinstance(expr, NameExpr):
  386. if expr.fullname == "builtins.True":
  387. return True
  388. if expr.fullname == "builtins.False":
  389. return False
  390. return None