Skip to content

Commit bd0558a

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

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
@@ -15208,14 +15208,17 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
1520815208
NotYetSupported,
1520915209
/// A duplicate typevar was provided.
1521015210
DuplicateTypevar(&'db str),
15211+
/// A `TypeVarTuple` was provided but not unpacked.
15212+
TypeVarTupleMustBeUnpacked,
1521115213
}
1521215214

1521315215
impl<'db> LegacyGenericContextError<'db> {
1521415216
const fn into_type(self) -> Type<'db> {
1521515217
match self {
1521615218
LegacyGenericContextError::InvalidArgument(_)
1521715219
| LegacyGenericContextError::VariadicTupleArguments
15218-
| LegacyGenericContextError::DuplicateTypevar(_) => Type::unknown(),
15220+
| LegacyGenericContextError::DuplicateTypevar(_)
15221+
| LegacyGenericContextError::TypeVarTupleMustBeUnpacked => Type::unknown(),
1521915222
LegacyGenericContextError::NotYetSupported => {
1522015223
todo_type!("ParamSpecs and TypeVarTuples")
1522115224
}
@@ -15257,6 +15260,10 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
1525715260
typevar.name(db),
1525815261
));
1525915262
}
15263+
} else if let Type::NominalInstance(instance) = argument_ty
15264+
&& instance.has_known_class(db, KnownClass::TypeVarTuple)
15265+
{
15266+
return Err(LegacyGenericContextError::TypeVarTupleMustBeUnpacked);
1526015267
} else if any_over_type(
1526115268
db,
1526215269
argument_ty,
@@ -15309,7 +15316,18 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
1530915316
},
1531015317
))
1531115318
}
15312-
Err(error) => Ok(error.into_type()),
15319+
Err(LegacyGenericContextError::TypeVarTupleMustBeUnpacked) => {
15320+
Err(SubscriptError::new(
15321+
Type::unknown(),
15322+
SubscriptErrorKind::TypeVarTupleNotUnpacked {
15323+
origin: LegacyGenericOrigin::Generic,
15324+
},
15325+
))
15326+
}
15327+
Err(
15328+
error @ (LegacyGenericContextError::NotYetSupported
15329+
| LegacyGenericContextError::VariadicTupleArguments),
15330+
) => Ok(error.into_type()),
1531315331
}
1531415332
}
1531515333
Type::SpecialForm(SpecialFormType::Protocol) => {
@@ -15335,7 +15353,18 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
1533515353
},
1533615354
))
1533715355
}
15338-
Err(error) => Ok(error.into_type()),
15356+
Err(LegacyGenericContextError::TypeVarTupleMustBeUnpacked) => {
15357+
Err(SubscriptError::new(
15358+
Type::unknown(),
15359+
SubscriptErrorKind::TypeVarTupleNotUnpacked {
15360+
origin: LegacyGenericOrigin::Protocol,
15361+
},
15362+
))
15363+
}
15364+
Err(
15365+
error @ (LegacyGenericContextError::NotYetSupported
15366+
| LegacyGenericContextError::VariadicTupleArguments),
15367+
) => Ok(error.into_type()),
1533915368
}
1534015369
}
1534115370
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)