Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
11 changes: 11 additions & 0 deletions mypy/checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,7 @@
LiteralType,
NoneType,
Overloaded,
ParamSpecType,
PartialType,
ProperType,
TupleType,
Expand Down Expand Up @@ -8954,6 +8955,16 @@ def overload_can_never_match(signature: CallableType, other: CallableType) -> bo

Assumes that both signatures have overlapping argument counts.
"""
# If the signature uses ParamSpec-flavored *args or **kwargs, we cannot
# reliably determine overlap. Erasing a ParamSpec to Any makes
# P.args/P.kwargs look like *Any/**Any, which appears to accept all
# arguments — but in reality the ParamSpec is constrained to the
# wrapped function's parameters. This leads to false positives where
# we incorrectly conclude that the other overload can never match.
for arg_type in signature.arg_types:
if isinstance(arg_type, ParamSpecType) and arg_type.flavor != 0: # BARE = 0
return False

# The extra erasure is needed to prevent spurious errors
# in situations where an `Any` overload is used as a fallback
# for an overload with type variables. The spurious error appears
Expand Down
16 changes: 16 additions & 0 deletions test-data/unit/check-parameter-specification.test
Original file line number Diff line number Diff line change
Expand Up @@ -2756,3 +2756,19 @@ reveal_type(Sneaky(f8, 1, y='').kwargs) # N: Revealed type is "builtins.dict[bu
reveal_type(Sneaky(f9, 1, y=0).kwargs) # N: Revealed type is "TypedDict('builtins.dict', {'x'?: builtins.int, 'y': builtins.int, 'z'?: builtins.str})"
reveal_type(Sneaky(f9, 1, y=0, z='').kwargs) # N: Revealed type is "TypedDict('builtins.dict', {'x'?: builtins.int, 'y': builtins.int, 'z'?: builtins.str})"
[builtins fixtures/paramspec.pyi]

[case testOverloadParamSpecNoFalsePositiveCannotMatch]
# Test that overloads using P.args/P.kwargs don't trigger false
# "overload will never be matched" errors when combined with
# overloads using explicit keyword-only parameters.
from typing import Any, overload, ParamSpec, TypeVar, Callable

P = ParamSpec("P")
T = TypeVar("T")

@overload
def bar(f: Callable[P, T], *a: P.args, **k: P.kwargs) -> T: ...
@overload
def bar(f: Callable[..., T], *a: Any, baz: int, **k: Any) -> T: ...
def bar(f, *a, **k): ...
[builtins fixtures/paramspec.pyi]
Loading