16
16
from mypy import applytype , erasetype , join , message_registry , nodes , operators , types
17
17
from mypy .argmap import ArgTypeExpander , map_actuals_to_formals , map_formals_to_actuals
18
18
from mypy .checker_shared import ExpressionCheckerSharedApi
19
- from mypy .checkmember import analyze_member_access
19
+ from mypy .checkmember import analyze_member_access , has_operator
20
20
from mypy .checkstrformat import StringFormatterChecker
21
21
from mypy .erasetype import erase_type , remove_instance_last_known_values , replace_meta_vars
22
22
from mypy .errors import ErrorWatcher , report_internal_error
@@ -3834,13 +3834,16 @@ def check_method_call_by_name(
3834
3834
arg_kinds : list [ArgKind ],
3835
3835
context : Context ,
3836
3836
original_type : Type | None = None ,
3837
+ self_type : Type | None = None ,
3837
3838
) -> tuple [Type , Type ]:
3838
3839
"""Type check a call to a named method on an object.
3839
3840
3840
3841
Return tuple (result type, inferred method type). The 'original_type'
3841
- is used for error messages.
3842
+ is used for error messages. The self_type is to bind self in methods
3843
+ (see analyze_member_access for more details).
3842
3844
"""
3843
3845
original_type = original_type or base_type
3846
+ self_type = self_type or base_type
3844
3847
# Unions are special-cased to allow plugins to act on each element of the union.
3845
3848
base_type = get_proper_type (base_type )
3846
3849
if isinstance (base_type , UnionType ):
@@ -3856,7 +3859,7 @@ def check_method_call_by_name(
3856
3859
is_super = False ,
3857
3860
is_operator = True ,
3858
3861
original_type = original_type ,
3859
- self_type = base_type ,
3862
+ self_type = self_type ,
3860
3863
chk = self .chk ,
3861
3864
in_literal_context = self .is_literal_context (),
3862
3865
)
@@ -3933,11 +3936,8 @@ def lookup_operator(op_name: str, base_type: Type) -> Type | None:
3933
3936
"""Looks up the given operator and returns the corresponding type,
3934
3937
if it exists."""
3935
3938
3936
- # This check is an important performance optimization,
3937
- # even though it is mostly a subset of
3938
- # analyze_member_access.
3939
- # TODO: Find a way to remove this call without performance implications.
3940
- if not self .has_member (base_type , op_name ):
3939
+ # This check is an important performance optimization.
3940
+ if not has_operator (base_type , op_name , self .named_type ):
3941
3941
return None
3942
3942
3943
3943
with self .msg .filter_errors () as w :
@@ -4097,14 +4097,8 @@ def lookup_definer(typ: Instance, attr_name: str) -> str | None:
4097
4097
errors .append (local_errors .filtered_errors ())
4098
4098
results .append (result )
4099
4099
else :
4100
- # In theory, we should never enter this case, but it seems
4101
- # we sometimes do, when dealing with Type[...]? E.g. see
4102
- # check-classes.testTypeTypeComparisonWorks.
4103
- #
4104
- # This is probably related to the TODO in lookup_operator(...)
4105
- # up above.
4106
- #
4107
- # TODO: Remove this extra case
4100
+ # Although we should not need this case anymore, we keep it just in case, as
4101
+ # otherwise we will get a crash if we introduce inconsistency in checkmember.py
4108
4102
return result
4109
4103
4110
4104
self .msg .add_errors (errors [0 ])
@@ -4365,13 +4359,19 @@ def visit_index_expr_helper(self, e: IndexExpr) -> Type:
4365
4359
return self .visit_index_with_type (left_type , e )
4366
4360
4367
4361
def visit_index_with_type (
4368
- self , left_type : Type , e : IndexExpr , original_type : ProperType | None = None
4362
+ self ,
4363
+ left_type : Type ,
4364
+ e : IndexExpr ,
4365
+ original_type : ProperType | None = None ,
4366
+ self_type : Type | None = None ,
4369
4367
) -> Type :
4370
4368
"""Analyze type of an index expression for a given type of base expression.
4371
4369
4372
- The 'original_type' is used for error messages (currently used for union types).
4370
+ The 'original_type' is used for error messages (currently used for union types). The
4371
+ 'self_type' is to bind self in methods (see analyze_member_access for more details).
4373
4372
"""
4374
4373
index = e .index
4374
+ self_type = self_type or left_type
4375
4375
left_type = get_proper_type (left_type )
4376
4376
4377
4377
# Visit the index, just to make sure we have a type for it available
@@ -4426,16 +4426,22 @@ def visit_index_with_type(
4426
4426
):
4427
4427
return self .named_type ("types.GenericAlias" )
4428
4428
4429
- if isinstance (left_type , TypeVarType ) and not self . has_member (
4430
- left_type . upper_bound , "__getitem__"
4431
- ):
4432
- return self . visit_index_with_type ( left_type . upper_bound , e , original_type )
4429
+ if isinstance (left_type , TypeVarType ):
4430
+ return self . visit_index_with_type (
4431
+ left_type . values_or_bound (), e , original_type , left_type
4432
+ )
4433
4433
elif isinstance (left_type , Instance ) and left_type .type .fullname == "typing._SpecialForm" :
4434
4434
# Allow special forms to be indexed and used to create union types
4435
4435
return self .named_type ("typing._SpecialForm" )
4436
4436
else :
4437
4437
result , method_type = self .check_method_call_by_name (
4438
- "__getitem__" , left_type , [e .index ], [ARG_POS ], e , original_type = original_type
4438
+ "__getitem__" ,
4439
+ left_type ,
4440
+ [e .index ],
4441
+ [ARG_POS ],
4442
+ e ,
4443
+ original_type = original_type ,
4444
+ self_type = self_type ,
4439
4445
)
4440
4446
e .method_type = method_type
4441
4447
return result
@@ -5995,45 +6001,6 @@ def is_valid_keyword_var_arg(self, typ: Type) -> bool:
5995
6001
or isinstance (typ , ParamSpecType )
5996
6002
)
5997
6003
5998
- def has_member (self , typ : Type , member : str ) -> bool :
5999
- """Does type have member with the given name?"""
6000
- # TODO: refactor this to use checkmember.analyze_member_access, otherwise
6001
- # these two should be carefully kept in sync.
6002
- # This is much faster than analyze_member_access, though, and so using
6003
- # it first as a filter is important for performance.
6004
- typ = get_proper_type (typ )
6005
-
6006
- if isinstance (typ , TypeVarType ):
6007
- typ = get_proper_type (typ .upper_bound )
6008
- if isinstance (typ , TupleType ):
6009
- typ = tuple_fallback (typ )
6010
- if isinstance (typ , LiteralType ):
6011
- typ = typ .fallback
6012
- if isinstance (typ , Instance ):
6013
- return typ .type .has_readable_member (member )
6014
- if isinstance (typ , FunctionLike ) and typ .is_type_obj ():
6015
- return typ .fallback .type .has_readable_member (member )
6016
- elif isinstance (typ , AnyType ):
6017
- return True
6018
- elif isinstance (typ , UnionType ):
6019
- result = all (self .has_member (x , member ) for x in typ .relevant_items ())
6020
- return result
6021
- elif isinstance (typ , TypeType ):
6022
- # Type[Union[X, ...]] is always normalized to Union[Type[X], ...],
6023
- # so we don't need to care about unions here.
6024
- item = typ .item
6025
- if isinstance (item , TypeVarType ):
6026
- item = get_proper_type (item .upper_bound )
6027
- if isinstance (item , TupleType ):
6028
- item = tuple_fallback (item )
6029
- if isinstance (item , Instance ) and item .type .metaclass_type is not None :
6030
- return self .has_member (item .type .metaclass_type , member )
6031
- if isinstance (item , AnyType ):
6032
- return True
6033
- return False
6034
- else :
6035
- return False
6036
-
6037
6004
def not_ready_callback (self , name : str , context : Context ) -> None :
6038
6005
"""Called when we can't infer the type of a variable because it's not ready yet.
6039
6006
0 commit comments