@@ -226,6 +226,8 @@ def __init__(
226
226
self .allow_required = allow_required
227
227
# Are we in a context where ParamSpec literals are allowed?
228
228
self .allow_param_spec_literals = allow_param_spec_literals
229
+ # Are we in context where literal "..." specifically is allowed?
230
+ self .allow_ellipsis = False
229
231
# Should we report an error whenever we encounter a RawExpressionType outside
230
232
# of a Literal context: e.g. whenever we encounter an invalid type? Normally,
231
233
# we want to report an error, but the caller may want to do more specialized
@@ -461,9 +463,9 @@ def apply_concatenate_operator(self, t: UnboundType) -> Type:
461
463
self .api .fail ("Concatenate needs type arguments" , t , code = codes .VALID_TYPE )
462
464
return AnyType (TypeOfAny .from_error )
463
465
464
- # last argument has to be ParamSpec
465
- ps = self .anal_type (t .args [- 1 ], allow_param_spec = True )
466
- if not isinstance (ps , ParamSpecType ):
466
+ # Last argument has to be ParamSpec or Ellipsis.
467
+ ps = self .anal_type (t .args [- 1 ], allow_param_spec = True , allow_ellipsis = True )
468
+ if not isinstance (ps , ( ParamSpecType , Parameters ) ):
467
469
if isinstance (ps , UnboundType ) and self .allow_unbound_tvars :
468
470
sym = self .lookup_qualified (ps .name , t )
469
471
if sym is not None and isinstance (sym .node , ParamSpecExpr ):
@@ -477,19 +479,19 @@ def apply_concatenate_operator(self, t: UnboundType) -> Type:
477
479
478
480
# TODO: this may not work well with aliases, if those worked.
479
481
# Those should be special-cased.
480
- elif ps .prefix .arg_types :
482
+ elif isinstance ( ps , ParamSpecType ) and ps .prefix .arg_types :
481
483
self .api .fail ("Nested Concatenates are invalid" , t , code = codes .VALID_TYPE )
482
484
483
485
args = self .anal_array (t .args [:- 1 ])
484
- pre = ps .prefix
486
+ pre = ps .prefix if isinstance ( ps , ParamSpecType ) else ps
485
487
486
488
# mypy can't infer this :(
487
489
names : list [str | None ] = [None ] * len (args )
488
490
489
491
pre = Parameters (
490
492
args + pre .arg_types , [ARG_POS ] * len (args ) + pre .arg_kinds , names + pre .arg_names
491
493
)
492
- return ps .copy_modified (prefix = pre )
494
+ return ps .copy_modified (prefix = pre ) if isinstance ( ps , ParamSpecType ) else pre
493
495
494
496
def try_analyze_special_unbound_type (self , t : UnboundType , fullname : str ) -> Type | None :
495
497
"""Bind special type that is recognized through magic name such as 'typing.Any'.
@@ -880,7 +882,7 @@ def visit_deleted_type(self, t: DeletedType) -> Type:
880
882
return t
881
883
882
884
def visit_type_list (self , t : TypeList ) -> Type :
883
- # paramspec literal (Z[[int, str, Whatever]])
885
+ # Parameters literal (Z[[int, str, Whatever]])
884
886
if self .allow_param_spec_literals :
885
887
params = self .analyze_callable_args (t )
886
888
if params :
@@ -893,7 +895,8 @@ def visit_type_list(self, t: TypeList) -> Type:
893
895
self .fail (
894
896
'Bracketed expression "[...]" is not valid as a type' , t , code = codes .VALID_TYPE
895
897
)
896
- self .note ('Did you mean "List[...]"?' , t )
898
+ if len (t .items ) == 1 :
899
+ self .note ('Did you mean "List[...]"?' , t )
897
900
return AnyType (TypeOfAny .from_error )
898
901
899
902
def visit_callable_argument (self , t : CallableArgument ) -> Type :
@@ -1106,7 +1109,7 @@ def visit_partial_type(self, t: PartialType) -> Type:
1106
1109
assert False , "Internal error: Unexpected partial type"
1107
1110
1108
1111
def visit_ellipsis_type (self , t : EllipsisType ) -> Type :
1109
- if self .allow_param_spec_literals :
1112
+ if self .allow_ellipsis or self . allow_param_spec_literals :
1110
1113
any_type = AnyType (TypeOfAny .explicit )
1111
1114
return Parameters (
1112
1115
[any_type , any_type ], [ARG_STAR , ARG_STAR2 ], [None , None ], is_ellipsis_args = True
@@ -1174,7 +1177,7 @@ def analyze_callable_args_for_paramspec(
1174
1177
1175
1178
def analyze_callable_args_for_concatenate (
1176
1179
self , callable_args : Type , ret_type : Type , fallback : Instance
1177
- ) -> CallableType | None :
1180
+ ) -> CallableType | AnyType | None :
1178
1181
"""Construct a 'Callable[C, RET]', where C is Concatenate[..., P], returning None if we
1179
1182
cannot.
1180
1183
"""
@@ -1189,7 +1192,7 @@ def analyze_callable_args_for_concatenate(
1189
1192
return None
1190
1193
1191
1194
tvar_def = self .anal_type (callable_args , allow_param_spec = True )
1192
- if not isinstance (tvar_def , ParamSpecType ):
1195
+ if not isinstance (tvar_def , ( ParamSpecType , Parameters ) ):
1193
1196
if self .allow_unbound_tvars and isinstance (tvar_def , UnboundType ):
1194
1197
sym = self .lookup_qualified (tvar_def .name , callable_args )
1195
1198
if sym is not None and isinstance (sym .node , ParamSpecExpr ):
@@ -1198,7 +1201,18 @@ def analyze_callable_args_for_concatenate(
1198
1201
return callable_with_ellipsis (
1199
1202
AnyType (TypeOfAny .explicit ), ret_type = ret_type , fallback = fallback
1200
1203
)
1201
- return None
1204
+ # Error was already given, so prevent further errors.
1205
+ return AnyType (TypeOfAny .from_error )
1206
+ if isinstance (tvar_def , Parameters ):
1207
+ # This comes from Concatenate[int, ...]
1208
+ return CallableType (
1209
+ arg_types = tvar_def .arg_types ,
1210
+ arg_names = tvar_def .arg_names ,
1211
+ arg_kinds = tvar_def .arg_kinds ,
1212
+ ret_type = ret_type ,
1213
+ fallback = fallback ,
1214
+ from_concatenate = True ,
1215
+ )
1202
1216
1203
1217
# ick, CallableType should take ParamSpecType
1204
1218
prefix = tvar_def .prefix
@@ -1257,7 +1271,7 @@ def analyze_callable_type(self, t: UnboundType) -> Type:
1257
1271
) or self .analyze_callable_args_for_concatenate (
1258
1272
callable_args , ret_type , fallback
1259
1273
)
1260
- if maybe_ret :
1274
+ if isinstance ( maybe_ret , CallableType ) :
1261
1275
maybe_ret = maybe_ret .copy_modified (
1262
1276
ret_type = ret_type .accept (self ), variables = variables
1263
1277
)
@@ -1274,6 +1288,8 @@ def analyze_callable_type(self, t: UnboundType) -> Type:
1274
1288
t ,
1275
1289
)
1276
1290
return AnyType (TypeOfAny .from_error )
1291
+ elif isinstance (maybe_ret , AnyType ):
1292
+ return maybe_ret
1277
1293
ret = maybe_ret
1278
1294
else :
1279
1295
if self .options .disallow_any_generics :
@@ -1527,17 +1543,27 @@ def anal_array(
1527
1543
self .allow_param_spec_literals = old_allow_param_spec_literals
1528
1544
return self .check_unpacks_in_list (res )
1529
1545
1530
- def anal_type (self , t : Type , nested : bool = True , * , allow_param_spec : bool = False ) -> Type :
1546
+ def anal_type (
1547
+ self ,
1548
+ t : Type ,
1549
+ nested : bool = True ,
1550
+ * ,
1551
+ allow_param_spec : bool = False ,
1552
+ allow_ellipsis : bool = False ,
1553
+ ) -> Type :
1531
1554
if nested :
1532
1555
self .nesting_level += 1
1533
1556
old_allow_required = self .allow_required
1534
1557
self .allow_required = False
<
F987
/code>
1558
+ old_allow_ellipsis = self .allow_ellipsis
1559
+ self .allow_ellipsis = allow_ellipsis
1535
1560
try :
1536
1561
analyzed = t .accept (self )
1537
1562
finally :
1538
1563
if nested :
1539
1564
self .nesting_level -= 1
1540
1565
self .allow_required = old_allow_required
1566
+ self .allow_ellipsis = old_allow_ellipsis
1541
1567
if (
1542
1568
not allow_param_spec
1543
1569
and isinstance (analyzed , ParamSpecType )
0 commit comments