77from typing_extensions import TypeAlias as _TypeAlias
88
99from mypy .erasetype import remove_instance_last_known_values
10- from mypy .join import join_simple
1110from mypy .literals import Key , literal , literal_hash , subkeys
1211from mypy .nodes import Expression , IndexExpr , MemberExpr , NameExpr , RefExpr , TypeInfo , Var
1312from mypy .subtypes import is_same_type , is_subtype
13+ from mypy .typeops import make_simplified_union
1414from mypy .types import (
1515 AnyType ,
1616 Instance ,
2121 Type ,
2222 TypeOfAny ,
2323 TypeType ,
24+ TypeVarType ,
2425 UnionType ,
2526 UnpackType ,
2627 find_unpack_in_list ,
@@ -237,9 +238,21 @@ def update_from_options(self, frames: list[Frame]) -> bool:
237238 ):
238239 type = AnyType (TypeOfAny .from_another_any , source_any = declaration_type )
239240 else :
240- for other in resulting_values [1 :]:
241- assert other is not None
242- type = join_simple (self .declarations [key ], type , other .type )
241+ possible_types = []
242+ for t in resulting_values :
243+ assert t is not None
244+ possible_types .append (t .type )
245+ if len (possible_types ) == 1 :
246+ # This is to avoid calling get_proper_type() unless needed, as this may
247+ # interfere with our (hacky) TypeGuard support.
248+ type = possible_types [0 ]
249+ else :
250+ type = make_simplified_union (possible_types )
251+ # Legacy guard for corner case when the original type is TypeVarType.
252+ if isinstance (declaration_type , TypeVarType ) and not is_subtype (
253+ type , declaration_type
254+ ):
255+ type = declaration_type
243256 # Try simplifying resulting type for unions involving variadic tuples.
244257 # Technically, everything is still valid without this step, but if we do
245258 # not do this, this may create long unions after exiting an if check like:
@@ -258,7 +271,7 @@ def update_from_options(self, frames: list[Frame]) -> bool:
258271 )
259272 if simplified == self .declarations [key ]:
260273 type = simplified
261- if current_value is None or not is_same_type (type , current_value [ 0 ] ):
274+ if current_value is None or not is_same_type (type , current_value . type ):
262275 self ._put (key , type , from_assignment = True )
263276 changed = True
264277
@@ -300,9 +313,7 @@ def accumulate_type_assignments(self) -> Iterator[Assigns]:
300313 yield self .type_assignments
301314 self .type_assignments = old_assignments
302315
303- def assign_type (
304- self , expr : Expression , type : Type , declared_type : Type | None , restrict_any : bool = False
305- ) -> None :
316+ def assign_type (self , expr : Expression , type : Type , declared_type : Type | None ) -> None :
306317 # We should erase last known value in binder, because if we are using it,
307318 # it means that the target is not final, and therefore can't hold a literal.
308319 type = remove_instance_last_known_values (type )
@@ -333,41 +344,39 @@ def assign_type(
333344
334345 p_declared = get_proper_type (declared_type )
335346 p_type = get_proper_type (type )
336- enclosing_type = get_proper_type (self .most_recent_enclosing_type (expr , type ))
337- if isinstance (enclosing_type , AnyType ) and not restrict_any :
338- # If x is Any and y is int, after x = y we do not infer that x is int.
339- # This could be changed.
340- # Instead, since we narrowed type from Any in a recent frame (probably an
341- # isinstance check), but now it is reassigned, we broaden back
342- # to Any (which is the most recent enclosing type)
343- self .put (expr , enclosing_type )
344- # As a special case, when assigning Any to a variable with a
345- # declared Optional type that has been narrowed to None,
346- # replace all the Nones in the declared Union type with Any.
347- # This overrides the normal behavior of ignoring Any assignments to variables
348- # in order to prevent false positives.
349- # (See discussion in #3526)
350- elif (
351- isinstance (p_type , AnyType )
352- and isinstance (p_declared , UnionType )
353- and any (isinstance (get_proper_type (item ), NoneType ) for item in p_declared .items )
354- and isinstance (
355- get_proper_type (self .most_recent_enclosing_type (expr , NoneType ())), NoneType
356- )
357- ):
358- # Replace any Nones in the union type with Any
359- new_items = [
360- type if isinstance (get_proper_type (item ), NoneType ) else item
361- for item in p_declared .items
362- ]
363- self .put (expr , UnionType (new_items ))
364- elif isinstance (p_type , AnyType ) and not (
365- isinstance (p_declared , UnionType )
366- and any (isinstance (get_proper_type (item ), AnyType ) for item in p_declared .items )
367- ):
368- # Assigning an Any value doesn't affect the type to avoid false negatives, unless
369- # there is an Any item in a declared union type.
370- self .put (expr , declared_type )
347+ if isinstance (p_type , AnyType ):
348+ # Any type requires some special casing, for both historical reasons,
349+ # and to optimise user experience without sacrificing correctness too much.
350+ if isinstance (expr , RefExpr ) and isinstance (expr .node , Var ) and expr .node .is_inferred :
351+ # First case: a local/global variable without explicit annotation,
352+ # in this case we just assign Any (essentially following the SSA logic).
353+ self .put (expr , type )
354+ elif isinstance (p_declared , UnionType ) and any (
355+ isinstance (get_proper_type (item ), NoneType ) for item in p_declared .items
356+ ):
357+ # Second case: explicit optional type, in this case we optimize for a common
358+ # pattern when an untyped value used as a fallback replacing None.
359+ new_items = [
360+ type if isinstance (get_proper_type (item ), NoneType ) else item
361+ for item in p_declared .items
362+ ]
363+ self .put (expr , UnionType (new_items ))
364+ elif isinstance (p_declared , UnionType ) and any (
365+ isinstance (get_proper_type (item ), AnyType ) for item in p_declared .items
366+ ):
367+ # Third case: a union already containing Any (most likely from an un-imported
368+ # name), in this case we allow assigning Any as well.
369+ self .put (expr , type )
370+ else :
371+ # In all other cases we don't narrow to Any to minimize false negatives.
372+ self .put (expr , declared_type )
373+ elif isinstance (p_declared , AnyType ):
374+ # Mirroring the first case above, we don't narrow to a precise type if the variable
375+ # has an explicit `Any` type annotation.
376+ if isinstance (expr , RefExpr ) and isinstance (expr .node , Var ) and expr .node .is_inferred :
377+ self .put (expr , type )
378+ else :
379+ self .put (expr , declared_type )
371380 else :
372381 self .put (expr , type )
373382
@@ -389,19 +398,6 @@ def invalidate_dependencies(self, expr: BindableExpression) -> None:
389398 for dep in self .dependencies .get (key , set ()):
390399 self ._cleanse_key (dep )
391400
392- def most_recent_enclosing_type (self , expr : BindableExpression , type : Type ) -> Type | None :
393- type = get_proper_type (type )
394- if isinstance (type , AnyType ):
395- return get_declaration (expr )
396- key = literal_hash (expr )
397- assert key is not None
398- enclosers = [get_declaration (expr )] + [
399- f .types [key ].type
400- for f in self .frames
401- if key in f .types and is_subtype (type , f .types [key ][0 ])
402- ]
403- return enclosers [- 1 ]
404-
405401 def allow_jump (self , index : int ) -> None :
406402 # self.frames and self.options_on_return have different lengths
407403 # so make sure the index is positive
0 commit comments