# Call expression

## Simple

```py
def get_int() -> int:
    return 42

reveal_type(get_int())  # revealed: int
```

## Gradual variadic parameters

```py
from typing import Any

def accepts_anything(first: int, *args: Any, **kwargs: Any) -> None: ...
def accepts_only_gradual(*args: Any, **kwargs: Any) -> None: ...

accepts_anything(1, "one", object(), keyword=object())
accepts_anything("not an int")  # error: [invalid-argument-type]
accepts_only_gradual(1, "one", keyword=object())
accepts_only_gradual(**{1: "one"})  # error: [invalid-argument-type]
```

## Object variadic parameters

`*args: object` and `**kwargs: object` can receive many unrelated argument types. Call binding
should not build large unions to remember the exact values that were supplied to these broad sinks.

```py
class C0: ...
class C1: ...
class C2: ...
class C3: ...
class C4: ...
class C5: ...
class C6: ...
class C7: ...
class C8: ...
class C9: ...

def accepts_objects(*args: object, **kwargs: object) -> None: ...

accepts_objects(
    C0(),
    C1(),
    C2(),
    C3(),
    C4(),
    C5(),
    C6(),
    C7(),
    C8(),
    C9(),
    k0=C0(),
    k1=C1(),
    k2=C2(),
    k3=C3(),
    k4=C4(),
    k5=C5(),
    k6=C6(),
    k7=C7(),
    k8=C8(),
    k9=C9(),
)
```

## Async

```py
async def get_int_async() -> int:
    return 42

reveal_type(get_int_async())  # revealed: CoroutineType[Any, Any, int]
```

## Generic

```toml
[environment]
python-version = "3.12"
```

```py
def get_int[T]() -> int:
    return 42

reveal_type(get_int())  # revealed: int
```

## Generic callable chains

Inferring a chain of generic callable parameters should discard internal typevar artifacts from
transitive constraints instead of expanding them recursively.

```py
from typing import Callable, TypeVar

_A = TypeVar("_A")
_B = TypeVar("_B")
_C = TypeVar("_C")
_D = TypeVar("_D")
_E = TypeVar("_E")
_F = TypeVar("_F")
_G = TypeVar("_G")
_H = TypeVar("_H")

def compose(
    fn1: Callable[[_A], _B],
    fn2: Callable[[_B], _C],
    fn3: Callable[[_C], _D],
    fn4: Callable[[_D], _E],
    fn5: Callable[[_E], _F],
    fn6: Callable[[_F], _G],
    fn7: Callable[[_G], _H],
) -> Callable[[_A], _H]:
    raise NotImplementedError

Func = Callable[[int], int]
fn: Func = lambda x: x + 42

cn = compose(fn, fn, fn, fn, fn, fn, fn)
reveal_type(cn)  # revealed: (int, /) -> int
```

## Recursive callable constraints in constructors

When inferring the generic constructor for `map`, an overloaded callable together with a gradual
iterable can produce expanding recursive constraints. We should fall back rather than repeatedly
substituting those constraints.

```py
import operator
from typing import Any

ints: list[int] = []
dynamic: Any = []

reveal_type(map(operator.add, ints, dynamic))  # revealed: map[Unknown]
```

## Decorated

```py
from typing import Callable

def foo() -> int:
    return 42

def decorator(func) -> Callable[[], int]:
    return foo

@decorator
def bar() -> str:
    return "bar"

reveal_type(bar())  # revealed: int
```

## Invalid callable

```py
nonsense = 123
x = nonsense()  # error: "Object of type `Literal[123]` is not callable"
```

## Potentially unbound function

```py
def _(flag: bool):
    if flag:
        def foo() -> int:
            return 42
    # error: [possibly-unresolved-reference]
    reveal_type(foo())  # revealed: int
```

## PEP-484 convention for positional-only parameters

<!-- snapshot-diagnostics -->

PEP 570, introduced in Python 3.8, added dedicated Python syntax for denoting positional-only
parameters (the `/` in a function signature). However, functions implemented in C were able to have
positional-only parameters prior to Python 3.8 (there was just no syntax for expressing this at the
Python level).

Stub files describing functions implemented in C nonetheless needed a way of expressing that certain
parameters were positional-only. In the absence of dedicated Python syntax, PEP 484 described a
convention that type checkers were expected to understand:

> Some functions are designed to take their arguments only positionally, and expect their callers
> never to use the argument’s name to provide that argument by keyword. All arguments with names
> beginning with `__` are assumed to be positional-only, except if their names also end with `__`.

While this convention is now redundant (following the implementation of PEP 570), many projects
still continue to use the old convention, so it is supported by ty as well.

```py
def f(__x: int): ...

f(1)
# error: [positional-only-parameter-as-kwarg]
f(__x=1)
```

But not if they follow a non-positional-only parameter. This is flagged with a different error code
since (per the typing spec), this is likely a mistake from the user:

```py
from typing import overload

# error: [invalid-legacy-positional-parameter]
def g(x: int, __y: str): ...

g(x=1, __y="foo")

# The earlier `g` definition is shadowed here,
# but we still emit a diagnostic on the earlier definition
def g(): ...
```

Because the lint is a syntactic check, we emit it for each overload if multiple overloads violate
the lint:

```py
import tkinter
from typing import Callable, TypeVar, Any

@overload
def g2(x: int, __y: str): ...  # error: [invalid-legacy-positional-parameter]
@overload
def g2(x: str, __y: int): ...  # error: [invalid-legacy-positional-parameter]
def g2(x: str | int, __y: int | str): ...  # error: [invalid-legacy-positional-parameter]

T = TypeVar("T")

def copy_type(f: T) -> Callable[[Any], T]:
    return lambda x: x

# Naively iterating over the overloads using `.iter_overloads_and_implementation()` and/or
# using `.signature()` would cause us to panic on this function, because the overloads
# of this function's public signature are defined in `stdlib/tkinter/__init__.pyi` due to
# the decorator.
@copy_type(tkinter.Text.__init__)
def g3(x, *args: Any, **kwargs: Any) -> None: ...
def new_signature(): ...

# The check is able to "see through" the decorators and examines the original function's
# signature:
@copy_type(new_signature)
def g4(a, __b): ...  # error: [invalid-legacy-positional-parameter]
```

Parameters are also not understood as positional-only if they both start and end with `__`:

```py
def h(__x__: str): ...

h(__x__="foo")
```

And if *any* parameters use the new PEP-570 convention, the old convention does not apply:

```py
def i(x: str, /, __y: int): ...

i("foo", __y=42)  # fine
```

And `self`/`cls` are implicitly positional-only:

