Skip to content

Commit

Permalink
Now overloads with ambiguous self are handled properly, refs #11347
Browse files Browse the repository at this point in the history
  • Loading branch information
sobolevn authored and flaeppe committed May 13, 2024
1 parent 35fbd2a commit dfdcd0e
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 3 deletions.
16 changes: 13 additions & 3 deletions mypy/checkexpr.py
Original file line number Diff line number Diff line change
Expand Up @@ -2820,6 +2820,7 @@ def infer_overload_return_type(
matches: list[CallableType] = []
return_types: list[Type] = []
inferred_types: list[Type] = []
self_contains_any = has_any_type(object_type) if object_type is not None else False
args_contain_any = any(map(has_any_type, arg_types))
type_maps: list[dict[Expression, Type]] = []

Expand All @@ -2840,7 +2841,7 @@ def infer_overload_return_type(
if is_match:
# Return early if possible; otherwise record info, so we can
# check for ambiguity due to 'Any' below.
if not args_contain_any:
if not args_contain_any and not self_contains_any:
self.chk.store_types(m)
return ret_type, infer_type
p_infer_type = get_proper_type(infer_type)
Expand All @@ -2856,7 +2857,14 @@ def infer_overload_return_type(

if not matches:
return None
elif any_causes_overload_ambiguity(matches, return_types, arg_types, arg_kinds, arg_names):
elif any_causes_overload_ambiguity(
matches,
return_types,
arg_types,
arg_kinds,
arg_names,
self_contains_any=self_contains_any,
):
# An argument of type or containing the type 'Any' caused ambiguity.
# We try returning a precise type if we can. If not, we give up and just return 'Any'.
if all_same_types(return_types):
Expand Down Expand Up @@ -6452,6 +6460,8 @@ def any_causes_overload_ambiguity(
arg_types: list[Type],
arg_kinds: list[ArgKind],
arg_names: Sequence[str | None] | None,
*,
self_contains_any: bool = False,
) -> bool:
"""May an argument containing 'Any' cause ambiguous result type on call to overloaded function?
Expand Down Expand Up @@ -6501,7 +6511,7 @@ def any_causes_overload_ambiguity(
if not all_same_types(matching_formals) and not all_same_types(matching_returns):
# Any maps to multiple different types, and the return types of these items differ.
return True
return False
return self_contains_any


def all_same_types(types: list[Type]) -> bool:
Expand Down
62 changes: 62 additions & 0 deletions test-data/unit/check-overloading.test
Original file line number Diff line number Diff line change
Expand Up @@ -6693,3 +6693,65 @@ class B:
def f(self, *args, **kwargs):
pass
[builtins fixtures/tuple.pyi]

[case testOverloadSelfArgWithMultipleMatches]
# https://github.com/python/mypy/issues/11347
from typing import Generic, TypeVar, overload, Any

T = TypeVar('T')

class Some(Generic[T]):
@overload
def method(self: Some[int]) -> str: ...
@overload
def method(self: Some[str]) -> float: ...
def method(self): ...

s1: Some[int]
s2: Some[str]
s3: Some[Any]
reveal_type(s1.method()) # N: Revealed type is "builtins.str"
reveal_type(s2.method()) # N: Revealed type is "builtins.float"
reveal_type(s3.method()) # N: Revealed type is "Any"
[builtins fixtures/dict.pyi]

[case testOverloadSelfArgWithOtherSameArgAndMultipleMatches]
from typing import Generic, TypeVar, overload, Any

T = TypeVar('T')

class Some(Generic[T]):
@overload
def method(self: Some[int], other: int) -> str: ...
@overload
def method(self: Some[str], other: int) -> float: ...
def method(self): ...

# was ok
s1: Some[int]
s2: Some[str]
s3: Some[Any]
reveal_type(s1.method(1)) # N: Revealed type is "builtins.str"
reveal_type(s2.method(1)) # N: Revealed type is "builtins.float"
reveal_type(s3.method(1)) # N: Revealed type is "Any"
[builtins fixtures/dict.pyi]

[case testOverloadSelfArgWithOtherDifferentArgAndMultipleMatches]
from typing import Generic, TypeVar, overload, Any

T = TypeVar('T')

class Some(Generic[T]):
@overload
def method(self: Some[int], other: int) -> str: ...
@overload
def method(self: Some[str], other: str) -> float: ...
def method(self): ...

s1: Some[int]
s2: Some[str]
s3: Some[Any]
reveal_type(s1.method(1)) # N: Revealed type is "builtins.str"
reveal_type(s2.method('a')) # N: Revealed type is "builtins.float"
reveal_type(s3.method(1)) # N: Revealed type is "builtins.str"
[builtins fixtures/dict.pyi]

0 comments on commit dfdcd0e

Please sign in to comment.