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
Prev Previous commit
Fix literal fingerprint fast-path incorrectly skipping optional argum…
…ents

build_literal_fingerprint included optional arguments (ARG_OPT / ARG_NAMED_OPT) in the fingerprint used to detect disjoint overloads.
When two overloads differed only in an optional Literal arg (e.g. as_index: Literal[True] vs Literal[False]), the fast-path treated them as mutually exclusive and skipped the full overlap check - even though a caller can omit the argument entirely, matching both overloads through their other arguments.

Fix by only fingerprinting required arguments (ARG_POS and ARG_NAMED).
Optional arguments can always be omitted by the caller, so they can never prove two overloads are disjoint.

The real-world symptom was pandas-stubs gaining spurious unused-ignore errors: overloads that master correctly flagged as overlapping were silently skipped by the fast-path, making existing type: ignore redundant.
  • Loading branch information
pelson committed Apr 14, 2026
commit 7a66cafcaaa057507b7fc4d8d4f2518580e958f8
18 changes: 13 additions & 5 deletions mypy/checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -8977,13 +8977,21 @@ def detach_callable(typ: CallableType, class_type_vars: list[TypeVarLikeType]) -
def build_literal_fingerprint(sig: CallableType) -> LiteralFingerprint:
"""Build a LiteralFingerprint for one overload signature.

Each argument position that carries only LiteralType values (including
unions such as ``Literal["a", "b"]``) is recorded as a frozenset of
``(type(value), value)`` pairs. Positions with any non-literal type are
omitted so the disjointedness check is conservative.
Each *required* argument position (ARG_POS or ARG_NAMED) that carries only
LiteralType values (including unions such as ``Literal["a", "b"]``) is
recorded as a frozenset of ``(type(value), value)`` pairs. Positions with
any non-literal type, or with an optional argument kind (ARG_OPT,
ARG_NAMED_OPT, ARG_STAR, ARG_STAR2), are omitted.

Optional arguments are excluded because a caller can always omit them,
meaning two overloads that differ only in an optional Literal argument still
overlap (a call that omits the argument matches both). Only required
arguments can prove that no single call can match both overloads.
"""
fingerprint: LiteralFingerprint = {}
for idx, arg_type in enumerate(sig.arg_types):
for idx, (arg_kind, arg_type) in enumerate(zip(sig.arg_kinds, sig.arg_types)):
if not arg_kind.is_required():
continue
proper = get_proper_type(arg_type)
if isinstance(proper, LiteralType):
fingerprint[idx] = frozenset([(type(proper.value), proper.value)])
Expand Down
22 changes: 22 additions & 0 deletions test-data/unit/check-overloading.test
Original file line number Diff line number Diff line change
Expand Up @@ -6963,3 +6963,25 @@ def f(x: Union[Literal["b"], str]) -> str: ...
def f(x: str) -> object:
return x
[builtins fixtures/tuple.pyi]

[case testOverloadLiteralOptionalArgNotDisjoint]
# Overloads that differ in an optional positional Literal arg still overlap:
# a call like f(1) omits mode and matches both signatures.
from typing import Literal, overload
@overload
def f(x: int, mode: Literal[True] = ...) -> int: ... # E: Overloaded function signatures 1 and 2 overlap with incompatible return types # N: Flipping the order of overloads will fix this error
@overload
def f(x: object, mode: Literal[False] = ...) -> str: ...
def f(x: object, mode: bool = True) -> object: ...
[builtins fixtures/tuple.pyi]

[case testOverloadLiteralOptionalKwOnlyNotDisjoint]
# Overloads that differ in an optional keyword-only Literal arg still overlap:
# a call like f(1) omits mode and matches both signatures.
from typing import Literal, overload
@overload
def f(x: int, *, mode: Literal[True] = ...) -> int: ... # E: Overloaded function signatures 1 and 2 overlap with incompatible return types # N: Flipping the order of overloads will fix this error
@overload
def f(x: object, *, mode: Literal[False] = ...) -> str: ...
def f(x: object, *, mode: bool = True) -> object: ...
[builtins fixtures/tuple.pyi]
Loading