diff --git a/crates/red_knot_python_semantic/resources/mdtest/type_of/basic.md b/crates/red_knot_python_semantic/resources/mdtest/type_of/basic.md new file mode 100644 index 00000000000000..c1b9a28e3ec499 --- /dev/null +++ b/crates/red_knot_python_semantic/resources/mdtest/type_of/basic.md @@ -0,0 +1,89 @@ +# type special form + +## Class literal + +```py +class A: ... + +def f() -> type[A]: + return A + +reveal_type(f()) # revealed: type[A] +``` + +## Nested class literal + +```py +class A: + class B: ... + +def f() -> type[A.B]: + return A.B + +reveal_type(f()) # revealed: type[B] +``` + +## Deeply nested class literal + +```py +class A: + class B: + class C: ... + +def f() -> type[A.B.C]: + return A.B.C + +reveal_type(f()) # revealed: type[C] +``` + +## Class literal from another module + +```py +from a import A + +def f() -> type[A]: + return A + +reveal_type(f()) # revealed: type[A] +``` + +```py path=a.py +class A: ... +``` + +## Qualified class literal from another module + +```py +import a + +def f() -> type[a.B]: + return a.B + +reveal_type(f()) # revealed: type[B] +``` + +```py path=a.py +class B: ... +``` + +## Deeply qualified class literal from another module + +```py path=a/test.py +import a.b + +# TODO: no diagnostic +# error: [unresolved-attribute] +def f() -> type[a.b.C]: + # TODO: no diagnostic + # error: [unresolved-attribute] + return a.b.C + +reveal_type(f()) # revealed: @Todo(unsupported type[X] special form) +``` + +```py path=a/__init__.py +``` + +```py path=a/b.py +class C: ... +``` diff --git a/crates/red_knot_python_semantic/src/types/infer.rs b/crates/red_knot_python_semantic/src/types/infer.rs index fbf592bd4a0baf..6a9945c065654a 100644 --- a/crates/red_knot_python_semantic/src/types/infer.rs +++ b/crates/red_knot_python_semantic/src/types/infer.rs @@ -4552,18 +4552,18 @@ impl<'db> TypeInferenceBuilder<'db> { /// Given the slice of a `type[]` annotation, return the type that the annotation represents fn infer_subclass_of_type_expression(&mut self, slice: &ast::Expr) -> Type<'db> { match slice { - ast::Expr::Name(_) => { + ast::Expr::Name(_) | ast::Expr::Attribute(_) => { let name_ty = self.infer_expression(slice); if let Some(ClassLiteralType { class }) = name_ty.into_class_literal() { Type::subclass_of(class) } else { - todo_type!() + todo_type!("unsupported type[X] special form") } } - // TODO: attributes, unions, subscripts, etc. + // TODO: unions, subscripts, etc. _ => { self.infer_type_expression(slice); - todo_type!() + todo_type!("unsupported type[X] special form") } } }