[ty] Do not bind self to non-positional parameters (#20850)

## Summary

closes https://github.com/astral-sh/ty/issues/1333

## Test Plan

Regression test
This commit is contained in:
David Peter 2025-10-13 20:44:27 +02:00 committed by GitHub
parent 71f8389f61
commit d912f13661
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 23 additions and 1 deletions

View file

@ -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

View file

@ -579,7 +579,15 @@ impl<'db> Signature<'db> {
}
pub(crate) fn bind_self(&self, db: &'db dyn Db, self_type: Option<Type<'db>>) -> 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(