Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions mypy/typeops.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -522,7 +522,7 @@ 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 (
Expand All @@ -533,6 +533,8 @@ def callable_corresponding_argument(
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


Expand Down
31 changes: 21 additions & 10 deletions mypy/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -1923,6 +1923,25 @@ def __hash__(self) -> int:
return hash((self.name, self.pos, self.typ, self.required))


def _synthesize_arg_from_vararg(vararg: FormalArgument | None, position: int | None) -> FormalArgument | None:
if vararg is None:
return None
typ = vararg.typ
if isinstance(typ, UnpackType):
# Similar to logic in ExpressionChecker.visit_tuple_index_helper
unpacked = get_proper_type(typ.type)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixing this here could prevent crashes in other places that don't expect to be handling UnpackType, but may result in false positives because upper_bound is lossy

if isinstance(unpacked, TypeVarTupleType):
bound = get_proper_type(unpacked.upper_bound)
assert isinstance(bound, Instance)
assert bound.type.fullname == "builtins.tuple"
typ = bound.args[0]
else:
assert isinstance(unpacked, Instance)
assert unpacked.type.fullname == "builtins.tuple"
typ = unpacked.args[0]
return FormalArgument(None, position, typ, False)


class Parameters(ProperType):
"""Type that represents the parameters to a function.

Expand Down Expand Up @@ -2069,11 +2088,7 @@ def try_synthesizing_arg_from_kwarg(self, name: str | None) -> FormalArgument |
return None

def try_synthesizing_arg_from_vararg(self, position: int | None) -> FormalArgument | None:
var_arg = self.var_arg()
if var_arg is not None:
return FormalArgument(None, position, var_arg.typ, False)
else:
return None
return _synthesize_arg_from_vararg(self.var_arg(), position)

def accept(self, visitor: TypeVisitor[T]) -> T:
return visitor.visit_parameters(self)
Expand Down Expand Up @@ -2418,11 +2433,7 @@ def try_synthesizing_arg_from_kwarg(self, name: str | None) -> FormalArgument |
return None

def try_synthesizing_arg_from_vararg(self, position: int | None) -> FormalArgument | None:
var_arg = self.var_arg()
if var_arg is not None:
return FormalArgument(None, position, var_arg.typ, False)
else:
return None
return _synthesize_arg_from_vararg(self.var_arg(), position)

@property
def items(self) -> list[CallableType]:
Expand Down
13 changes: 13 additions & 0 deletions test-data/unit/check-overloading.test
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not really related to this PR, just adds #19619 (comment)

pass
[builtins fixtures/tuple.pyi]


[case testTypeCheckOverloadWithImplTooSpecificRetType]
from typing import overload, Any

Expand Down
22 changes: 22 additions & 0 deletions test-data/unit/check-typevar-tuple.test
Original file line number Diff line number Diff line change
Expand Up @@ -2716,3 +2716,25 @@ 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"


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]
Loading