Skip to content

Commit

Permalink
Fix error code of binary operation involving overload (#8124)
Browse files Browse the repository at this point in the history
Previously the error code for the error message was 'call-overload',
which was inconsistent. In addition, the related note had the 'operator'
error code, which made things worse. Now both the error message and
the note have the 'operator' error code.
  • Loading branch information
JukkaL committed Dec 11, 2019
1 parent 7e65f1e commit 4fc7400
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 6 deletions.
16 changes: 15 additions & 1 deletion mypy/checkexpr.py
Original file line number Diff line number Diff line change
Expand Up @@ -1495,8 +1495,12 @@ def check_overload_call(self,
target = AnyType(TypeOfAny.from_error)

if not self.chk.should_suppress_optional_error(arg_types):
if not is_operator_method(callable_name):
code = None
else:
code = codes.OPERATOR
arg_messages.no_variant_matches_arguments(
plausible_targets, callee, arg_types, context)
plausible_targets, callee, arg_types, context, code=code)

result = self.check_call(target, args, arg_kinds, context, arg_names,
arg_messages=arg_messages,
Expand Down Expand Up @@ -4277,3 +4281,13 @@ def type_info_from_type(typ: Type) -> Optional[TypeInfo]:
# A complicated type. Too tricky, give up.
# TODO: Do something more clever here.
return None


def is_operator_method(fullname: Optional[str]) -> bool:
if fullname is None:
return False
short_name = fullname.split('.')[-1]
return (
short_name in nodes.op_methods.values() or
short_name in nodes.reverse_op_methods.values() or
short_name in nodes.unary_op_methods.values())
13 changes: 8 additions & 5 deletions mypy/messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -659,7 +659,10 @@ def no_variant_matches_arguments(self,
plausible_targets: List[CallableType],
overload: Overloaded,
arg_types: List[Type],
context: Context) -> None:
context: Context,
*,
code: Optional[ErrorCode] = None) -> None:
code = code or codes.CALL_OVERLOAD
name = callable_name(overload)
if name:
name_str = ' of {}'.format(name)
Expand All @@ -669,16 +672,16 @@ def no_variant_matches_arguments(self,
num_args = len(arg_types)
if num_args == 0:
self.fail('All overload variants{} require at least one argument'.format(name_str),
context, code=codes.CALL_OVERLOAD)
context, code=code)
elif num_args == 1:
self.fail('No overload variant{} matches argument type {}'
.format(name_str, arg_types_str), context, code=codes.CALL_OVERLOAD)
.format(name_str, arg_types_str), context, code=code)
else:
self.fail('No overload variant{} matches argument types {}'
.format(name_str, arg_types_str), context, code=codes.CALL_OVERLOAD)
.format(name_str, arg_types_str), context, code=code)

self.pretty_overload_matches(plausible_targets, overload, context, offset=2, max_items=2,
code=codes.CALL_OVERLOAD)
code=code)

def wrong_number_values_to_unpack(self, provided: int, expected: int,
context: Context) -> None:
Expand Down
26 changes: 26 additions & 0 deletions test-data/unit/check-errorcodes.test
Original file line number Diff line number Diff line change
Expand Up @@ -703,3 +703,29 @@ class InvalidReturn:
# N: If return type of "__exit__" implies that it may return True, the context manager may swallow exceptions
return False
[builtins fixtures/bool.pyi]

[case testErrorCodeOverloadedOperatorMethod]
# flags: --strict-optional
from typing import Optional, overload

class A:
@overload
def __add__(self, x: int) -> A: ...
@overload
def __add__(self, x: str) -> str: ...
def __add__(self, x): pass

class B:
pass

x: Optional[B]
A() + x # type: ignore[operator]

class C:
@overload
def __rsub__(self, x: int) -> A: ...
@overload
def __rsub__(self, x: str) -> str: ...
def __rsub__(self, x): pass

x - C() # type: ignore[operator]

0 comments on commit 4fc7400

Please sign in to comment.