diff --git a/crates/red_knot_python_semantic/resources/mdtest/call/subclass_of.md b/crates/red_knot_python_semantic/resources/mdtest/call/subclass_of.md new file mode 100644 index 0000000000..37082cfaa4 --- /dev/null +++ b/crates/red_knot_python_semantic/resources/mdtest/call/subclass_of.md @@ -0,0 +1,50 @@ +# Call `type[...]` + +## Single class + +### Trivial constructor + +```py +class C: ... + +def _(subclass_of_c: type[C]): + reveal_type(subclass_of_c()) # revealed: C +``` + +### Non-trivial constructor + +```py +class C: + def __init__(self, x: int): ... + +def _(subclass_of_c: type[C]): + reveal_type(subclass_of_c(1)) # revealed: C + + # TODO: Those should all be errors + reveal_type(subclass_of_c("a")) # revealed: C + reveal_type(subclass_of_c()) # revealed: C + reveal_type(subclass_of_c(1, 2)) # revealed: C +``` + +## Dynamic base + +```py +from typing import Any +from knot_extensions import Unknown + +def _(subclass_of_any: type[Any], subclass_of_unknown: type[Unknown]): + reveal_type(subclass_of_any()) # revealed: Any + reveal_type(subclass_of_any("any", "args", 1, 2)) # revealed: Any + reveal_type(subclass_of_unknown()) # revealed: Unknown + reveal_type(subclass_of_unknown("any", "args", 1, 2)) # revealed: Unknown +``` + +## Unions of classes + +```py +class A: ... +class B: ... + +def _(subclass_of_ab: type[A | B]): + reveal_type(subclass_of_ab()) # revealed: A | B +``` diff --git a/crates/red_knot_python_semantic/src/types.rs b/crates/red_knot_python_semantic/src/types.rs index a6b1520e71..9041af59e8 100644 --- a/crates/red_knot_python_semantic/src/types.rs +++ b/crates/red_knot_python_semantic/src/types.rs @@ -2678,6 +2678,13 @@ impl<'db> Type<'db> { ))) } + Type::SubclassOf(subclass_of_type) => match subclass_of_type.subclass_of() { + ClassBase::Dynamic(dynamic_type) => Ok(CallOutcome::Single( + CallBinding::from_return_type(Type::Dynamic(dynamic_type)), + )), + ClassBase::Class(class) => Type::class_literal(class).try_call(db, arguments), + }, + instance_ty @ Type::Instance(_) => { instance_ty .try_call_dunder(db, "__call__", arguments)