```py
class C:
    def method(self, __x: int): ...
    @classmethod
    def class_method(cls, __x: str): ...
    # (the name of the first parameter is irrelevant;
    # a staticmethod works the same as a free function in the global scope)
    @staticmethod
    def static_method(self, __x: int): ...  # error: [invalid-legacy-positional-parameter]
    # `__new__` is a staticmethod, but the `cls` parameter works in the same way as the `cls`
    # parameter in a classmethod, and is always passed positionally at runtime,
    # We therefore understand both `cls` and `__x` here as positional-only; we do not
    # emit `[invalid-legacy-positional-parameter]` on the method.
    def __new__(cls, __x: int): ...

# error: [positional-only-parameter-as-kwarg]
C(42).method(__x=1)
# error: [positional-only-parameter-as-kwarg]
C.class_method(__x="1")
C.static_method("x", __x=42)  # fine
```

## Splatted arguments

### Unknown argument length

```py
def takes_zero() -> None: ...
def takes_one(x: int) -> None: ...
def takes_two(x: int, y: int) -> None: ...
def takes_two_positional_only(x: int, y: int, /) -> None: ...
def takes_two_different(x: int, y: str) -> None: ...
def takes_two_different_positional_only(x: int, y: str, /) -> None: ...
def takes_at_least_zero(*args) -> None: ...
def takes_at_least_one(x: int, *args) -> None: ...
def takes_at_least_two(x: int, y: int, *args) -> None: ...
def takes_at_least_two_positional_only(x: int, y: int, /, *args) -> None: ...

# Test all of the above with a number of different splatted argument types

def _(args: list[int]) -> None:
    takes_zero(*args)
    takes_one(*args)
    takes_two(*args)
    takes_two(*b"ab")
    takes_two(*b"abc")  # error: [too-many-positional-arguments]
    takes_two_positional_only(*args)
    takes_two_different(*args)  # error: [invalid-argument-type]
    takes_two_different_positional_only(*args)  # error: [invalid-argument-type]
    takes_at_least_zero(*args)
    takes_at_least_one(*args)
    takes_at_least_two(*args)
    takes_at_least_two_positional_only(*args)

def _(args: tuple[int, ...]) -> None:
    takes_zero(*args)
    takes_one(*args)
    takes_two(*args)
    takes_two_positional_only(*args)
    takes_two_different(*args)  # error: [invalid-argument-type]
    takes_two_different_positional_only(*args)  # error: [invalid-argument-type]
    takes_at_least_zero(*args)
    takes_at_least_one(*args)
    takes_at_least_two(*args)
    takes_at_least_two_positional_only(*args)
```

### Fixed-length tuple argument

```py
def takes_zero() -> None: ...
def takes_one(x: int) -> None: ...
def takes_two(x: int, y: int) -> None: ...
def takes_two_positional_only(x: int, y: int, /) -> None: ...
def takes_two_different(x: int, y: str) -> None: ...
def takes_two_different_positional_only(x: int, y: str, /) -> None: ...
def takes_at_least_zero(*args) -> None: ...
def takes_at_least_one(x: int, *args) -> None: ...
def takes_at_least_two(x: int, y: int, *args) -> None: ...
def takes_at_least_two_positional_only(x: int, y: int, /, *args) -> None: ...

# Test all of the above with a number of different splatted argument types

def _(args: tuple[int]) -> None:
    takes_zero(*args)  # error: [too-many-positional-arguments]
    takes_one(*args)
    takes_two(*args)  # error: [missing-argument]
    takes_two_positional_only(*args)  # error: [missing-argument]
    takes_two_different(*args)  # error: [missing-argument]
    takes_two_different_positional_only(*args)  # error: [missing-argument]
    takes_at_least_zero(*args)
    takes_at_least_one(*args)
    takes_at_least_two(*args)  # error: [missing-argument]
    takes_at_least_two_positional_only(*args)  # error: [missing-argument]

def _(args: tuple[int, int]) -> None:
    takes_zero(*args)  # error: [too-many-positional-arguments]
    takes_one(*args)  # error: [too-many-positional-arguments]
    takes_two(*args)
    takes_two_positional_only(*args)
    takes_two_different(*args)  # error: [invalid-argument-type]
    takes_two_different_positional_only(*args)  # error: [invalid-argument-type]
    takes_at_least_zero(*args)
    takes_at_least_one(*args)
    takes_at_least_two(*args)
    takes_at_least_two_positional_only(*args)

def _(args: tuple[int, str]) -> None:
    takes_zero(*args)  # error: [too-many-positional-arguments]
    takes_one(*args)  # error: [too-many-positional-arguments]
    takes_two(*args)  # error: [invalid-argument-type]
    takes_two_positional_only(*args)  # error: [invalid-argument-type]
    takes_two_different(*args)
    takes_two_different_positional_only(*args)
    takes_at_least_zero(*args)
    takes_at_least_one(*args)
    takes_at_least_two(*args)  # error: [invalid-argument-type]
    takes_at_least_two_positional_only(*args)  # error: [invalid-argument-type]
```

### Subclass of fixed-length tuple argument

```py
def takes_zero() -> None: ...
def takes_one(x: int) -> None: ...
def takes_two(x: int, y: int) -> None: ...
def takes_two_positional_only(x: int, y: int, /) -> None: ...
def takes_two_different(x: int, y: str) -> None: ...
def takes_two_different_positional_only(x: int, y: str, /) -> None: ...
def takes_at_least_zero(*args) -> None: ...
def takes_at_least_one(x: int, *args) -> None: ...
def takes_at_least_two(x: int, y: int, *args) -> None: ...
def takes_at_least_two_positional_only(x: int, y: int, /, *args) -> None: ...

# Test all of the above with a number of different splatted argument types

class SingleElementTuple(tuple[int]): ...

def _(args: SingleElementTuple) -> None:
    takes_zero(*args)  # error: [too-many-positional-arguments]

    takes_one(*args)

    takes_two(*args)  # error: [missing-argument]
    takes_two_positional_only(*args)  # error: [missing-argument]

    takes_two_different(*args)  # error: [missing-argument]
    takes_two_different_positional_only(*args)  # error: [missing-argument]

    takes_at_least_zero(*args)
    takes_at_least_one(*args)

    takes_at_least_two(*args)  # error: [missing-argument]
    takes_at_least_two_positional_only(*args)  # error: [missing-argument]

class TwoElementIntTuple(tuple[int, int]): ...

def _(args: TwoElementIntTuple) -> None:
    takes_zero(*args)  # error: [too-many-positional-arguments]
    takes_one(*args)  # error: [too-many-positional-arguments]
    takes_two(*args)
    takes_two_positional_only(*args)
    takes_two_different(*args)  # error: [invalid-argument-type]
    takes_two_different_positional_only(*args)  # error: [invalid-argument-type]
    takes_at_least_zero(*args)
    takes_at_least_one(*args)
    takes_at_least_two(*args)
    takes_at_least_two_positional_only(*args)

class IntStrTuple(tuple[int, str]): ...

def _(args: IntStrTuple) -> None:
    takes_zero(*args)  # error: [too-many-positional-arguments]

    takes_one(*args)  # error: [too-many-positional-arguments]

    # error: [invalid-argument-type]
    takes_two(*args)
    # error: [invalid-argument-type]
    takes_two_positional_only(*args)

    takes_two_different(*args)
    takes_two_different_positional_only(*args)
    takes_at_least_zero(*args)
    takes_at_least_one(*args)

    # error: [invalid-argument-type]
    takes_at_least_two(*args)
    # error: [invalid-argument-type]
    takes_at_least_two_positional_only(*args)
```

