diff --git a/mypy/checkmember.py b/mypy/checkmember.py index 1633eaf52983..8f62fee699c0 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -968,7 +968,13 @@ def expand_and_bind_callable( # TODO: a decorated property can result in Overloaded here. assert isinstance(expanded, CallableType) if var.is_settable_property and mx.is_lvalue and var.setter_type is not None: - # TODO: use check_call() to infer better type, same as for __set__(). + if expanded.variables: + type_ctx = mx.rvalue or TempNode(AnyType(TypeOfAny.special_form), context=mx.context) + _, inferred_expanded = mx.chk.expr_checker.check_call( + expanded, [type_ctx], [ARG_POS], mx.context + ) + expanded = get_proper_type(inferred_expanded) + assert isinstance(expanded, CallableType) if not expanded.arg_types: # This can happen when accessing invalid property from its own body, # error will be reported elsewhere. diff --git a/test-data/unit/check-generics.test b/test-data/unit/check-generics.test index 809c3c4eca48..8839dfb954f4 100644 --- a/test-data/unit/check-generics.test +++ b/test-data/unit/check-generics.test @@ -3628,3 +3628,32 @@ def draw_none( takes_int_str_none(c2) takes_int_str_none(c3) [builtins fixtures/tuple.pyi] + +[case testPropertyWithGenericSetter] +from typing import TypeVar + +class B: ... +class C(B): ... +T = TypeVar("T", bound=B) + +class Test: + @property + def foo(self) -> list[C]: ... + @foo.setter + def foo(self, val: list[T]) -> None: ... + +t1: Test +t2: Test + +lb: list[B] +lc: list[C] +li: list[int] + +t1.foo = lb +t1.foo = lc +t1.foo = li # E: Value of type variable "T" of "foo" of "Test" cannot be "int" + +t2.foo = [B()] +t2.foo = [C()] +t2.foo = [1] # E: Value of type variable "T" of "foo" of "Test" cannot be "int" +[builtins fixtures/property.pyi]