99 Any , Dict , Set , List , cast , Tuple , TypeVar , Union , Optional , NamedTuple , Iterator ,
1010 Iterable , Sequence , Mapping , Generic , AbstractSet , Callable , overload
1111)
12- from typing_extensions import Final , TypeAlias as _TypeAlias
12+ from typing_extensions import Final , TypeAlias as _TypeAlias , TypeGuard
1313
1414from mypy .backports import nullcontext
1515from mypy .errors import Errors , report_internal_error
@@ -4703,7 +4703,7 @@ def is_narrowable_literal(expr: Expression) -> bool:
47034703 not is_literal_none (expr ) and
47044704 not is_literal_enum (type_map , expr ))
47054705
4706- def is_len_of_narrowable_literal (expr : Expression ) -> bool :
4706+ def is_len_of_narrowable_literal (expr : Expression ) -> TypeGuard [ CallExpr ] :
47074707 return (
47084708 isinstance (expr , CallExpr ) and
47094709 refers_to_fullname (expr .callee , 'builtins.len' ) and
@@ -5268,15 +5268,16 @@ def refine_len_comparison_expression(self,
52685268 too aggressive of a narrowing, depending on context.
52695269 """
52705270
5271- target = None # type : Optional[int]
5272- target_index = None # type : Optional[int]
5271+ target : Optional [int ] = None
5272+ target_index : Optional [int ] = None
52735273 possible_target_indices = []
52745274 for i in chain_indices :
52755275 expr_type = operand_types [i ]
52765276 expr_type = coerce_to_literal (expr_type )
5277- if not isinstance (get_proper_type (expr_type ), LiteralType ):
5277+ proper_type = get_proper_type (expr_type )
5278+ if not isinstance (proper_type , LiteralType ):
52785279 continue
5279- if target and target != expr_type .value :
5280+ if target and target != proper_type .value :
52805281 if operator in {'==' , '!=' }:
52815282 # We have multiple different target values. So the 'if' branch
52825283 # must be unreachable.
@@ -5285,7 +5286,7 @@ def refine_len_comparison_expression(self,
52855286 # Other operators can go either way
52865287 return {}, {}
52875288
5288- target = expr_type .value
5289+ target = proper_type .value # type: ignore[assignment]
52895290 target_index = i
52905291 possible_target_indices .append (i )
52915292
@@ -5300,7 +5301,8 @@ def refine_len_comparison_expression(self,
53005301 if i not in narrowable_operand_indices :
53015302 continue
53025303
5303- expr = operands [i ].args [0 ]
5304+ # we already checked that operand[i] is CallExpr since it is narrowable
5305+ expr = operands [i ].args [0 ] # type: ignore[attr-defined]
53045306 expr_type = self .type_map [expr ]
53055307
53065308 # We intentionally use 'conditional_type_map' directly here instead of
@@ -5314,8 +5316,10 @@ def refine_len_comparison_expression(self,
53145316 def narrow_type_by_length (self , operator : str , typ : Type , length : int ) -> Type :
53155317 if operator not in {"==" , "!=" }:
53165318 return typ
5317- if (isinstance (typ , Instance ) and typ .type .fullname == "builtins.tuple" and length >= 0 ):
5318- return TupleType (typ .args [0 :1 ] * length , self .named_type ('builtins.tuple' ))
5319+ proper_type = get_proper_type (typ )
5320+ if (isinstance (proper_type , Instance ) and proper_type .type .fullname == "builtins.tuple"
5321+ and length >= 0 ):
5322+ return TupleType ([proper_type .args [0 ]] * length , self .named_type ('builtins.tuple' ))
53195323 return typ
53205324
53215325 def conditional_len_map (self ,
@@ -5324,7 +5328,7 @@ def conditional_len_map(self,
53245328 current_type : Optional [Type ],
53255329 expr_index : int ,
53265330 length : Optional [int ],
5327- target_index : int ,
5331+ target_index : Optional [ int ] ,
53285332 ) -> Tuple [TypeMap , TypeMap ]:
53295333 """Takes in an expression, the current type of the expression, and a
53305334 proposed length of that expression.
@@ -5333,8 +5337,9 @@ def conditional_len_map(self,
53335337 the proposed type, if the expression can be the proposed length. The
53345338 second element is a map from the expression to the type it would hold
53355339 if it was not the proposed length, if any. None means bot, {} means top"""
5336- if length is not None and current_type is not None :
5337- if isinstance (current_type , AnyType ):
5340+ if length is not None and current_type is not None and target_index is not None :
5341+ proper_type = get_proper_type (current_type )
5342+ if isinstance (proper_type , AnyType ):
53385343 # We don't really know much about the proposed type, so we shouldn't
53395344 # attempt to narrow anything. Instead, we broaden the expr to Any to
53405345 # avoid false positives
@@ -5373,10 +5378,10 @@ def conditional_len_map(self,
53735378 remaining_type = make_simplified_union ([
53745379 typ for typ , l in zip (possible_types , len_of_types )
53755380 if l is None or not length_op (l , length )])
5376- if_map = (
5381+ if_map : TypeMap = (
53775382 {} if is_same_type (proposed_type , current_type )
53785383 else {expr : proposed_type })
5379- else_map = (
5384+ else_map : TypeMap = (
53805385 {} if is_same_type (remaining_type , current_type )
53815386 else {expr : remaining_type })
53825387 return if_map , else_map
@@ -5964,13 +5969,14 @@ def conditional_types_to_typemaps(expr: Expression,
59645969 return cast (Tuple [TypeMap , TypeMap ], tuple (maps ))
59655970
59665971
5967- def len_of_type (typ : Type ) -> int :
5972+ def len_of_type (typ : Type ) -> Optional [ int ] :
59685973 """Takes a type and returns an int that represents the length
59695974 of instances of that type or None if not applicable or variant length"""
5970- if isinstance (typ , TupleType ):
5971- return len (typ .items )
5972- if isinstance (typ , LiteralType ) and isinstance (typ .value , str ):
5973- return len (typ .value )
5975+ proper_type = get_proper_type (typ )
5976+ if isinstance (proper_type , TupleType ):
5977+ return len (proper_type .items )
5978+ if isinstance (proper_type , LiteralType ) and isinstance (proper_type .value , str ):
5979+ return len (proper_type .value )
59745980 return None
59755981
59765982
0 commit comments