From d912f136619dd3643d39402be4e119c79aa6a3d0 Mon Sep 17 00:00:00 2001 From: David Peter Date: Mon, 13 Oct 2025 20:44:27 +0200 Subject: [PATCH] [ty] Do not bind self to non-positional parameters (#20850) ## Summary closes https://github.com/astral-sh/ty/issues/1333 ## Test Plan Regression test --- .../resources/mdtest/annotations/self.md | 14 ++++++++++++++ crates/ty_python_semantic/src/types/signatures.rs | 10 +++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/crates/ty_python_semantic/resources/mdtest/annotations/self.md b/crates/ty_python_semantic/resources/mdtest/annotations/self.md index f06862e4b0..91a2bcaf07 100644 --- a/crates/ty_python_semantic/resources/mdtest/annotations/self.md +++ b/crates/ty_python_semantic/resources/mdtest/annotations/self.md @@ -473,4 +473,18 @@ class C: reveal_type(generic_context(C.f)) # revealed: None ``` +## Non-positional first parameters + +This makes sure that we don't bind `self` if it's not a positional parameter: + +```py +from ty_extensions import CallableTypeOf + +class C: + def method(*args, **kwargs) -> None: ... + +def _(c: CallableTypeOf[C().method]): + reveal_type(c) # revealed: (...) -> None +``` + [self attribute]: https://typing.python.org/en/latest/spec/generics.html#use-in-attribute-annotations diff --git a/crates/ty_python_semantic/src/types/signatures.rs b/crates/ty_python_semantic/src/types/signatures.rs index 039b89a6eb..8547d41a8e 100644 --- a/crates/ty_python_semantic/src/types/signatures.rs +++ b/crates/ty_python_semantic/src/types/signatures.rs @@ -579,7 +579,15 @@ impl<'db> Signature<'db> { } pub(crate) fn bind_self(&self, db: &'db dyn Db, self_type: Option>) -> Self { - let mut parameters = Parameters::new(self.parameters().iter().skip(1).cloned()); + let mut parameters = self.parameters.iter().cloned().peekable(); + + // TODO: Theoretically, for a signature like `f(*args: *tuple[MyClass, int, *tuple[str, ...]])` with + // a variadic first parameter, we should also "skip the first parameter" by modifying the tuple type. + if parameters.peek().is_some_and(Parameter::is_positional) { + parameters.next(); + } + + let mut parameters = Parameters::new(parameters); let mut return_ty = self.return_ty; if let Some(self_type) = self_type { parameters = parameters.apply_type_mapping_impl(