[ty] Match variadic argument to variadic parameter (#20511)

## Summary

Closes: https://github.com/astral-sh/ty/issues/1236

This PR fixes a bug where the variadic argument wouldn't match against
the variadic parameter in certain scenarios.

This was happening because I didn't realize that the `all_elements`
iterator wouldn't keep on returning the variable element (which is
correct, I just didn't realize it back then).

I don't think we can use the `resize` method here because we don't know
how many parameters this variadic argument is matching against as this
is where the actual parameter matching occurs.

## Test Plan

Expand test cases to consider a few more combinations of arguments and
parameters which are variadic.
This commit is contained in:
Dhruv Manilawala 2025-09-25 13:21:56 +05:30 committed by GitHub
parent edeb45804e
commit e1bb74b25a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 150 additions and 67 deletions

View file

@ -642,6 +642,96 @@ def f(*args: int) -> int:
reveal_type(f("foo")) # revealed: int
```
### Variadic argument, variadic parameter
```toml
[environment]
python-version = "3.11"
```
```py
def f(*args: int) -> int:
return 1
def _(args: list[str]) -> None:
# error: [invalid-argument-type] "Argument to function `f` is incorrect: Expected `int`, found `str`"
reveal_type(f(*args)) # revealed: int
```
Considering a few different shapes of tuple for the splatted argument:
```py
def f1(*args: str): ...
def _(
args1: tuple[str, ...],
args2: tuple[str, *tuple[str, ...]],
args3: tuple[str, *tuple[str, ...], str],
args4: tuple[int, *tuple[str, ...]],
args5: tuple[int, *tuple[str, ...], str],
args6: tuple[*tuple[str, ...], str],
args7: tuple[*tuple[str, ...], int],
args8: tuple[int, *tuple[str, ...], int],
args9: tuple[str, *tuple[str, ...], int],
args10: tuple[str, *tuple[int, ...], str],
):
f1(*args1)
f1(*args2)
f1(*args3)
f1(*args4) # error: [invalid-argument-type]
f1(*args5) # error: [invalid-argument-type]
f1(*args6)
f1(*args7) # error: [invalid-argument-type]
# The reason for two errors here is because of the two fixed elements in the tuple of `args8`
# which are both `int`
# error: [invalid-argument-type]
# error: [invalid-argument-type]
f1(*args8)
f1(*args9) # error: [invalid-argument-type]
f1(*args10) # error: [invalid-argument-type]
```
### Mixed argument and parameter containing variadic
```toml
[environment]
python-version = "3.11"
```
```py
def f(x: int, *args: str) -> int:
return 1
def _(
args1: list[int],
args2: tuple[int],
args3: tuple[int, int],
args4: tuple[int, ...],
args5: tuple[int, *tuple[str, ...]],
args6: tuple[int, int, *tuple[str, ...]],
) -> None:
# error: [invalid-argument-type] "Argument to function `f` is incorrect: Expected `str`, found `int`"
reveal_type(f(*args1)) # revealed: int
# This shouldn't raise an error because the unpacking doesn't match the variadic parameter.
reveal_type(f(*args2)) # revealed: int
# But, this should because the second tuple element is not assignable.
# error: [invalid-argument-type] "Argument to function `f` is incorrect: Expected `str`, found `int`"
reveal_type(f(*args3)) # revealed: int
# error: [invalid-argument-type] "Argument to function `f` is incorrect: Expected `str`, found `int`"
reveal_type(f(*args4)) # revealed: int
# The first element of the tuple matches the required argument;
# all subsequent elements match the variadic argument
reveal_type(f(*args5)) # revealed: int
# error: [invalid-argument-type] "Argument to function `f` is incorrect: Expected `str`, found `int`"
reveal_type(f(*args6)) # revealed: int
```
### Keyword argument, positional-or-keyword parameter
```py