diff --git a/docs/source/error_code_list.rst b/docs/source/error_code_list.rst index a865a4dd1532..4decd37e6e8a 100644 --- a/docs/source/error_code_list.rst +++ b/docs/source/error_code_list.rst @@ -1114,6 +1114,41 @@ Warn about cases where a bytes object may be converted to a string in an unexpec print(f"The alphabet starts with {b!r}") # The alphabet starts with b'abc' print(f"The alphabet starts with {b.decode('utf-8')}") # The alphabet starts with abc +.. _code-overload-overlap: + +Check that overloaded functions don't overlap [overload-overlap] +---------------------------------------------------------------- + +Warn if multiple ``@overload`` variants overlap in potentially unsafe ways. +This guards against the following situation: + +.. code-block:: python + + from typing import overload + + class A: ... + class B(A): ... + + @overload + def foo(x: B) -> int: ... # Error: Overloaded function signatures 1 and 2 overlap with incompatible return types [overload-overlap] + @overload + def foo(x: A) -> str: ... + def foo(x): ... + + def takes_a(a: A) -> str: + return foo(a) + + a: A = B() + value = takes_a(a) + # mypy will think that value is a str, but it could actually be an int + reveal_type(value) # Revealed type is "builtins.str" + + +Note that in cases where you ignore this error, mypy will usually still infer the +types you expect. + +See :ref:`overloading ` for more explanation. + .. _code-annotation-unchecked: Notify about an annotation in an unchecked function [annotation-unchecked] diff --git a/docs/source/more_types.rst b/docs/source/more_types.rst index 4e6e9204fdca..b27764a9e87c 100644 --- a/docs/source/more_types.rst +++ b/docs/source/more_types.rst @@ -501,7 +501,7 @@ To prevent these kinds of issues, mypy will detect and prohibit inherently unsaf overlapping overloads on a best-effort basis. Two variants are considered unsafely overlapping when both of the following are true: -1. All of the arguments of the first variant are compatible with the second. +1. All of the arguments of the first variant are potentially compatible with the second. 2. The return type of the first variant is *not* compatible with (e.g. is not a subtype of) the second. @@ -510,6 +510,9 @@ the ``object`` argument in the second, yet the ``int`` return type is not a subt ``str``. Both conditions are true, so mypy will correctly flag ``unsafe_func`` as being unsafe. +Note that in cases where you ignore the overlapping overload error, mypy will usually +still infer the types you expect at callsites. + However, mypy will not detect *all* unsafe uses of overloads. For example, suppose we modify the above snippet so it calls ``summarize`` instead of ``unsafe_func``: diff --git a/mypy/errorcodes.py b/mypy/errorcodes.py index 70b8cffe9053..cd9978c2f31c 100644 --- a/mypy/errorcodes.py +++ b/mypy/errorcodes.py @@ -262,8 +262,8 @@ def __hash__(self) -> int: # This is a catch-all for remaining uncategorized errors. MISC: Final = ErrorCode("misc", "Miscellaneous other checks", "General") -UNSAFE_OVERLOAD: Final[ErrorCode] = ErrorCode( - "unsafe-overload", +OVERLOAD_OVERLAP: Final[ErrorCode] = ErrorCode( + "overload-overlap", "Warn if multiple @overload variants overlap in unsafe ways", "General", sub_code_of=MISC, diff --git a/mypy/messages.py b/mypy/messages.py index a58c5f91c4b1..b6fdaf06a8e0 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -1604,7 +1604,7 @@ def overloaded_signatures_overlap(self, index1: int, index2: int, context: Conte "Overloaded function signatures {} and {} overlap with " "incompatible return types".format(index1, index2), context, - code=codes.UNSAFE_OVERLOAD, + code=codes.OVERLOAD_OVERLAP, ) def overloaded_signature_will_never_match( diff --git a/mypy/types.py b/mypy/types.py index cee4595b67cc..04d90c9dc124 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -3019,7 +3019,7 @@ def get_proper_type(typ: Type | None) -> ProperType | None: @overload -def get_proper_types(types: list[Type] | tuple[Type, ...]) -> list[ProperType]: # type: ignore[unsafe-overload] +def get_proper_types(types: list[Type] | tuple[Type, ...]) -> list[ProperType]: # type: ignore[overload-overlap] ... diff --git a/test-data/unit/check-errorcodes.test b/test-data/unit/check-errorcodes.test index 72edf2f22c05..ac7c8b4c9f9d 100644 --- a/test-data/unit/check-errorcodes.test +++ b/test-data/unit/check-errorcodes.test @@ -1077,7 +1077,7 @@ x = 1 # type: ignore # E: Unused "type: ignore" comment [unused-ignore] from typing import overload, Union @overload -def unsafe_func(x: int) -> int: ... # E: Overloaded function signatures 1 and 2 overlap with incompatible return types [unsafe-overload] +def unsafe_func(x: int) -> int: ... # E: Overloaded function signatures 1 and 2 overlap with incompatible return types [overload-overlap] @overload def unsafe_func(x: object) -> str: ... def unsafe_func(x: object) -> Union[int, str]: