Skip to content

Commit

Permalink
Document and rename overload-overlap error code (#16074)
Browse files Browse the repository at this point in the history
A new error code was introduced in
#16061

As per #16068, we didn't previously
run doc builds on changes to errorcodes.py, causing tests to fail on
master when this was merged.

Renaming the code as per:
#16061 (comment) All
type ignores should be unsafe, so we should save the unsafe adjective
for things that are really unsafe. As it stands, there are many cases
where overloads overlap somewhat benignly.

Fixes #8656
  • Loading branch information
hauntsaninja authored Sep 10, 2023
1 parent f9dc561 commit ed18fea
Show file tree
Hide file tree
Showing 6 changed files with 44 additions and 6 deletions.
35 changes: 35 additions & 0 deletions docs/source/error_code_list.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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 <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 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``:
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

0 comments on commit ed18fea

Please sign in to comment.