| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent ac511d6 commit 929377a
15 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -117,7 +117,6 @@ | |||
| 117 | 117 | TypeAlias, | |
| 118 | 118 | TypeAliasStmt, | |
| 119 | 119 | TypeInfo, | |
| 120 | - TypeVarExpr, | ||
| 121 | 120 | UnaryExpr, | |
| 122 | 121 | Var, | |
| 123 | 122 | WhileStmt, | |
@@ -2858,29 +2857,6 @@ def check_multiple_inheritance(self, typ: TypeInfo) -> None: | |||
| 2858 | 2857 | if name in base2.names and base2 not in base.mro: | |
| 2859 | 2858 | self.check_compatibility(name, base, base2, typ) | |
| 2860 | 2859 | ||
| 2861 | - def determine_type_of_member(self, sym: SymbolTableNode) -> Type | None: | ||
| 2862 | - # TODO: this duplicates both checkmember.py and analyze_ref_expr(), delete. | ||
| 2863 | - if sym.type is not None: | ||
| 2864 | - return sym.type | ||
| 2865 | - if isinstance(sym.node, SYMBOL_FUNCBASE_TYPES): | ||
| 2866 | - return self.function_type(sym.node) | ||
| 2867 | - if isinstance(sym.node, TypeInfo): | ||
| 2868 | - if sym.node.typeddict_type: | ||
| 2869 | - # We special-case TypedDict, because they don't define any constructor. | ||
| 2870 | - return self.expr_checker.typeddict_callable(sym.node) | ||
| 2871 | - else: | ||
| 2872 | - return type_object_type(sym.node, self.named_type) | ||
| 2873 | - if isinstance(sym.node, TypeVarExpr): | ||
| 2874 | - # Use of TypeVars is rejected in an expression/runtime context, so | ||
| 2875 | - # we don't need to check supertype compatibility for them. | ||
| 2876 | - return AnyType(TypeOfAny.special_form) | ||
| 2877 | - if isinstance(sym.node, TypeAlias): | ||
| 2878 | - with self.msg.filter_errors(): | ||
| 2879 | - # Suppress any errors, they will be given when analyzing the corresponding node. | ||
| 2880 | - # Here we may have incorrect options and location context. | ||
| 2881 | - return self.expr_checker.alias_type_in_runtime_context(sym.node, ctx=sym.node) | ||
| 2882 | - return None | ||
| 2883 | - | ||
| 2884 | 2860 | def check_compatibility( | |
| 2885 | 2861 | self, name: str, base1: TypeInfo, base2: TypeInfo, ctx: TypeInfo | |
| 2886 | 2862 | ) -> None: | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -21,7 +21,7 @@ | |||
| 21 | 21 | MypyFile, | |
| 22 | 22 | Node, | |
| 23 | 23 | RefExpr, | |
| 24 | - TypeAlias, | ||
| 24 | + SymbolNode, | ||
| 25 | 25 | TypeInfo, | |
| 26 | 26 | Var, | |
| 27 | 27 | ) | |
@@ -64,10 +64,6 @@ def accept( | |||
| 64 | 64 | def analyze_ref_expr(self, e: RefExpr, lvalue: bool = False) -> Type: | |
| 65 | 65 | raise NotImplementedError | |
| 66 | 66 | ||
| 67 | - @abstractmethod | ||
| 68 | - def module_type(self, node: MypyFile) -> Instance: | ||
| 69 | - raise NotImplementedError | ||
| 70 | - | ||
| 71 | 67 | @abstractmethod | |
| 72 | 68 | def check_call( | |
| 73 | 69 | self, | |
@@ -112,24 +108,26 @@ def check_method_call_by_name( | |||
| 112 | 108 | ) -> tuple[Type, Type]: | |
| 113 | 109 | raise NotImplementedError | |
| 114 | 110 | ||
| 115 | - @abstractmethod | ||
| 116 | - def alias_type_in_runtime_context( | ||
| 117 | - self, alias: TypeAlias, *, ctx: Context, alias_definition: bool = False | ||
| 118 | - ) -> Type: | ||
| 119 | - raise NotImplementedError | ||
| 120 | - | ||
| 121 | 111 | @abstractmethod | |
| 122 | 112 | def visit_typeddict_index_expr( | |
| 123 | 113 | self, td_type: TypedDictType, index: Expression, setitem: bool = False | |
| 124 | 114 | ) -> tuple[Type, set[str]]: | |
| 125 | 115 | raise NotImplementedError | |
| 126 | 116 | ||
| 127 | 117 | @abstractmethod | |
| 128 | - def typeddict_callable(self, info: TypeInfo) -> CallableType: | ||
| 118 | + def infer_literal_expr_type(self, value: LiteralValue, fallback_name: str) -> Type: | ||
| 129 | 119 | raise NotImplementedError | |
| 130 | 120 | ||
| 131 | 121 | @abstractmethod | |
| 132 | - def infer_literal_expr_type(self, value: LiteralValue, fallback_name: str) -> Type: | ||
| 122 | + def analyze_static_reference( | ||
| 123 | + self, | ||
| 124 | + node: SymbolNode, | ||
| 125 | + ctx: Context, | ||
| 126 | + is_lvalue: bool, | ||
| 127 | + *, | ||
| 128 | + include_modules: bool = True, | ||
| 129 | + suppress_errors: bool = False, | ||
| 130 | + ) -> Type: | ||
| 133 | 131 | raise NotImplementedError | |
| 134 | 132 | ||
| 135 | 133 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -7,7 +7,7 @@ | |||
| 7 | 7 | import time | |
| 8 | 8 | from collections import defaultdict | |
| 9 | 9 | from collections.abc import Iterable, Iterator, Sequence | |
| 10 | - from contextlib import contextmanager | ||
| 10 | + from contextlib import contextmanager, nullcontext | ||
| 11 | 11 | from typing import Callable, ClassVar, Final, Optional, cast, overload | |
| 12 | 12 | from typing_extensions import TypeAlias as _TypeAlias, assert_never | |
| 13 | 13 | ||
@@ -94,6 +94,7 @@ | |||
| 94 | 94 | TypedDictExpr, | |
| 95 | 95 | TypeInfo, | |
| 96 | 96 | TypeVarExpr, | |
| 97 | + TypeVarLikeExpr, | ||
| 97 | 98 | TypeVarTupleExpr, | |
| 98 | 99 | UnaryExpr, | |
| 99 | 100 | Var, | |
@@ -173,6 +174,7 @@ | |||
| 173 | 174 | TypeOfAny, | |
| 174 | 175 | TypeType, | |
| 175 | 176 | TypeVarId, | |
| 177 | + TypeVarLikeType, | ||
| 176 | 178 | TypeVarTupleType, | |
| 177 | 179 | TypeVarType, | |
| 178 | 180 | UnboundType, | |
@@ -377,26 +379,24 @@ def analyze_ref_expr(self, e: RefExpr, lvalue: bool = False) -> Type: | |||
| 377 | 379 | result = self.analyze_var_ref(node, e) | |
| 378 | 380 | if isinstance(result, PartialType): | |
| 379 | 381 | result = self.chk.handle_partial_var_type(result, lvalue, node, e) | |
| 380 | - elif isinstance(node, FuncDef): | ||
| 381 | - # Reference to a global function. | ||
| 382 | - result = function_type(node, self.named_type("builtins.function")) | ||
| 382 | + elif isinstance(node, Decorator): | ||
| 383 | + result = self.analyze_var_ref(node.var, e) | ||
| 383 | 384 | elif isinstance(node, OverloadedFuncDef): | |
| 384 | 385 | if node.type is None: | |
| 385 | 386 | if self.chk.in_checked_function() and node.items: | |
| 386 | 387 | self.chk.handle_cannot_determine_type(node.name, e) | |
| 387 | 388 | result = AnyType(TypeOfAny.from_error) | |
| 388 | 389 | else: | |
| 389 | 390 | result = node.type | |
| 390 | - elif isinstance(node, TypeInfo): | ||
| 391 | - # Reference to a type object. | ||
| 392 | - if node.typeddict_type: | ||
| 393 | - # We special-case TypedDict, because they don't define any constructor. | ||
| 394 | - result = self.typeddict_callable(node) | ||
| 395 | - elif node.fullname == "types.NoneType": | ||
| 396 | - # We special case NoneType, because its stub definition is not related to None. | ||
| 397 | - result = TypeType(NoneType()) | ||
| 398 | - else: | ||
| 399 | - result = type_object_type(node, self.named_type) | ||
| 391 | + elif isinstance(node, (FuncDef, TypeInfo, TypeAlias, MypyFile, TypeVarLikeExpr)): | ||
| 392 | + result = self.analyze_static_reference(node, e, e.is_alias_rvalue or lvalue) | ||
| 393 | + else: | ||
| 394 | + if isinstance(node, PlaceholderNode): | ||
| 395 | + assert False, f"PlaceholderNode {node.fullname!r} leaked to checker" | ||
| 396 | + # Unknown reference; use any type implicitly to avoid | ||
| 397 | + # generating extra type errors. | ||
| 398 | + result = AnyType(TypeOfAny.from_error) | ||
| 399 | + if isinstance(node, TypeInfo): | ||
| 400 | 400 | if isinstance(result, CallableType) and isinstance( # type: ignore[misc] | |
| 401 | 401 | result.ret_type, Instance | |
| 402 | 402 | ): | |
@@ -408,30 +408,56 @@ def analyze_ref_expr(self, e: RefExpr, lvalue: bool = False) -> Type: | |||
| 408 | 408 | # This is the type in a type[] expression, so substitute type | |
| 409 | 409 | # variables with Any. | |
| 410 | 410 | result = erasetype.erase_typevars(result) | |
| 411 | - elif isinstance(node, MypyFile): | ||
| 412 | - # Reference to a module object. | ||
| 413 | - result = self.module_type(node) | ||
| 414 | - elif isinstance(node, Decorator): | ||
| 415 | - result = self.analyze_var_ref(node.var, e) | ||
| 411 | + assert result is not None | ||
| 412 | + return result | ||
| 413 | + | ||
| 414 | + def analyze_static_reference( | ||
| 415 | + self, | ||
| 416 | + node: SymbolNode, | ||
| 417 | + ctx: Context, | ||
| 418 | + is_lvalue: bool, | ||
| 419 | + *, | ||
| 420 | + include_modules: bool = True, | ||
| 421 | + suppress_errors: bool = False, | ||
| 422 | + ) -> Type: | ||
| 423 | + """ | ||
| 424 | + This is the version of analyze_ref_expr() that doesn't do any deferrals. | ||
| 425 | + | ||
| 426 | + This function can be used by member access to "static" attributes. For example, | ||
| 427 | + when accessing module attributes in protocol checks, or accessing attributes of | ||
| 428 | + special kinds (like TypeAlias, TypeInfo, etc.) on an instance or class object. | ||
| 429 | + # TODO: merge with analyze_ref_expr() when we are confident about performance. | ||
| 430 | + """ | ||
| 431 | + if isinstance(node, (Var, Decorator, OverloadedFuncDef)): | ||
| 432 | + return node.type or AnyType(TypeOfAny.special_form) | ||
| 433 | + elif isinstance(node, FuncDef): | ||
| 434 | + return function_type(node, self.named_type("builtins.function")) | ||
| 435 | + elif isinstance(node, TypeInfo): | ||
| 436 | + # Reference to a type object. | ||
| 437 | + if node.typeddict_type: | ||
| 438 | + # We special-case TypedDict, because they don't define any constructor. | ||
| 439 | + return self.typeddict_callable(node) | ||
| 440 | + elif node.fullname == "types.NoneType": | ||
| 441 | + # We special case NoneType, because its stub definition is not related to None. | ||
| 442 | + return TypeType(NoneType()) | ||
| 443 | + else: | ||
| 444 | + return type_object_type(node, self.named_type) | ||
| 416 | 445 | elif isinstance(node, TypeAlias): | |
| 417 | 446 | # Something that refers to a type alias appears in runtime context. | |
| 418 | 447 | # Note that we suppress bogus errors for alias redefinitions, | |
| 419 | 448 | # they are already reported in semanal.py. | |
| 420 | - result = self.alias_type_in_runtime_context( | ||
| 421 | - node, ctx=e, alias_definition=e.is_alias_rvalue or lvalue | ||
| 422 | - ) | ||
| 449 | + with self.msg.filter_errors() if suppress_errors else nullcontext(): | ||
| 450 | + return self.alias_type_in_runtime_context( | ||
| 451 | + node, ctx=ctx, alias_definition=is_lvalue | ||
| 452 | + ) | ||
| 423 | 453 | elif isinstance(node, TypeVarExpr): | |
| 424 | 454 | return self.named_type("typing.TypeVar") | |
| 425 | 455 | elif isinstance(node, (ParamSpecExpr, TypeVarTupleExpr)): | |
| 426 | - result = self.object_type() | ||
| 427 | - else: | ||
| 428 | - if isinstance(node, PlaceholderNode): | ||
| 429 | - assert False, f"PlaceholderNode {node.fullname!r} leaked to checker" | ||
| 430 | - # Unknown reference; use any type implicitly to avoid | ||
| 431 | - # generating extra type errors. | ||
| 432 | - result = AnyType(TypeOfAny.from_error) | ||
| 433 | - assert result is not None | ||
| 434 | - return result | ||
| 456 | + return self.object_type() | ||
| 457 | + elif isinstance(node, MypyFile): | ||
| 458 | + # Reference to a module object. | ||
| 459 | + return self.module_type(node) if include_modules else AnyType(TypeOfAny.special_form) | ||
| 460 | + return AnyType(TypeOfAny.from_error) | ||
| 435 | 461 | ||
| 436 | 462 | def analyze_var_ref(self, var: Var, context: Context) -> Type: | |
| 437 | 463 | if var.type: | |
@@ -459,20 +485,21 @@ def module_type(self, node: MypyFile) -> Instance: | |||
| 459 | 485 | # Fall back to a dummy 'object' type instead to | |
| 460 | 486 | # avoid a crash. | |
| 461 | 487 | result = self.named_type("builtins.object") | |
| 462 | - module_attrs = {} | ||
| 488 | + module_attrs: dict[str, Type] = {} | ||
| 463 | 489 | immutable = set() | |
| 464 | 490 | for name, n in node.names.items(): | |
| 465 | 491 | if not n.module_public: | |
| 466 | 492 | continue | |
| 467 | 493 | if isinstance(n.node, Var) and n.node.is_final: | |
| 468 | 494 | immutable.add(name) | |
| 469 | - typ = self.chk.determine_type_of_member(n) | ||
| 470 | - if typ: | ||
| 471 | - module_attrs[name] = typ | ||
| 495 | + if n.node is None: | ||
| 496 | + module_attrs[name] = AnyType(TypeOfAny.from_error) | ||
| 472 | 497 | else: | |
| 473 | 498 | # TODO: what to do about nested module references? | |
| 474 | 499 | # They are non-trivial because there may be import cycles. | |
| 475 | - module_attrs[name] = AnyType(TypeOfAny.special_form) | ||
| 500 | + module_attrs[name] = self.analyze_static_reference( | ||
| 501 | + n.node, n.node, False, include_modules=False, suppress_errors=True | ||
| 502 | + ) | ||
| 476 | 503 | result.extra_attrs = ExtraAttrs(module_attrs, immutable, node.fullname) | |
| 477 | 504 | return result | |
| 478 | 505 | ||
@@ -961,19 +988,11 @@ def typeddict_callable(self, info: TypeInfo) -> CallableType: | |||
| 961 | 988 | assert info.special_alias is not None | |
| 962 | 989 | target = info.special_alias.target | |
| 963 | 990 | assert isinstance(target, ProperType) and isinstance(target, TypedDictType) | |
| 964 | - expected_types = list(target.items.values()) | ||
| 965 | - kinds = [ArgKind.ARG_NAMED] * len(expected_types) | ||
| 966 | - names = list(target.items.keys()) | ||
| 967 | - return CallableType( | ||
| 968 | - expected_types, | ||
| 969 | - kinds, | ||
| 970 | - names, | ||
| 971 | - target, | ||
| 972 | - self.named_type("builtins.type"), | ||
| 973 | - variables=info.defn.type_vars, | ||
| 974 | - ) | ||
| 991 | + return self.typeddict_callable_from_context(target, info.defn.type_vars) | ||
| 975 | 992 | ||
| 976 | - def typeddict_callable_from_context(self, callee: TypedDictType) -> CallableType: | ||
| 993 | + def typeddict_callable_from_context( | ||
| 994 | + self, callee: TypedDictType, variables: Sequence[TypeVarLikeType] | None = None | ||
| 995 | + ) -> CallableType: | ||
| 977 | 996 | return CallableType( | |
| 978 | 997 | list(callee.items.values()), | |
| 979 | 998 | [ | |
@@ -983,6 +1002,8 @@ def typeddict_callable_from_context(self, callee: TypedDictType) -> CallableType | |||
| 983 | 1002 | list(callee.items.keys()), | |
| 984 | 1003 | callee, | |
| 985 | 1004 | self.named_type("builtins.type"), | |
| 1005 | + variables=variables, | ||
| 1006 | + is_bound=True, | ||
| 986 | 1007 | ) | |
| 987 | 1008 | ||
| 988 | 1009 | def check_typeddict_call_with_kwargs( | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -34,7 +34,7 @@ | |||
| 34 | 34 | TempNode, | |
| 35 | 35 | TypeAlias, | |
| 36 | 36 | TypeInfo, | |
| 37 | - TypeVarExpr, | ||
| 37 | + TypeVarLikeExpr, | ||
| 38 | 38 | Var, | |
| 39 | 39 | is_final_node, | |
| 40 | 40 | ) | |
@@ -49,7 +49,6 @@ | |||
| 49 | 49 | make_simplified_union, | |
| 50 | 50 | supported_self_type, | |
| 51 | 51 | tuple_fallback, | |
| 52 | - type_object_type, | ||
| 53 | 52 | ) | |
| 54 | 53 | from mypy.types import ( | |
| 55 | 54 | AnyType, | |
@@ -537,24 +536,20 @@ def analyze_member_var_access( | |||
| 537 | 536 | is_trivial_self = vv.func.is_trivial_self and not vv.decorators | |
| 538 | 537 | if mx.is_super and not mx.suppress_errors: | |
| 539 | 538 | validate_super_call(vv.func, mx) | |
| 539 | + if isinstance(v, FuncDef): | ||
| 540 | + assert False, "Did not expect a function" | ||
| 541 | + if isinstance(v, MypyFile): | ||
| 542 | + mx.chk.module_refs.add(v.fullname) | ||
| 540 | 543 | ||
| 541 | - if isinstance(vv, TypeInfo): | ||
| 544 | + if isinstance(vv, (TypeInfo, TypeAlias, MypyFile, TypeVarLikeExpr)): | ||
| 542 | 545 | # If the associated variable is a TypeInfo synthesize a Var node for | |
| 543 | 546 | # the purposes of type checking. This enables us to type check things | |
| 544 | - # like accessing class attributes on an inner class. | ||
| 545 | - v = Var(name, type=type_object_type(vv, mx.named_type)) | ||
| 546 | - v.info = info | ||
| 547 | - | ||
| 548 | - if isinstance(vv, TypeAlias): | ||
| 549 | - # Similar to the above TypeInfo case, we allow using | ||
| 550 | - # qualified type aliases in runtime context if it refers to an | ||
| 551 | - # instance type. For example: | ||
| 547 | + # like accessing class attributes on an inner class. Similar we allow | ||
| 548 | + # using qualified type aliases in runtime context. For example: | ||
| 552 | 549 | # class C: | |
| 553 | 550 | # A = List[int] | |
| 554 | 551 | # x = C.A() <- this is OK | |
| 555 | - typ = mx.chk.expr_checker.alias_type_in_runtime_context( | ||
| 556 | - vv, ctx=mx.context, alias_definition=mx.is_lvalue | ||
| 557 | - ) | ||
| 552 | + typ = mx.chk.expr_checker.analyze_static_reference(vv, mx.context, mx.is_lvalue) | ||
| 558 | 553 | v = Var(name, type=typ) | |
| 559 | 554 | v.info = info | |
| 560 | 555 | ||
@@ -567,13 +562,6 @@ def analyze_member_var_access( | |||
| 567 | 562 | check_final_member(name, info, mx.msg, mx.context) | |
| 568 | 563 | ||
| 569 | 564 | return analyze_var(name, v, itype, mx, implicit=implicit, is_trivial_self=is_trivial_self) | |
| 570 | - elif isinstance(v, FuncDef): | ||
| 571 | - assert False, "Did not expect a function" | ||
| 572 | - elif isinstance(v, MypyFile): | ||
| 573 | - mx.chk.module_refs.add(v.fullname) | ||
| 574 | - return mx.chk.expr_checker.module_type(v) | ||
| 575 | - elif isinstance(v, TypeVarExpr): | ||
| 576 | - return mx.chk.named_type("typing.TypeVar") | ||
| 577 | 565 | elif ( | |
| 578 | 566 | not v | |
| 579 | 567 | and name not in ["__getattr__", "__setattr__", "__getattribute__"] | |
@@ -1259,29 +1247,9 @@ def analyze_class_attribute_access( | |||
| 1259 | 1247 | mx.not_ready_callback(name, mx.context) | |
| 1260 | 1248 | return AnyType(TypeOfAny.special_form) | |
| 1261 | 1249 | ||
| 1262 | - if isinstance(node.node, TypeVarExpr): | ||
| 1263 | - mx.fail(message_registry.CANNOT_USE_TYPEVAR_AS_EXPRESSION.format(info.name, name)) | ||
| 1264 | - return AnyType(TypeOfAny.from_error) | ||
| 1265 | - | ||
| 1266 | - # TODO: some logic below duplicates analyze_ref_expr in checkexpr.py | ||
| 1267 | - if isinstance(node.node, TypeInfo): | ||
| 1268 | - if node.node.typeddict_type: | ||
| 1269 | - # We special-case TypedDict, because they don't define any constructor. | ||
| 1270 | - return mx.chk.expr_checker.typeddict_callable(node.node) | ||
| 1271 | - elif node.node.fullname == "types.NoneType": | ||
| 1272 | - # We special case NoneType, because its stub definition is not related to None. | ||
| 1273 | - return TypeType(NoneType()) | ||
| 1274 | - else: | ||
| 1275 | - return type_object_type(node.node, mx.named_type) | ||
| 1276 | - | ||
| 1277 | - if isinstance(node.node, MypyFile): | ||
| 1278 | - # Reference to a module object. | ||
| 1279 | - return mx.named_type("types.ModuleType") | ||
| 1280 | - | ||
| 1281 | - if isinstance(node.node, TypeAlias): | ||
| 1282 | - return mx.chk.expr_checker.alias_type_in_runtime_context( | ||
| 1283 | - node.node, ctx=mx.context, alias_definition=mx.is_lvalue | ||
| 1284 | - ) | ||
| 1250 | + if isinstance(node.node, (TypeInfo, TypeAlias, MypyFile, TypeVarLikeExpr)): | ||
| 1251 | + # TODO: should we apply class plugin here (similar to instance access)? | ||
| 1252 | + return mx.chk.expr_checker.analyze_static_reference(node.node, mx.context, mx.is_lvalue) | ||
| 1285 | 1253 | ||
| 1286 | 1254 | if is_decorated: | |
| 1287 | 1255 | assert isinstance(node.node, Decorator) | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -188,7 +188,6 @@ def with_additional_msg(self, info: str) -> ErrorMessage: | |||
| 188 | 188 | ||
| 189 | 189 | # TypeVar | |
| 190 | 190 | INCOMPATIBLE_TYPEVAR_VALUE: Final = 'Value of type variable "{}" of {} cannot be {}' | |
| 191 | - CANNOT_USE_TYPEVAR_AS_EXPRESSION: Final = 'Type variable "{}.{}" cannot be used as an expression' | ||
| 192 | 191 | INVALID_TYPEVAR_AS_TYPEARG: Final = 'Type variable "{}" not valid as type argument value for "{}"' | |
| 193 | 192 | INVALID_TYPEVAR_ARG_BOUND: Final = 'Type argument {} of "{}" must be a subtype of {}' | |
| 194 | 193 | INVALID_TYPEVAR_ARG_VALUE: Final = 'Invalid type argument value for "{}"' | |
| Back | FazBrowse Home | New Git URL |
0 commit comments