diff --git a/mypy/semanal.py b/mypy/semanal.py index 597f23fc92a7e..ec503d9d8ad24 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -1985,15 +1985,27 @@ def analyze_metaclass(self, defn: ClassDef) -> None: if isinstance(sym.node, PlaceholderNode): self.defer(defn) return - if not isinstance(sym.node, TypeInfo) or sym.node.tuple_type is not None: + + # Support type aliases, like `_Meta: TypeAlias = type` + if ( + isinstance(sym.node, TypeAlias) + and sym.node.no_args + and isinstance(sym.node.target, ProperType) + and isinstance(sym.node.target, Instance) + ): + metaclass_info: Optional[Node] = sym.node.target.type + else: + metaclass_info = sym.node + + if not isinstance(metaclass_info, TypeInfo) or metaclass_info.tuple_type is not None: self.fail(f'Invalid metaclass "{metaclass_name}"', defn.metaclass) return - if not sym.node.is_metaclass(): + if not metaclass_info.is_metaclass(): self.fail( 'Metaclasses not inheriting from "type" are not supported', defn.metaclass ) return - inst = fill_typevars(sym.node) + inst = fill_typevars(metaclass_info) assert isinstance(inst, Instance) defn.info.declared_metaclass = inst defn.info.metaclass_type = defn.info.calculate_metaclass_type() diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test index 744c3d778ea51..a620c63ef58be 100644 --- a/test-data/unit/check-classes.test +++ b/test-data/unit/check-classes.test @@ -4486,6 +4486,57 @@ class A(metaclass=M): reveal_type(A.y) # N: Revealed type is "builtins.int" A.x # E: "Type[A]" has no attribute "x" +[case testValidTypeAliasAsMetaclass] +from typing_extensions import TypeAlias + +Explicit: TypeAlias = type +Implicit = type + +class E(metaclass=Explicit): ... +class I(metaclass=Implicit): ... +[builtins fixtures/classmethod.pyi] + +[case testValidTypeAliasOfTypeAliasAsMetaclass] +from typing_extensions import TypeAlias + +Explicit: TypeAlias = type +Implicit = type + +A1: TypeAlias = Explicit +A2 = Explicit +A3: TypeAlias = Implicit +A4 = Implicit + +class C1(metaclass=A1): ... +class C2(metaclass=A2): ... +class C3(metaclass=A3): ... +class C4(metaclass=A4): ... +[builtins fixtures/classmethod.pyi] + +[case testTypeAliasWithArgsAsMetaclass] +from typing import Generic, TypeVar +from typing_extensions import TypeAlias + +T = TypeVar('T') +class Meta(Generic[T]): ... + +Explicit: TypeAlias = Meta[T] +Implicit = Meta[T] + +class E(metaclass=Explicit): ... # E: Invalid metaclass "Explicit" +class I(metaclass=Implicit): ... # E: Invalid metaclass "Implicit" +[builtins fixtures/classmethod.pyi] + +[case testTypeAliasNonTypeAsMetaclass] +from typing_extensions import TypeAlias + +Explicit: TypeAlias = int +Implicit = int + +class E(metaclass=Explicit): ... # E: Metaclasses not inheriting from "type" are not supported +class I(metaclass=Implicit): ... # E: Metaclasses not inheriting from "type" are not supported +[builtins fixtures/classmethod.pyi] + [case testInvalidVariableAsMetaclass] from typing import Any M = 0 # type: int