38
38
get_proper_types , is_literal_type , TypeAliasType )
39
39
from mypy .sametypes import is_same_type
40
40
from mypy .messages import (
41
- MessageBuilder , make_inferred_type_note , append_invariance_notes ,
41
+ MessageBuilder , make_inferred_type_note , append_invariance_notes , pretty_seq ,
42
42
format_type , format_type_bare , format_type_distinctly , SUGGESTED_TEST_FIXTURES
43
43
)
44
44
import mypy .checkexpr
63
63
from mypy .maptype import map_instance_to_supertype
64
64
from mypy .typevars import fill_typevars , has_no_typevars , fill_typevars_with_any
65
65
from mypy .semanal import set_callable_name , refers_to_fullname
66
- from mypy .mro import calculate_mro
66
+ from mypy .mro import calculate_mro , MroError
67
67
from mypy .erasetype import erase_typevars , remove_instance_last_known_values , erase_type
68
68
from mypy .expandtype import expand_type , expand_type_by_instance
69
69
from mypy .visitor import NodeVisitor
@@ -1963,13 +1963,15 @@ def visit_block(self, b: Block) -> None:
1963
1963
return
1964
1964
for s in b .body :
1965
1965
if self .binder .is_unreachable ():
1966
- if (self .options .warn_unreachable
1967
- and not self .binder .is_unreachable_warning_suppressed ()
1968
- and not self .is_raising_or_empty (s )):
1966
+ if self .should_report_unreachable_issues () and not self .is_raising_or_empty (s ):
1969
1967
self .msg .unreachable_statement (s )
1970
1968
break
1971
1969
self .accept (s )
1972
1970
1971
+ def should_report_unreachable_issues (self ) -> bool :
1972
+ return (self .options .warn_unreachable
1973
+ and not self .binder .is_unreachable_warning_suppressed ())
1974
+
1973
1975
def is_raising_or_empty (self , s : Statement ) -> bool :
1974
1976
"""Returns 'true' if the given statement either throws an error of some kind
1975
1977
or is a no-op.
@@ -3636,6 +3638,78 @@ def visit_continue_stmt(self, s: ContinueStmt) -> None:
3636
3638
self .binder .handle_continue ()
3637
3639
return None
3638
3640
3641
+ def make_fake_typeinfo (self ,
3642
+ curr_module_fullname : str ,
3643
+ class_gen_name : str ,
3644
+ class_short_name : str ,
3645
+ bases : List [Instance ],
3646
+ ) -> Tuple [ClassDef , TypeInfo ]:
3647
+ # Build the fake ClassDef and TypeInfo together.
3648
+ # The ClassDef is full of lies and doesn't actually contain a body.
3649
+ # Use format_bare to generate a nice name for error messages.
3650
+ # We skip fully filling out a handful of TypeInfo fields because they
3651
+ # should be irrelevant for a generated type like this:
3652
+ # is_protocol, protocol_members, is_abstract
3653
+ cdef = ClassDef (class_short_name , Block ([]))
3654
+ cdef .fullname = curr_module_fullname + '.' + class_gen_name
3655
+ info = TypeInfo (SymbolTable (), cdef , curr_module_fullname )
3656
+ cdef .info = info
3657
+ info .bases = bases
3658
+ calculate_mro (info )
3659
+ info .calculate_metaclass_type ()
3660
+ return cdef , info
3661
+
3662
+ def intersect_instances (self ,
3663
+ instances : Sequence [Instance ],
3664
+ ctx : Context ,
3665
+ ) -> Optional [Instance ]:
3666
+ curr_module = self .scope .stack [0 ]
3667
+ assert isinstance (curr_module , MypyFile )
3668
+
3669
+ base_classes = []
3670
+ formatted_names = []
3671
+ for inst in instances :
3672
+ expanded = [inst ]
3673
+ if inst .type .is_intersection :
3674
+ expanded = inst .type .bases
3675
+
3676
+ for expanded_inst in expanded :
3677
+ base_classes .append (expanded_inst )
3678
+ formatted_names .append (format_type_bare (expanded_inst ))
3679
+
3680
+ pretty_names_list = pretty_seq (format_type_distinctly (* base_classes , bare = True ), "and" )
3681
+ short_name = '<subclass of {}>' .format (pretty_names_list )
3682
+ full_name = gen_unique_name (short_name , curr_module .names )
3683
+
3684
+ old_msg = self .msg
3685
+ new_msg = self .msg .clean_copy ()
3686
+ self .msg = new_msg
3687
+ try :
3688
+ cdef , info = self .make_fake_typeinfo (
3689
+ curr_module .fullname ,
3690
+ full_name ,
3691
+ short_name ,
3692
+ base_classes ,
3693
+ )
3694
+ self .check_multiple_inheritance (info )
3695
+ info .is_intersection = True
3696
+ except MroError :
3697
+ if self .should_report_unreachable_issues ():
3698
+ old_msg .impossible_intersection (
3699
+ pretty_names_list , "inconsistent method resolution order" , ctx )
3700
+ return None
3701
+ finally :
3702
+ self .msg = old_msg
3703
+
3704
+ if new_msg .is_errors ():
3705
+ if self .should_report_unreachable_issues ():
3706
+ self .msg .impossible_intersection (
3707
+ pretty_names_list , "incompatible method signatures" , ctx )
3708
+ return None
3709
+
3710
+ curr_module .names [full_name ] = SymbolTableNode (GDEF , info )
3711
+ return Instance (info , [])
3712
+
3639
3713
def intersect_instance_callable (self , typ : Instance , callable_type : CallableType ) -> Instance :
3640
3714
"""Creates a fake type that represents the intersection of an Instance and a CallableType.
3641
3715
@@ -3650,20 +3724,9 @@ def intersect_instance_callable(self, typ: Instance, callable_type: CallableType
3650
3724
gen_name = gen_unique_name ("<callable subtype of {}>" .format (typ .type .name ),
3651
3725
cur_module .names )
3652
3726
3653
- # Build the fake ClassDef and TypeInfo together.
3654
- # The ClassDef is full of lies and doesn't actually contain a body.
3655
- # Use format_bare to generate a nice name for error messages.
3656
- # We skip fully filling out a handful of TypeInfo fields because they
3657
- # should be irrelevant for a generated type like this:
3658
- # is_protocol, protocol_members, is_abstract
3727
+ # Synthesize a fake TypeInfo
3659
3728
short_name = format_type_bare (typ )
3660
- cdef = ClassDef (short_name , Block ([]))
3661
- cdef .fullname = cur_module .fullname + '.' + gen_name
3662
- info = TypeInfo (SymbolTable (), cdef , cur_module .fullname )
3663
- cdef .info = info
3664
- info .bases = [typ ]
3665
- calculate_mro (info )
3666
- info .calculate_metaclass_type ()
3729
+ cdef , info = self .make_fake_typeinfo (cur_module .fullname , gen_name , short_name , [typ ])
3667
3730
3668
3731
# Build up a fake FuncDef so we can populate the symbol table.
3669
3732
func_def = FuncDef ('__call__' , [], Block ([]), callable_type )
@@ -3828,9 +3891,11 @@ def find_isinstance_check_helper(self, node: Expression) -> Tuple[TypeMap, TypeM
3828
3891
return {}, {}
3829
3892
expr = node .args [0 ]
3830
3893
if literal (expr ) == LITERAL_TYPE :
3831
- vartype = type_map [expr ]
3832
- type = get_isinstance_type (node .args [1 ], type_map )
3833
- return conditional_type_map (expr , vartype , type )
3894
+ return self .conditional_type_map_with_intersection (
3895
+ expr ,
3896
+ type_map [expr ],
3897
+ get_isinstance_type (node .args [1 ], type_map ),
3898
+ )
3834
3899
elif refers_to_fullname (node .callee , 'builtins.issubclass' ):
3835
3900
if len (node .args ) != 2 : # the error will be reported elsewhere
3836
3901
return {}, {}
@@ -4309,6 +4374,10 @@ def refine_identity_comparison_expression(self,
4309
4374
4310
4375
if enum_name is not None :
4311
4376
expr_type = try_expanding_enum_to_union (expr_type , enum_name )
4377
+
4378
+ # We intentionally use 'conditional_type_map' directly here instead of
4379
+ # 'self.conditional_type_map_with_intersection': we only compute ad-hoc
4380
+ # intersections when working with pure instances.
4312
4381
partial_type_maps .append (conditional_type_map (expr , expr_type , target_type ))
4313
4382
4314
4383
return reduce_conditional_maps (partial_type_maps )
@@ -4726,10 +4795,48 @@ def infer_issubclass_maps(self, node: CallExpr,
4726
4795
# Any other object whose type we don't know precisely
4727
4796
# for example, Any or a custom metaclass.
4728
4797
return {}, {} # unknown type
4729
- yes_map , no_map = conditional_type_map (expr , vartype , type )
4798
+ yes_map , no_map = self . conditional_type_map_with_intersection (expr , vartype , type )
4730
4799
yes_map , no_map = map (convert_to_typetype , (yes_map , no_map ))
4731
4800
return yes_map , no_map
4732
4801
4802
+ def conditional_type_map_with_intersection (self ,
4803
+ expr : Expression ,
4804
+ expr_type : Type ,
4805
+ type_ranges : Optional [List [TypeRange ]],
4806
+ ) -> Tuple [TypeMap , TypeMap ]:
4807
+ yes_map , no_map = conditional_type_map (expr , expr_type , type_ranges )
4808
+
4809
+ if yes_map is not None or type_ranges is None :
4810
+ return yes_map , no_map
4811
+
4812
+ # If we couldn't infer anything useful, try again by trying to compute an intersection
4813
+ expr_type = get_proper_type (expr_type )
4814
+ if isinstance (expr_type , UnionType ):
4815
+ possible_expr_types = get_proper_types (expr_type .relevant_items ())
4816
+ else :
4817
+ possible_expr_types = [expr_type ]
4818
+
4819
+ possible_target_types = []
4820
+ for tr in type_ranges :
4821
+ item = get_proper_type (tr .item )
4822
+ if not isinstance (item , Instance ) or tr .is_upper_bound :
4823
+ return yes_map , no_map
4824
+ possible_target_types .append (item )
4825
+
4826
+ out = []
4827
+ for v in possible_expr_types :
4828
+ if not isinstance (v , Instance ):
4829
+ return yes_map , no_map
4830
+ for t in possible_target_types :
4831
+ intersection = self .intersect_instances ([v , t ], expr )
4832
+ if intersection is None :
4833
+ continue
4834
+ out .append (intersection )
4835
+ if len (out ) == 0 :
4836
+ return None , {}
4837
+ new_yes_type = make_simplified_union (out )
4838
+ return {expr : new_yes_type }, {}
4839
+
4733
4840
4734
4841
def conditional_type_map (expr : Expression ,
4735
4842
current_type : Optional [Type ],
0 commit comments