### Mixed tuple argument

```toml
[environment]
python-version = "3.11"
```

```py
def takes_zero() -> None: ...
def takes_one(x: int) -> None: ...
def takes_two(x: int, y: int) -> None: ...
def takes_two_positional_only(x: int, y: int, /) -> None: ...
def takes_two_different(x: int, y: str) -> None: ...
def takes_two_different_positional_only(x: int, y: str, /) -> None: ...
def takes_at_least_zero(*args) -> None: ...
def takes_at_least_one(x: int, *args) -> None: ...
def takes_at_least_two(x: int, y: int, *args) -> None: ...
def takes_at_least_two_positional_only(x: int, y: int, /, *args) -> None: ...

# Test all of the above with a number of different splatted argument types

def _(args: tuple[int, *tuple[int, ...]]) -> None:
    takes_zero(*args)  # error: [too-many-positional-arguments]
    takes_one(*args)
    takes_two(*args)
    takes_two_positional_only(*args)
    takes_two_different(*args)  # error: [invalid-argument-type]
    takes_two_different_positional_only(*args)  # error: [invalid-argument-type]
    takes_at_least_zero(*args)
    takes_at_least_one(*args)
    takes_at_least_two(*args)
    takes_at_least_two_positional_only(*args)

def _(args: tuple[int, *tuple[str, ...]]) -> None:
    takes_zero(*args)  # error: [too-many-positional-arguments]
    takes_one(*args)
    takes_two(*args)  # error: [invalid-argument-type]
    takes_two_positional_only(*args)  # error: [invalid-argument-type]
    takes_two_different(*args)
    takes_two_different_positional_only(*args)
    takes_at_least_zero(*args)
    takes_at_least_one(*args)
    takes_at_least_two(*args)  # error: [invalid-argument-type]
    takes_at_least_two_positional_only(*args)  # error: [invalid-argument-type]

def _(args: tuple[int, int, *tuple[int, ...]]) -> None:
    takes_zero(*args)  # error: [too-many-positional-arguments]
    takes_one(*args)  # error: [too-many-positional-arguments]
    takes_two(*args)
    takes_two_positional_only(*args)
    takes_two_different(*args)  # error: [invalid-argument-type]
    takes_two_different_positional_only(*args)  # error: [invalid-argument-type]
    takes_at_least_zero(*args)
    takes_at_least_one(*args)
    takes_at_least_two(*args)
    takes_at_least_two_positional_only(*args)

def _(args: tuple[int, int, *tuple[str, ...]]) -> None:
    takes_zero(*args)  # error: [too-many-positional-arguments]
    takes_one(*args)  # error: [too-many-positional-arguments]
    takes_two(*args)
    takes_two_positional_only(*args)
    takes_two_different(*args)  # error: [invalid-argument-type]
    takes_two_different_positional_only(*args)  # error: [invalid-argument-type]
    takes_at_least_zero(*args)
    takes_at_least_one(*args)
    takes_at_least_two(*args)
    takes_at_least_two_positional_only(*args)

def _(args: tuple[int, *tuple[int, ...], int]) -> None:
    takes_zero(*args)  # error: [too-many-positional-arguments]
    takes_one(*args)  # error: [too-many-positional-arguments]
    takes_two(*args)
    takes_two_positional_only(*args)
    takes_two_different(*args)  # error: [invalid-argument-type]
    takes_two_different_positional_only(*args)  # error: [invalid-argument-type]
    takes_at_least_zero(*args)
    takes_at_least_one(*args)
    takes_at_least_two(*args)
    takes_at_least_two_positional_only(*args)

def _(args: tuple[int, *tuple[str, ...], int]) -> None:
    takes_zero(*args)  # error: [too-many-positional-arguments]
    takes_one(*args)  # error: [too-many-positional-arguments]
    takes_two(*args)  # error: [invalid-argument-type]
    takes_two_positional_only(*args)  # error: [invalid-argument-type]
    takes_two_different(*args)
    takes_two_different_positional_only(*args)
    takes_at_least_zero(*args)
    takes_at_least_one(*args)
    takes_at_least_two(*args)  # error: [invalid-argument-type]
    takes_at_least_two_positional_only(*args)  # error: [invalid-argument-type]
```

### Subclass of mixed tuple argument

```toml
[environment]
python-version = "3.11"
```

