|
#[test_case(Ty::BuiltinInstance("str"), Ty::BuiltinInstance("object"))] |
|
#[test_case(Ty::BuiltinInstance("int"), Ty::BuiltinInstance("object"))] |
|
#[test_case(Ty::BuiltinInstance("bool"), Ty::BuiltinInstance("object"))] |
|
#[test_case(Ty::BuiltinInstance("bool"), Ty::BuiltinInstance("int"))] |
|
#[test_case(Ty::Never, Ty::IntLiteral(1))] |
|
#[test_case(Ty::IntLiteral(1), Ty::BuiltinInstance("int"))] |
|
#[test_case(Ty::IntLiteral(1), Ty::BuiltinInstance("object"))] |
|
#[test_case(Ty::BooleanLiteral(true), Ty::BuiltinInstance("bool"))] |
|
#[test_case(Ty::BooleanLiteral(true), Ty::BuiltinInstance("int"))] |
|
#[test_case(Ty::BooleanLiteral(true), Ty::BuiltinInstance("object"))] |
|
#[test_case(Ty::StringLiteral("foo"), Ty::BuiltinInstance("str"))] |
|
#[test_case(Ty::StringLiteral("foo"), Ty::BuiltinInstance("object"))] |
|
#[test_case(Ty::StringLiteral("foo"), Ty::LiteralString)] |
|
#[test_case(Ty::LiteralString, Ty::BuiltinInstance("str"))] |
|
#[test_case(Ty::LiteralString, Ty::BuiltinInstance("object"))] |
|
#[test_case(Ty::BytesLiteral("foo"), Ty::BuiltinInstance("bytes"))] |
|
#[test_case(Ty::BytesLiteral("foo"), Ty::BuiltinInstance("object"))] |
|
#[test_case(Ty::IntLiteral(1), Ty::Union(vec![Ty::BuiltinInstance("int"), Ty::BuiltinInstance("str")]))] |
|
#[test_case(Ty::Union(vec![Ty::BuiltinInstance("str"), Ty::BuiltinInstance("int")]), Ty::BuiltinInstance("object"))] |
|
#[test_case(Ty::Union(vec![Ty::IntLiteral(1), Ty::IntLiteral(2)]), Ty::Union(vec![Ty::IntLiteral(1), Ty::IntLiteral(2), Ty::IntLiteral(3)]))] |
|
#[test_case(Ty::BuiltinInstance("TypeError"), Ty::BuiltinInstance("Exception"))] |
|
#[test_case(Ty::Tuple(vec![]), Ty::Tuple(vec![]))] |
|
#[test_case(Ty::Tuple(vec![Ty::IntLiteral(42)]), Ty::Tuple(vec![Ty::BuiltinInstance("int")]))] |
|
#[test_case(Ty::Tuple(vec![Ty::IntLiteral(42), Ty::StringLiteral("foo")]), Ty::Tuple(vec![Ty::BuiltinInstance("int"), Ty::BuiltinInstance("str")]))] |
|
#[test_case(Ty::Tuple(vec![Ty::BuiltinInstance("int"), Ty::StringLiteral("foo")]), Ty::Tuple(vec![Ty::BuiltinInstance("int"), Ty::BuiltinInstance("str")]))] |
|
#[test_case(Ty::Tuple(vec![Ty::IntLiteral(42), Ty::BuiltinInstance("str")]), Ty::Tuple(vec![Ty::BuiltinInstance("int"), Ty::BuiltinInstance("str")]))] |
|
#[test_case( |
|
Ty::BuiltinInstance("FloatingPointError"), |
|
Ty::BuiltinInstance("Exception") |
|
)] |
|
#[test_case(Ty::Intersection{pos: vec![Ty::BuiltinInstance("int")], neg: vec![Ty::IntLiteral(2)]}, Ty::BuiltinInstance("int"))] |
|
#[test_case(Ty::Intersection{pos: vec![Ty::BuiltinInstance("int")], neg: vec![Ty::IntLiteral(2)]}, Ty::Intersection{pos: vec![], neg: vec![Ty::IntLiteral(2)]})] |
|
#[test_case(Ty::Intersection{pos: vec![], neg: vec![Ty::BuiltinInstance("int")]}, Ty::Intersection{pos: vec![], neg: vec![Ty::IntLiteral(2)]})] |
|
#[test_case(Ty::IntLiteral(1), Ty::Intersection{pos: vec![Ty::BuiltinInstance("int")], neg: vec![Ty::IntLiteral(2)]})] |
|
#[test_case(Ty::Intersection{pos: vec![Ty::BuiltinInstance("str")], neg: vec![Ty::StringLiteral("foo")]}, Ty::Intersection{pos: vec![], neg: vec![Ty::IntLiteral(2)]})] |
|
#[test_case(Ty::BuiltinClassLiteral("int"), Ty::BuiltinClassLiteral("int"))] |
|
#[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"))] |
|
#[test_case(Ty::Tuple(vec![Ty::BuiltinInstance("int")]), Ty::BuiltinInstance("tuple"))] |
|
#[test_case(Ty::BuiltinClassLiteral("str"), Ty::BuiltinInstance("type"))] |
|
#[test_case( |
|
Ty::StdlibModule(KnownModule::Typing), |
|
Ty::KnownClassInstance(KnownClass::ModuleType) |
|
)] |
|
#[test_case(Ty::SliceLiteral(1, 2, 3), Ty::BuiltinInstance("slice"))] |
|
#[test_case(Ty::SubclassOfBuiltinClass("str"), Ty::Intersection{pos: vec![], neg: vec![Ty::None]})] |
|
#[test_case(Ty::IntLiteral(1), Ty::AlwaysTruthy)] |
|
#[test_case(Ty::IntLiteral(0), Ty::AlwaysFalsy)] |
|
#[test_case(Ty::AlwaysTruthy, Ty::BuiltinInstance("object"))] |
|
#[test_case(Ty::AlwaysFalsy, Ty::BuiltinInstance("object"))] |
|
#[test_case(Ty::Never, Ty::AlwaysTruthy)] |
|
#[test_case(Ty::Never, Ty::AlwaysFalsy)] |
|
#[test_case(Ty::BuiltinClassLiteral("bool"), Ty::SubclassOfBuiltinClass("int"))] |
|
#[test_case(Ty::Intersection{pos: vec![], neg: vec![Ty::LiteralString]}, Ty::BuiltinInstance("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))); |
|
} |
|
|
|
#[test_case(Ty::BuiltinInstance("object"), Ty::BuiltinInstance("int"))] |
|
#[test_case(Ty::Unknown, Ty::Unknown)] |
|
#[test_case(Ty::Unknown, Ty::IntLiteral(1))] |
|
#[test_case(Ty::Any, Ty::Any)] |
|
#[test_case(Ty::Any, Ty::IntLiteral(1))] |
|
#[test_case(Ty::IntLiteral(1), Ty::Unknown)] |
|
#[test_case(Ty::IntLiteral(1), Ty::Any)] |
|
#[test_case(Ty::IntLiteral(1), Ty::Union(vec![Ty::Unknown, Ty::BuiltinInstance("str")]))] |
|
#[test_case(Ty::IntLiteral(1), Ty::BuiltinInstance("str"))] |
|
#[test_case(Ty::Union(vec![Ty::IntLiteral(1), Ty::IntLiteral(2)]), Ty::IntLiteral(1))] |
|
#[test_case(Ty::Union(vec![Ty::IntLiteral(1), Ty::IntLiteral(2)]), Ty::Union(vec![Ty::IntLiteral(1), Ty::IntLiteral(3)]))] |
|
#[test_case(Ty::BuiltinInstance("int"), Ty::BuiltinInstance("str"))] |
|
#[test_case(Ty::BuiltinInstance("int"), Ty::IntLiteral(1))] |
|
#[test_case(Ty::Tuple(vec![]), Ty::Tuple(vec![Ty::IntLiteral(1)]))] |
|
#[test_case(Ty::Tuple(vec![Ty::IntLiteral(42)]), Ty::Tuple(vec![Ty::BuiltinInstance("str")]))] |
|
#[test_case(Ty::Tuple(vec![Ty::Todo]), Ty::Tuple(vec![Ty::IntLiteral(2)]))] |
|
#[test_case(Ty::Tuple(vec![Ty::IntLiteral(2)]), Ty::Tuple(vec![Ty::Todo]))] |
|
#[test_case(Ty::Intersection{pos: vec![Ty::BuiltinInstance("int")], neg: vec![Ty::IntLiteral(2)]}, Ty::Intersection{pos: vec![Ty::BuiltinInstance("int")], neg: vec![Ty::IntLiteral(3)]})] |
|
#[test_case(Ty::Intersection{pos: vec![], neg: vec![Ty::IntLiteral(2)]}, Ty::Intersection{pos: vec![], neg: vec![Ty::IntLiteral(3)]})] |
|
#[test_case(Ty::Intersection{pos: vec![], neg: vec![Ty::IntLiteral(2)]}, Ty::Intersection{pos: vec![], neg: vec![Ty::BuiltinInstance("int")]})] |
|
#[test_case(Ty::BuiltinInstance("int"), Ty::Intersection{pos: vec![], neg: vec![Ty::IntLiteral(3)]})] |
|
#[test_case(Ty::IntLiteral(1), Ty::Intersection{pos: vec![Ty::BuiltinInstance("int")], neg: vec![Ty::IntLiteral(1)]})] |
|
#[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"))] |
|
#[test_case(Ty::SubclassOfBuiltinClass("str"), Ty::BuiltinClassLiteral("str"))] |
|
#[test_case(Ty::IntLiteral(1), Ty::AlwaysFalsy)] |
|
#[test_case(Ty::IntLiteral(0), Ty::AlwaysTruthy)] |
|
#[test_case(Ty::BuiltinInstance("str"), Ty::AlwaysTruthy)] |
|
#[test_case(Ty::BuiltinInstance("str"), Ty::AlwaysFalsy)] |
|
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))); |
|
} |
|
|
|
#[test] |
|
fn is_subtype_of_class_literals() { |
|
let mut db = setup_db(); |
|
db.write_dedented( |
|
"/src/module.py", |
|
" |
|
class Base: ... |
|
class Derived(Base): ... |
|
class Unrelated: ... |
|
U = Base if flag else Unrelated |
|
", |
|
) |
|
.unwrap(); |
|
let module = ruff_db::files::system_path_to_file(&db, "/src/module.py").unwrap(); |
|
|
|
// `literal_base` represents `Literal[Base]`. |
|
let literal_base = super::global_symbol(&db, module, "Base").expect_type(); |
|
let literal_derived = super::global_symbol(&db, module, "Derived").expect_type(); |
|
let u = super::global_symbol(&db, module, "U").expect_type(); |
|
|
|
assert!(literal_base.is_class_literal()); |
|
assert!(literal_base.is_subtype_of(&db, Ty::BuiltinInstance("type").into_type(&db))); |
|
assert!(literal_base.is_subtype_of(&db, Ty::BuiltinInstance("object").into_type(&db))); |
|
|
|
assert!(literal_derived.is_class_literal()); |
|
|
|
// `subclass_of_base` represents `Type[Base]`. |
|
let subclass_of_base = SubclassOfType::from(&db, literal_base.expect_class_literal().class); |
|
assert!(literal_base.is_subtype_of(&db, subclass_of_base)); |
|
assert!(literal_derived.is_subtype_of(&db, subclass_of_base)); |
|
|
|
let subclass_of_derived = |
|
SubclassOfType::from(&db, literal_derived.expect_class_literal().class); |
|
assert!(literal_derived.is_subtype_of(&db, subclass_of_derived)); |
|
assert!(!literal_base.is_subtype_of(&db, subclass_of_derived)); |
|
|
|
// Type[Derived] <: Type[Base] |
|
assert!(subclass_of_derived.is_subtype_of(&db, subclass_of_base)); |
|
|
|
assert!(u.is_union()); |
|
assert!(u.is_subtype_of(&db, Ty::BuiltinInstance("type").into_type(&db))); |
|
assert!(u.is_subtype_of(&db, Ty::BuiltinInstance("object").into_type(&db))); |
|
} |
|
|
|
#[test] |
|
fn is_subtype_of_intersection_of_class_instances() { |
|
let mut db = setup_db(); |
|
db.write_dedented( |
|
"/src/module.py", |
|
" |
|
class A: ... |
|
a = A() |
|
class B: ... |
|
b = B() |
|
", |
|
) |
|
.unwrap(); |
|
let module = ruff_db::files::system_path_to_file(&db, "/src/module.py").unwrap(); |
|
|
|
let a_ty = super::global_symbol(&db, module, "a").expect_type(); |
|
let b_ty = super::global_symbol(&db, module, "b").expect_type(); |
|
let intersection = IntersectionBuilder::new(&db) |
|
.add_positive(a_ty) |
|
.add_positive(b_ty) |
|
.build(); |
|
|
|
assert_eq!(intersection.display(&db).to_string(), "A & B"); |
|
assert!(!a_ty.is_subtype_of(&db, b_ty)); |
|
assert!(intersection.is_subtype_of(&db, b_ty)); |
|
assert!(intersection.is_subtype_of(&db, a_ty)); |
|
} |
|
|
|
#[test_case( |
|
Ty::Union(vec![Ty::IntLiteral(1), Ty::IntLiteral(2)]), |
|
Ty::Union(vec![Ty::IntLiteral(1), Ty::IntLiteral(2)]) |
|
)] |
|
#[test_case(Ty::SubclassOfBuiltinClass("object"), Ty::BuiltinInstance("type"))] |
|
fn is_equivalent_to(from: Ty, to: Ty) { |
|
let db = setup_db(); |
|
let from = from.into_type(&db); |
|
let to = to.into_type(&db); |
|
assert!(from.is_equivalent_to(&db, to)); |
|
assert!(to.is_equivalent_to(&db, from)); |
|
} |
|
|
|
#[test_case(Ty::Any, Ty::Any)] |
|
#[test_case(Ty::Any, Ty::None)] |
|
#[test_case(Ty::Unknown, Ty::Unknown)] |
|
#[test_case(Ty::Todo, Ty::Todo)] |
|
#[test_case(Ty::Union(vec![Ty::IntLiteral(1), Ty::IntLiteral(2)]), Ty::Union(vec![Ty::IntLiteral(1), Ty::IntLiteral(0)]))] |
|
#[test_case(Ty::Union(vec![Ty::IntLiteral(1), Ty::IntLiteral(2)]), Ty::Union(vec![Ty::IntLiteral(1), Ty::IntLiteral(2), Ty::IntLiteral(3)]))] |
|
fn is_not_equivalent_to(from: Ty, to: Ty) { |
|
let db = setup_db(); |
|
let from = from.into_type(&db); |
|
let to = to.into_type(&db); |
|
assert!(!from.is_equivalent_to(&db, to)); |
|
assert!(!to.is_equivalent_to(&db, from)); |
|
} |
|
|
|
#[test_case(Ty::Never, Ty::Never)] |
|
#[test_case(Ty::Never, Ty::None)] |
|
#[test_case(Ty::Never, Ty::BuiltinInstance("int"))] |
|
#[test_case(Ty::None, Ty::BooleanLiteral(true))] |
|
#[test_case(Ty::None, Ty::IntLiteral(1))] |
|
#[test_case(Ty::None, Ty::StringLiteral("test"))] |
|
#[test_case(Ty::None, Ty::BytesLiteral("test"))] |
|
#[test_case(Ty::None, Ty::LiteralString)] |
|
#[test_case(Ty::None, Ty::BuiltinInstance("int"))] |
|
#[test_case(Ty::None, Ty::Tuple(vec![Ty::None]))] |
|
#[test_case(Ty::BooleanLiteral(true), Ty::BooleanLiteral(false))] |
|
#[test_case(Ty::BooleanLiteral(true), Ty::Tuple(vec![Ty::None]))] |
|
#[test_case(Ty::BooleanLiteral(true), Ty::IntLiteral(1))] |
|
#[test_case(Ty::BooleanLiteral(false), Ty::IntLiteral(0))] |
|
#[test_case(Ty::IntLiteral(1), Ty::IntLiteral(2))] |
|
#[test_case(Ty::IntLiteral(1), Ty::Tuple(vec![Ty::None]))] |
|
#[test_case(Ty::StringLiteral("a"), Ty::StringLiteral("b"))] |
|
#[test_case(Ty::StringLiteral("a"), Ty::Tuple(vec![Ty::None]))] |
|
#[test_case(Ty::LiteralString, Ty::BytesLiteral("a"))] |
|
#[test_case(Ty::BytesLiteral("a"), Ty::BytesLiteral("b"))] |
|
#[test_case(Ty::BytesLiteral("a"), Ty::Tuple(vec![Ty::None]))] |
|
#[test_case(Ty::BytesLiteral("a"), Ty::StringLiteral("a"))] |
|
#[test_case(Ty::Union(vec![Ty::IntLiteral(1), Ty::IntLiteral(2)]), Ty::IntLiteral(3))] |
|
#[test_case(Ty::Union(vec![Ty::IntLiteral(1), Ty::IntLiteral(2)]), Ty::Union(vec![Ty::IntLiteral(3), Ty::IntLiteral(4)]))] |
|
#[test_case(Ty::Intersection{pos: vec![Ty::BuiltinInstance("int"), Ty::IntLiteral(1)], neg: vec![]}, Ty::IntLiteral(2))] |
|
#[test_case(Ty::Tuple(vec![Ty::IntLiteral(1)]), Ty::Tuple(vec![Ty::IntLiteral(2)]))] |
|
#[test_case(Ty::Tuple(vec![Ty::IntLiteral(1), Ty::IntLiteral(2)]), Ty::Tuple(vec![Ty::IntLiteral(1)]))] |
|
#[test_case(Ty::Tuple(vec![Ty::IntLiteral(1), Ty::IntLiteral(2)]), Ty::Tuple(vec![Ty::IntLiteral(1), Ty::IntLiteral(3)]))] |
|
#[test_case(Ty::Tuple(vec![]), Ty::BuiltinClassLiteral("object"))] |
|
#[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); |
|
let b = b.into_type(&db); |
|
|
|
assert!(a.is_disjoint_from(&db, b)); |
|
assert!(b.is_disjoint_from(&db, a)); |
|
} |
|
|
|
#[test_case(Ty::Any, Ty::BuiltinInstance("int"))] |
|
#[test_case(Ty::None, Ty::None)] |
|
#[test_case(Ty::None, Ty::BuiltinInstance("object"))] |
|
#[test_case(Ty::BuiltinInstance("int"), Ty::BuiltinInstance("int"))] |
|
#[test_case(Ty::BuiltinInstance("str"), Ty::LiteralString)] |
|
#[test_case(Ty::BooleanLiteral(true), Ty::BooleanLiteral(true))] |
|
#[test_case(Ty::BooleanLiteral(false), Ty::BooleanLiteral(false))] |
|
#[test_case(Ty::BooleanLiteral(true), Ty::BuiltinInstance("bool"))] |
|
#[test_case(Ty::BooleanLiteral(true), Ty::BuiltinInstance("int"))] |
|
#[test_case(Ty::IntLiteral(1), Ty::IntLiteral(1))] |
|
#[test_case(Ty::StringLiteral("a"), Ty::StringLiteral("a"))] |
|
#[test_case(Ty::StringLiteral("a"), Ty::LiteralString)] |
|
#[test_case(Ty::StringLiteral("a"), Ty::BuiltinInstance("str"))] |
|
#[test_case(Ty::LiteralString, Ty::LiteralString)] |
|
#[test_case(Ty::Union(vec![Ty::IntLiteral(1), Ty::IntLiteral(2)]), Ty::IntLiteral(2))] |
|
#[test_case(Ty::Union(vec![Ty::IntLiteral(1), Ty::IntLiteral(2)]), Ty::Union(vec![Ty::IntLiteral(2), Ty::IntLiteral(3)]))] |
|
#[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)] |
|
#[test_case(Ty::AbcClassLiteral("ABC"), Ty::AbcInstance("ABCMeta"))] |
|
#[test_case(Ty::BuiltinInstance("str"), Ty::AlwaysTruthy)] |
|
#[test_case(Ty::BuiltinInstance("str"), Ty::AlwaysFalsy)] |
|
fn is_not_disjoint_from(a: Ty, b: Ty) { |
|
let db = setup_db(); |
|
let a = a.into_type(&db); |
|
let b = b.into_type(&db); |
|
|
|
assert!(!a.is_disjoint_from(&db, b)); |
|
assert!(!b.is_disjoint_from(&db, a)); |
|
} |
|
|
|
#[test] |
|
fn is_disjoint_from_union_of_class_types() { |
|
let mut db = setup_db(); |
|
db.write_dedented( |
|
"/src/module.py", |
|
" |
|
class A: ... |
|
class B: ... |
|
U = A if flag else B |
|
", |
|
) |
|
.unwrap(); |
|
let module = ruff_db::files::system_path_to_file(&db, "/src/module.py").unwrap(); |
|
|
|
let type_a = super::global_symbol(&db, module, "A").expect_type(); |
|
let type_u = super::global_symbol(&db, module, "U").expect_type(); |
|
|
|
assert!(type_a.is_class_literal()); |
|
assert!(type_u.is_union()); |
|
|
|
assert!(!type_a.is_disjoint_from(&db, type_u)); |
|
} |
|
|
|
#[test] |
|
fn is_disjoint_type_subclass_of() { |
|
let mut db = setup_db(); |
|
db.write_dedented( |
|
"/src/module.py", |
|
" |
|
class A: ... |
|
class B: ... |
|
", |
|
) |
|
.unwrap(); |
|
let module = ruff_db::files::system_path_to_file(&db, "/src/module.py").unwrap(); |
|
|
|
let literal_a = super::global_symbol(&db, module, "A").expect_type(); |
|
let literal_b = super::global_symbol(&db, module, "B").expect_type(); |
|
|
|
let subclass_of_a = SubclassOfType::from(&db, literal_a.expect_class_literal().class); |
|
let subclass_of_b = SubclassOfType::from(&db, literal_b.expect_class_literal().class); |
|
|
|
// Class literals are always disjoint. They are singleton types |
|
assert!(literal_a.is_disjoint_from(&db, literal_b)); |
|
|
|
// The class A is a subclass of A, so A is not disjoint from type[A] |
|
assert!(!literal_a.is_disjoint_from(&db, subclass_of_a)); |
|
|
|
// The class A is disjoint from type[B] because it's not a subclass |
|
// of B: |
|
assert!(literal_a.is_disjoint_from(&db, subclass_of_b)); |
|
|
|
// However, type[A] is not disjoint from type[B], as there could be |
|
// classes that inherit from both A and B: |
|
assert!(!subclass_of_a.is_disjoint_from(&db, subclass_of_b)); |
|
} |
|
|
|
#[test] |
|
fn is_disjoint_module_literals() { |
|
let mut db = setup_db(); |
|
db.write_dedented( |
|
"/src/module.py", |
|
" |
|
import random |
|
import math |
|
", |
|
) |
|
.unwrap(); |
|
|
|
let module = ruff_db::files::system_path_to_file(&db, "/src/module.py").unwrap(); |
|
|
|
let module_literal_random = super::global_symbol(&db, module, "random").expect_type(); |
|
let module_literal_math = super::global_symbol(&db, module, "math").expect_type(); |
|
|
|
assert!(module_literal_random.is_disjoint_from(&db, module_literal_math)); |
|
|
|
assert!(!module_literal_random.is_disjoint_from( |
|
&db, |
|
Ty::KnownClassInstance(KnownClass::ModuleType).into_type(&db) |
|
)); |
|
assert!(!module_literal_random.is_disjoint_from( |
|
&db, |
|
Ty::KnownClassInstance(KnownClass::Object).into_type(&db) |
|
)); |
|
} |
|
|
|
#[test] |
|
fn is_disjoint_function_literals() { |
|
let mut db = setup_db(); |
|
db.write_dedented( |
|
"/src/module.py", |
|
" |
|
def f(): ... |
|
def g(): ... |
|
", |
|
) |
|
.unwrap(); |
|
|
|
let module = ruff_db::files::system_path_to_file(&db, "/src/module.py").unwrap(); |
|
|
|
let function_literal_f = super::global_symbol(&db, module, "f").expect_type(); |
|
let function_literal_g = super::global_symbol(&db, module, "g").expect_type(); |
|
|
|
assert!(function_literal_f.is_disjoint_from(&db, function_literal_g)); |
|
|
|
assert!(!function_literal_f.is_disjoint_from( |
|
&db, |
|
Ty::KnownClassInstance(KnownClass::FunctionType).into_type(&db) |
|
)); |
|
assert!(!function_literal_f.is_disjoint_from( |
|
&db, |
|
Ty::KnownClassInstance(KnownClass::Object).into_type(&db) |
|
)); |
|
} |
|
|
|
#[test_case(Ty::None)] |
|
#[test_case(Ty::BooleanLiteral(true))] |
|
#[test_case(Ty::BooleanLiteral(false))] |
|
#[test_case(Ty::SubclassOfBuiltinClass("bool"))] // a `@final` class |
|
fn is_singleton(from: Ty) { |
|
let db = setup_db(); |
|
|
|
assert!(from.into_type(&db).is_singleton(&db)); |
|
} |
|
|
|
/// Explicitly test for Python version <3.13 and >=3.13, to ensure that |
|
/// the fallback to `typing_extensions` is working correctly. |
|
/// See [`KnownClass::canonical_module`] for more information. |
|
#[test_case(PythonVersion::PY312)] |
|
#[test_case(PythonVersion::PY313)] |
|
fn no_default_type_is_singleton(python_version: PythonVersion) { |
|
let db = TestDbBuilder::new() |
|
.with_python_version(python_version) |
|
.build() |
|
.unwrap(); |
|
|
|
let no_default = Ty::KnownClassInstance(KnownClass::NoDefaultType).into_type(&db); |
|
|
|
assert!(no_default.is_singleton(&db)); |
|
} |
|
|
|
#[test_case(Ty::None)] |
|
#[test_case(Ty::BooleanLiteral(true))] |
|
#[test_case(Ty::IntLiteral(1))] |
|
#[test_case(Ty::StringLiteral("abc"))] |
|
#[test_case(Ty::BytesLiteral("abc"))] |
|
#[test_case(Ty::Tuple(vec![]))] |
|
#[test_case(Ty::Tuple(vec![Ty::BooleanLiteral(true), Ty::IntLiteral(1)]))] |
|
fn is_single_valued(from: Ty) { |
|
let db = setup_db(); |
|
|
|
assert!(from.into_type(&db).is_single_valued(&db)); |
|
} |
|
|
|
#[test_case(Ty::Never)] |
|
#[test_case(Ty::Any)] |
|
#[test_case(Ty::Union(vec![Ty::IntLiteral(1), Ty::IntLiteral(2)]))] |
|
#[test_case(Ty::Tuple(vec![Ty::None, Ty::BuiltinInstance("int")]))] |
|
#[test_case(Ty::BuiltinInstance("str"))] |
|
#[test_case(Ty::LiteralString)] |
|
fn is_not_single_valued(from: Ty) { |
|
let db = setup_db(); |
|
|
|
assert!(!from.into_type(&db).is_single_valued(&db)); |
|
} |
|
|
|
#[test_case(Ty::Never)] |
|
#[test_case(Ty::IntLiteral(345))] |
|
#[test_case(Ty::BuiltinInstance("str"))] |
|
#[test_case(Ty::Union(vec![Ty::IntLiteral(1), Ty::IntLiteral(2)]))] |
|
#[test_case(Ty::Tuple(vec![]))] |
|
#[test_case(Ty::Tuple(vec![Ty::None]))] |
|
#[test_case(Ty::Tuple(vec![Ty::None, Ty::BooleanLiteral(true)]))] |
|
fn is_not_singleton(from: Ty) { |
|
let db = setup_db(); |
|
|
|
assert!(!from.into_type(&db).is_singleton(&db)); |
|
} |
|
|
|
#[test_case(Ty::Never)] |
|
#[test_case(Ty::None)] |
|
#[test_case(Ty::IntLiteral(1))] |
|
#[test_case(Ty::BooleanLiteral(true))] |
|
#[test_case(Ty::StringLiteral("abc"))] |
|
#[test_case(Ty::LiteralString)] |
|
#[test_case(Ty::BytesLiteral("abc"))] |
|
#[test_case(Ty::KnownClassInstance(KnownClass::Str))] |
|
#[test_case(Ty::KnownClassInstance(KnownClass::Object))] |
|
#[test_case(Ty::KnownClassInstance(KnownClass::Type))] |
|
#[test_case(Ty::BuiltinClassLiteral("str"))] |
|
#[test_case(Ty::TypingLiteral)] |
|
#[test_case(Ty::Union(vec![Ty::KnownClassInstance(KnownClass::Str), Ty::None]))] |
|
#[test_case(Ty::Intersection{pos: vec![Ty::KnownClassInstance(KnownClass::Str)], neg: vec![Ty::LiteralString]})] |
|
#[test_case(Ty::Tuple(vec![]))] |
|
#[test_case(Ty::Tuple(vec![Ty::KnownClassInstance(KnownClass::Int), Ty::KnownClassInstance(KnownClass::Object)]))] |
|
#[test_case(Ty::BuiltinInstance("type"))] |
|
#[test_case(Ty::SubclassOfBuiltinClass("object"))] |
|
#[test_case(Ty::SubclassOfBuiltinClass("str"))] |
|
fn is_fully_static(from: Ty) { |
|
let db = setup_db(); |
|
|
|
assert!(from.into_type(&db).is_fully_static(&db)); |
|
} |
|
|
|
#[test_case(Ty::Any)] |
|
#[test_case(Ty::Unknown)] |
|
#[test_case(Ty::Todo)] |
|
#[test_case(Ty::Union(vec![Ty::Any, Ty::KnownClassInstance(KnownClass::Str)]))] |
|
#[test_case(Ty::Union(vec![Ty::KnownClassInstance(KnownClass::Str), Ty::Unknown]))] |
|
#[test_case(Ty::Intersection{pos: vec![Ty::Any], neg: vec![Ty::LiteralString]})] |
|
#[test_case(Ty::Tuple(vec![Ty::KnownClassInstance(KnownClass::Int), Ty::Any]))] |
|
#[test_case(Ty::SubclassOfAny)] |
|
fn is_not_fully_static(from: Ty) { |
|
let db = setup_db(); |
|
|
|
assert!(!from.into_type(&db).is_fully_static(&db)); |
|
} |
|
|
|
#[test_case(Ty::IntLiteral(1); "is_int_literal_truthy")] |
|
#[test_case(Ty::IntLiteral(-1))] |
|
#[test_case(Ty::StringLiteral("foo"))] |
|
#[test_case(Ty::Tuple(vec![Ty::IntLiteral(0)]))] |
|
#[test_case(Ty::Union(vec![Ty::IntLiteral(1), Ty::IntLiteral(2)]))] |
|
fn is_truthy(ty: Ty) { |
|
let db = setup_db(); |
|
assert_eq!(ty.into_type(&db).bool(&db), Truthiness::AlwaysTrue); |
|
} |
|
|
|
#[test_case(Ty::Tuple(vec![]))] |
|
#[test_case(Ty::IntLiteral(0))] |
|
#[test_case(Ty::StringLiteral(""))] |
|
#[test_case(Ty::Union(vec![Ty::IntLiteral(0), Ty::IntLiteral(0)]))] |
|
fn is_falsy(ty: Ty) { |
|
let db = setup_db(); |
|
assert_eq!(ty.into_type(&db).bool(&db), Truthiness::AlwaysFalse); |
|
} |
|
|
|
#[test_case(Ty::BuiltinInstance("str"))] |
|
#[test_case(Ty::Union(vec![Ty::IntLiteral(1), Ty::IntLiteral(0)]))] |
|
#[test_case(Ty::Union(vec![Ty::BuiltinInstance("str"), Ty::IntLiteral(0)]))] |
|
#[test_case(Ty::Union(vec![Ty::BuiltinInstance("str"), Ty::IntLiteral(1)]))] |
|
fn boolean_value_is_unknown(ty: Ty) { |
|
let db = setup_db(); |
|
assert_eq!(ty.into_type(&db).bool(&db), Truthiness::Ambiguous); |
|
} |
The various type-property tests here …
ruff/crates/red_knot_python_semantic/src/types.rs
Lines 4155 to 4673 in b861551
… can now be moved to Markdown-based tests which are more concise, easier to read, can be run without recompilation, and can contain additional prose for documentation.
An example on how to do this can be seen in this PR which moved the
is_assignable_totests (ignore the changes inproperty_tests.rs).If you would like to work on this, let us know and pick one of these:
tuple_containing_never_simplifies_to_neveris_assignable_tois_subtype_ofis_equivalent_tois_disjoint_fromis_singletonis_single_valuedis_fully_static