diff --git a/crates/red_knot_python_semantic/resources/mdtest/type_of/any.md b/crates/red_knot_python_semantic/resources/mdtest/type_of/dynamic.md similarity index 67% rename from crates/red_knot_python_semantic/resources/mdtest/type_of/any.md rename to crates/red_knot_python_semantic/resources/mdtest/type_of/dynamic.md index b04db115091c3..9bc257ddaee56 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/type_of/any.md +++ b/crates/red_knot_python_semantic/resources/mdtest/type_of/dynamic.md @@ -1,13 +1,20 @@ # `type[Any]` +This file contains tests for non-fully-static `type[]` types, such as `type[Any]` and +`type[Unknown]`. + ## Simple ```py -def f(x: type[Any]): +def f(x: type[Any], y: type[str]): reveal_type(x) # revealed: type[Any] # TODO: could be ` & Any` reveal_type(x.__repr__) # revealed: Any + # type[str] and type[Any] are assignable to each other + a: type[str] = x + b: type[Any] = y + class A: ... x: type[Any] = object @@ -70,3 +77,26 @@ def test(x: Any, y: SomethingUnknown): reveal_type(y.__class__) # revealed: type[Unknown] reveal_type(y.__class__.__class__.__class__.__class__) # revealed: type[Unknown] ``` + +## `type[Unknown]` has similar properties to `type[Any]` + +```py +import abc +from typing import Any +from does_not_exist import SomethingUnknown # error: [unresolved-import] + +has_unknown_type = SomethingUnknown.__class__ +reveal_type(has_unknown_type) # revealed: type[Unknown] + +def test(x: type[str], y: type[Any]): + """Both `type[Any]` and `type[Unknown]` are assignable to all `type[]` types""" + a: type[Any] = x + b: type[str] = y + c: type[Any] = has_unknown_type + d: type[str] = has_unknown_type + +def test2(a: type[Any]): + """`type[Any]` and `type[Unknown]` are also assignable to all instances of `type` subclasses""" + b: abc.ABCMeta = a + b: abc.ABCMeta = has_unknown_type +``` diff --git a/crates/red_knot_python_semantic/src/types.rs b/crates/red_knot_python_semantic/src/types.rs index a838a4abad859..acd38384f2fd8 100644 --- a/crates/red_knot_python_semantic/src/types.rs +++ b/crates/red_knot_python_semantic/src/types.rs @@ -854,26 +854,26 @@ impl<'db> Type<'db> { } ( Type::SubclassOf(SubclassOfType { - base: ClassBase::Any, + base: ClassBase::Any | ClassBase::Todo(_) | ClassBase::Unknown, }), Type::SubclassOf(_), ) => true, ( Type::SubclassOf(SubclassOfType { - base: ClassBase::Any, + base: ClassBase::Any | ClassBase::Todo(_) | ClassBase::Unknown, }), - Type::Instance(target), - ) if target.class.is_known(db, KnownClass::Type) => true, + Type::Instance(_), + ) if target.is_assignable_to(db, KnownClass::Type.to_instance(db)) => true, ( - Type::Instance(class), + Type::Instance(_), Type::SubclassOf(SubclassOfType { - base: ClassBase::Any, + base: ClassBase::Any | ClassBase::Todo(_) | ClassBase::Unknown, }), - ) if class.class.is_known(db, KnownClass::Type) => true, + ) if self.is_assignable_to(db, KnownClass::Type.to_instance(db)) => true, ( Type::ClassLiteral(_) | Type::SubclassOf(_), Type::SubclassOf(SubclassOfType { - base: ClassBase::Any, + base: ClassBase::Any | ClassBase::Todo(_) | ClassBase::Unknown, }), ) => true, // TODO other types containing gradual forms (e.g. generics containing Any/Unknown) @@ -3351,6 +3351,7 @@ pub(crate) mod tests { }, Tuple(Vec), SubclassOfAny, + SubclassOfUnknown, SubclassOfBuiltinClass(&'static str), StdlibModule(CoreStdlibModule), SliceLiteral(i32, i32, i32), @@ -3398,6 +3399,7 @@ pub(crate) mod tests { Type::tuple(db, elements) } Ty::SubclassOfAny => Type::subclass_of_base(ClassBase::Any), + Ty::SubclassOfUnknown => Type::subclass_of_base(ClassBase::Unknown), Ty::SubclassOfBuiltinClass(s) => Type::subclass_of( builtins_symbol(db, s) .expect_type() @@ -3471,6 +3473,10 @@ pub(crate) mod tests { #[test_case(Ty::BuiltinInstance("type"), Ty::SubclassOfBuiltinClass("object"))] #[test_case(Ty::BuiltinInstance("type"), Ty::BuiltinInstance("type"))] #[test_case(Ty::BuiltinClassLiteral("str"), Ty::SubclassOfAny)] + #[test_case(Ty::SubclassOfBuiltinClass("str"), Ty::SubclassOfUnknown)] + #[test_case(Ty::SubclassOfUnknown, Ty::SubclassOfBuiltinClass("str"))] + #[test_case(Ty::SubclassOfAny, Ty::AbcInstance("ABCMeta"))] + #[test_case(Ty::SubclassOfUnknown, Ty::AbcInstance("ABCMeta"))] 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)));