[red-knot] Fixup a few edge cases regarding type[] (#14918)

This commit is contained in:
Alex Waygood 2024-12-12 16:53:03 +00:00 committed by GitHub
parent 53f2d72e02
commit 58930905eb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 48 additions and 17 deletions

View file

@ -15,6 +15,8 @@ pub(crate) enum CoreStdlibModule {
TypingExtensions,
Typing,
Sys,
#[allow(dead_code)]
Abc, // currently only used in tests
}
impl CoreStdlibModule {
@ -26,6 +28,7 @@ impl CoreStdlibModule {
Self::Typeshed => "_typeshed",
Self::TypingExtensions => "typing_extensions",
Self::Sys => "sys",
Self::Abc => "abc",
}
}

View file

@ -654,14 +654,19 @@ impl<'db> Type<'db> {
},
)
}
(Type::ClassLiteral(self_class), Type::SubclassOf(target_class)) => {
self_class.class.is_subclass_of_base(db, target_class.base)
}
(
Type::Instance(InstanceType { class: self_class }),
Type::SubclassOf(target_class),
) if self_class.is_known(db, KnownClass::Type) => {
self_class.is_subclass_of_base(db, target_class.base)
Type::ClassLiteral(ClassLiteralType { class: self_class }),
Type::SubclassOf(SubclassOfType {
base: ClassBase::Class(target_class),
}),
) => self_class.is_subclass_of(db, target_class),
(
Type::Instance(_),
Type::SubclassOf(SubclassOfType {
base: ClassBase::Class(target_class),
}),
) if target_class.is_known(db, KnownClass::Object) => {
self.is_subtype_of(db, KnownClass::Type.to_instance(db))
}
(
Type::SubclassOf(SubclassOfType {
@ -928,10 +933,18 @@ impl<'db> Type<'db> {
| Type::ClassLiteral(..)),
) => left != right,
(Type::SubclassOf(type_class), Type::ClassLiteral(class_literal))
| (Type::ClassLiteral(class_literal), Type::SubclassOf(type_class)) => {
!class_literal.class.is_subclass_of_base(db, type_class.base)
}
(
Type::SubclassOf(SubclassOfType {
base: ClassBase::Class(class_a),
}),
Type::ClassLiteral(ClassLiteralType { class: class_b }),
)
| (
Type::ClassLiteral(ClassLiteralType { class: class_b }),
Type::SubclassOf(SubclassOfType {
base: ClassBase::Class(class_a),
}),
) => !class_b.is_subclass_of(db, class_a),
(Type::SubclassOf(_), Type::SubclassOf(_)) => false,
(Type::SubclassOf(_), Type::Instance(_)) | (Type::Instance(_), Type::SubclassOf(_)) => {
false
@ -2599,11 +2612,7 @@ impl<'db> Class<'db> {
pub fn is_subclass_of(self, db: &'db dyn Db, other: Class) -> bool {
// `is_subclass_of` is checking the subtype relation, in which gradual types do not
// participate, so we should not return `True` if we find `Any/Unknown` in the MRO.
self.is_subclass_of_base(db, other)
}
fn is_subclass_of_base(self, db: &'db dyn Db, other: impl Into<ClassBase<'db>>) -> bool {
self.iter_mro(db).contains(&other.into())
self.iter_mro(db).contains(&ClassBase::Class(other))
}
/// Return the explicit `metaclass` of this class, if one is defined.
@ -3038,12 +3047,18 @@ pub(crate) mod tests {
// BuiltinInstance("str") corresponds to an instance of the builtin `str` class
BuiltinInstance(&'static str),
TypingInstance(&'static str),
/// Members of the `abc` stdlib module
AbcInstance(&'static str),
AbcClassLiteral(&'static str),
TypingLiteral,
// BuiltinClassLiteral("str") corresponds to the builtin `str` class object itself
BuiltinClassLiteral(&'static str),
KnownClassInstance(KnownClass),
Union(Vec<Ty>),
Intersection { pos: Vec<Ty>, neg: Vec<Ty> },
Intersection {
pos: Vec<Ty>,
neg: Vec<Ty>,
},
Tuple(Vec<Ty>),
SubclassOfAny,
SubclassOfBuiltinClass(&'static str),
@ -3063,6 +3078,12 @@ pub(crate) mod tests {
Ty::LiteralString => Type::LiteralString,
Ty::BytesLiteral(s) => Type::bytes_literal(db, s.as_bytes()),
Ty::BuiltinInstance(s) => builtins_symbol(db, s).expect_type().to_instance(db),
Ty::AbcInstance(s) => core_module_symbol(db, CoreStdlibModule::Abc, s)
.expect_type()
.to_instance(db),
Ty::AbcClassLiteral(s) => {
core_module_symbol(db, CoreStdlibModule::Abc, s).expect_type()
}
Ty::TypingInstance(s) => typing_symbol(db, s).expect_type().to_instance(db),
Ty::TypingLiteral => Type::KnownInstance(KnownInstanceType::Literal),
Ty::BuiltinClassLiteral(s) => builtins_symbol(db, s).expect_type(),
@ -3148,6 +3169,7 @@ pub(crate) mod tests {
#[test_case(Ty::BuiltinInstance("type"), Ty::SubclassOfAny)]
#[test_case(Ty::BuiltinInstance("type"), Ty::SubclassOfBuiltinClass("object"))]
#[test_case(Ty::BuiltinInstance("type"), Ty::BuiltinInstance("type"))]
#[test_case(Ty::BuiltinClassLiteral("str"), Ty::SubclassOfAny)]
fn is_assignable_to(from: Ty, to: Ty) {
let db = setup_db();
assert!(from.into_type(&db).is_assignable_to(&db, to.into_type(&db)));
@ -3214,6 +3236,8 @@ pub(crate) mod tests {
#[test_case(Ty::BuiltinClassLiteral("int"), Ty::BuiltinInstance("object"))]
#[test_case(Ty::TypingLiteral, Ty::TypingInstance("_SpecialForm"))]
#[test_case(Ty::TypingLiteral, Ty::BuiltinInstance("object"))]
#[test_case(Ty::AbcClassLiteral("ABC"), Ty::AbcInstance("ABCMeta"))]
#[test_case(Ty::AbcInstance("ABCMeta"), Ty::SubclassOfBuiltinClass("object"))]
fn is_subtype_of(from: Ty, to: Ty) {
let db = setup_db();
assert!(from.into_type(&db).is_subtype_of(&db, to.into_type(&db)));
@ -3244,6 +3268,9 @@ pub(crate) mod tests {
#[test_case(Ty::BuiltinClassLiteral("int"), Ty::BuiltinClassLiteral("object"))]
#[test_case(Ty::BuiltinInstance("int"), Ty::BuiltinClassLiteral("int"))]
#[test_case(Ty::TypingInstance("_SpecialForm"), Ty::TypingLiteral)]
#[test_case(Ty::BuiltinInstance("type"), Ty::SubclassOfBuiltinClass("str"))]
#[test_case(Ty::BuiltinClassLiteral("str"), Ty::SubclassOfAny)]
#[test_case(Ty::AbcInstance("ABCMeta"), Ty::SubclassOfBuiltinClass("type"))]
fn is_not_subtype_of(from: Ty, to: Ty) {
let db = setup_db();
assert!(!from.into_type(&db).is_subtype_of(&db, to.into_type(&db)));
@ -3403,6 +3430,7 @@ pub(crate) mod tests {
#[test_case(Ty::Intersection{pos: vec![Ty::BuiltinInstance("int"), Ty::IntLiteral(2)], neg: vec![]}, Ty::IntLiteral(2))]
#[test_case(Ty::Tuple(vec![Ty::IntLiteral(1), Ty::IntLiteral(2)]), Ty::Tuple(vec![Ty::IntLiteral(1), Ty::BuiltinInstance("int")]))]
#[test_case(Ty::BuiltinClassLiteral("str"), Ty::BuiltinInstance("type"))]
#[test_case(Ty::BuiltinClassLiteral("str"), Ty::SubclassOfAny)]
fn is_not_disjoint_from(a: Ty, b: Ty) {
let db = setup_db();
let a = a.into_type(&db);