From d0bee85b4641e6408fd0c5ee53a1aee7608f49bb Mon Sep 17 00:00:00 2001 From: Michael Lee Date: Wed, 20 Jun 2018 08:28:01 -0700 Subject: [PATCH] Make overload checks more strict when there are multiple 'Any's Resolves https://github.com/python/mypy/issues/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. --- mypy/checkexpr.py | 11 +++--- test-data/unit/check-overloading.test | 52 +++++++++++++++++++++++++-- 2 files changed, 54 insertions(+), 9 deletions(-) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index ae2a4907e0c20..c133748cdaa7a 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -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 @@ -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), diff --git a/test-data/unit/check-overloading.test b/test-data/unit/check-overloading.test index c74ec5e329587..5e7ff54818ae1 100644 --- a/test-data/unit/check-overloading.test +++ b/test-data/unit/check-overloading.test @@ -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]' @@ -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