```py
def takes_zero() -> None: ...
def takes_one(x: int) -> None: ...
def takes_two(x: int, y: int) -> None: ...
def takes_two_positional_only(x: int, y: int, /) -> None: ...
def takes_two_different(x: int, y: str) -> None: ...
def takes_two_different_positional_only(x: int, y: str, /) -> None: ...
def takes_at_least_zero(*args) -> None: ...
def takes_at_least_one(x: int, *args) -> None: ...
def takes_at_least_two(x: int, y: int, *args) -> None: ...
def takes_at_least_two_positional_only(x: int, y: int, /, *args) -> None: ...

# Test all of the above with a number of different splatted argument types

class IntStarInt(tuple[int, *tuple[int, ...]]): ...

def _(args: IntStarInt) -> None:
    takes_zero(*args)  # error: [too-many-positional-arguments]
    takes_one(*args)
    takes_two(*args)
    takes_two_positional_only(*args)
    takes_two_different(*args)  # error: [invalid-argument-type]
    takes_two_different_positional_only(*args)  # error: [invalid-argument-type]
    takes_at_least_zero(*args)
    takes_at_least_one(*args)
    takes_at_least_two(*args)
    takes_at_least_two_positional_only(*args)

class IntStarStr(tuple[int, *tuple[str, ...]]): ...

def _(args: IntStarStr) -> None:
    takes_zero(*args)  # error: [too-many-positional-arguments]

    takes_one(*args)

    # error: [invalid-argument-type]
    takes_two(*args)
    # error: [invalid-argument-type]
    takes_two_positional_only(*args)

    takes_two_different(*args)
    takes_two_different_positional_only(*args)

    takes_at_least_zero(*args)

    takes_at_least_one(*args)

    # error: [invalid-argument-type]
    takes_at_least_two(*args)
    # error: [invalid-argument-type]
    takes_at_least_two_positional_only(*args)

class IntIntStarInt(tuple[int, int, *tuple[int, ...]]): ...

def _(args: IntIntStarInt) -> None:
    takes_zero(*args)  # error: [too-many-positional-arguments]
    takes_one(*args)  # error: [too-many-positional-arguments]
    takes_two(*args)
    takes_two_positional_only(*args)
    takes_two_different(*args)  # error: [invalid-argument-type]
    takes_two_different_positional_only(*args)  # error: [invalid-argument-type]
    takes_at_least_zero(*args)
    takes_at_least_one(*args)
    takes_at_least_two(*args)
    takes_at_least_two_positional_only(*args)

class IntIntStarStr(tuple[int, int, *tuple[str, ...]]): ...

def _(args: IntIntStarStr) -> None:
    takes_zero(*args)  # error: [too-many-positional-arguments]

    takes_one(*args)  # error: [too-many-positional-arguments]

    takes_two(*args)
    takes_two_positional_only(*args)

    # error: [invalid-argument-type]
    takes_two_different(*args)
    # error: [invalid-argument-type]
    takes_two_different_positional_only(*args)

    takes_at_least_zero(*args)

    takes_at_least_one(*args)

    takes_at_least_two(*args)

    takes_at_least_two_positional_only(*args)

class IntStarIntInt(tuple[int, *tuple[int, ...], int]): ...

def _(args: IntStarIntInt) -> None:
    takes_zero(*args)  # error: [too-many-positional-arguments]
    takes_one(*args)  # error: [too-many-positional-arguments]
    takes_two(*args)
    takes_two_positional_only(*args)
    takes_two_different(*args)  # error: [invalid-argument-type]
    takes_two_different_positional_only(*args)  # error: [invalid-argument-type]
    takes_at_least_zero(*args)
    takes_at_least_one(*args)
    takes_at_least_two(*args)
    takes_at_least_two_positional_only(*args)

class IntStarStrInt(tuple[int, *tuple[str, ...], int]): ...

def _(args: IntStarStrInt) -> None:
    takes_zero(*args)  # error: [too-many-positional-arguments]

    takes_one(*args)  # error: [too-many-positional-arguments]

    # error: [invalid-argument-type]
    takes_two(*args)
    # error: [invalid-argument-type]
    takes_two_positional_only(*args)

    takes_two_different(*args)
    takes_two_different_positional_only(*args)

    takes_at_least_zero(*args)

    takes_at_least_one(*args)

    # error: [invalid-argument-type]
    takes_at_least_two(*args)

    # error: [invalid-argument-type]
    takes_at_least_two_positional_only(*args)
```

### String argument

```py
from typing import Literal

def takes_zero() -> None: ...
def takes_one(x: str) -> None: ...
def takes_two(x: str, y: str) -> None: ...
def takes_two_positional_only(x: str, y: str, /) -> None: ...
def takes_two_different(x: int, y: str) -> None: ...
def takes_two_different_positional_only(x: int, y: str, /) -> None: ...
def takes_at_least_zero(*args) -> None: ...
def takes_at_least_one(x: str, *args) -> None: ...
def takes_at_least_two(x: str, y: str, *args) -> None: ...
def takes_at_least_two_positional_only(x: str, y: str, /, *args) -> None: ...

# Test all of the above with a number of different splatted argument types

def _(args: Literal["a"]) -> None:
    takes_zero(*args)  # error: [too-many-positional-arguments]
    takes_one(*args)
    takes_two(*args)  # error: [missing-argument]
    takes_two_positional_only(*args)  # error: [missing-argument]
    # error: [invalid-argument-type]
    # error: [missing-argument]
    takes_two_different(*args)
    # error: [invalid-argument-type]
    # error: [missing-argument]
    takes_two_different_positional_only(*args)
    takes_at_least_zero(*args)
    takes_at_least_one(*args)
    takes_at_least_two(*args)  # error: [missing-argument]
    takes_at_least_two_positional_only(*args)  # error: [missing-argument]

def _(args: Literal["ab"]) -> None:
    takes_zero(*args)  # error: [too-many-positional-arguments]
    takes_one(*args)  # error: [too-many-positional-arguments]
    takes_two(*args)
    takes_two_positional_only(*args)
    takes_two_different(*args)  # error: [invalid-argument-type]
    takes_two_different_positional_only(*args)  # error: [invalid-argument-type]
    takes_at_least_zero(*args)
    takes_at_least_one(*args)
    takes_at_least_two(*args)
    takes_at_least_two_positional_only(*args)

def _(args: Literal["abc"]) -> None:
    takes_zero(*args)  # error: [too-many-positional-arguments]
    takes_one(*args)  # error: [too-many-positional-arguments]
    takes_two(*args)  # error: [too-many-positional-arguments]
    takes_two_positional_only(*args)  # error: [too-many-positional-arguments]
    # error: [invalid-argument-type]
    # error: [too-many-positional-arguments]
    takes_two_different(*args)
    # error: [invalid-argument-type]
    # error: [too-many-positional-arguments]
    takes_two_different_positional_only(*args)
    takes_at_least_zero(*args)
    takes_at_least_one(*args)
    takes_at_least_two(*args)
    takes_at_least_two_positional_only(*args)

def _(args: str) -> None:
    takes_zero(*args)
    takes_one(*args)
    takes_two(*args)
    takes_two_positional_only(*args)
    takes_two_different(*args)  # error: [invalid-argument-type]
    takes_two_different_positional_only(*args)  # error: [invalid-argument-type]
    takes_at_least_zero(*args)
    takes_at_least_one(*args)
    takes_at_least_two(*args)
    takes_at_least_two_positional_only(*args)
```

## Wrong argument type

### Positional argument, positional-or-keyword parameter

```py
def f(x: int) -> int:
    return 1

# error: 15 [invalid-argument-type] "Argument to function `f` is incorrect: Expected `int`, found `Literal["foo"]`"
reveal_type(f("foo"))  # revealed: int
```

### Positional argument, positional-only parameter

```py
def f(x: int, /) -> int:
    return 1

# error: 15 [invalid-argument-type] "Argument to function `f` is incorrect: Expected `int`, found `Literal["foo"]`"
reveal_type(f("foo"))  # revealed: int
```

### Positional argument, variadic parameter

```py
def f(*args: int) -> int:
    return 1

# error: 15 [invalid-argument-type] "Argument to function `f` is incorrect: Expected `int`, found `Literal["foo"]`"
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]
```

