9
9
Any , Dict , Set , List , cast , Tuple , TypeVar , Union , Optional , NamedTuple , Iterator ,
10
10
Iterable , Sequence , Mapping , Generic , AbstractSet , Callable , overload
11
11
)
12
- from typing_extensions import Final , TypeAlias as _TypeAlias
12
+ from typing_extensions import Final , TypeAlias as _TypeAlias , TypeGuard
13
13
14
14
from mypy .backports import nullcontext
15
15
from mypy .errors import Errors , report_internal_error
@@ -4703,7 +4703,7 @@ def is_narrowable_literal(expr: Expression) -> bool:
4703
4703
not is_literal_none (expr ) and
4704
4704
not is_literal_enum (type_map , expr ))
4705
4705
4706
- def is_len_of_narrowable_literal (expr : Expression ) -> bool :
4706
+ def is_len_of_narrowable_literal (expr : Expression ) -> TypeGuard [ CallExpr ] :
4707
4707
return (
4708
4708
isinstance (expr , CallExpr ) and
4709
4709
refers_to_fullname (expr .callee , 'builtins.len' ) and
@@ -5268,15 +5268,16 @@ def refine_len_comparison_expression(self,
5268
5268
too aggressive of a narrowing, depending on context.
5269
5269
"""
5270
5270
5271
- target = None # type : Optional[int]
5272
- target_index = None # type : Optional[int]
5271
+ target : Optional [int ] = None
5272
+ target_index : Optional [int ] = None
5273
5273
possible_target_indices = []
5274
5274
for i in chain_indices :
5275
5275
expr_type = operand_types [i ]
5276
5276
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 ):
5278
5279
continue
5279
- if target and target != expr_type .value :
5280
+ if target and target != proper_type .value :
5280
5281
if operator in {'==' , '!=' }:
5281
5282
# We have multiple different target values. So the 'if' branch
5282
5283
# must be unreachable.
@@ -5285,7 +5286,7 @@ def refine_len_comparison_expression(self,
5285
5286
# Other operators can go either way
5286
5287
return {}, {}
5287
5288
5288
- target = expr_type .value
5289
+ target = proper_type .value #type: ignore[assignment]
5289
5290
target_index = i
5290
5291
possible_target_indices .append (i )
5291
5292
@@ -5300,7 +5301,8 @@ def refine_len_comparison_expression(self,
5300
5301
if i not in narrowable_operand_indices :
5301
5302
continue
5302
5303
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]
5304
5306
expr_type = self .type_map [expr ]
5305
5307
5306
5308
# We intentionally use 'conditional_type_map' directly here instead of
@@ -5314,8 +5316,9 @@ def refine_len_comparison_expression(self,
5314
5316
def narrow_type_by_length (self , operator : str , typ : Type , length : int ) -> Type :
5315
5317
if operator not in {"==" , "!=" }:
5316
5318
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" and length >= 0 ):
5321
+ return TupleType ([proper_type .args [0 ]] * length , self .named_type ('builtins.tuple' ))
5319
5322
return typ
5320
5323
5321
5324
def conditional_len_map (self ,
@@ -5324,7 +5327,7 @@ def conditional_len_map(self,
5324
5327
current_type : Optional [Type ],
5325
5328
expr_index : int ,
5326
5329
length : Optional [int ],
5327
- target_index : int ,
5330
+ target_index : Optional [ int ] ,
5328
5331
) -> Tuple [TypeMap , TypeMap ]:
5329
5332
"""Takes in an expression, the current type of the expression, and a
5330
5333
proposed length of that expression.
@@ -5333,8 +5336,9 @@ def conditional_len_map(self,
5333
5336
the proposed type, if the expression can be the proposed length. The
5334
5337
second element is a map from the expression to the type it would hold
5335
5338
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 ):
5339
+ if length is not None and current_type is not None and target_index is not None :
5340
+ proper_type = get_proper_type (current_type )
5341
+ if isinstance (proper_type , AnyType ):
5338
5342
# We don't really know much about the proposed type, so we shouldn't
5339
5343
# attempt to narrow anything. Instead, we broaden the expr to Any to
5340
5344
# avoid false positives
@@ -5373,10 +5377,10 @@ def conditional_len_map(self,
5373
5377
remaining_type = make_simplified_union ([
5374
5378
typ for typ , l in zip (possible_types , len_of_types )
5375
5379
if l is None or not length_op (l , length )])
5376
- if_map = (
5380
+ if_map : TypeMap = (
5377
5381
{} if is_same_type (proposed_type , current_type )
5378
5382
else {expr : proposed_type })
5379
- else_map = (
5383
+ else_map : TypeMap = (
5380
5384
{} if is_same_type (remaining_type , current_type )
5381
5385
else {expr : remaining_type })
5382
5386
return if_map , else_map
@@ -5964,13 +5968,14 @@ def conditional_types_to_typemaps(expr: Expression,
5964
5968
return cast (Tuple [TypeMap , TypeMap ], tuple (maps ))
5965
5969
5966
5970
5967
- def len_of_type (typ : Type ) -> int :
5971
+ def len_of_type (typ : Type ) -> Optional [ int ] :
5968
5972
"""Takes a type and returns an int that represents the length
5969
5973
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 )
5974
+ proper_type = get_proper_type (typ )
5975
+ if isinstance (proper_type , TupleType ):
5976
+ return len (proper_type .items )
5977
+ if isinstance (proper_type , LiteralType ) and isinstance (proper_type .value , str ):
5978
+ return len (proper_type .value )
5974
5979
return None
5975
5980
5976
5981
0 commit comments