From cde5e4e3436f003fee1f1f5f0ab080e1f38080d5 Mon Sep 17 00:00:00 2001 From: David Peter Date: Wed, 10 Sep 2025 21:13:23 +0200 Subject: [PATCH] =?UTF-8?q?[ty]=20Fix=20`CallableTypeOf[=E2=80=A6]`=20for?= =?UTF-8?q?=20bound=20methods=20(#20338)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary `CallableTypeOf[bound_method]` would previously bind `self` to the bound method type itself, instead of binding it to the instance type stored inside the bound method type. ## Test Plan Added regression test --- .../ty_python_semantic/resources/mdtest/ty_extensions.md | 8 ++++++++ crates/ty_python_semantic/src/types.rs | 7 +++++-- crates/ty_python_semantic/src/types/infer.rs | 6 ++++-- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/crates/ty_python_semantic/resources/mdtest/ty_extensions.md b/crates/ty_python_semantic/resources/mdtest/ty_extensions.md index 0d93d3ed2e..8c3730e9b2 100644 --- a/crates/ty_python_semantic/resources/mdtest/ty_extensions.md +++ b/crates/ty_python_semantic/resources/mdtest/ty_extensions.md @@ -469,6 +469,8 @@ c4: CallableTypeOf[()] Using it in annotation to reveal the signature of the callable object: ```py +from typing_extensions import Self + class Foo: def __init__(self, x: int) -> None: pass @@ -476,12 +478,16 @@ class Foo: def __call__(self, x: int) -> str: return "foo" + def returns_self(self, x: int) -> Self: + return self + def _( c1: CallableTypeOf[f1], c2: CallableTypeOf[f2], c3: CallableTypeOf[f3], c4: CallableTypeOf[Foo], c5: CallableTypeOf[Foo(42).__call__], + c6: CallableTypeOf[Foo(42).returns_self], ) -> None: reveal_type(c1) # revealed: () -> Unknown reveal_type(c2) # revealed: () -> int @@ -491,4 +497,6 @@ def _( reveal_type(c4) # revealed: (...) -> Foo reveal_type(c5) # revealed: (x: int) -> str + + reveal_type(c6) # revealed: (x: int) -> Foo ``` diff --git a/crates/ty_python_semantic/src/types.rs b/crates/ty_python_semantic/src/types.rs index 636839f4f3..7126af975a 100644 --- a/crates/ty_python_semantic/src/types.rs +++ b/crates/ty_python_semantic/src/types.rs @@ -1046,8 +1046,11 @@ impl<'db> Type<'db> { matches!(self, Type::FunctionLiteral(..)) } - pub(crate) const fn is_bound_method(&self) -> bool { - matches!(self, Type::BoundMethod(..)) + pub(crate) const fn into_bound_method(self) -> Option> { + match self { + Type::BoundMethod(bound_method) => Some(bound_method), + _ => None, + } } pub(crate) fn is_union_of_single_valued(&self, db: &'db dyn Db) -> bool { diff --git a/crates/ty_python_semantic/src/types/infer.rs b/crates/ty_python_semantic/src/types/infer.rs index 439b3cf1b8..8356be44bb 100644 --- a/crates/ty_python_semantic/src/types/infer.rs +++ b/crates/ty_python_semantic/src/types/infer.rs @@ -11040,8 +11040,10 @@ impl<'db> TypeInferenceBuilder<'db, '_> { .expect("`Bindings` should have at least one `CallableBinding`"); let mut signature_iter = callable_binding.into_iter().map(|binding| { - if argument_type.is_bound_method() { - binding.signature.bind_self(self.db(), Some(argument_type)) + if let Some(bound_method) = argument_type.into_bound_method() { + binding + .signature + .bind_self(self.db(), Some(bound_method.self_instance(db))) } else { binding.signature.clone() }