A union of heterogeneous tuples provided to a variadic parameter:

```py
# Test inspired by ecosystem code at:
# - <https://github.com/home-assistant/core/blob/bde4eb50111a72f9717fe73ee5929e50eb06911b/homeassistant/components/lovelace/websocket.py#L50-L59>
# - <https://github.com/pydata/xarray/blob/3572f4e70f2b12ef9935c1f8c3c1b74045d2a092/xarray/tests/test_groupby.py#L3058-L3059>

def f2(a: str, b: bool): ...
def f3(coinflip: bool):
    if coinflip:
        args = "foo", True
    else:
        args = "bar", False

    # revealed: tuple[Literal["foo"], Literal[True]] | tuple[Literal["bar"], Literal[False]]
    reveal_type(args)
    f2(*args)  # fine

    if coinflip:
        other_args = "foo", True
    else:
        other_args = "bar", (True,)

    # revealed: tuple[Literal["foo"], Literal[True]] | tuple[Literal["bar"], tuple[Literal[True]]]
    reveal_type(other_args)
    # error: [invalid-argument-type] "Argument to function `f2` is incorrect: Expected `bool`, found `Literal[True] | tuple[Literal[True]]`"
    f2(*other_args)

def f4(a=None, b=None, c=None, d=None, e=None): ...

my_args = ((1, 2), (3, 4), (5, 6))

for tup in my_args:
    f4(*tup, e=None)  # fine

my_other_args = (
    (1, 2, 3, 4, 5),
    (6, 7, 8, 9, 10),
)

for tup in my_other_args:
    # error: [parameter-already-assigned] "Multiple values provided for parameter `e` of function `f4`"
    f4(*tup, e=None)
```

Regression test for <https://github.com/astral-sh/ty/issues/2734>.

```py
def f5(x: int | None = None, y: str = "") -> None: ...
def f6(args: tuple[()] | tuple[int]) -> None:
    f5(*args)

def f7(x: int | None = None, y: str = "") -> None: ...
def f8(args: tuple[()] | tuple[str]) -> None:
    f7(*args)  # error: [invalid-argument-type]

def f11(*args: int) -> None: ...
def f12(args: tuple[int] | int) -> None:
    f11(*args)  # error: [not-iterable]

def f13(a: int, b: int, c: str) -> None: ...
def f14(a: int, b: int, c: str, d: list[float], e: list[float]) -> None: ...
def f15(profile: bool, line: str) -> None:
    matcher = f13
    timings = []
    if profile:
        matcher = f14
        timings = [[0.0], [1.0], [2.0], [3.0]]
    matcher(1, 2, line, *timings[:2])

def f9(x: int = 0, y: str = "") -> None: ...
def f10(args: tuple[int, ...] | tuple[int, str]) -> None:
    # The variable-length element `int` from `tuple[int, ...]` unions with `str`
    # from `tuple[int, str]` at position 1, giving `int | str` for `y: str`.
    f9(*args)  # error: [invalid-argument-type]

def f18(x: int = 0, y: int = 0) -> None: ...
def f19(args: tuple[int, ...] | tuple[int, int]) -> None:
    f18(*args)

# Union variadic unpacking also works when the non-defaulted parameters are covered by
# the shortest union element, even if not all remaining parameters are defaulted.
def f16(a: int, b: int = 0, c: str = "") -> None: ...
def f17(x: tuple[int] | tuple[int, int]) -> None:
    f16(*x)

# Longer union elements must still be rejected when they would contribute
# extra positional arguments.
def f20(a: int, b: int) -> None: ...
def f21(x: tuple[int, int] | tuple[int, int, int]) -> None:
    f20(*x)  # error: [too-many-positional-arguments]

# Shorter union elements must also be rejected when they cannot provide a required
# positional argument.
def f22(a: int, b: int, c: int) -> None: ...
def f23(x: tuple[int, int] | tuple[int, int, int]) -> None:
    f22(*x)  # error: [missing-argument]

# Later positional arguments must not be allowed to "slide left" when a longer
# union member would still bind an incompatible tuple element. We currently
# handle this conservatively, so this still reports the broader iterator-based
# family of errors.
def f24(a: int, b: int, c: int = 0) -> None: ...
def f25(x: tuple[int] | tuple[int, str]) -> None:
    # error: [invalid-argument-type]
    # error: [invalid-argument-type]
    # error: [too-many-positional-arguments]
    f24(*x, 1)  # 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
```

### Variable-length unpacking with explicit keyword arguments

When a variable-length iterable (like a list) is unpacked and followed by explicit keyword
arguments, the variadic unpacking should not greedily consume parameters that have explicit keyword
bindings. This prevents false positive "parameter already assigned" errors.

Regression test for <https://github.com/astral-sh/ty/issues/1584>.

```py
from typing import TypedDict

def f(a: str, b: str, c: float) -> None: ...

# Explicit keyword argument takes precedence over variable-length variadic expansion.
# The list unpacking should only fill `a` and `b`, leaving `c` for the keyword argument.
def _(args: list[str]) -> None:
    f(*args, c=1.0)

# Fixed-length tuple unpacking with keyword argument also works correctly.
def _(args: tuple[str, str]) -> None:
    f(*args, c=1.0)

# But, with a fixed-length tuple that is too long, we get the expected error.
def _(args: tuple[str, str, str]) -> None:
    # error: [invalid-argument-type] "Argument to function `f` is incorrect: Expected `int | float`, found `str`"
    # error: [parameter-already-assigned] "Multiple values provided for parameter `c` of function `f`"
    f(*args, c=1.0)
```

However, when there's no explicit keyword argument, the behavior remains conservative:

```py
# Positional argument after variable-length variadic is ambiguous
def _(args: list[str]) -> None:
    # error: [invalid-argument-type]
    # error: [too-many-positional-arguments]
    f(*args, 1.0)

# Multiple variable-length variadics are also ambiguous
def _(args1: list[str], args2: list[float]) -> None:
    # error: [invalid-argument-type]
    f(*args1, *args2)

# Keyword variadic (**dict) with unknown keys is also ambiguous
def _(args: list[str], kwargs: dict[str, float]) -> None:
    # error: [invalid-argument-type]
    f(*args, **kwargs)

# Keyword variadic with TypedDict has known keys but is still handled conservatively
# but we could possibly improve this in the future.
class CKwargs(TypedDict):
    c: float

def _(args: list[str]) -> None:
    # error: [invalid-argument-type]
    # error: [parameter-already-assigned]
    f(*args, **CKwargs(c=1.0))
```

### Keyword argument, positional-or-keyword parameter

```py
def f(x: int) -> int:
    return 1

# error: 15 [invalid-argument-type] "Argument to function `f` is incorrect: Expected `int`, found `Literal["foo"]`"
reveal_type(f(x="foo"))  # revealed: int
```

### Keyword argument, keyword-only parameter

```py
def f(*, x: int) -> int:
    return 1

# error: 15 [invalid-argument-type] "Argument to function `f` is incorrect: Expected `int`, found `Literal["foo"]`"
reveal_type(f(x="foo"))  # revealed: int
```

### Keyword argument, keywords parameter

```py
def f(**kwargs: int) -> int:
    return 1

# error: 15 [invalid-argument-type] "Argument to function `f` is incorrect: Expected `int`, found `Literal["foo"]`"
reveal_type(f(x="foo"))  # revealed: int
```

### Correctly match keyword out-of-order

```py
def f(x: int = 1, y: str = "foo") -> int:
    return 1

# error: 15 [invalid-argument-type] "Argument to function `f` is incorrect: Expected `str`, found `Literal[2]`"
# error: 20 [invalid-argument-type] "Argument to function `f` is incorrect: Expected `int`, found `Literal["bar"]`"
reveal_type(f(y=2, x="bar"))  # revealed: int
```

### Diagnostics for union types where the union is not assignable

<!-- snapshot-diagnostics -->

```py
from typing import Sized

class Foo: ...
class Bar: ...
class Baz: ...

def f(x: Sized): ...
def g(
    a: str | Foo,
    b: list[str] | str | dict[str, str] | tuple[str, ...] | bytes | frozenset[str] | set[str] | Foo,
    c: list[str] | str | dict[str, str] | tuple[str, ...] | bytes | frozenset[str] | set[str] | Foo | Bar,
    d: list[str] | str | dict[str, str] | tuple[str, ...] | bytes | frozenset[str] | set[str] | Foo | Bar | Baz,
):
    f(a)  # error: [invalid-argument-type]
    f(b)  # error: [invalid-argument-type]
    f(c)  # error: [invalid-argument-type]
    f(d)  # error: [invalid-argument-type]
```

## Too many positional arguments

### One too many

```py
def f() -> int:
    return 1

# error: 15 [too-many-positional-arguments] "Too many positional arguments to function `f`: expected 0, got 1"
reveal_type(f("foo"))  # revealed: int
```

### Two too many

```py
def f() -> int:
    return 1

# error: 15 [too-many-positional-arguments] "Too many positional arguments to function `f`: expected 0, got 2"
reveal_type(f("foo", "bar"))  # revealed: int
```

### No too-many-positional if variadic is taken

```py
def f(*args: int) -> int:
    return 1

reveal_type(f(1, 2, 3))  # revealed: int
```

### Multiple keyword arguments map to keyword variadic parameter

```py
def f(**kwargs: int) -> int:
    return 1

reveal_type(f(foo=1, bar=2))  # revealed: int
```

## Missing arguments

### No defaults or variadic

```py
def f(x: int) -> int:
    return 1

# error: 13 [missing-argument] "No argument provided for required parameter `x` of function `f`"
reveal_type(f())  # revealed: int
```

### With default

```py
def f(x: int, y: str = "foo") -> int:
    return 1

# error: 13 [missing-argument] "No argument provided for required parameter `x` of function `f`"
reveal_type(f())  # revealed: int
```

### Defaulted argument is not required

```py
def f(x: int = 1) -> int:
    return 1

reveal_type(f())  # revealed: int
```

### With variadic

```py
def f(x: int, *y: str) -> int:
    return 1

# error: 13 [missing-argument] "No argument provided for required parameter `x` of function `f`"
reveal_type(f())  # revealed: int
```

### Variadic argument is not required

```py
def f(*args: int) -> int:
    return 1

reveal_type(f())  # revealed: int
```

### Keywords argument is not required

```py
def f(**kwargs: int) -> int:
    return 1

reveal_type(f())  # revealed: int
```

### Multiple

```py
def f(x: int, y: int) -> int:
    return 1

# error: 13 [missing-argument] "No arguments provided for required parameters `x`, `y` of function `f`"
reveal_type(f())  # revealed: int
```

## Unknown argument

```py
def f(x: int) -> int:
    return 1

# error: 20 [unknown-argument] "Argument `y` does not match any known parameter of function `f`"
reveal_type(f(x=1, y=2))  # revealed: int
```

## Parameter already assigned

```py
def f(x: int) -> int:
    return 1

# error: 18 [parameter-already-assigned] "Multiple values provided for parameter `x` of function `f`"
reveal_type(f(1, x=2))  # revealed: int
```

## Special functions

Some functions require special handling in type inference. Here, we make sure that we still emit
proper diagnostics in case of missing or superfluous arguments.

### `reveal_type`

```py
from typing_extensions import reveal_type

# error: [missing-argument] "No argument provided for required parameter `obj` of function `reveal_type`"
reveal_type()

# error: [too-many-positional-arguments] "Too many positional arguments to function `reveal_type`: expected 1, got 2"
reveal_type(1, 2)
```

### `static_assert`

```py
from ty_extensions import static_assert

# error: [missing-argument] "No argument provided for required parameter `condition` of function `static_assert`"
static_assert()

# error: [static-assert-error] "Static assertion error: argument evaluates to `False`"
static_assert(condition=False)

# error: [static-assert-error] "Static assertion error: argument of type `Unknown` has an ambiguous static truthiness"
# error: [invalid-syntax]
static_assert(**)

# error: [too-many-positional-arguments] "Too many positional arguments to function `static_assert`: expected 2, got 3"
# error: [invalid-argument-type] "Argument to function `static_assert` is incorrect: Expected `LiteralString | None`, found `Literal[2]`"
static_assert(True, 2, 3)
```

### `len`

```py
# error: [missing-argument] "No argument provided for required parameter `obj` of function `len`"
len()

# error: [too-many-positional-arguments] "Too many positional arguments to function `len`: expected 1, got 2"
len([], 1)
```

### Type property predicates

```py
from ty_extensions._internal import is_subtype_of

# error: [missing-argument]
is_subtype_of()

# error: [missing-argument]
is_subtype_of(int)

# error: [too-many-positional-arguments]
is_subtype_of(int, int, int)

# error: [too-many-positional-arguments]
is_subtype_of(int, int, int, int)
```

## Keywords argument

A double-starred argument (`**kwargs`) can be used to pass an argument that implements the mapping
protocol. This is matched against any of the *unmatched* standard (positional or keyword),
keyword-only, and keywords (`**kwargs`) parameters.

### Empty

```py
def empty() -> None: ...
def _(kwargs: dict[str, int]) -> None:
    empty(**kwargs)

empty(**{})
empty(**dict())
```

### Single parameter

```py
from typing_extensions import TypedDict

def f(**kwargs: int) -> None: ...

class Foo(TypedDict):
    a: int
    b: int

def _(kwargs: dict[str, int]) -> None:
    f(**kwargs)

f(**{"foo": 1})
f(**dict(foo=1))
# error: [invalid-argument-type] "Argument to function `f` is incorrect: Possible extra items in unpacked open `TypedDict` have type `object`, expected `int`"
f(**Foo(a=1, b=2))
```

### Positional-only and variadic parameters

```py
def f1(a: int, b: int, /) -> None: ...
def f2(*args: int) -> None: ...
def _(kwargs: dict[str, int]) -> None:
    # error: [missing-argument] "No arguments provided for required parameters `a`, `b` of function `f1`"
    f1(**kwargs)

    # This doesn't raise an error because `*args` is an optional parameter and `**kwargs` can be empty.
    f2(**kwargs)
```

### Standard parameters

```py
from typing_extensions import TypedDict

class Foo(TypedDict):
    a: int
    b: int

def f(a: int, b: int) -> None: ...
def _(kwargs: dict[str, int]) -> None:
    f(**kwargs)

f(**{"a": 1, "b": 2})
f(**dict(a=1, b=2))
f(**Foo(a=1, b=2))
```

### Keyword-only parameters

```py
from typing_extensions import TypedDict

class Foo(TypedDict):
    a: int
    b: int

def f(*, a: int, b: int) -> None: ...
def _(kwargs: dict[str, int]) -> None:
    f(**kwargs)

f(**{"a": 1, "b": 2})
f(**dict(a=1, b=2))
f(**Foo(a=1, b=2))
```

### Multiple keywords argument

```py
def f(**kwargs: int) -> None: ...
def _(kwargs1: dict[str, int], kwargs2: dict[str, int], kwargs3: dict[str, str], kwargs4: dict[int, list[int]]) -> None:
    f(**kwargs1, **kwargs2)
    # error: [invalid-argument-type] "Argument to function `f` is incorrect: Expected `int`, found `str`"
    f(**kwargs1, **kwargs3)
    # error: [invalid-argument-type] "Argument to function `f` is incorrect: Expected `int`, found `str`"
    # error: [invalid-argument-type] "Argument expression after ** must be a mapping with `str` key type: Found `int`"
    # error: [invalid-argument-type] "Argument to function `f` is incorrect: Expected `int`, found `list[int]`"
    f(**kwargs3, **kwargs4)
```

### Keyword-only after keywords

```py
class B: ...

def f(*, a: int, b: B, **kwargs: int) -> None: ...
def _(kwargs: dict[str, int]):
    # Make sure that the `b` argument is not being matched against `kwargs` by passing an integer
    # instead of the annotated type which should raise an
    # error: [invalid-argument-type] "Argument to function `f` is incorrect: Expected `B`, found `Literal[2]`"
    f(a=1, **kwargs, b=2)
```

### Mixed parameter kind

```py
def f1(*, a: int, b: int, **kwargs: int) -> None: ...
def f2(a: int, *, b: int, **kwargs: int) -> None: ...
def f3(a: int, /, *args: int, b: int, **kwargs: int) -> None: ...
def _(kwargs1: dict[str, int], kwargs2: dict[str, str]):
    f1(**kwargs1)
    f2(**kwargs1)
    f3(1, **kwargs1)
```

### TypedDict

```py
from typing_extensions import NotRequired, TypedDict

class Foo1(TypedDict):
    a: int
    b: str

class Foo2(TypedDict):
    a: int
    b: NotRequired[str]

def f(**kwargs: int) -> None: ...

# snapshot: invalid-argument-type
# snapshot: invalid-argument-type
f(**Foo1(a=1, b="b"))
# error: [invalid-argument-type] "Argument to function `f` is incorrect: Expected `int`, found `str`"
# error: [invalid-argument-type] "Argument to function `f` is incorrect: Possible extra items in unpacked open `TypedDict` have type `object`, expected `int`"
f(**Foo2(a=1))
```

```snapshot
error[invalid-argument-type]: Argument to function `f` is incorrect
  --> src/mdtest_snippet.py:15:3
   |
15 | f(**Foo1(a=1, b="b"))
   |   ^^^^^^^^^^^^^^^^^^ Expected `int`, found `str`
   |
info: Function defined here
  --> src/mdtest_snippet.py:11:5
   |
11 | def f(**kwargs: int) -> None: ...
   |     ^ ------------- Parameter declared here
   |


error[invalid-argument-type]: Argument to function `f` is incorrect
  --> src/mdtest_snippet.py:15:3
   |
15 | f(**Foo1(a=1, b="b"))
   |   ^^^^^^^^^^^^^^^^^^ Possible extra items in unpacked open `TypedDict` have type `object`, expected `int`
   |
info: Function defined here
  --> src/mdtest_snippet.py:11:5
   |
11 | def f(**kwargs: int) -> None: ...
   |     ^ ------------- Parameter declared here
   |
```

### TypedDict union

```py
from typing_extensions import TypedDict

class GoodA(TypedDict):
    a: int
    b: int

class GoodB(TypedDict):
    a: int
    b: int

class BadA(TypedDict):
    a: int
    b: str

class BadB(TypedDict):
    a: int
    b: str

def needs_known_keys(*, a: int, b: int, c: int) -> None: ...
def takes_int_kwargs(**kwargs: int) -> None: ...
def _(good: GoodA | GoodB, bad: BadA | BadB) -> None:
    # error: [missing-argument] "No argument provided for required parameter `c` of function `needs_known_keys`"
    needs_known_keys(**good)

    # error: [invalid-argument-type] "Argument to function `takes_int_kwargs` is incorrect: Expected `int`, found `str`"
    # error: [invalid-argument-type] "Argument to function `takes_int_kwargs` is incorrect: Possible extra items in unpacked open `TypedDict` have type `object`, expected `int`"
    takes_int_kwargs(**bad)
```

### Keys must be strings

The keys of the mapping passed to a double-starred argument must be strings.

```py
from collections.abc import Mapping

def f(**kwargs: int) -> None: ...

class DictSubclass(dict[int, int]): ...

class MappingSubclass(Mapping[int, int]):
    def __iter__(self): ...
    def __len__(self): ...
    def __getitem__(self, key): ...

class MappingProtocol:
    def keys(self) -> list[int]:
        return [1]

    def __getitem__(self, key: int) -> int:
        return 1

def _(kwargs: dict[int, int]) -> None:
    # error: [invalid-argument-type] "Argument expression after ** must be a mapping with `str` key type: Found `int`"
    f(**kwargs)

# error: [invalid-argument-type] "Argument expression after ** must be a mapping with `str` key type: Found `int`"
f(**DictSubclass())
# error: [invalid-argument-type] "Argument expression after ** must be a mapping with `str` key type: Found `int`"
f(**MappingSubclass())
# error: [invalid-argument-type] "Argument expression after ** must be a mapping with `str` key type: Found `int`"
f(**MappingProtocol())
```

