[red-knot] Improve Type::is_disjoint_from() for KnownInstanceTypes (#15261)

This commit is contained in:
Alex Waygood 2025-01-05 22:49:42 +00:00 committed by GitHub
parent 980ce941c7
commit 0743838438
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -1194,19 +1194,22 @@ impl<'db> Type<'db> {
ty.bool(db).is_always_true()
}
(Type::SubclassOf(_), _) | (_, Type::SubclassOf(_)) => {
// TODO: Once we have support for final classes, we can determine disjointness in some cases
(Type::SubclassOf(_), other) | (other, Type::SubclassOf(_)) => {
// TODO: Once we have support for final classes, we can determine disjointness in more 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
other.is_disjoint_from(db, KnownClass::Type.to_instance(db))
}
(Type::KnownInstance(left), right) => {
left.instance_fallback(db).is_disjoint_from(db, right)
(Type::KnownInstance(known_instance), Type::Instance(InstanceType { class }))
| (Type::Instance(InstanceType { class }), Type::KnownInstance(known_instance)) => {
!known_instance.is_instance_of(db, class)
}
(left, Type::KnownInstance(right)) => {
left.is_disjoint_from(db, right.instance_fallback(db))
(known_instance_ty @ Type::KnownInstance(_), Type::Tuple(_))
| (Type::Tuple(_), known_instance_ty @ Type::KnownInstance(_)) => {
known_instance_ty.is_disjoint_from(db, KnownClass::Tuple.to_instance(db))
}
(
@ -2352,6 +2355,15 @@ impl<'db> KnownClass {
.unwrap_or(Type::subclass_of_base(ClassBase::Unknown))
}
/// Return `true` if this symbol can be resolved to a class definition `class` in typeshed,
/// *and* `class` is a subclass of `other`.
pub fn is_subclass_of(self, db: &'db dyn Db, other: Class<'db>) -> bool {
known_module_symbol(db, self.canonical_module(db), self.as_str())
.ignore_possibly_unbound()
.and_then(Type::into_class_literal)
.is_some_and(|ClassLiteralType { class }| class.is_subclass_of(db, other))
}
/// Return the module in which we should look up the definition for this class
pub(crate) fn canonical_module(self, db: &'db dyn Db) -> KnownModule {
match self {
@ -2738,6 +2750,10 @@ impl<'db> KnownInstanceType<'db> {
self.class().to_instance(db)
}
pub fn is_instance_of(self, db: &'db dyn Db, class: Class<'db>) -> bool {
self.class().is_subclass_of(db, class)
}
pub fn try_from_file_and_name(db: &'db dyn Db, file: File, symbol_name: &str) -> Option<Self> {
let candidate = match symbol_name {
"Any" => Self::Any,
@ -4160,6 +4176,8 @@ pub(crate) mod tests {
#[test_case(Ty::SubclassOfBuiltinClass("object"), Ty::None)]
#[test_case(Ty::SubclassOfBuiltinClass("str"), Ty::LiteralString)]
#[test_case(Ty::AlwaysFalsy, Ty::AlwaysTruthy)]
#[test_case(Ty::Tuple(vec![]), Ty::TypingLiteral)]
#[test_case(Ty::TypingLiteral, Ty::SubclassOfBuiltinClass("object"))]
fn is_disjoint_from(a: Ty, b: Ty) {
let db = setup_db();
let a = a.into_type(&db);