Skip to content

Commit a0448dc

Browse files
emmatypingJukkaL
authored andcommitted
Check signature of reverse operator methods (#4249)
Due to an old change, the signatures of reverse operator methods were not checked. This change corrects that. Fixes #4241.
1 parent 69a8556 commit a0448dc

File tree

2 files changed

+27
-5
lines changed

2 files changed

+27
-5
lines changed

mypy/checker.py

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -641,7 +641,7 @@ def check_func_def(self, defn: FuncItem, typ: CallableType, name: Optional[str])
641641

642642
if name: # Special method names
643643
if name in nodes.reverse_op_method_set:
644-
self.check_reverse_op_method(item, typ, name)
644+
self.check_reverse_op_method(item, typ, name, defn)
645645
elif name in ('__getattr__', '__getattribute__'):
646646
self.check_getattr_method(typ, defn, name)
647647
elif name == '__setattr__':
@@ -820,13 +820,24 @@ def is_trivial_body(self, block: Block) -> bool:
820820
isinstance(stmt.expr, EllipsisExpr)))
821821

822822
def check_reverse_op_method(self, defn: FuncItem, typ: CallableType,
823-
method: str) -> None:
823+
method: str, context: Context) -> None:
824824
"""Check a reverse operator method such as __radd__."""
825825

826826
# This used to check for some very obscure scenario. It now
827827
# just decides whether it's worth calling
828828
# check_overlapping_op_methods().
829829

830+
# First check for a valid signature
831+
method_type = CallableType([AnyType(TypeOfAny.special_form),
832+
AnyType(TypeOfAny.special_form)],
833+
[nodes.ARG_POS, nodes.ARG_POS],
834+
[None, None],
835+
AnyType(TypeOfAny.special_form),
836+
self.named_type('builtins.function'))
837+
if not is_subtype(typ, method_type):
838+
self.msg.invalid_signature(typ, context)
839+
return
840+
830841
if method in ('__eq__', '__ne__'):
831842
# These are defined for all objects => can't cause trouble.
832843
return
@@ -839,9 +850,8 @@ def check_reverse_op_method(self, defn: FuncItem, typ: CallableType,
839850
if isinstance(ret_type, Instance):
840851
if ret_type.type.fullname() == 'builtins.object':
841852
return
842-
# Plausibly the method could have too few arguments, which would result
843-
# in an error elsewhere.
844-
if len(typ.arg_types) <= 2:
853+
854+
if len(typ.arg_types) == 2:
845855
# TODO check self argument kind
846856

847857
# Check for the issue described above.

test-data/unit/check-classes.test

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1639,6 +1639,18 @@ class C:
16391639
def __radd__(self, x: Any) -> int: pass
16401640
[out]
16411641

1642+
[case testReverseOperatorMethodInvalid]
1643+
from foo import *
1644+
[file foo.pyi]
1645+
class A: ...
1646+
class B:
1647+
def __rmul__(self) -> A: ...
1648+
class C:
1649+
def __radd__(self, other, oops) -> int: ...
1650+
[out]
1651+
tmp/foo.pyi:3: error: Invalid signature "def (foo.B) -> foo.A"
1652+
tmp/foo.pyi:5: error: Invalid signature "def (foo.C, Any, Any) -> builtins.int"
1653+
16421654
[case testReverseOperatorMethodForwardIsAny]
16431655
from typing import Any
16441656
def deco(f: Any) -> Any: return f

0 commit comments

Comments
 (0)