[red-knot] Use type[Unknown] rather than Unknown as the fallback metaclass for invalid classes (#14961)

This commit is contained in:
Alex Waygood 2024-12-13 19:48:51 +00:00 committed by GitHub
parent 4b2b126b9f
commit 90a5439791
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 8 additions and 10 deletions

View file

@ -68,7 +68,7 @@ class B(metaclass=M2): ...
# error: [conflicting-metaclass] "The metaclass of a derived class (`C`) must be a subclass of the metaclasses of all its bases, but `M1` (metaclass of base class `A`) and `M2` (metaclass of base class `B`) have no subclass relationship" # error: [conflicting-metaclass] "The metaclass of a derived class (`C`) must be a subclass of the metaclasses of all its bases, but `M1` (metaclass of base class `A`) and `M2` (metaclass of base class `B`) have no subclass relationship"
class C(A, B): ... class C(A, B): ...
reveal_type(C.__class__) # revealed: Unknown reveal_type(C.__class__) # revealed: type[Unknown]
``` ```
## Conflict (2) ## Conflict (2)
@ -85,7 +85,7 @@ class A(metaclass=M1): ...
# error: [conflicting-metaclass] "The metaclass of a derived class (`B`) must be a subclass of the metaclasses of all its bases, but `M2` (metaclass of `B`) and `M1` (metaclass of base class `A`) have no subclass relationship" # error: [conflicting-metaclass] "The metaclass of a derived class (`B`) must be a subclass of the metaclasses of all its bases, but `M2` (metaclass of `B`) and `M1` (metaclass of base class `A`) have no subclass relationship"
class B(A, metaclass=M2): ... class B(A, metaclass=M2): ...
reveal_type(B.__class__) # revealed: Unknown reveal_type(B.__class__) # revealed: type[Unknown]
``` ```
## Common metaclass ## Common metaclass
@ -129,7 +129,7 @@ class C(metaclass=M12): ...
# error: [conflicting-metaclass] "The metaclass of a derived class (`D`) must be a subclass of the metaclasses of all its bases, but `M1` (metaclass of base class `A`) and `M2` (metaclass of base class `B`) have no subclass relationship" # error: [conflicting-metaclass] "The metaclass of a derived class (`D`) must be a subclass of the metaclasses of all its bases, but `M1` (metaclass of base class `A`) and `M2` (metaclass of base class `B`) have no subclass relationship"
class D(A, B, C): ... class D(A, B, C): ...
reveal_type(D.__class__) # revealed: Unknown reveal_type(D.__class__) # revealed: type[Unknown]
``` ```
## Unknown ## Unknown
@ -183,7 +183,7 @@ class A(B): ... # error: [cyclic-class-definition]
class B(C): ... # error: [cyclic-class-definition] class B(C): ... # error: [cyclic-class-definition]
class C(A): ... # error: [cyclic-class-definition] class C(A): ... # error: [cyclic-class-definition]
reveal_type(A.__class__) # revealed: Unknown reveal_type(A.__class__) # revealed: type[Unknown]
``` ```
## PEP 695 generic ## PEP 695 generic

View file

@ -2925,10 +2925,10 @@ impl<'db> Class<'db> {
Some(metaclass_ty) Some(metaclass_ty)
} }
/// Return the metaclass of this class, or `Unknown` if the metaclass cannot be inferred. /// Return the metaclass of this class, or `type[Unknown]` if the metaclass cannot be inferred.
pub(crate) fn metaclass(self, db: &'db dyn Db) -> Type<'db> { pub(crate) fn metaclass(self, db: &'db dyn Db) -> Type<'db> {
// TODO: `type[Unknown]` would be a more precise fallback self.try_metaclass(db)
self.try_metaclass(db).unwrap_or(Type::Unknown) .unwrap_or_else(|_| Type::subclass_of_base(ClassBase::Unknown))
} }
/// Return the metaclass of this class, or an error if the metaclass cannot be inferred. /// Return the metaclass of this class, or an error if the metaclass cannot be inferred.
@ -2941,9 +2941,7 @@ impl<'db> Class<'db> {
// We emit diagnostics for cyclic class definitions elsewhere. // We emit diagnostics for cyclic class definitions elsewhere.
// Avoid attempting to infer the metaclass if the class is cyclically defined: // Avoid attempting to infer the metaclass if the class is cyclically defined:
// it would be easy to enter an infinite loop. // it would be easy to enter an infinite loop.
// return Ok(Type::subclass_of_base(ClassBase::Unknown));
// TODO: `type[Unknown]` might be better here?
return Ok(Type::Unknown);
} }
let explicit_metaclass = self.explicit_metaclass(db); let explicit_metaclass = self.explicit_metaclass(db);