Skip to content

Commit bbac93f

Browse files
committed
Tidy up the loop in check_overlapping_overloads, reducing the None checking by preparing the list of valid items
1 parent 86dc7fe commit bbac93f

File tree

1 file changed

+25
-38
lines changed

1 file changed

+25
-38
lines changed

mypy/checker.py

Lines changed: 25 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -913,37 +913,24 @@ def check_overlapping_overloads(self, defn: OverloadedFuncDef) -> None:
913913

914914
is_descriptor_get = defn.info and defn.name == "__get__"
915915

916-
# Pre-extract callable types and literal fingerprints for each overload item.
917-
item_sigs: list[CallableType | None] = []
918-
item_literal_fingerprints: list[LiteralFingerprint] = []
919-
for item in defn.items:
920-
assert isinstance(item, Decorator)
921-
sig = self.extract_callable_type(item.var.type, item)
922-
item_sigs.append(sig)
923-
item_literal_fingerprints.append(
924-
build_literal_fingerprint(sig) if sig is not None else {}
925-
)
926-
916+
# Pre-extract callable types and literal fingerprints for each overload
917+
# item, skipping items whose signature could not be extracted.
918+
# Each entry is (original 0-based index, Decorator, sig, fingerprint).
919+
prepared_items: list[tuple[int, Decorator, CallableType, LiteralFingerprint]] = []
927920
for i, item in enumerate(defn.items):
928921
assert isinstance(item, Decorator)
929-
sig1 = item_sigs[i]
930-
if sig1 is None:
931-
continue
932-
933-
for j, item2 in enumerate(defn.items[i + 1 :], i + 1):
934-
assert isinstance(item2, Decorator)
935-
sig2 = item_sigs[j]
936-
if sig2 is None:
937-
continue
922+
sig = self.extract_callable_type(item.var.type, item)
923+
if sig is not None:
924+
prepared_items.append((i, item, sig, build_literal_fingerprint(sig)))
938925

926+
for prepared_items_i, (i, item, sig1, literals_fingerprint1) in enumerate(prepared_items):
927+
for j, item2, sig2, literals_fingerprint2 in prepared_items[prepared_items_i + 1 :]:
939928
if not are_argument_counts_overlapping(sig1, sig2):
940929
continue
941930

942931
# If there is any argument position where both overloads
943932
# carry a LiteralType with different values they are disjoint.
944-
if literal_args_are_disjoint(
945-
item_literal_fingerprints[i], item_literal_fingerprints[j]
946-
):
933+
if literal_args_are_disjoint(literals_fingerprint1, literals_fingerprint2):
947934
continue
948935

949936
if overload_can_never_match(sig1, sig2):
@@ -8983,24 +8970,10 @@ def detach_callable(typ: CallableType, class_type_vars: list[TypeVarLikeType]) -
89838970
# Using type(value) as part of the key means Literal[1] (int) and
89848971
# Literal[True] (bool) are kept distinct even though 1 == True in Python.
89858972
# A union such as Literal["a", "b"] or Literal["a"] | Literal["b"] produces
8986-
# a frozenset of two entries; a plain Literal["a"] produces a singleton set.
8973+
# a frozenset of two entries; a plain Literal["a"] produces a length 1 set.
89878974
LiteralFingerprint = dict[int, frozenset[tuple[type, LiteralValue]]]
89888975

89898976

8990-
def literal_args_are_disjoint(fp1: LiteralFingerprint, fp2: LiteralFingerprint) -> bool:
8991-
"""Return True if two overloads are provably disjoint via a Literal argument.
8992-
8993-
If there is any argument position where both carry only LiteralType values
8994-
and those value sets are disjoint, no single call can match both overloads
8995-
and the pairwise overlap check can be skipped entirely.
8996-
"""
8997-
for idx, vals1 in fp1.items():
8998-
vals2 = fp2.get(idx)
8999-
if vals2 is not None and vals1.isdisjoint(vals2):
9000-
return True
9001-
return False
9002-
9003-
90048977
def build_literal_fingerprint(sig: CallableType) -> LiteralFingerprint:
90058978
"""Build a LiteralFingerprint for one overload signature.
90068979
@@ -9033,6 +9006,20 @@ def build_literal_fingerprint(sig: CallableType) -> LiteralFingerprint:
90339006
return fingerprint
90349007

90359008

9009+
def literal_args_are_disjoint(fp1: LiteralFingerprint, fp2: LiteralFingerprint) -> bool:
9010+
"""Return True if two overloads are provably disjoint via a Literal argument.
9011+
9012+
If there is any argument position where both carry only LiteralType values
9013+
and those value sets are disjoint, no single call can match both overloads
9014+
and the pairwise overlap check can be skipped entirely.
9015+
"""
9016+
for idx, vals1 in fp1.items():
9017+
vals2 = fp2.get(idx)
9018+
if vals2 is not None and vals1.isdisjoint(vals2):
9019+
return True
9020+
return False
9021+
9022+
90369023
def overload_can_never_match(signature: CallableType, other: CallableType) -> bool:
90379024
"""Check if the 'other' method can never be matched due to 'signature'.
90389025

0 commit comments

Comments
 (0)