Skip to content

Commit 49a4670

Browse files
committed
Cleanup check_reverse_op_method
* Remove old comments, clean bail-out logic * Use fallback to find Instance when possible * Remove cast; move formatting logic to messages.py * remove unused method * rename variables to match check_overlapping_op_methods terminology * remove many unused imports
1 parent c332ea0 commit 49a4670

File tree

4 files changed

+41
-58
lines changed

4 files changed

+41
-58
lines changed

mypy/checker.py

Lines changed: 34 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -3,39 +3,34 @@
33
import itertools
44
import fnmatch
55
from contextlib import contextmanager
6-
import sys
76

87
from typing import (
98
Dict, Set, List, cast, Tuple, TypeVar, Union, Optional, NamedTuple, Iterator
109
)
1110

1211
from mypy.errors import Errors, report_internal_error
1312
from 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,
22+
Import, ImportFrom, ImportAll, ImportBase,
23+
ARG_POS, LITERAL_TYPE, MDEF, GDEF,
24+
CONTRAVARIANT, COVARIANT, INVARIANT,
25+
)
3126
from mypy import nodes
3227
from mypy.literals import literal, literal_hash
3328
from mypy.typeanal import has_any_from_unimported_type, check_for_explicit_any
3429
from 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
)
4035
from mypy.sametypes import is_same_type, is_same_types
4136
from mypy.messages import MessageBuilder, make_inferred_type_note
@@ -811,59 +806,50 @@ def is_trivial_body(self, block: Block) -> bool:
811806
(isinstance(stmt, ExpressionStmt) and
812807
isinstance(stmt.expr, EllipsisExpr)))
813808

814-
def check_reverse_op_method(self, defn: FuncItem, typ: CallableType,
815-
method: str) -> None:
809+
def check_reverse_op_method(self, defn: FuncItem,
810+
reverse_type: CallableType, reverse_name: str) -> None:
816811
"""Check a reverse operator method such as __radd__."""
812+
# Decides whether it's worth calling check_overlapping_op_methods().
817813

818-
# This used to check for some very obscure scenario. It now
819-
# just decides whether it's worth calling
820-
# check_overlapping_op_methods().
821-
822-
if method in ('__eq__', '__ne__'):
814+
if reverse_name in ('__eq__', '__ne__'):
823815
# These are defined for all objects => can't cause trouble.
824816
return
825817

826818
# With 'Any' or 'object' return type we are happy, since any possible
827819
# return value is valid.
828-
ret_type = typ.ret_type
820+
ret_type = reverse_type.ret_type
829821
if isinstance(ret_type, AnyType):
830822
return
831823
if isinstance(ret_type, Instance):
832824
if ret_type.type.fullname() == 'builtins.object':
833825
return
826+
834827
# Plausibly the method could have too few arguments, which would result
835828
# in an error elsewhere.
836-
if len(typ.arg_types) <= 2:
837-
# TODO check self argument kind
838-
839-
# Check for the issue described above.
840-
arg_type = typ.arg_types[1]
841-
other_method = nodes.normal_from_reverse_op[method]
842-
if isinstance(arg_type, Instance):
843-
if not arg_type.type.has_readable_member(other_method):
844-
return
845-
elif isinstance(arg_type, AnyType):
846-
return
847-
elif isinstance(arg_type, UnionType):
848-
if not arg_type.has_readable_member(other_method):
849-
return
850-
else:
851-
return
829+
if len(reverse_type.arg_types) != 2:
830+
return
852831

853-
typ2 = self.expr_checker.analyze_external_member_access(
854-
other_method, arg_type, defn)
855-
self.check_overlapping_op_methods(
856-
typ, method, defn.info,
857-
typ2, other_method, cast(Instance, arg_type),
858-
defn)
832+
forward_name = nodes.normal_from_reverse_op[reverse_name]
833+
forward_base = reverse_type.arg_types[1]
834+
if isinstance(forward_base, (FunctionLike, TupleType, TypedDictType)):
835+
forward_base = forward_base.fallback
836+
if not (isinstance(forward_base, (Instance, UnionType))
837+
and forward_base.has_readable_member(forward_name)):
838+
return
839+
840+
forward_type = self.expr_checker.analyze_external_member_access(forward_name, forward_base,
841+
context=defn)
842+
self.check_overlapping_op_methods(reverse_type, reverse_name, defn.info,
843+
forward_type, forward_name, forward_base,
844+
context=defn)
859845

860846
def check_overlapping_op_methods(self,
861847
reverse_type: CallableType,
862848
reverse_name: str,
863849
reverse_class: TypeInfo,
864850
forward_type: Type,
865851
forward_name: str,
866-
forward_base: Instance,
852+
forward_base: Type,
867853
context: Context) -> None:
868854
"""Check for overlapping method and reverse method signatures.
869855
@@ -924,8 +910,8 @@ def check_overlapping_op_methods(self,
924910
if is_unsafe_overlapping_signatures(forward_tweaked,
925911
reverse_tweaked):
926912
self.msg.operator_method_signatures_overlap(
927-
reverse_class.name(), reverse_name,
928-
forward_base.type.name(), forward_name, context)
913+
reverse_class, reverse_name,
914+
forward_base, forward_name, context)
929915
elif isinstance(forward_item, Overloaded):
930916
for item in forward_item.items():
931917
self.check_overlapping_op_methods(

mypy/messages.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -915,12 +915,12 @@ def overloaded_signatures_ret_specific(self, index1: int, context: Context) -> N
915915
'of signature {}'.format(index1), context)
916916

917917
def operator_method_signatures_overlap(
918-
self, reverse_class: str, reverse_method: str, forward_class: str,
918+
self, reverse_class: TypeInfo, reverse_method: str, forward_class: Type,
919919
forward_method: str, context: Context) -> None:
920-
self.fail('Signatures of "{}" of "{}" and "{}" of "{}" '
920+
self.fail('Signatures of "{}" of "{}" and "{}" of {} '
921921
'are unsafely overlapping'.format(
922-
reverse_method, reverse_class,
923-
forward_method, forward_class),
922+
reverse_method, reverse_class.name(),
923+
forward_method, self.format(forward_class)),
924924
context)
925925

926926
def forward_operator_not_callable(

mypy/nodes.py

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2058,15 +2058,9 @@ def __getitem__(self, name: str) -> 'SymbolTableNode':
20582058
def __repr__(self) -> str:
20592059
return '<TypeInfo %s>' % self.fullname()
20602060

2061-
# IDEA: Refactor the has* methods to be more consistent and document
2062-
# them.
2063-
20642061
def has_readable_member(self, name: str) -> bool:
20652062
return self.get(name) is not None
20662063

2067-
def has_method(self, name: str) -> bool:
2068-
return self.get_method(name) is not None
2069-
20702064
def get_method(self, name: str) -> Optional[FuncBase]:
20712065
if self.mro is None: # Might be because of a previous error.
20722066
return None

mypy/types.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -515,6 +515,9 @@ def deserialize(cls, data: Union[JsonDict, str]) -> 'Instance':
515515
def copy_modified(self, *, args: List[Type]) -> 'Instance':
516516
return Instance(self.type, args, self.line, self.column, self.erased)
517517

518+
def has_readable_member(self, name: str) -> bool:
519+
return self.type.has_readable_member(name)
520+
518521

519522
class TypeVarType(Type):
520523
"""A type variable type.

0 commit comments

Comments
 (0)