[red-knot] Treat classes as instances of their respective metaclasses in boolean tests (#15105)

## Summary

Follow-up to #15089.

## Test Plan

Markdown tests.

---------

Co-authored-by: Carl Meyer <carl@astral.sh>
This commit is contained in:
InSync 2024-12-23 08:30:51 +07:00 committed by GitHub
parent 3b27d5dbad
commit f764f59971
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 68 additions and 24 deletions

View file

@ -1206,14 +1206,6 @@ impl<'db> Type<'db> {
Type::SubclassOf(_),
) => true,
(Type::SubclassOf(_), _) | (_, Type::SubclassOf(_)) => {
// TODO: Once we have support for final classes, we can determine disjointness in some cases
// here. However, note that it might be better to turn `Type::SubclassOf('FinalClass')` into
// `Type::ClassLiteral('FinalClass')` during construction, instead of adding special cases for
// final classes inside `Type::SubclassOf` everywhere.
false
}
(Type::AlwaysTruthy, ty) | (ty, Type::AlwaysTruthy) => {
// `Truthiness::Ambiguous` may include `AlwaysTrue` as a subset, so it's not guaranteed to be disjoint.
// Thus, they are only disjoint if `ty.bool() == AlwaysFalse`.
@ -1224,6 +1216,14 @@ impl<'db> Type<'db> {
matches!(ty.bool(db), Truthiness::AlwaysTrue)
}
(Type::SubclassOf(_), _) | (_, Type::SubclassOf(_)) => {
// TODO: Once we have support for final classes, we can determine disjointness in some cases
// here. However, note that it might be better to turn `Type::SubclassOf('FinalClass')` into
// `Type::ClassLiteral('FinalClass')` during construction, instead of adding special cases for
// final classes inside `Type::SubclassOf` everywhere.
false
}
(Type::KnownInstance(left), right) => {
left.instance_fallback(db).is_disjoint_from(db, right)
}
@ -1678,15 +1678,13 @@ impl<'db> Type<'db> {
Type::Any | Type::Todo(_) | Type::Never | Type::Unknown => Truthiness::Ambiguous,
Type::FunctionLiteral(_) => Truthiness::AlwaysTrue,
Type::ModuleLiteral(_) => Truthiness::AlwaysTrue,
Type::ClassLiteral(_) => {
// TODO: lookup `__bool__` and `__len__` methods on the class's metaclass
// More info in https://docs.python.org/3/library/stdtypes.html#truth-value-testing
Truthiness::Ambiguous
}
Type::SubclassOf(_) => {
// TODO: see above
Truthiness::Ambiguous
Type::ClassLiteral(ClassLiteralType { class }) => {
class.metaclass(db).to_instance(db).bool(db)
}
Type::SubclassOf(SubclassOfType { base }) => base
.into_class()
.map(|class| Type::class_literal(class).bool(db))
.unwrap_or(Truthiness::Ambiguous),
Type::AlwaysTruthy => Truthiness::AlwaysTrue,
Type::AlwaysFalsy => Truthiness::AlwaysFalse,
instance_ty @ Type::Instance(InstanceType { class }) => {