@@ -7905,6 +7905,10 @@ def is_writable_attribute(self, node: Node) -> bool:
79057905 return False
79067906
79077907 def get_isinstance_type (self , expr : Expression ) -> list [TypeRange ] | None :
7908+ """Get the type(s) resulting from an isinstance check.
7909+
7910+ Returns an empty list for isinstance(x, ()).
7911+ """
79087912 if isinstance (expr , OpExpr ) and expr .op == "|" :
79097913 left = self .get_isinstance_type (expr .left )
79107914 if left is None and is_literal_none (expr .left ):
@@ -7944,11 +7948,6 @@ def get_isinstance_type(self, expr: Expression) -> list[TypeRange] | None:
79447948 types .append (TypeRange (typ , is_upper_bound = False ))
79457949 else : # we didn't see an actual type, but rather a variable with unknown value
79467950 return None
7947- if not types :
7948- # this can happen if someone has empty tuple as 2nd argument to isinstance
7949- # strictly speaking, we should return UninhabitedType but for simplicity we will simply
7950- # refuse to do any type inference for now
7951- return None
79527951 return types
79537952
79547953 def is_literal_enum (self , n : Expression ) -> bool :
@@ -8185,59 +8184,82 @@ def conditional_types(
81858184 UninhabitedType means unreachable.
81868185 None means no new information can be inferred.
81878186 """
8188- if proposed_type_ranges :
8189- if len (proposed_type_ranges ) == 1 :
8190- target = proposed_type_ranges [0 ].item
8191- target = get_proper_type (target )
8192- if isinstance (target , LiteralType ) and (
8193- target .is_enum_literal () or isinstance (target .value , bool )
8194- ):
8195- enum_name = target .fallback .type .fullname
8196- current_type = try_expanding_sum_type_to_union (current_type , enum_name )
8197- proposed_items = [type_range .item for type_range in proposed_type_ranges ]
8198- proposed_type = make_simplified_union (proposed_items )
8199- if isinstance (get_proper_type (current_type ), AnyType ):
8200- return proposed_type , current_type
8201- elif isinstance (proposed_type , AnyType ):
8202- # We don't really know much about the proposed type, so we shouldn't
8203- # attempt to narrow anything. Instead, we broaden the expr to Any to
8204- # avoid false positives
8205- return proposed_type , default
8206- elif not any (type_range .is_upper_bound for type_range in proposed_type_ranges ) and (
8207- # concrete subtypes
8208- is_proper_subtype (current_type , proposed_type , ignore_promotions = True )
8209- # structural subtypes
8210- or (
8211- (
8212- isinstance (proposed_type , CallableType )
8213- or (isinstance (proposed_type , Instance ) and proposed_type .type .is_protocol )
8214- )
8215- and is_subtype (current_type , proposed_type , ignore_promotions = True )
8216- )
8187+ if proposed_type_ranges is None :
8188+ # An isinstance check, but we don't understand the type
8189+ return current_type , default
8190+
8191+ if not proposed_type_ranges :
8192+ # This is the case for `if isinstance(x, ())` which always returns False.
8193+ return UninhabitedType (), default
8194+
8195+ if len (proposed_type_ranges ) == 1 :
8196+ # expand e.g. bool -> Literal[True] | Literal[False]
8197+ target = proposed_type_ranges [0 ].item
8198+ target = get_proper_type (target )
8199+ if isinstance (target , LiteralType ) and (
8200+ target .is_enum_literal () or isinstance (target .value , bool )
82178201 ):
8218- # Expression is always of one of the types in proposed_type_ranges
8219- return default , UninhabitedType ()
8220- elif not is_overlapping_types (current_type , proposed_type , ignore_promotions = True ):
8221- # Expression is never of any type in proposed_type_ranges
8222- return UninhabitedType (), default
8223- else :
8224- # we can only restrict when the type is precise, not bounded
8225- proposed_precise_type = UnionType .make_union (
8226- [
8227- type_range .item
8228- for type_range in proposed_type_ranges
8229- if not type_range .is_upper_bound
8230- ]
8231- )
8232- remaining_type = restrict_subtype_away (
8233- current_type ,
8234- proposed_precise_type ,
8202+ enum_name = target .fallback .type .fullname
8203+ current_type = try_expanding_sum_type_to_union (current_type , enum_name )
8204+
8205+ proper_type = get_proper_type (current_type )
8206+ # factorize over union types: isinstance(A|B, C) -> yes = A_yes | B_yes
8207+ if isinstance (proper_type , UnionType ):
8208+ result : list [tuple [Type | None , Type | None ]] = [
8209+ conditional_types (
8210+ union_item ,
8211+ proposed_type_ranges ,
8212+ default = union_item ,
82358213 consider_runtime_isinstance = consider_runtime_isinstance ,
82368214 )
8237- return proposed_type , remaining_type
8215+ for union_item in get_proper_types (proper_type .items )
8216+ ]
8217+ # separate list of tuples into two lists
8218+ yes_types , no_types = zip (* result )
8219+ proposed_type = make_simplified_union ([t for t in yes_types if t is not None ])
82388220 else :
8239- # An isinstance check, but we don't understand the type
8240- return current_type , default
8221+ proposed_items = [type_range .item for type_range in proposed_type_ranges ]
8222+ proposed_type = make_simplified_union (proposed_items )
8223+
8224+ if isinstance (proper_type , AnyType ):
8225+ return proposed_type , current_type
8226+ elif isinstance (proposed_type , AnyType ):
8227+ # We don't really know much about the proposed type, so we shouldn't
8228+ # attempt to narrow anything. Instead, we broaden the expr to Any to
8229+ # avoid false positives
8230+ return proposed_type , default
8231+ elif not any (type_range .is_upper_bound for type_range in proposed_type_ranges ) and (
8232+ # concrete subtypes
8233+ is_proper_subtype (current_type , proposed_type , ignore_promotions = True )
8234+ # structural subtypes
8235+ or (
8236+ (
8237+ isinstance (proposed_type , CallableType )
8238+ or (isinstance (proposed_type , Instance ) and proposed_type .type .is_protocol )
8239+ )
8240+ and is_subtype (current_type , proposed_type , ignore_promotions = True )
8241+ )
8242+ ):
8243+ # Expression is always of one of the types in proposed_type_ranges
8244+ return default , UninhabitedType ()
8245+ elif not is_overlapping_types (current_type , proposed_type , ignore_promotions = True ):
8246+ # Expression is never of any type in proposed_type_ranges
8247+ return UninhabitedType (), default
8248+ else :
8249+ # we can only restrict when the type is precise, not bounded
8250+ proposed_precise_type = UnionType .make_union (
8251+ [
8252+ type_range .item
8253+ for type_range in proposed_type_ranges
8254+ if not type_range .is_upper_bound
8255+ ]
8256+ )
8257+ remaining_type = restrict_subtype_away (
8258+ current_type ,
8259+ proposed_precise_type ,
8260+ consider_runtime_isinstance = consider_runtime_isinstance ,
8261+ )
8262+ return proposed_type , remaining_type
82418263
82428264
82438265def conditional_types_to_typemaps (
0 commit comments