@@ -4725,11 +4725,11 @@ def visit_if_stmt(self, s: IfStmt) -> None:
47254725
47264726 # XXX Issue a warning if condition is always False?
47274727 with self .binder .frame_context (can_skip = True , fall_through = 2 ):
4728- self .push_type_map (if_map )
4728+ self .push_type_map (if_map , from_assignment = False )
47294729 self .accept (b )
47304730
47314731 # XXX Issue a warning if condition is always True?
4732- self .push_type_map (else_map )
4732+ self .push_type_map (else_map , from_assignment = False )
47334733
47344734 with self .binder .frame_context (can_skip = False , fall_through = 2 ):
47354735 if s .else_body :
@@ -5310,18 +5310,21 @@ def visit_match_stmt(self, s: MatchStmt) -> None:
53105310 if b .is_unreachable or isinstance (
53115311 get_proper_type (pattern_type .type ), UninhabitedType
53125312 ):
5313- self .push_type_map (None )
5313+ self .push_type_map (None , from_assignment = False )
53145314 else_map : TypeMap = {}
53155315 else :
53165316 pattern_map , else_map = conditional_types_to_typemaps (
53175317 named_subject , pattern_type .type , pattern_type .rest_type
53185318 )
53195319 self .remove_capture_conflicts (pattern_type .captures , inferred_types )
5320- self .push_type_map (pattern_map )
5320+ self .push_type_map (pattern_map , from_assignment = False )
53215321 if pattern_map :
53225322 for expr , typ in pattern_map .items ():
5323- self .push_type_map (self ._get_recursive_sub_patterns_map (expr , typ ))
5324- self .push_type_map (pattern_type .captures )
5323+ self .push_type_map (
5324+ self ._get_recursive_sub_patterns_map (expr , typ ),
5325+ from_assignment = False ,
5326+ )
5327+ self .push_type_map (pattern_type .captures , from_assignment = False )
53255328 if g is not None :
53265329 with self .binder .frame_context (can_skip = False , fall_through = 3 ):
53275330 gt = get_proper_type (self .expr_checker .accept (g ))
@@ -5347,11 +5350,11 @@ def visit_match_stmt(self, s: MatchStmt) -> None:
53475350 continue
53485351 type_map [named_subject ] = type_map [expr ]
53495352
5350- self .push_type_map (guard_map )
5353+ self .push_type_map (guard_map , from_assignment = False )
53515354 self .accept (b )
53525355 else :
53535356 self .accept (b )
5354- self .push_type_map (else_map )
5357+ self .push_type_map (else_map , from_assignment = False )
53555358
53565359 # This is needed due to a quirk in frame_context. Without it types will stay narrowed
53575360 # after the match.
@@ -6274,10 +6277,6 @@ def has_no_custom_eq_checks(t: Type) -> bool:
62746277 coerce_only_in_literal_context ,
62756278 )
62766279
6277- # Strictly speaking, we should also skip this check if the objects in the expr
6278- # chain have custom __eq__ or __ne__ methods. But we (maybe optimistically)
6279- # assume nobody would actually create a custom objects that considers itself
6280- # equal to None.
62816280 if if_map == {} and else_map == {}:
62826281 if_map , else_map = self .refine_away_none_in_comparison (
62836282 operands , operand_types , expr_indices , narrowable_operand_index_to_hash .keys ()
@@ -6602,25 +6601,36 @@ def refine_away_none_in_comparison(
66026601 For more details about what the different arguments mean, see the
66036602 docstring of 'refine_identity_comparison_expression' up above.
66046603 """
6604+
66056605 non_optional_types = []
66066606 for i in chain_indices :
66076607 typ = operand_types [i ]
66086608 if not is_overlapping_none (typ ):
66096609 non_optional_types .append (typ )
66106610
6611- # Make sure we have a mixture of optional and non-optional types.
6612- if len (non_optional_types ) == 0 or len (non_optional_types ) == len (chain_indices ):
6613- return {}, {}
6611+ if_map , else_map = {}, {}
66146612
6615- if_map = {}
6616- for i in narrowable_operand_indices :
6617- expr_type = operand_types [i ]
6618- if not is_overlapping_none (expr_type ):
6619- continue
6620- if any (is_overlapping_erased_types (expr_type , t ) for t in non_optional_types ):
6621- if_map [operands [i ]] = remove_optional (expr_type )
6613+ if not non_optional_types or (len (non_optional_types ) != len (chain_indices )):
66226614
6623- return if_map , {}
6615+ # Narrow e.g. `Optional[A] == "x"` or `Optional[A] is "x"` to `A` (which may be
6616+ # convenient but is strictly not type-safe):
6617+ for i in narrowable_operand_indices :
6618+ expr_type = operand_types [i ]
6619+ if not is_overlapping_none (expr_type ):
6620+ continue
6621+ if any (is_overlapping_erased_types (expr_type , t ) for t in non_optional_types ):
6622+ if_map [operands [i ]] = remove_optional (expr_type )
6623+
6624+ # Narrow e.g. `Optional[A] != None` to `A` (which is stricter than the above step and
6625+ # so type-safe but less convenient, because e.g. `Optional[A] == None` still results
6626+ # in `Optional[A]`):
6627+ if any (isinstance (get_proper_type (ot ), NoneType ) for ot in operand_types ):
6628+ for i in narrowable_operand_indices :
6629+ expr_type = operand_types [i ]
6630+ if is_overlapping_none (expr_type ):
6631+ else_map [operands [i ]] = remove_optional (expr_type )
6632+
6633+ return if_map , else_map
66246634
66256635 def is_len_of_tuple (self , expr : Expression ) -> bool :
66266636 """Is this expression a `len(x)` call where x is a tuple or union of tuples?"""
@@ -7365,12 +7375,12 @@ def iterable_item_type(
73657375 def function_type (self , func : FuncBase ) -> FunctionLike :
73667376 return function_type (func , self .named_type ("builtins.function" ))
73677377
7368- def push_type_map (self , type_map : TypeMap ) -> None :
7378+ def push_type_map (self , type_map : TypeMap , * , from_assignment : bool = True ) -> None :
73697379 if type_map is None :
73707380 self .binder .unreachable ()
73717381 else :
73727382 for expr , type in type_map .items ():
7373- self .binder .put (expr , type )
7383+ self .binder .put (expr , type , from_assignment = from_assignment )
73747384
73757385 def infer_issubclass_maps (self , node : CallExpr , expr : Expression ) -> tuple [TypeMap , TypeMap ]:
73767386 """Infer type restrictions for an expression in issubclass call."""
@@ -7686,7 +7696,7 @@ def warn_deprecated(self, node: SymbolNode | None, context: Context) -> None:
76867696 and ((deprecated := node .deprecated ) is not None )
76877697 and not self .is_typeshed_stub
76887698 ):
7689- warn = self .msg .fail if self .options .report_deprecated_as_error else self .msg .note
7699+ warn = self .msg .note if self .options .report_deprecated_as_note else self .msg .fail
76907700 warn (deprecated , context , code = codes .DEPRECATED )
76917701
76927702
@@ -7743,9 +7753,7 @@ def conditional_types(
77437753 ) and is_proper_subtype (current_type , proposed_type , ignore_promotions = True ):
77447754 # Expression is always of one of the types in proposed_type_ranges
77457755 return default , UninhabitedType ()
7746- elif not is_overlapping_types (
7747- current_type , proposed_type , prohibit_none_typevar_overlap = True , ignore_promotions = True
7748- ):
7756+ elif not is_overlapping_types (current_type , proposed_type , ignore_promotions = True ):
77497757 # Expression is never of any type in proposed_type_ranges
77507758 return UninhabitedType (), default
77517759 else :
0 commit comments