Skip to content

Commit

Permalink
Make overload checks more strict when there are multiple 'Any's
Browse files Browse the repository at this point in the history
Resolves #5250

This makes the "multiple overload matches due to Any" even more strict:
we now return a non-Any type only if all of the return types are the
same.
  • Loading branch information
Michael0x2a committed Jun 20, 2018
1 parent 5dc7454 commit d0bee85
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 9 deletions.
11 changes: 4 additions & 7 deletions mypy/checkexpr.py
Expand Up @@ -36,7 +36,7 @@
from mypy import join
from mypy.meet import narrow_declared_type
from mypy.maptype import map_instance_to_supertype
from mypy.subtypes import is_subtype, is_equivalent, find_member, non_method_protocol_members
from mypy.subtypes import is_subtype, is_proper_subtype, is_equivalent, find_member, non_method_protocol_members
from mypy import applytype
from mypy import erasetype
from mypy.checkmember import analyze_member_access, type_object_type, bind_self
Expand Down Expand Up @@ -1314,12 +1314,9 @@ def infer_overload_return_type(self,
return None
elif any_causes_overload_ambiguity(matches, return_types, arg_types, arg_kinds, arg_names):
# An argument of type or containing the type 'Any' caused ambiguity.
if all(is_subtype(ret_type, return_types[-1]) and
is_subtype(return_types[-1], ret_type)
for ret_type in return_types[:-1]):
# The last match is mutually compatible with all previous ones, so it's safe
# to return that inferred type.
return return_types[-1], inferred_types[-1]
if all_same_types(return_types):
# The return types are all the same, so it's safe to return that instead of 'Any'
return return_types[0], inferred_types[0]
else:
# We give up and return 'Any'.
return self.check_call(callee=AnyType(TypeOfAny.special_form),
Expand Down
52 changes: 50 additions & 2 deletions test-data/unit/check-overloading.test
Expand Up @@ -1400,8 +1400,8 @@ a: Any
b: List[Any]
c: List[str]
d: List[int]
reveal_type(f(a)) # E: Revealed type is 'builtins.list[Any]'
reveal_type(f(b)) # E: Revealed type is 'builtins.list[Any]'
reveal_type(f(a)) # E: Revealed type is 'Any'
reveal_type(f(b)) # E: Revealed type is 'Any'
reveal_type(f(c)) # E: Revealed type is 'builtins.list[Any]'
reveal_type(f(d)) # E: Revealed type is 'builtins.list[builtins.int]'

Expand Down Expand Up @@ -1443,6 +1443,54 @@ reveal_type(f(**a)) # E: Revealed type is 'Any'

[builtins fixtures/dict.pyi]

[case testOverloadWithOverlappingItemsAndAnyArgument12]
from typing import overload, Any

@overload
def f(x: int) -> Any: ...
@overload
def f(x: str) -> str: ...
def f(x): pass

a: Any
reveal_type(f(a)) # E: Revealed type is 'Any'

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

class slice: pass

T = TypeVar('T')
class A(Generic[T]):
@overload
def f(self, x: int) -> T: ...
@overload
def f(self, x: slice) -> A[T]: ...
def f(self, x): ...

i: Any
a: A[Any]
reveal_type(a.f(i)) # E: Revealed type is 'Any'

[case testOverloadWithOverlappingItemsAndAnyArgument14]
from typing import overload, Any, Union

@overload
def f(x: int) -> str: ...
@overload
def f(x: str) -> str: ...
def f(x): pass

@overload
def g(x: int) -> Union[str, int]: ...
@overload
def g(x: str) -> Union[int, str]: ...
def g(x): pass

a: Any
reveal_type(f(a)) # E: Revealed type is 'builtins.str'
reveal_type(g(a)) # E: Revealed type is 'Union[builtins.str, builtins.int]'

[case testOverloadOnOverloadWithType]
from typing import Any, Type, TypeVar, overload
from mod import MyInt
Expand Down

0 comments on commit d0bee85

Please sign in to comment.