Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Document and rename overload-overlap error code #16074

Merged
merged 3 commits into from
Sep 10, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions docs/source/error_code_list.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1114,6 +1114,40 @@ 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 unsafe ways.
hauntsaninja marked this conversation as resolved.
Show resolved Hide resolved

.. 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 you ignore this error, mypy will usually still infer the
hauntsaninja marked this conversation as resolved.
Show resolved Hide resolved
types you expect.

See :ref:`overloading <function-overloading>` for more explanation.

.. _code-annotation-unchecked:

Notify about an annotation in an unchecked function [annotation-unchecked]
Expand Down
5 changes: 4 additions & 1 deletion docs/source/more_types.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand All @@ -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 you ignore the overlapping overload error, mypy will usually
hauntsaninja marked this conversation as resolved.
Show resolved Hide resolved
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``:
Expand Down
4 changes: 2 additions & 2 deletions mypy/errorcodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion mypy/messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
2 changes: 1 addition & 1 deletion mypy/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -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]
...


Expand Down
2 changes: 1 addition & 1 deletion test-data/unit/check-errorcodes.test
Original file line number Diff line number Diff line change
Expand Up @@ -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]:
Expand Down