33import itertools
44import fnmatch
55from contextlib import contextmanager
6- import sys
76
87from typing import (
98 Dict , Set , List , cast , Tuple , TypeVar , Union , Optional , NamedTuple , Iterator
109)
1110
1211from mypy .errors import Errors , report_internal_error
1312from mypy .nodes import (
14- SymbolTable , Statement , MypyFile , Var , Expression , Lvalue ,
13+ SymbolTable , Statement , MypyFile , Var , Expression , Lvalue , Node ,
1514 OverloadedFuncDef , FuncDef , FuncItem , FuncBase , TypeInfo ,
16- ClassDef , GDEF , Block , AssignmentStmt , NameExpr , MemberExpr , IndexExpr ,
15+ ClassDef , Block , AssignmentStmt , NameExpr , MemberExpr , IndexExpr ,
1716 TupleExpr , ListExpr , ExpressionStmt , ReturnStmt , IfStmt ,
1817 WhileStmt , OperatorAssignmentStmt , WithStmt , AssertStmt ,
1918 RaiseStmt , TryStmt , ForStmt , DelStmt , CallExpr , IntExpr , StrExpr ,
20- BytesExpr , UnicodeExpr , FloatExpr , OpExpr , UnaryExpr , CastExpr , RevealTypeExpr , SuperExpr ,
21- TypeApplication , DictExpr , SliceExpr , LambdaExpr , TempNode , SymbolTableNode ,
22- Context , ListComprehension , ConditionalExpr , GeneratorExpr ,
23- Decorator , SetExpr , TypeVarExpr , NewTypeExpr , PrintStmt ,
24- LITERAL_TYPE , BreakStmt , PassStmt , ContinueStmt , ComparisonExpr , StarExpr ,
25- YieldFromExpr , NamedTupleExpr , TypedDictExpr , SetComprehension ,
26- DictionaryComprehension , ComplexExpr , EllipsisExpr , TypeAliasExpr ,
27- RefExpr , YieldExpr , BackquoteExpr , Import , ImportFrom , ImportAll , ImportBase ,
28- AwaitExpr , PromoteExpr , Node , EnumCallExpr ,
29- ARG_POS , MDEF ,
30- CONTRAVARIANT , COVARIANT , INVARIANT )
19+ UnicodeExpr , OpExpr , UnaryExpr , LambdaExpr , TempNode , SymbolTableNode ,
20+ Context , Decorator , PrintStmt , BreakStmt , PassStmt , ContinueStmt ,
21+ ComparisonExpr , StarExpr , EllipsisExpr , RefExpr , PromoteExpr ,
22+ Import , ImportFrom , ImportAll , ImportBase ,
23+ ARG_POS , ARG_STAR , LITERAL_TYPE , MDEF , GDEF ,
24+ CONTRAVARIANT , COVARIANT , INVARIANT ,
25+ )
3126from mypy import nodes
3227from mypy .literals import literal , literal_hash
3328from mypy .typeanal import has_any_from_unimported_type , check_for_explicit_any
3429from mypy .types import (
3530 Type , AnyType , CallableType , FunctionLike , Overloaded , TupleType , TypedDictType ,
3631 Instance , NoneTyp , strip_type , TypeType , TypeOfAny ,
3732 UnionType , TypeVarId , TypeVarType , PartialType , DeletedType , UninhabitedType , TypeVarDef ,
38- true_only , false_only , function_type , is_named_instance , union_items
33+ true_only , false_only , function_type , is_named_instance , union_items ,
3934)
4035from mypy .sametypes import is_same_type , is_same_types
4136from mypy .messages import MessageBuilder , make_inferred_type_note
@@ -875,9 +870,11 @@ def is_trivial_body(self, block: Block) -> bool:
875870 (isinstance (stmt , ExpressionStmt ) and
876871 isinstance (stmt .expr , EllipsisExpr )))
877872
878- def check_reverse_op_method (self , defn : FuncItem , typ : CallableType ,
879- method : str , context : Context ) -> None :
873+ def check_reverse_op_method (self , defn : FuncItem ,
874+ reverse_type : CallableType , reverse_name : str ,
875+ context : Context ) -> None :
880876 """Check a reverse operator method such as __radd__."""
877+ # Decides whether it's worth calling check_overlapping_op_methods().
881878
882879 # This used to check for some very obscure scenario. It now
883880 # just decides whether it's worth calling
@@ -890,54 +887,57 @@ def check_reverse_op_method(self, defn: FuncItem, typ: CallableType,
890887 [None , None ],
891888 AnyType (TypeOfAny .special_form ),
892889 self .named_type ('builtins.function' ))
893- if not is_subtype (typ , method_type ):
894- self .msg .invalid_signature (typ , context )
890+ if not is_subtype (reverse_type , method_type ):
891+ self .msg .invalid_signature (reverse_type , context )
895892 return
896893
897- if method in ('__eq__' , '__ne__' ):
894+ if reverse_name in ('__eq__' , '__ne__' ):
898895 # These are defined for all objects => can't cause trouble.
899896 return
900897
901898 # With 'Any' or 'object' return type we are happy, since any possible
902899 # return value is valid.
903- ret_type = typ .ret_type
900+ ret_type = reverse_type .ret_type
904901 if isinstance (ret_type , AnyType ):
905902 return
906903 if isinstance (ret_type , Instance ):
907904 if ret_type .type .fullname () == 'builtins.object' :
908905 return
909-
910- if len (typ .arg_types ) == 2 :
911- # TODO check self argument kind
912-
913- # Check for the issue described above.
914- arg_type = typ .arg_types [1 ]
915- other_method = nodes .normal_from_reverse_op [method ]
916- if isinstance (arg_type , Instance ):
917- if not arg_type .type .has_readable_member (other_method ):
918- return
919- elif isinstance (arg_type , AnyType ):
920- return
921- elif isinstance (arg_type , UnionType ):
922- if not arg_type .has_readable_member (other_method ):
923- return
924- else :
925- return
926-
927- typ2 = self .expr_checker .analyze_external_member_access (
928- other_method , arg_type , defn )
929- self .check_overlapping_op_methods (
930- typ , method , defn .info ,
931- typ2 , other_method , cast (Instance , arg_type ),
932- defn )
906+ if reverse_type .arg_kinds [0 ] == ARG_STAR :
907+ reverse_type = reverse_type .copy_modified (arg_types = [reverse_type .arg_types [0 ]] * 2 ,
908+ arg_kinds = [ARG_POS ] * 2 ,
909+ arg_names = [reverse_type .arg_names [0 ], "_" ])
910+ assert len (reverse_type .arg_types ) == 2
911+
912+ forward_name = nodes .normal_from_reverse_op [reverse_name ]
913+ forward_inst = reverse_type .arg_types [1 ]
914+ if isinstance (forward_inst , TypeVarType ):
915+ forward_inst = forward_inst .upper_bound
916+ if isinstance (forward_inst , (FunctionLike , TupleType , TypedDictType )):
917+ forward_inst = forward_inst .fallback
918+ if isinstance (forward_inst , TypeType ):
919+ item = forward_inst .item
920+ if isinstance (item , Instance ):
921+ opt_meta = item .type .metaclass_type
922+ if opt_meta is not None :
923+ forward_inst = opt_meta
924+ if not (isinstance (forward_inst , (Instance , UnionType ))
925+ and forward_inst .has_readable_member (forward_name )):
926+ return
927+ forward_base = reverse_type .arg_types [1 ]
928+ forward_type = self .expr_checker .analyze_external_member_access (forward_name , forward_base ,
929+ context = defn )
930+ self .check_overlapping_op_methods (reverse_type , reverse_name , defn .info ,
931+ forward_type , forward_name , forward_base ,
932+ context = defn )
933933
934934 def check_overlapping_op_methods (self ,
935935 reverse_type : CallableType ,
936936 reverse_name : str ,
937937 reverse_class : TypeInfo ,
938938 forward_type : Type ,
939939 forward_name : str ,
940- forward_base : Instance ,
940+ forward_base : Type ,
941941 context : Context ) -> None :
942942 """Check for overlapping method and reverse method signatures.
943943
@@ -998,8 +998,8 @@ def check_overlapping_op_methods(self,
998998 if is_unsafe_overlapping_signatures (forward_tweaked ,
999999 reverse_tweaked ):
10001000 self .msg .operator_method_signatures_overlap (
1001- reverse_class . name () , reverse_name ,
1002- forward_base . type . name () , forward_name , context )
1001+ reverse_class , reverse_name ,
1002+ forward_base , forward_name , context )
10031003 elif isinstance (forward_item , Overloaded ):
10041004 for item in forward_item .items ():
10051005 self .check_overlapping_op_methods (
0 commit comments