-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Closed
Labels
help wantedContributions especially welcomeContributions especially welcometyMulti-file analysis & type inferenceMulti-file analysis & type inference
Description
The various type-property tests here …
ruff/crates/red_knot_python_semantic/src/types.rs
Lines 4155 to 4673 in b861551
| #[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); | |
| } |
… 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_to tests (ignore the changes in property_tests.rs).
If you would like to work on this, let us know and pick one of these:
-
tuple_containing_never_simplifies_to_never -
is_assignable_to -
is_subtype_of -
is_equivalent_to -
is_disjoint_from -
is_singleton -
is_single_valued -
is_fully_static - truthiness
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
help wantedContributions especially welcomeContributions especially welcometyMulti-file analysis & type inferenceMulti-file analysis & type inference