mirror of
https://github.com/astral-sh/ruff.git
synced 2025-11-19 12:16:43 +00:00
[ty] Support type[…] and Type[…] in implicit type aliases (#21421)
## Summary
Support `type[…]` in implicit type aliases, for example:
```py
SubclassOfInt = type[int]
reveal_type(SubclassOfInt) # GenericAlias
def _(subclass_of_int: SubclassOfInt):
reveal_type(subclass_of_int) # type[int]
```
part of https://github.com/astral-sh/ty/issues/221
## Typing conformance
```diff
-specialtypes_type.py:138:5: error[type-assertion-failure] Argument does not have asserted type `type[Any]`
-specialtypes_type.py:140:5: error[type-assertion-failure] Argument does not have asserted type `type[Any]`
```
Two new tests passing ✔️
```diff
-specialtypes_type.py:146:1: error[unresolved-attribute] Object of type `GenericAlias` has no attribute `unknown`
```
An `TA4.unknown` attribute on a PEP 613 alias (`TA4: TypeAlias =
type[Any]`) is being accessed, and the conformance suite expects this to
be an error. Since we currently use the inferred type for these type
aliases (and possibly in the future as well), we treat this as a direct
access of the attribute on `type[Any]`, which falls back to an access on
`Any` itself, which succeeds. 🔴
```
+specialtypes_type.py:152:16: error[invalid-type-form] `typing.TypeVar` is not a generic class
+specialtypes_type.py:156:16: error[invalid-type-form] `typing.TypeVar` is not a generic class
```
New errors because we don't handle `T = TypeVar("T"); MyType = type[T];
MyType[T]` yet. Support for this is being tracked in
https://github.com/astral-sh/ty/issues/221 🔴
## Ecosystem impact
Looks mostly good, a few known problems.
## Test Plan
New Markdown tests
This commit is contained in:
parent
f9cc26aa12
commit
9e80e5a3a6
6 changed files with 207 additions and 8 deletions
|
|
@ -66,6 +66,8 @@ IntOrAnnotated = int | Annotated[str, "meta"]
|
||||||
AnnotatedOrInt = Annotated[str, "meta"] | int
|
AnnotatedOrInt = Annotated[str, "meta"] | int
|
||||||
IntOrOptional = int | Optional[str]
|
IntOrOptional = int | Optional[str]
|
||||||
OptionalOrInt = Optional[str] | int
|
OptionalOrInt = Optional[str] | int
|
||||||
|
IntOrTypeOfStr = int | type[str]
|
||||||
|
TypeOfStrOrInt = type[str] | int
|
||||||
|
|
||||||
reveal_type(IntOrStr) # revealed: types.UnionType
|
reveal_type(IntOrStr) # revealed: types.UnionType
|
||||||
reveal_type(IntOrStrOrBytes1) # revealed: types.UnionType
|
reveal_type(IntOrStrOrBytes1) # revealed: types.UnionType
|
||||||
|
|
@ -97,6 +99,8 @@ reveal_type(IntOrAnnotated) # revealed: types.UnionType
|
||||||
reveal_type(AnnotatedOrInt) # revealed: types.UnionType
|
reveal_type(AnnotatedOrInt) # revealed: types.UnionType
|
||||||
reveal_type(IntOrOptional) # revealed: types.UnionType
|
reveal_type(IntOrOptional) # revealed: types.UnionType
|
||||||
reveal_type(OptionalOrInt) # revealed: types.UnionType
|
reveal_type(OptionalOrInt) # revealed: types.UnionType
|
||||||
|
reveal_type(IntOrTypeOfStr) # revealed: types.UnionType
|
||||||
|
reveal_type(TypeOfStrOrInt) # revealed: types.UnionType
|
||||||
|
|
||||||
def _(
|
def _(
|
||||||
int_or_str: IntOrStr,
|
int_or_str: IntOrStr,
|
||||||
|
|
@ -129,6 +133,8 @@ def _(
|
||||||
annotated_or_int: AnnotatedOrInt,
|
annotated_or_int: AnnotatedOrInt,
|
||||||
int_or_optional: IntOrOptional,
|
int_or_optional: IntOrOptional,
|
||||||
optional_or_int: OptionalOrInt,
|
optional_or_int: OptionalOrInt,
|
||||||
|
int_or_type_of_str: IntOrTypeOfStr,
|
||||||
|
type_of_str_or_int: TypeOfStrOrInt,
|
||||||
):
|
):
|
||||||
reveal_type(int_or_str) # revealed: int | str
|
reveal_type(int_or_str) # revealed: int | str
|
||||||
reveal_type(int_or_str_or_bytes1) # revealed: int | str | bytes
|
reveal_type(int_or_str_or_bytes1) # revealed: int | str | bytes
|
||||||
|
|
@ -160,6 +166,8 @@ def _(
|
||||||
reveal_type(annotated_or_int) # revealed: str | int
|
reveal_type(annotated_or_int) # revealed: str | int
|
||||||
reveal_type(int_or_optional) # revealed: int | str | None
|
reveal_type(int_or_optional) # revealed: int | str | None
|
||||||
reveal_type(optional_or_int) # revealed: str | None | int
|
reveal_type(optional_or_int) # revealed: str | None | int
|
||||||
|
reveal_type(int_or_type_of_str) # revealed: int | type[str]
|
||||||
|
reveal_type(type_of_str_or_int) # revealed: type[str] | int
|
||||||
```
|
```
|
||||||
|
|
||||||
If a type is unioned with itself in a value expression, the result is just that type. No
|
If a type is unioned with itself in a value expression, the result is just that type. No
|
||||||
|
|
@ -599,6 +607,158 @@ def _(
|
||||||
reveal_type(invalid) # revealed: str | Unknown
|
reveal_type(invalid) # revealed: str | Unknown
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## `type[…]` and `Type[…]`
|
||||||
|
|
||||||
|
### `type[…]`
|
||||||
|
|
||||||
|
We support implicit type aliases using `type[…]`:
|
||||||
|
|
||||||
|
```py
|
||||||
|
from typing import Any, Union, Protocol, TypeVar, Generic
|
||||||
|
|
||||||
|
T = TypeVar("T")
|
||||||
|
|
||||||
|
class A: ...
|
||||||
|
class B: ...
|
||||||
|
class G(Generic[T]): ...
|
||||||
|
|
||||||
|
class P(Protocol):
|
||||||
|
def method(self) -> None: ...
|
||||||
|
|
||||||
|
SubclassOfA = type[A]
|
||||||
|
SubclassOfAny = type[Any]
|
||||||
|
SubclassOfAOrB1 = type[A | B]
|
||||||
|
SubclassOfAOrB2 = type[A] | type[B]
|
||||||
|
SubclassOfAOrB3 = Union[type[A], type[B]]
|
||||||
|
SubclassOfG = type[G]
|
||||||
|
SubclassOfGInt = type[G[int]]
|
||||||
|
SubclassOfP = type[P]
|
||||||
|
|
||||||
|
reveal_type(SubclassOfA) # revealed: GenericAlias
|
||||||
|
reveal_type(SubclassOfAny) # revealed: GenericAlias
|
||||||
|
reveal_type(SubclassOfAOrB1) # revealed: GenericAlias
|
||||||
|
reveal_type(SubclassOfAOrB2) # revealed: types.UnionType
|
||||||
|
reveal_type(SubclassOfAOrB3) # revealed: types.UnionType
|
||||||
|
reveal_type(SubclassOfG) # revealed: GenericAlias
|
||||||
|
reveal_type(SubclassOfGInt) # revealed: GenericAlias
|
||||||
|
reveal_type(SubclassOfP) # revealed: GenericAlias
|
||||||
|
|
||||||
|
def _(
|
||||||
|
subclass_of_a: SubclassOfA,
|
||||||
|
subclass_of_any: SubclassOfAny,
|
||||||
|
subclass_of_a_or_b1: SubclassOfAOrB1,
|
||||||
|
subclass_of_a_or_b2: SubclassOfAOrB2,
|
||||||
|
subclass_of_a_or_b3: SubclassOfAOrB3,
|
||||||
|
subclass_of_g: SubclassOfG,
|
||||||
|
subclass_of_g_int: SubclassOfGInt,
|
||||||
|
subclass_of_p: SubclassOfP,
|
||||||
|
):
|
||||||
|
reveal_type(subclass_of_a) # revealed: type[A]
|
||||||
|
reveal_type(subclass_of_a()) # revealed: A
|
||||||
|
|
||||||
|
reveal_type(subclass_of_any) # revealed: type[Any]
|
||||||
|
reveal_type(subclass_of_any()) # revealed: Any
|
||||||
|
|
||||||
|
reveal_type(subclass_of_a_or_b1) # revealed: type[A] | type[B]
|
||||||
|
reveal_type(subclass_of_a_or_b1()) # revealed: A | B
|
||||||
|
|
||||||
|
reveal_type(subclass_of_a_or_b2) # revealed: type[A] | type[B]
|
||||||
|
reveal_type(subclass_of_a_or_b2()) # revealed: A | B
|
||||||
|
|
||||||
|
reveal_type(subclass_of_a_or_b3) # revealed: type[A] | type[B]
|
||||||
|
reveal_type(subclass_of_a_or_b3()) # revealed: A | B
|
||||||
|
|
||||||
|
reveal_type(subclass_of_g) # revealed: type[G[Unknown]]
|
||||||
|
reveal_type(subclass_of_g()) # revealed: G[Unknown]
|
||||||
|
|
||||||
|
reveal_type(subclass_of_g_int) # revealed: type[G[int]]
|
||||||
|
reveal_type(subclass_of_g_int()) # revealed: G[int]
|
||||||
|
|
||||||
|
reveal_type(subclass_of_p) # revealed: type[P]
|
||||||
|
```
|
||||||
|
|
||||||
|
Invalid uses result in diagnostics:
|
||||||
|
|
||||||
|
```py
|
||||||
|
# error: [invalid-type-form]
|
||||||
|
InvalidSubclass = type[1]
|
||||||
|
```
|
||||||
|
|
||||||
|
### `Type[…]`
|
||||||
|
|
||||||
|
The same also works for `typing.Type[…]`:
|
||||||
|
|
||||||
|
```py
|
||||||
|
from typing import Any, Union, Protocol, TypeVar, Generic, Type
|
||||||
|
|
||||||
|
T = TypeVar("T")
|
||||||
|
|
||||||
|
class A: ...
|
||||||
|
class B: ...
|
||||||
|
class G(Generic[T]): ...
|
||||||
|
|
||||||
|
class P(Protocol):
|
||||||
|
def method(self) -> None: ...
|
||||||
|
|
||||||
|
SubclassOfA = Type[A]
|
||||||
|
SubclassOfAny = Type[Any]
|
||||||
|
SubclassOfAOrB1 = Type[A | B]
|
||||||
|
SubclassOfAOrB2 = Type[A] | Type[B]
|
||||||
|
SubclassOfAOrB3 = Union[Type[A], Type[B]]
|
||||||
|
SubclassOfG = Type[G]
|
||||||
|
SubclassOfGInt = Type[G[int]]
|
||||||
|
SubclassOfP = Type[P]
|
||||||
|
|
||||||
|
reveal_type(SubclassOfA) # revealed: GenericAlias
|
||||||
|
reveal_type(SubclassOfAny) # revealed: GenericAlias
|
||||||
|
reveal_type(SubclassOfAOrB1) # revealed: GenericAlias
|
||||||
|
reveal_type(SubclassOfAOrB2) # revealed: types.UnionType
|
||||||
|
reveal_type(SubclassOfAOrB3) # revealed: types.UnionType
|
||||||
|
reveal_type(SubclassOfG) # revealed: GenericAlias
|
||||||
|
reveal_type(SubclassOfGInt) # revealed: GenericAlias
|
||||||
|
reveal_type(SubclassOfP) # revealed: GenericAlias
|
||||||
|
|
||||||
|
def _(
|
||||||
|
subclass_of_a: SubclassOfA,
|
||||||
|
subclass_of_any: SubclassOfAny,
|
||||||
|
subclass_of_a_or_b1: SubclassOfAOrB1,
|
||||||
|
subclass_of_a_or_b2: SubclassOfAOrB2,
|
||||||
|
subclass_of_a_or_b3: SubclassOfAOrB3,
|
||||||
|
subclass_of_g: SubclassOfG,
|
||||||
|
subclass_of_g_int: SubclassOfGInt,
|
||||||
|
subclass_of_p: SubclassOfP,
|
||||||
|
):
|
||||||
|
reveal_type(subclass_of_a) # revealed: type[A]
|
||||||
|
reveal_type(subclass_of_a()) # revealed: A
|
||||||
|
|
||||||
|
reveal_type(subclass_of_any) # revealed: type[Any]
|
||||||
|
reveal_type(subclass_of_any()) # revealed: Any
|
||||||
|
|
||||||
|
reveal_type(subclass_of_a_or_b1) # revealed: type[A] | type[B]
|
||||||
|
reveal_type(subclass_of_a_or_b1()) # revealed: A | B
|
||||||
|
|
||||||
|
reveal_type(subclass_of_a_or_b2) # revealed: type[A] | type[B]
|
||||||
|
reveal_type(subclass_of_a_or_b2()) # revealed: A | B
|
||||||
|
|
||||||
|
reveal_type(subclass_of_a_or_b3) # revealed: type[A] | type[B]
|
||||||
|
reveal_type(subclass_of_a_or_b3()) # revealed: A | B
|
||||||
|
|
||||||
|
reveal_type(subclass_of_g) # revealed: type[G[Unknown]]
|
||||||
|
reveal_type(subclass_of_g()) # revealed: G[Unknown]
|
||||||
|
|
||||||
|
reveal_type(subclass_of_g_int) # revealed: type[G[int]]
|
||||||
|
reveal_type(subclass_of_g_int()) # revealed: G[int]
|
||||||
|
|
||||||
|
reveal_type(subclass_of_p) # revealed: type[P]
|
||||||
|
```
|
||||||
|
|
||||||
|
Invalid uses result in diagnostics:
|
||||||
|
|
||||||
|
```py
|
||||||
|
# error: [invalid-type-form]
|
||||||
|
InvalidSubclass = Type[1]
|
||||||
|
```
|
||||||
|
|
||||||
## Stringified annotations?
|
## Stringified annotations?
|
||||||
|
|
||||||
From the [typing spec on type aliases](https://typing.python.org/en/latest/spec/aliases.html):
|
From the [typing spec on type aliases](https://typing.python.org/en/latest/spec/aliases.html):
|
||||||
|
|
@ -633,15 +793,18 @@ from typing import Union
|
||||||
|
|
||||||
ListOfInts = list["int"]
|
ListOfInts = list["int"]
|
||||||
StrOrStyle = Union[str, "Style"]
|
StrOrStyle = Union[str, "Style"]
|
||||||
|
SubclassOfStyle = type["Style"]
|
||||||
|
|
||||||
class Style: ...
|
class Style: ...
|
||||||
|
|
||||||
def _(
|
def _(
|
||||||
list_of_ints: ListOfInts,
|
list_of_ints: ListOfInts,
|
||||||
str_or_style: StrOrStyle,
|
str_or_style: StrOrStyle,
|
||||||
|
subclass_of_style: SubclassOfStyle,
|
||||||
):
|
):
|
||||||
reveal_type(list_of_ints) # revealed: list[int]
|
reveal_type(list_of_ints) # revealed: list[int]
|
||||||
reveal_type(str_or_style) # revealed: str | Style
|
reveal_type(str_or_style) # revealed: str | Style
|
||||||
|
reveal_type(subclass_of_style) # revealed: type[Style]
|
||||||
```
|
```
|
||||||
|
|
||||||
## Recursive
|
## Recursive
|
||||||
|
|
|
||||||
|
|
@ -149,8 +149,7 @@ from ty_extensions import reveal_mro
|
||||||
|
|
||||||
class Foo(type[int]): ...
|
class Foo(type[int]): ...
|
||||||
|
|
||||||
# TODO: should be `tuple[<class 'Foo'>, <class 'type'>, <class 'object'>]
|
reveal_mro(Foo) # revealed: (<class 'Foo'>, <class 'type'>, <class 'object'>)
|
||||||
reveal_mro(Foo) # revealed: (<class 'Foo'>, @Todo(GenericAlias instance), <class 'object'>)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Display of generic `type[]` types
|
## Display of generic `type[]` types
|
||||||
|
|
|
||||||
|
|
@ -6607,6 +6607,17 @@ impl<'db> Type<'db> {
|
||||||
.inner(db)
|
.inner(db)
|
||||||
.in_type_expression(db, scope_id, typevar_binding_context)?)
|
.in_type_expression(db, scope_id, typevar_binding_context)?)
|
||||||
}
|
}
|
||||||
|
KnownInstanceType::TypeGenericAlias(ty) => {
|
||||||
|
// When `type[…]` appears in a value position (e.g. in an implicit type alias),
|
||||||
|
// we infer its argument as a type expression. This ensures that we can emit
|
||||||
|
// diagnostics for invalid type expressions, and more importantly, that we can
|
||||||
|
// make use of stringified annotations. The drawback is that we need to turn
|
||||||
|
// instances back into the corresponding subclass-of types here. This process
|
||||||
|
// (`int` -> instance of `int` -> subclass of `int`) can be lossy, but it is
|
||||||
|
// okay for all valid arguments to `type[…]`.
|
||||||
|
|
||||||
|
Ok(ty.inner(db).to_meta_type(db))
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
Type::SpecialForm(special_form) => match special_form {
|
Type::SpecialForm(special_form) => match special_form {
|
||||||
|
|
@ -7847,6 +7858,9 @@ pub enum KnownInstanceType<'db> {
|
||||||
/// A single instance of `typing.Annotated`
|
/// A single instance of `typing.Annotated`
|
||||||
Annotated(InternedType<'db>),
|
Annotated(InternedType<'db>),
|
||||||
|
|
||||||
|
/// An instance of `typing.GenericAlias` representing a `type[...]` expression.
|
||||||
|
TypeGenericAlias(InternedType<'db>),
|
||||||
|
|
||||||
/// An identity callable created with `typing.NewType(name, base)`, which behaves like a
|
/// An identity callable created with `typing.NewType(name, base)`, which behaves like a
|
||||||
/// subtype of `base` in type expressions. See the `struct NewType` payload for an example.
|
/// subtype of `base` in type expressions. See the `struct NewType` payload for an example.
|
||||||
NewType(NewType<'db>),
|
NewType(NewType<'db>),
|
||||||
|
|
@ -7881,7 +7895,9 @@ fn walk_known_instance_type<'db, V: visitor::TypeVisitor<'db> + ?Sized>(
|
||||||
visitor.visit_type(db, *element);
|
visitor.visit_type(db, *element);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
KnownInstanceType::Literal(ty) | KnownInstanceType::Annotated(ty) => {
|
KnownInstanceType::Literal(ty)
|
||||||
|
| KnownInstanceType::Annotated(ty)
|
||||||
|
| KnownInstanceType::TypeGenericAlias(ty) => {
|
||||||
visitor.visit_type(db, ty.inner(db));
|
visitor.visit_type(db, ty.inner(db));
|
||||||
}
|
}
|
||||||
KnownInstanceType::NewType(newtype) => {
|
KnownInstanceType::NewType(newtype) => {
|
||||||
|
|
@ -7928,6 +7944,7 @@ impl<'db> KnownInstanceType<'db> {
|
||||||
Self::UnionType(list) => Self::UnionType(list.normalized_impl(db, visitor)),
|
Self::UnionType(list) => Self::UnionType(list.normalized_impl(db, visitor)),
|
||||||
Self::Literal(ty) => Self::Literal(ty.normalized_impl(db, visitor)),
|
Self::Literal(ty) => Self::Literal(ty.normalized_impl(db, visitor)),
|
||||||
Self::Annotated(ty) => Self::Annotated(ty.normalized_impl(db, visitor)),
|
Self::Annotated(ty) => Self::Annotated(ty.normalized_impl(db, visitor)),
|
||||||
|
Self::TypeGenericAlias(ty) => Self::TypeGenericAlias(ty.normalized_impl(db, visitor)),
|
||||||
Self::NewType(newtype) => Self::NewType(
|
Self::NewType(newtype) => Self::NewType(
|
||||||
newtype
|
newtype
|
||||||
.map_base_class_type(db, |class_type| class_type.normalized_impl(db, visitor)),
|
.map_base_class_type(db, |class_type| class_type.normalized_impl(db, visitor)),
|
||||||
|
|
@ -7950,8 +7967,9 @@ impl<'db> KnownInstanceType<'db> {
|
||||||
Self::Field(_) => KnownClass::Field,
|
Self::Field(_) => KnownClass::Field,
|
||||||
Self::ConstraintSet(_) => KnownClass::ConstraintSet,
|
Self::ConstraintSet(_) => KnownClass::ConstraintSet,
|
||||||
Self::UnionType(_) => KnownClass::UnionType,
|
Self::UnionType(_) => KnownClass::UnionType,
|
||||||
Self::Literal(_) => KnownClass::GenericAlias,
|
Self::Literal(_) | Self::Annotated(_) | Self::TypeGenericAlias(_) => {
|
||||||
Self::Annotated(_) => KnownClass::GenericAlias,
|
KnownClass::GenericAlias
|
||||||
|
}
|
||||||
Self::NewType(_) => KnownClass::NewType,
|
Self::NewType(_) => KnownClass::NewType,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -8037,6 +8055,7 @@ impl<'db> KnownInstanceType<'db> {
|
||||||
KnownInstanceType::Annotated(_) => {
|
KnownInstanceType::Annotated(_) => {
|
||||||
f.write_str("<typing.Annotated special form>")
|
f.write_str("<typing.Annotated special form>")
|
||||||
}
|
}
|
||||||
|
KnownInstanceType::TypeGenericAlias(_) => f.write_str("GenericAlias"),
|
||||||
KnownInstanceType::NewType(declaration) => {
|
KnownInstanceType::NewType(declaration) => {
|
||||||
write!(f, "<NewType pseudo-class '{}'>", declaration.name(self.db))
|
write!(f, "<NewType pseudo-class '{}'>", declaration.name(self.db))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -180,6 +180,9 @@ impl<'db> ClassBase<'db> {
|
||||||
// wrappers are just identity callables at runtime, so this sort of inheritance
|
// wrappers are just identity callables at runtime, so this sort of inheritance
|
||||||
// doesn't work and isn't allowed.
|
// doesn't work and isn't allowed.
|
||||||
| KnownInstanceType::NewType(_) => None,
|
| KnownInstanceType::NewType(_) => None,
|
||||||
|
KnownInstanceType::TypeGenericAlias(_) => {
|
||||||
|
Self::try_from_type(db, KnownClass::Type.to_class_literal(db), subclass)
|
||||||
|
}
|
||||||
KnownInstanceType::Annotated(ty) => Self::try_from_type(db, ty.inner(db), subclass),
|
KnownInstanceType::Annotated(ty) => Self::try_from_type(db, ty.inner(db), subclass),
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9457,7 +9457,8 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
| Type::KnownInstance(
|
| Type::KnownInstance(
|
||||||
KnownInstanceType::UnionType(_)
|
KnownInstanceType::UnionType(_)
|
||||||
| KnownInstanceType::Literal(_)
|
| KnownInstanceType::Literal(_)
|
||||||
| KnownInstanceType::Annotated(_),
|
| KnownInstanceType::Annotated(_)
|
||||||
|
| KnownInstanceType::TypeGenericAlias(_),
|
||||||
),
|
),
|
||||||
Type::ClassLiteral(..)
|
Type::ClassLiteral(..)
|
||||||
| Type::SubclassOf(..)
|
| Type::SubclassOf(..)
|
||||||
|
|
@ -9466,7 +9467,8 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
| Type::KnownInstance(
|
| Type::KnownInstance(
|
||||||
KnownInstanceType::UnionType(_)
|
KnownInstanceType::UnionType(_)
|
||||||
| KnownInstanceType::Literal(_)
|
| KnownInstanceType::Literal(_)
|
||||||
| KnownInstanceType::Annotated(_),
|
| KnownInstanceType::Annotated(_)
|
||||||
|
| KnownInstanceType::TypeGenericAlias(_),
|
||||||
),
|
),
|
||||||
ast::Operator::BitOr,
|
ast::Operator::BitOr,
|
||||||
) if pep_604_unions_allowed() => {
|
) if pep_604_unions_allowed() => {
|
||||||
|
|
@ -10627,7 +10629,13 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
// special cases, too.
|
// special cases, too.
|
||||||
if class.is_tuple(self.db()) {
|
if class.is_tuple(self.db()) {
|
||||||
return tuple_generic_alias(self.db(), self.infer_tuple_type_expression(slice));
|
return tuple_generic_alias(self.db(), self.infer_tuple_type_expression(slice));
|
||||||
|
} else if class.is_known(self.db(), KnownClass::Type) {
|
||||||
|
let argument_ty = self.infer_type_expression(slice);
|
||||||
|
return Type::KnownInstance(KnownInstanceType::TypeGenericAlias(
|
||||||
|
InternedType::new(self.db(), argument_ty),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(generic_context) = class.generic_context(self.db()) {
|
if let Some(generic_context) = class.generic_context(self.db()) {
|
||||||
return self.infer_explicit_class_specialization(
|
return self.infer_explicit_class_specialization(
|
||||||
subscript,
|
subscript,
|
||||||
|
|
@ -10764,6 +10772,13 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Type::SpecialForm(SpecialFormType::Type) => {
|
||||||
|
// Similar to the branch above that handles `type[…]`, handle `typing.Type[…]`
|
||||||
|
let argument_ty = self.infer_type_expression(slice);
|
||||||
|
return Type::KnownInstance(KnownInstanceType::TypeGenericAlias(
|
||||||
|
InternedType::new(self.db(), argument_ty),
|
||||||
|
));
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -826,7 +826,7 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
|
||||||
self.infer_type_expression(slice);
|
self.infer_type_expression(slice);
|
||||||
todo_type!("Generic specialization of types.UnionType")
|
todo_type!("Generic specialization of types.UnionType")
|
||||||
}
|
}
|
||||||
KnownInstanceType::Literal(ty) => {
|
KnownInstanceType::Literal(ty) | KnownInstanceType::TypeGenericAlias(ty) => {
|
||||||
self.infer_type_expression(slice);
|
self.infer_type_expression(slice);
|
||||||
if let Some(builder) = self.context.report_lint(&INVALID_TYPE_FORM, subscript) {
|
if let Some(builder) = self.context.report_lint(&INVALID_TYPE_FORM, subscript) {
|
||||||
builder.into_diagnostic(format_args!(
|
builder.into_diagnostic(format_args!(
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue