diff --git a/mypy/typeops.py b/mypy/typeops.py index 050252eb6205..f6646740031d 100644 --- a/mypy/typeops.py +++ b/mypy/typeops.py @@ -508,7 +508,7 @@ def erase_to_bound(t: Type) -> Type: def callable_corresponding_argument( typ: NormalizedCallableType | Parameters, model: FormalArgument ) -> FormalArgument | None: - """Return the argument a function that corresponds to `model`""" + """Return the argument of a function that corresponds to `model`""" by_name = typ.argument_by_name(model.name) by_pos = typ.argument_by_position(model.pos) @@ -522,17 +522,23 @@ def callable_corresponding_argument( # taking both *args and **args, or a pair of functions like so: # def right(a: int = ...) -> None: ... - # def left(__a: int = ..., *, a: int = ...) -> None: ... + # def left(x: int = ..., /, *, a: int = ...) -> None: ... from mypy.meet import meet_types if ( not (by_name.required or by_pos.required) and by_pos.name is None and by_name.pos is None + # This is not principled, but prevents a crash. It's weird to have a FormalArgument + # that has an UnpackType. + and not isinstance(by_name.typ, UnpackType) + and not isinstance(by_pos.typ, UnpackType) ): return FormalArgument( by_name.name, by_pos.pos, meet_types(by_name.typ, by_pos.typ), False ) + return by_name + return by_name if by_name is not None else by_pos diff --git a/test-data/unit/check-overloading.test b/test-data/unit/check-overloading.test index be55a182b87b..1830a0c5ce3c 100644 --- a/test-data/unit/check-overloading.test +++ b/test-data/unit/check-overloading.test @@ -263,6 +263,19 @@ def foo(*args: int | str, **kw: int | Foo) -> None: pass [builtins fixtures/tuple.pyi] + +[case testTypeCheckOverloadImplOverlapVarArgsAndKwargsNever] +from __future__ import annotations +from typing import overload + +@overload # E: Single overload definition, multiple required +def foo(x: int) -> None: ... + +def foo(*args: int, **kw: str) -> None: # E: Overloaded function implementation does not accept all possible arguments of signature 1 + pass +[builtins fixtures/tuple.pyi] + + [case testTypeCheckOverloadWithImplTooSpecificRetType] from typing import overload, Any diff --git a/test-data/unit/check-typevar-tuple.test b/test-data/unit/check-typevar-tuple.test index cb5029ee4e6d..c60d0aec0835 100644 --- a/test-data/unit/check-typevar-tuple.test +++ b/test-data/unit/check-typevar-tuple.test @@ -2716,3 +2716,26 @@ class MyTuple(tuple[Unpack[Union[int, str]]], Generic[Unpack[Ts]]): # E: "Union x: MyTuple[int, str] reveal_type(x[0]) # N: Revealed type is "Any" [builtins fixtures/tuple.pyi] + +[case testHigherOrderFunctionUnpackTypeVarTupleViaParamSpec] +from typing import Callable, ParamSpec, TypeVar, TypeVarTuple, Unpack + +P = ParamSpec("P") +T = TypeVar("T") +Ts = TypeVarTuple("Ts") + +def call(func: Callable[P, T], *args: P.args, **kwargs: P.kwargs) -> T: + return func(*args, **kwargs) + + +def run(func: Callable[[Unpack[Ts]], T], *args: Unpack[Ts], some_kwarg: str = "asdf") -> T: + raise + + +def foo() -> str: + return "hello" + + +# this is a false positive, but it no longer crashes +call(run, foo, some_kwarg="a") # E: Argument 1 to "call" has incompatible type "def [Ts`-1, T] run(func: def (*Unpack[Ts]) -> T, *args: Unpack[Ts], some_kwarg: str = ...) -> T"; expected "Callable[[Callable[[], str], str], str]" +[builtins fixtures/tuple.pyi]