Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[red-knot] Move type-property tests to Markdown #15397

Open
2 of 9 tasks
sharkdp opened this issue Jan 10, 2025 · 3 comments
Open
2 of 9 tasks

[red-knot] Move type-property tests to Markdown #15397

sharkdp opened this issue Jan 10, 2025 · 3 comments
Labels
help wanted Contributions especially welcome red-knot Multi-file analysis & type inference

Comments

@sharkdp
Copy link
Contributor

sharkdp commented Jan 10, 2025

The various type-property tests here …

#[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
@sharkdp sharkdp added help wanted Contributions especially welcome red-knot Multi-file analysis & type inference labels Jan 10, 2025
@InSyncWithFoo
Copy link
Contributor

[...] and pick one of these

Why only one? Wouldn't it be easier for both the contributor and the reviewers to have one instead of multiple PRs? These are closely related, after all.

@sharkdp
Copy link
Contributor Author

sharkdp commented Jan 10, 2025

They are related tasks of course, but they can be cleanly separated. The Rust code in these tests changes from time to time (the latest refactoring that changed these was yesterday), and new test cases are added. So if we merge all of these into one giant contribution, it increases the risk for annoying merge conflicts that could be avoided otherwise. As a maintainer, I also prefer multiple smaller contributions. That said, obviously I won't complain if someone wants to tackle more or all of these at once.

@AlexWaygood
Copy link
Member

I strongly agree that small, incremental PRs are much easier to review, and therefore much more helpful

sharkdp added a commit that referenced this issue Jan 10, 2025
## Summary

See title.

Part of #15397

## Test Plan

Ran new Markdown test.

---------

Co-authored-by: Alex Waygood <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
help wanted Contributions especially welcome red-knot Multi-file analysis & type inference
Projects
None yet
Development

No branches or pull requests

3 participants