[red-knot] a few metaclass cleanups (#14142)

Just cleaning up a few small things I noticed in post-land review.
This commit is contained in:
Carl Meyer 2024-11-06 14:13:39 -08:00 committed by GitHub
parent 626f716de6
commit 03a5788aa1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 18 additions and 11 deletions

View file

@ -21,16 +21,16 @@ reveal_type(type.__class__) # revealed: Literal[type]
## Basic
```py
class A(type): ...
class B(metaclass=A): ...
class M(type): ...
class B(metaclass=M): ...
reveal_type(B.__class__) # revealed: Literal[A]
reveal_type(B.__class__) # revealed: Literal[M]
```
## Invalid metaclass
If a class is a subclass of a class with a custom metaclass, then the subclass will also have that
metaclass.
A class which doesn't inherit `type` (and/or doesn't implement a custom `__new__` accepting the same
arguments as `type.__new__`) isn't a valid metaclass.
```py
class M: ...
@ -139,13 +139,14 @@ from nonexistent_module import UnknownClass # error: [unresolved-import]
class C(UnknownClass): ...
# TODO: should be `type[type] & Unknown`
reveal_type(C.__class__) # revealed: Literal[type]
class M(type): ...
class A(metaclass=M): ...
class B(A, UnknownClass): ...
# TODO: This should resolve to `type[M] | Unknown` instead
# TODO: should be `type[M] & Unknown`
reveal_type(B.__class__) # revealed: Literal[M]
```
@ -161,12 +162,16 @@ reveal_type(B.__class__) # revealed: Literal[M]
## Non-class
When a class has an explicit `metaclass` that is not a class, the value should be returned as is.
When a class has an explicit `metaclass` that is not a class, but is a callable that accepts
`type.__new__` arguments, we should return the meta type of its return type.
```py
class A(metaclass=1): ...
def f(*args, **kwargs) -> int: ...
reveal_type(A.__class__) # revealed: Literal[1]
class A(metaclass=f): ...
# TODO should be `type[int]`
reveal_type(A.__class__) # revealed: @Todo
```
## Cyclic

View file

@ -2100,8 +2100,10 @@ impl<'db> Class<'db> {
};
let Type::ClassLiteral(mut candidate) = metaclass else {
// If the metaclass is not a class, return it directly.
return Ok(metaclass);
// TODO: If the metaclass is not a class, we should verify that it's a callable
// which accepts the same arguments as `type.__new__` (otherwise error), and return
// the meta-type of its return type. (And validate that is a class type?)
return Ok(Type::Todo);
};
// Reconcile all base classes' metaclasses with the candidate metaclass.