|
155 | 155 | from mypy.scope import Scope |
156 | 156 | from mypy.semanal import is_trivial_body, refers_to_fullname, set_callable_name |
157 | 157 | from mypy.semanal_enum import ENUM_BASES, ENUM_SPECIAL_PROPS |
| 158 | +from mypy.semanal_shared import SemanticAnalyzerCoreInterface |
158 | 159 | from mypy.sharedparse import BINARY_MAGIC_METHODS |
159 | 160 | from mypy.state import state |
160 | 161 | from mypy.subtypes import ( |
@@ -346,6 +347,8 @@ class TypeChecker(NodeVisitor[None], TypeCheckerSharedApi): |
346 | 347 |
|
347 | 348 | tscope: Scope |
348 | 349 | scope: CheckerScope |
| 350 | + # Innermost enclosing type |
| 351 | + type: TypeInfo | None |
349 | 352 | # Stack of function return types |
350 | 353 | return_types: list[Type] |
351 | 354 | # Flags; true for dynamically typed functions |
@@ -423,6 +426,7 @@ def __init__( |
423 | 426 | self.scope = CheckerScope(tree) |
424 | 427 | self.binder = ConditionalTypeBinder(options) |
425 | 428 | self.globals = tree.names |
| 429 | + self.type = None |
426 | 430 | self.return_types = [] |
427 | 431 | self.dynamic_funcs = [] |
428 | 432 | self.partial_types = [] |
@@ -2661,7 +2665,11 @@ def visit_class_def(self, defn: ClassDef) -> None: |
2661 | 2665 | self.fail(message_registry.CANNOT_INHERIT_FROM_FINAL.format(base.name), defn) |
2662 | 2666 | if not can_have_shared_disjoint_base(typ.bases): |
2663 | 2667 | self.fail(message_registry.INCOMPATIBLE_DISJOINT_BASES.format(typ.name), defn) |
2664 | | - with self.tscope.class_scope(defn.info), self.enter_partial_types(is_class=True): |
| 2668 | + with ( |
| 2669 | + self.tscope.class_scope(defn.info), |
| 2670 | + self.enter_partial_types(is_class=True), |
| 2671 | + self.enter_class(defn.info), |
| 2672 | + ): |
2665 | 2673 | old_binder = self.binder |
2666 | 2674 | self.binder = ConditionalTypeBinder(self.options) |
2667 | 2675 | with self.binder.top_frame_context(): |
@@ -2729,6 +2737,15 @@ def visit_class_def(self, defn: ClassDef) -> None: |
2729 | 2737 | self.check_enum(defn) |
2730 | 2738 | infer_class_variances(defn.info) |
2731 | 2739 |
|
| 2740 | + @contextmanager |
| 2741 | + def enter_class(self, type: TypeInfo) -> Iterator[None]: |
| 2742 | + original_type = self.type |
| 2743 | + self.type = type |
| 2744 | + try: |
| 2745 | + yield |
| 2746 | + finally: |
| 2747 | + self.type = original_type |
| 2748 | + |
2732 | 2749 | def check_final_deletable(self, typ: TypeInfo) -> None: |
2733 | 2750 | # These checks are only for mypyc. Only perform some checks that are easier |
2734 | 2751 | # to implement here than in mypyc. |
@@ -8023,7 +8040,9 @@ def add_any_attribute_to_type(self, typ: Type, name: str) -> Type: |
8023 | 8040 | fallback = typ.fallback.copy_with_extra_attr(name, any_type) |
8024 | 8041 | return typ.copy_modified(fallback=fallback) |
8025 | 8042 | if isinstance(typ, TypeType) and isinstance(typ.item, Instance): |
8026 | | - return TypeType.make_normalized(self.add_any_attribute_to_type(typ.item, name)) |
| 8043 | + return TypeType.make_normalized( |
| 8044 | + self.add_any_attribute_to_type(typ.item, name), is_type_form=typ.is_type_form |
| 8045 | + ) |
8027 | 8046 | if isinstance(typ, TypeVarType): |
8028 | 8047 | return typ.copy_modified( |
8029 | 8048 | upper_bound=self.add_any_attribute_to_type(typ.upper_bound, name), |
@@ -8151,6 +8170,97 @@ def visit_global_decl(self, o: GlobalDecl, /) -> None: |
8151 | 8170 | return None |
8152 | 8171 |
|
8153 | 8172 |
|
| 8173 | +class TypeCheckerAsSemanticAnalyzer(SemanticAnalyzerCoreInterface): |
| 8174 | + """ |
| 8175 | + Adapts TypeChecker to the SemanticAnalyzerCoreInterface, |
| 8176 | + allowing most type expressions to be parsed during the TypeChecker pass. |
| 8177 | +
|
| 8178 | + See ExpressionChecker.try_parse_as_type_expression() to understand how this |
| 8179 | + class is used. |
| 8180 | + """ |
| 8181 | + |
| 8182 | + _chk: TypeChecker |
| 8183 | + _names: dict[str, SymbolTableNode] |
| 8184 | + did_fail: bool |
| 8185 | + |
| 8186 | + def __init__(self, chk: TypeChecker, names: dict[str, SymbolTableNode]) -> None: |
| 8187 | + self._chk = chk |
| 8188 | + self._names = names |
| 8189 | + self.did_fail = False |
| 8190 | + |
| 8191 | + def lookup_qualified( |
| 8192 | + self, name: str, ctx: Context, suppress_errors: bool = False |
| 8193 | + ) -> SymbolTableNode | None: |
| 8194 | + sym = self._names.get(name) |
| 8195 | + # All names being looked up should have been previously gathered, |
| 8196 | + # even if the related SymbolTableNode does not refer to a valid SymbolNode |
| 8197 | + assert sym is not None, name |
| 8198 | + return sym |
| 8199 | + |
| 8200 | + def lookup_fully_qualified(self, fullname: str, /) -> SymbolTableNode: |
| 8201 | + ret = self.lookup_fully_qualified_or_none(fullname) |
| 8202 | + assert ret is not None, fullname |
| 8203 | + return ret |
| 8204 | + |
| 8205 | + def lookup_fully_qualified_or_none(self, fullname: str, /) -> SymbolTableNode | None: |
| 8206 | + try: |
| 8207 | + return self._chk.lookup_qualified(fullname) |
| 8208 | + except KeyError: |
| 8209 | + return None |
| 8210 | + |
| 8211 | + def fail( |
| 8212 | + self, |
| 8213 | + msg: str, |
| 8214 | + ctx: Context, |
| 8215 | + serious: bool = False, |
| 8216 | + *, |
| 8217 | + blocker: bool = False, |
| 8218 | + code: ErrorCode | None = None, |
| 8219 | + ) -> None: |
| 8220 | + self.did_fail = True |
| 8221 | + |
| 8222 | + def note(self, msg: str, ctx: Context, *, code: ErrorCode | None = None) -> None: |
| 8223 | + pass |
| 8224 | + |
| 8225 | + def incomplete_feature_enabled(self, feature: str, ctx: Context) -> bool: |
| 8226 | + if feature not in self._chk.options.enable_incomplete_feature: |
| 8227 | + self.fail("__ignored__", ctx) |
| 8228 | + return False |
| 8229 | + return True |
| 8230 | + |
| 8231 | + def record_incomplete_ref(self) -> None: |
| 8232 | + pass |
| 8233 | + |
| 8234 | + def defer(self, debug_context: Context | None = None, force_progress: bool = False) -> None: |
| 8235 | + pass |
| 8236 | + |
| 8237 | + def is_incomplete_namespace(self, fullname: str) -> bool: |
| 8238 | + return False |
| 8239 | + |
| 8240 | + @property |
| 8241 | + def final_iteration(self) -> bool: |
| 8242 | + return True |
| 8243 | + |
| 8244 | + def is_future_flag_set(self, flag: str) -> bool: |
| 8245 | + return self._chk.tree.is_future_flag_set(flag) |
| 8246 | + |
| 8247 | + @property |
| 8248 | + def is_stub_file(self) -> bool: |
| 8249 | + return self._chk.tree.is_stub |
| 8250 | + |
| 8251 | + def is_func_scope(self) -> bool: |
| 8252 | + # Return arbitrary value. |
| 8253 | + # |
| 8254 | + # This method is currently only used to decide whether to pair |
| 8255 | + # a fail() message with a note() message or not. Both of those |
| 8256 | + # message types are ignored. |
| 8257 | + return False |
| 8258 | + |
| 8259 | + @property |
| 8260 | + def type(self) -> TypeInfo | None: |
| 8261 | + return self._chk.type |
| 8262 | + |
| 8263 | + |
8154 | 8264 | class CollectArgTypeVarTypes(TypeTraverserVisitor): |
8155 | 8265 | """Collects the non-nested argument types in a set.""" |
8156 | 8266 |
|
|
0 commit comments