Skip to content

Commit a943836

Browse files
authored
[ty] Ban type qualifiers in PEP-695 type aliases (#24242)
1 parent 6505b07 commit a943836

6 files changed

Lines changed: 76 additions & 5 deletions

File tree

crates/ty_python_semantic/resources/mdtest/generics/pep695/aliases.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,17 @@ You cannot use the same typevar more than once.
4545
type RepeatedTypevar[T, T] = tuple[T, T]
4646
```
4747

48+
Legacy type variables cannot be used:
49+
50+
```py
51+
from typing import TypeVar
52+
53+
V = TypeVar("V")
54+
55+
# error: [unbound-type-variable]
56+
type TA1[K] = dict[K, V]
57+
```
58+
4859
## Specializing type aliases explicitly
4960

5061
The type parameter can be specified explicitly:

crates/ty_python_semantic/resources/mdtest/pep695_type_aliases.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,36 @@ type OptionalInt = int | None
4040
x: OptionalInt = "1"
4141
```
4242

43+
## No type qualifiers
44+
45+
The right-hand side of a type alias definition is a type expression, not an annotation expression.
46+
Type qualifiers like `ClassVar` and `Final` are only valid in annotation expressions, so they cannot
47+
appear at the top level of a PEP 695 alias definition:
48+
49+
```py
50+
from typing_extensions import ClassVar, Final, Required, NotRequired, ReadOnly
51+
from dataclasses import InitVar
52+
53+
# error: [invalid-type-form] "Type qualifier `typing.ClassVar` is not allowed in type expressions (only in annotation expressions)"
54+
type Bad1 = ClassVar[str]
55+
# error: [invalid-type-form] "Type qualifier `typing.ClassVar` is not allowed in type expressions (only in annotation expressions)"
56+
type Bad2 = ClassVar
57+
# error: [invalid-type-form] "Type qualifier `typing.Final` is not allowed in type expressions (only in annotation expressions)"
58+
type Bad3 = Final[int]
59+
# error: [invalid-type-form] "Type qualifier `typing.Final` is not allowed in type expressions (only in annotation expressions)"
60+
type Bad4 = Final
61+
# error: [invalid-type-form] "Type qualifier `typing.Required` is not allowed in type expressions (only in annotation expressions)"
62+
type Bad5 = Required[int]
63+
# error: [invalid-type-form] "Type qualifier `typing.NotRequired` is not allowed in type expressions (only in annotation expressions)"
64+
type Bad6 = NotRequired[int]
65+
# error: [invalid-type-form] "Type qualifier `typing.ReadOnly` is not allowed in type expressions (only in annotation expressions)"
66+
type Bad7 = ReadOnly[int]
67+
# error: [invalid-type-form] "Type qualifier `dataclasses.InitVar` is not allowed in type expressions (only in annotation expressions)"
68+
type Bad8 = InitVar[int]
69+
# error: [invalid-type-form] "Type qualifier `dataclasses.InitVar` is not allowed in type expressions (only in annotation expressions, and only with exactly one argument)"
70+
type Bad9 = InitVar
71+
```
72+
4373
## Type aliases in type aliases
4474

4575
```py

crates/ty_python_semantic/src/types.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5019,6 +5019,12 @@ impl<'db> Type<'db> {
50195019
let ty = match class.known(db) {
50205020
Some(KnownClass::Complex) => KnownUnion::Complex.to_type(db),
50215021
Some(KnownClass::Float) => KnownUnion::Float.to_type(db),
5022+
Some(KnownClass::InitVar) => {
5023+
return Err(InvalidTypeExpressionError {
5024+
invalid_expressions: smallvec_inline![InvalidTypeExpression::InitVar],
5025+
fallback_type: Type::unknown(),
5026+
});
5027+
}
50225028
_ => Type::instance(db, class.default_specialization(db)),
50235029
};
50245030
Ok(ty)
@@ -6730,6 +6736,7 @@ enum InvalidTypeExpression<'db> {
67306736
/// Type qualifiers that are invalid in type expressions,
67316737
/// and which would require exactly one argument even if they appeared in an annotation expression
67326738
TypeQualifierRequiresOneArgument(TypeQualifier),
6739+
InitVar,
67336740
/// `typing.Self` cannot be used in `@staticmethod` definitions.
67346741
TypingSelfInStaticMethod,
67356742
/// `typing.Self` cannot be used in metaclass definitions.
@@ -6803,6 +6810,10 @@ impl<'db> InvalidTypeExpression<'db> {
68036810
"Type qualifier `{qualifier}` is not allowed in type expressions \
68046811
(only in annotation expressions, and only with exactly one argument)",
68056812
),
6813+
InvalidTypeExpression::InitVar => f.write_str(
6814+
"Type qualifier `dataclasses.InitVar` is not allowed in type expressions \
6815+
(only in annotation expressions, and only with exactly one argument)",
6816+
),
68066817
InvalidTypeExpression::TypingSelfInStaticMethod => {
68076818
f.write_str("`Self` cannot be used in a static method")
68086819
}

crates/ty_python_semantic/src/types/infer.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -984,6 +984,7 @@ bitflags::bitflags! {
984984
}
985985

986986
impl InferenceFlags {
987+
#[must_use = "Inference flags should always be restored to the original value after being temporarily modified"]
987988
fn replace(&mut self, other: Self, set_to: bool) -> bool {
988989
let previously_contained_flag = self.contains(other);
989990
self.set(other, set_to);

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

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1242,8 +1242,14 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
12421242
}
12431243

12441244
fn infer_type_alias(&mut self, type_alias: &ast::StmtTypeAlias) {
1245-
let value_ty =
1246-
self.infer_annotation_expression(&type_alias.value, DeferredExpressionState::None);
1245+
let previous_check_unbound_typevars = self
1246+
.inference_flags
1247+
.replace(InferenceFlags::CHECK_UNBOUND_TYPEVARS, true);
1248+
let value_ty = self.infer_type_expression(&type_alias.value);
1249+
self.inference_flags.set(
1250+
InferenceFlags::CHECK_UNBOUND_TYPEVARS,
1251+
previous_check_unbound_typevars,
1252+
);
12471253

12481254
// A type alias where a value type points to itself, i.e. the expanded type is `Divergent` is meaningless
12491255
// (but a type alias that expands to something like `list[Divergent]` may be a valid recursive type alias)
@@ -1259,7 +1265,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
12591265
// type IntOrStr = int | StrOrInt # It's redundant, but OK
12601266
// type StrOrInt = str | IntOrStr # It's redundant, but OK
12611267
// ```
1262-
let expanded = value_ty.inner_type().expand_eagerly(self.db());
1268+
let expanded = value_ty.expand_eagerly(self.db());
12631269
if expanded.is_divergent() {
12641270
if let Some(builder) = self
12651271
.context

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

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@ use super::{DeferredExpressionState, TypeInferenceBuilder};
55
use crate::semantic_index::scope::ScopeKind;
66
use crate::types::diagnostic::{
77
self, INVALID_TYPE_FORM, NOT_SUBSCRIPTABLE, UNBOUND_TYPE_VARIABLE, UNSUPPORTED_OPERATOR,
8-
note_py_version_too_old_for_pep_604, report_invalid_argument_number_to_special_form,
9-
report_invalid_arguments_to_callable, report_invalid_concatenate_last_arg,
8+
add_type_expression_reference_link, note_py_version_too_old_for_pep_604,
9+
report_invalid_argument_number_to_special_form, report_invalid_arguments_to_callable,
10+
report_invalid_concatenate_last_arg,
1011
};
1112
use crate::types::infer::InferenceFlags;
1213
use crate::types::signatures::{ConcatenateTail, Signature};
@@ -683,6 +684,17 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
683684
Type::ClassLiteral(class_literal) => match class_literal.known(self.db()) {
684685
Some(KnownClass::Tuple) => Type::tuple(self.infer_tuple_type_expression(subscript)),
685686
Some(KnownClass::Type) => self.infer_subclass_of_type_expression(slice),
687+
Some(KnownClass::InitVar) => {
688+
if let Some(builder) = self.context.report_lint(&INVALID_TYPE_FORM, subscript) {
689+
let diagnostic = builder.into_diagnostic(
690+
"Type qualifier `dataclasses.InitVar` is not allowed in type \
691+
expressions (only in annotation expressions)",
692+
);
693+
add_type_expression_reference_link(diagnostic);
694+
}
695+
self.infer_expression(slice, TypeContext::default());
696+
Type::unknown()
697+
}
686698
_ => self.infer_subscript_type_expression(subscript, value_ty),
687699
},
688700
_ => self.infer_subscript_type_expression(subscript, value_ty),

0 commit comments

Comments
 (0)