The key can also be a custom type that inherits from `str`.

```py
class SubStr(str): ...
class SubInt(int): ...

def _(kwargs1: dict[SubStr, int], kwargs2: dict[SubInt, int]) -> None:
    f(**kwargs1)
    # error: [invalid-argument-type] "Argument expression after ** must be a mapping with `str` key type: Found `SubInt`"
    f(**kwargs2)
```

Or, it can be a type that is assignable to `str`.

```py
from typing import Any
from ty_extensions import Unknown

def _(kwargs1: dict[Any, int], kwargs2: dict[Unknown, int]) -> None:
    f(**kwargs1)
    f(**kwargs2)
```

### Invalid value type

```py
from collections.abc import Mapping

def f(**kwargs: str) -> None: ...

class DictSubclass(dict[str, int]): ...

class MappingSubclass(Mapping[str, int]):
    def __iter__(self): ...
    def __len__(self): ...
    def __getitem__(self, key) -> int:
        return 42

class MappingProtocol:
    def keys(self) -> list[str]:
        return ["foo"]

    def __getitem__(self, key: str) -> int:
        return 1

def _(kwargs: dict[str, int]) -> None:
    # error: [invalid-argument-type] "Argument to function `f` is incorrect: Expected `str`, found `int`"
    f(**kwargs)
    # error: [invalid-argument-type] "Argument to function `f` is incorrect: Expected `str`, found `int`"
    f(**DictSubclass())
    # error: [invalid-argument-type] "Argument to function `f` is incorrect: Expected `str`, found `int`"
    f(**MappingSubclass())
    # error: [invalid-argument-type] "Argument to function `f` is incorrect: Expected `str`, found `int`"
    f(**MappingProtocol())
```

### `Unknown` type

```py
from ty_extensions import Unknown

def f(**kwargs: int) -> None: ...
def _(kwargs: Unknown):
    f(**kwargs)
```

### Not a mapping

```py
def f(**kwargs: int) -> None: ...

class A: ...

class InvalidMapping:
    def keys(self) -> A:
        return A()

    def __getitem__(self, key: str) -> int:
        return 1

def _(kwargs: dict[str, int] | int):
    # error: [invalid-argument-type] "Argument expression after ** must be a mapping type: Found `dict[str, int] | int`"
    f(**kwargs)
    # error: [invalid-argument-type] "Argument expression after ** must be a mapping type: Found `InvalidMapping`"
    f(**InvalidMapping())
```

### Not a mapping with overloaded function

When `**kwargs` with a non-mapping type is passed to an overloaded function, the error should report
the specific mapping type error.

`overloaded.pyi`:

```pyi
from typing import overload

@overload
def f(x: int, **kwargs: int) -> int: ...
@overload
def f(x: str, **kwargs: str) -> str: ...
```

```py
from overloaded import f

# error: [invalid-argument-type] "Argument expression after ** must be a mapping type: Found `None`"
f(1, **None)

def _(kwargs: dict[str, int] | int):
    # error: [invalid-argument-type] "Argument expression after ** must be a mapping type: Found `dict[str, int] | int`"
    f(1, **kwargs)
```

### Generic

For a generic keywords parameter, the type variable should be specialized to the value type of the
mapping.

```py
from typing import TypeVar

_MappingT = TypeVar("_MappingT")

def f(**kwargs: _MappingT) -> _MappingT:
    return kwargs["a"]

def _(kwargs: dict[str, int]) -> None:
    reveal_type(f(**kwargs))  # revealed: int
```

For an open `TypedDict`, the type variable must also account for hidden values of type `object`.

```py
from typing import TypeVar
from typing_extensions import TypedDict

_T = TypeVar("_T")

class Foo(TypedDict):
    a: int
    b: str

def f(**kwargs: _T) -> _T:
    return kwargs["a"]

reveal_type(f(**Foo(a=1, b="b")))  # revealed: object
```

## Non-iterable variadic argument

A starred argument must be iterable. If it is not, an error should be reported.

```py
def some_fn(a: int):
    pass

# error: [not-iterable] "Object of type `None` is not iterable"
some_fn(*None)
```

This also applies when the type might not be iterable:

```py
def f(*args: int) -> int:
    return 1

def _(x: int | list[int]):
    # error: [not-iterable] "Object of type `int | list[int]` may not be iterable"
    f(*x)
```

## Non-iterable variadic argument with overloaded functions

`overloaded.pyi`:

```pyi
from typing import overload

@overload
def foo(a: int) -> tuple[int]: ...
@overload
def foo(a: int, b: int) -> tuple[int, int]: ...
```

```py
from overloaded import foo

# error: [not-iterable] "Object of type `None` is not iterable"
foo(*None)

def _(arg: int):
    # error: [not-iterable] "Object of type `int` is not iterable"
    foo(*arg)
```

## Union variadic unpacking with explicit keyword arguments

When a union type containing variable-length elements (like `Unknown`) is unpacked as `*args`, the
variadic expansion should not greedily consume optional positional parameters that are also provided
as explicit keyword arguments.

```py
from ty_extensions import Unknown

def f(a: int = 0, b: int = 0, c: int = 0, fmt: str | None = None) -> None: ...
def _(args: "Unknown | tuple[int, int, int]"):
    f(*args, fmt="{key}")  # fine

def g(a: int, b: int = 0, c: int = 0) -> None: ...
def _(args: tuple[int, int] | tuple[int, int, int]):
    g(*args, c=1)  # error: [parameter-already-assigned]
```

## Variadic unpacking should stop at max known arity

When unpacking (a union of) fixed-length tuples, variadic matching should stop once the known
positions are exhausted. Otherwise, optional positional parameters can be incorrectly treated as
already assigned, causing false positives for `**kwargs`.

(This test uses `**kwargs` unpacking of a `TypedDict` instead of the simpler `c=1` keyword argument,
because `c=1` is a known keyword argument and we always prevent unpacking `*args` over an
explicitly-provided keyword argument. The case shown here, without the explicit keyword argument,
requires instead that we use our knowledge of the tuple length to prevent over-unpacking.)

```py
from typing import TypedDict

class CKwargs(TypedDict):
    c: int

def f(a: int = 0, b: int = 0, c: int = 0) -> None: ...
def _(args_tuple: tuple[int, int], args_union: tuple[int] | tuple[int, int], kwargs: CKwargs) -> None:
    f(*args_tuple, **kwargs)  # fine
    f(*args_union, **kwargs)  # fine
```
