From 4554bd0c6d49506a4a1c77dcf344dca4efee92e1 Mon Sep 17 00:00:00 2001 From: Katrina Connors <32425204+katconnors@users.noreply.github.com> Date: Wed, 18 Sep 2024 14:22:06 -0700 Subject: [PATCH] Added error code for overlapping function signatures (#17597) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes #17570. This is my first contribution to mypy! 🐍 Added an error code for overlapping function signatures. Test in check-errorcodes.test is a derivative of this post: https://stackoverflow.com/q/69341607 Co-authored-by: Jelle Zijlstra Co-authored-by: Alex Waygood --- docs/source/error_code_list.rst | 28 ++++++++++++++++++++++++++++ mypy/errorcodes.py | 8 ++++++++ mypy/messages.py | 1 + test-data/unit/check-errorcodes.test | 14 ++++++++++++++ 4 files changed, 51 insertions(+) diff --git a/docs/source/error_code_list.rst b/docs/source/error_code_list.rst index 85c8d437a856..ad73bc999f00 100644 --- a/docs/source/error_code_list.rst +++ b/docs/source/error_code_list.rst @@ -1149,6 +1149,34 @@ types you expect. See :ref:`overloading ` for more explanation. + +.. _code-overload-cannot-match: + +Check for overload signatures that cannot match [overload-cannot-match] +-------------------------------------------------------------------------- + +Warn if an ``@overload`` variant can never be matched, because an earlier +overload has a wider signature. For example, this can happen if the two +overloads accept the same parameters and each parameter on the first overload +has the same type or a wider type than the corresponding parameter on the second +overload. + +Example: + +.. code-block:: python + + from typing import overload, Union + + @overload + def process(response1: object, response2: object) -> object: + ... + @overload + def process(response1: int, response2: int) -> int: # E: Overloaded function signature 2 will never be matched: signature 1's parameter type(s) are the same or broader [overload-cannot-match] + ... + + def process(response1: object, response2: object) -> object: + return response1 + response2 + .. _code-annotation-unchecked: Notify about an annotation in an unchecked function [annotation-unchecked] diff --git a/mypy/errorcodes.py b/mypy/errorcodes.py index 6e8763264ddd..ad061b161af1 100644 --- a/mypy/errorcodes.py +++ b/mypy/errorcodes.py @@ -273,6 +273,14 @@ def __hash__(self) -> int: # This is a catch-all for remaining uncategorized errors. MISC: Final[ErrorCode] = ErrorCode("misc", "Miscellaneous other checks", "General") +OVERLOAD_CANNOT_MATCH: Final[ErrorCode] = ErrorCode( + "overload-cannot-match", + "Warn if an @overload signature can never be matched", + "General", + sub_code_of=MISC, +) + + OVERLOAD_OVERLAP: Final[ErrorCode] = ErrorCode( "overload-overlap", "Warn if multiple @overload variants overlap in unsafe ways", diff --git a/mypy/messages.py b/mypy/messages.py index 62846c536f3d..dadce149680e 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -1653,6 +1653,7 @@ def overloaded_signature_will_never_match( index1=index1, index2=index2 ), context, + code=codes.OVERLOAD_CANNOT_MATCH, ) def overloaded_signatures_typevar_specific(self, index: int, context: Context) -> None: diff --git a/test-data/unit/check-errorcodes.test b/test-data/unit/check-errorcodes.test index cca13347dd86..10cc145d0c70 100644 --- a/test-data/unit/check-errorcodes.test +++ b/test-data/unit/check-errorcodes.test @@ -1222,3 +1222,17 @@ def f(x: str) -> TypeIs[int]: # E: Narrowed type "int" is not a subtype of inpu pass [builtins fixtures/tuple.pyi] + + +[case testOverloadedFunctionSignature] +from typing import overload, Union + +@overload +def process(response1: float,response2: float) -> float: + ... +@overload +def process(response1: int,response2: int) -> int: # E: Overloaded function signature 2 will never be matched: signature 1's parameter type(s) are the same or broader [overload-cannot-match] + ... + +def process(response1,response2)-> Union[float,int]: + return response1 + response2