diff --git a/crates/ty_python_semantic/resources/mdtest/annotations/self.md b/crates/ty_python_semantic/resources/mdtest/annotations/self.md index f06862e4b0f6a..91a2bcaf075b2 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 039b89a6ebd17..8547d41a8e28c 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(