Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Open
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
Next Next commit
fix(checker): avoid false overload-cannot-match with ParamSpec args
When an overload uses ParamSpec-flavored *args (P.args) or **kwargs
(P.kwargs), erasing the ParamSpec to Any makes the signature appear to
accept all arguments. This causes a false 'overload will never be
matched' error when combined with a second overload that has explicit
keyword-only parameters.

Skip the can-never-match check when the first overload has
ParamSpec-flavored variadic arguments, since we cannot reliably
determine overlap after erasure.

Fixes #21171

Signed-off-by: bahtya <[email protected]>
  • Loading branch information
Bahtya committed Apr 9, 2026
commit d37f5ab73662efcdeb127200f94645fa15c7df63
11 changes: 11 additions & 0 deletions mypy/checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,7 @@
TypeVarId,
TypeVarLikeType,
TypeVarTupleType,
ParamSpecType,
TypeVarType,
UnboundType,
UninhabitedType,
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