diff --git a/mypy/semanal.py b/mypy/semanal.py index 7e428f06e9ba..761e88c27769 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -2074,12 +2074,17 @@ def get_declared_metaclass( # Probably a name error - it is already handled elsewhere return None, False if isinstance(sym.node, Var) and isinstance(get_proper_type(sym.node.type), AnyType): - # 'Any' metaclass -- just ignore it. - # - # TODO: A better approach would be to record this information - # and assume that the type object supports arbitrary - # attributes, similar to an 'Any' base class. - return None, False + # Create a fake TypeInfo that fallbacks to `Any`, basically allowing + # all the attributes. Same thing as we do for `Any` base class. + any_info = self.make_empty_type_info(ClassDef(sym.node.name, Block([]))) + any_info.fallback_to_any = True + any_info._fullname = sym.node.fullname + if self.options.disallow_subclassing_any: + self.fail( + f'Class cannot use "{any_info.fullname}" as a metaclass (has type "Any")', + metaclass_expr, + ) + return Instance(any_info, []), False if isinstance(sym.node, PlaceholderNode): return None, True # defer later in the caller diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test index 9bf2bbd839ed..1f7b60145de5 100644 --- a/test-data/unit/check-classes.test +++ b/test-data/unit/check-classes.test @@ -4392,6 +4392,35 @@ def f(TB: Type[B]): reveal_type(TB) # N: Revealed type is "Type[__main__.B]" reveal_type(TB.x) # N: Revealed type is "builtins.int" +[case testMetaclassAsAny] +from typing import Any, ClassVar + +MyAny: Any +class WithMeta(metaclass=MyAny): + x: ClassVar[int] + +reveal_type(WithMeta.a) # N: Revealed type is "Any" +reveal_type(WithMeta.m) # N: Revealed type is "Any" +reveal_type(WithMeta.x) # N: Revealed type is "builtins.int" +reveal_type(WithMeta().x) # N: Revealed type is "builtins.int" +WithMeta().m # E: "WithMeta" has no attribute "m" +WithMeta().a # E: "WithMeta" has no attribute "a" + +[case testMetaclassAsAnyWithAFlag] +# flags: --disallow-subclassing-any +from typing import Any, ClassVar + +MyAny: Any +class WithMeta(metaclass=MyAny): # E: Class cannot use "__main__.MyAny" as a metaclass (has type "Any") + x: ClassVar[int] + +reveal_type(WithMeta.a) # N: Revealed type is "Any" +reveal_type(WithMeta.m) # N: Revealed type is "Any" +reveal_type(WithMeta.x) # N: Revealed type is "builtins.int" +reveal_type(WithMeta().x) # N: Revealed type is "builtins.int" +WithMeta().m # E: "WithMeta" has no attribute "m" +WithMeta().a # E: "WithMeta" has no attribute "a" + [case testMetaclassIterable] from typing import Iterable, Iterator @@ -4476,15 +4505,7 @@ from missing import M class A(metaclass=M): y = 0 reveal_type(A.y) # N: Revealed type is "builtins.int" -A.x # E: "Type[A]" has no attribute "x" - -[case testAnyMetaclass] -from typing import Any -M = None # type: Any -class A(metaclass=M): - y = 0 -reveal_type(A.y) # N: Revealed type is "builtins.int" -A.x # E: "Type[A]" has no attribute "x" +reveal_type(A.x) # N: Revealed type is "Any" [case testValidTypeAliasAsMetaclass] from typing_extensions import TypeAlias