Skip to content

Commit 1f689bf

Browse files
committed
[ty] Emit an error if a TypeVarTuple is used to subscript Generic or Protocol without being unpacked
1 parent 5a479da commit 1f689bf

3 files changed

Lines changed: 49 additions & 3 deletions

File tree

crates/ty_python_semantic/resources/mdtest/annotations/unsupported_special_types.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,3 +45,10 @@ class Baz[*Ts]: ...
4545
# TODO: false positive
4646
z: Baz[int, str, bytes] # error: [not-subscriptable]
4747
```
48+
49+
And we also provide some basic validation in some cases:
50+
51+
```py
52+
# error: [invalid-generic-class] "`TypeVarTuple` must be unpacked with `*` or `Unpack[]` when used as an argument to `Generic`"
53+
class Spam(Generic[Ts]): ...
54+
```

crates/ty_python_semantic/src/types/infer/builder.rs

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15252,14 +15252,17 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
1525215252
NotYetSupported,
1525315253
/// A duplicate typevar was provided.
1525415254
DuplicateTypevar(&'db str),
15255+
/// A `TypeVarTuple` was provided but not unpacked.
15256+
TypeVarTupleMustBeUnpacked,
1525515257
}
1525615258

1525715259
impl<'db> LegacyGenericContextError<'db> {
1525815260
const fn into_type(self) -> Type<'db> {
1525915261
match self {
1526015262
LegacyGenericContextError::InvalidArgument(_)
1526115263
| LegacyGenericContextError::VariadicTupleArguments
15262-
| LegacyGenericContextError::DuplicateTypevar(_) => Type::unknown(),
15264+
| LegacyGenericContextError::DuplicateTypevar(_)
15265+
| LegacyGenericContextError::TypeVarTupleMustBeUnpacked => Type::unknown(),
1526315266
LegacyGenericContextError::NotYetSupported => {
1526415267
todo_type!("ParamSpecs and TypeVarTuples")
1526515268
}
@@ -15301,6 +15304,10 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
1530115304
typevar.name(db),
1530215305
));
1530315306
}
15307+
} else if let Type::NominalInstance(instance) = argument_ty
15308+
&& instance.has_known_class(db, KnownClass::TypeVarTuple)
15309+
{
15310+
return Err(LegacyGenericContextError::TypeVarTupleMustBeUnpacked);
1530415311
} else if any_over_type(
1530515312
db,
1530615313
argument_ty,
@@ -15353,7 +15360,18 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
1535315360
},
1535415361
))
1535515362
}
15356-
Err(error) => Ok(error.into_type()),
15363+
Err(LegacyGenericContextError::TypeVarTupleMustBeUnpacked) => {
15364+
Err(SubscriptError::new(
15365+
Type::unknown(),
15366+
SubscriptErrorKind::TypeVarTupleNotUnpacked {
15367+
origin: LegacyGenericOrigin::Generic,
15368+
},
15369+
))
15370+
}
15371+
Err(
15372+
error @ (LegacyGenericContextError::NotYetSupported
15373+
| LegacyGenericContextError::VariadicTupleArguments),
15374+
) => Ok(error.into_type()),
1535715375
}
1535815376
}
1535915377
Type::SpecialForm(SpecialFormType::Protocol) => {
@@ -15379,7 +15397,18 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
1537915397
},
1538015398
))
1538115399
}
15382-
Err(error) => Ok(error.into_type()),
15400+
Err(LegacyGenericContextError::TypeVarTupleMustBeUnpacked) => {
15401+
Err(SubscriptError::new(
15402+
Type::unknown(),
15403+
SubscriptErrorKind::TypeVarTupleNotUnpacked {
15404+
origin: LegacyGenericOrigin::Protocol,
15405+
},
15406+
))
15407+
}
15408+
Err(
15409+
error @ (LegacyGenericContextError::NotYetSupported
15410+
| LegacyGenericContextError::VariadicTupleArguments),
15411+
) => Ok(error.into_type()),
1538315412
}
1538415413
}
1538515414
Type::SpecialForm(SpecialFormType::Concatenate) => {

crates/ty_python_semantic/src/types/subscript.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,8 @@ pub(crate) enum SubscriptErrorKind<'db> {
147147
origin: LegacyGenericOrigin,
148148
typevar_name: &'db str,
149149
},
150+
/// A `TypeVarTuple` was provided to `Generic` or `Protocol` without being unpacked.
151+
TypeVarTupleNotUnpacked { origin: LegacyGenericOrigin },
150152
}
151153

152154
impl<'db> SubscriptError<'db> {
@@ -334,6 +336,14 @@ impl<'db> SubscriptErrorKind<'db> {
334336
));
335337
}
336338
}
339+
Self::TypeVarTupleNotUnpacked { origin } => {
340+
if let Some(builder) = context.report_lint(&INVALID_GENERIC_CLASS, subscript) {
341+
builder.into_diagnostic(format_args!(
342+
"`TypeVarTuple` must be unpacked with `*` or `Unpack[]` when \
343+
used as an argument to `{origin}`",
344+
));
345+
}
346+
}
337347
}
338348
}
339349

0 commit comments

Comments
 (0)