diff --git a/crates/ty_python_semantic/resources/mdtest/generics/legacy/functions.md b/crates/ty_python_semantic/resources/mdtest/generics/legacy/functions.md index e0625c3fb31e8..21b574b78ea4f 100644 --- a/crates/ty_python_semantic/resources/mdtest/generics/legacy/functions.md +++ b/crates/ty_python_semantic/resources/mdtest/generics/legacy/functions.md @@ -673,6 +673,27 @@ def decorated(t: T) -> None: reveal_type(cast(T, t)) # revealed: T@decorated ``` +## Attribute access on `Callable`-bounded TypeVars + +```py +from typing import Callable, Generic, TypeVar + +F = TypeVar("F", bound=Callable) + +def my_decorator(f: F) -> None: + # error: [unresolved-attribute] + f.whatever + # error: [unresolved-attribute] + f.whatever = 1 + +class Box(Generic[F]): + cls: type[F] + +def specialized(box: Box[Callable]) -> None: + # error: [unresolved-attribute] + box.cls.whatever +``` + ## Solving TypeVars with upper bounds in unions ```py diff --git a/crates/ty_python_semantic/resources/mdtest/generics/pep695/functions.md b/crates/ty_python_semantic/resources/mdtest/generics/pep695/functions.md index b69350c98b1fb..406629542135c 100644 --- a/crates/ty_python_semantic/resources/mdtest/generics/pep695/functions.md +++ b/crates/ty_python_semantic/resources/mdtest/generics/pep695/functions.md @@ -716,6 +716,25 @@ def decorated[T](t: T) -> None: reveal_type(cast(T, t)) # revealed: T@decorated ``` +## Attribute access on `Callable`-bounded TypeVars + +```py +from typing import Callable + +def my_decorator[T: Callable](f: T) -> None: + # error: [unresolved-attribute] + f.whatever + # error: [unresolved-attribute] + f.whatever = 1 + +class Box[T: Callable]: + cls: type[T] + +def specialized(box: Box[Callable]) -> None: + # error: [unresolved-attribute] + box.cls.whatever +``` + ## Solving TypeVars with upper bounds in unions ```py diff --git a/crates/ty_python_semantic/resources/mdtest/type_of/generics.md b/crates/ty_python_semantic/resources/mdtest/type_of/generics.md index e0bb86ef45ff7..f7f2a14c1085b 100644 --- a/crates/ty_python_semantic/resources/mdtest/type_of/generics.md +++ b/crates/ty_python_semantic/resources/mdtest/type_of/generics.md @@ -331,8 +331,7 @@ def f2[T](x: T) -> type[T]: reveal_type(f2(int(1))) # revealed: type[int] reveal_type(f2(object())) # revealed: type -# TODO: This should reveal `type[Literal[1]]`. -reveal_type(f2(1)) # revealed: type[Unknown] +reveal_type(f2(1)) # revealed: def f3[T](x: type[T]) -> T: return x() diff --git a/crates/ty_python_semantic/src/types/subclass_of.rs b/crates/ty_python_semantic/src/types/subclass_of.rs index 42241a6153499..a0ec57a9f1296 100644 --- a/crates/ty_python_semantic/src/types/subclass_of.rs +++ b/crates/ty_python_semantic/src/types/subclass_of.rs @@ -163,8 +163,7 @@ impl<'db> SubclassOfType<'db> { if mapped.is_never() { Type::Never } else { - SubclassOfType::try_from_instance(db, mapped) - .unwrap_or(SubclassOfType::subclass_of_unknown()) + mapped.to_meta_type(db) } } } @@ -460,16 +459,12 @@ impl<'db> SubclassOfInner<'db> { .unwrap_or(SubclassOfType::subclass_of_unknown()), ), Some(TypeVarBoundOrConstraints::UpperBound(bound)) => { - TypeVarBoundOrConstraints::UpperBound( - SubclassOfType::try_from_instance(db, bound) - .unwrap_or(SubclassOfType::subclass_of_unknown()), - ) + TypeVarBoundOrConstraints::UpperBound(bound.to_meta_type(db)) } Some(TypeVarBoundOrConstraints::Constraints(constraints)) => { - TypeVarBoundOrConstraints::Constraints(constraints.map(db, |constraint| { - SubclassOfType::try_from_instance(db, *constraint) - .unwrap_or(SubclassOfType::subclass_of_unknown()) - })) + TypeVarBoundOrConstraints::Constraints( + constraints.map(db, |constraint| constraint.to_meta_type(db)), + ) } }) });