Skip to content

Commit e700c86

Browse files
committed
[ty] Generic "manual" PEP 695 type aliases
1 parent fdcb5a7 commit e700c86

File tree

7 files changed

+278
-98
lines changed

7 files changed

+278
-98
lines changed

crates/ty_ide/src/goto_type_definition.rs

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -740,8 +740,26 @@ mod tests {
740740
"#,
741741
);
742742

743-
// TODO: This should jump to the definition of `Alias` above.
744-
assert_snapshot!(test.goto_type_definition(), @"No type definitions found");
743+
assert_snapshot!(test.goto_type_definition(), @r#"
744+
info[goto-type-definition]: Type definition
745+
--> main.py:4:1
746+
|
747+
2 | from typing_extensions import TypeAliasType
748+
3 |
749+
4 | Alias = TypeAliasType("Alias", tuple[int, int])
750+
| ^^^^^
751+
5 |
752+
6 | Alias
753+
|
754+
info: Source
755+
--> main.py:6:1
756+
|
757+
4 | Alias = TypeAliasType("Alias", tuple[int, int])
758+
5 |
759+
6 | Alias
760+
| ^^^^^
761+
|
762+
"#);
745763
}
746764

747765
#[test]

crates/ty_python_semantic/resources/mdtest/pep613_type_aliases.md

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -217,8 +217,7 @@ MyList = TypeAliasType("MyList", list[T], type_params=(T,))
217217
MyAlias5 = Callable[[MyList[T]], int]
218218

219219
def _(c: MyAlias5[int]):
220-
# TODO: should be (list[int], /) -> int
221-
reveal_type(c) # revealed: (Unknown, /) -> int
220+
reveal_type(c) # revealed: (list[int], /) -> int
222221

223222
K = TypeVar("K")
224223
V = TypeVar("V")
@@ -228,14 +227,12 @@ MyDict = TypeAliasType("MyDict", dict[K, V], type_params=(K, V))
228227
MyAlias6 = Callable[[MyDict[K, V]], int]
229228

230229
def _(c: MyAlias6[str, bytes]):
231-
# TODO: should be (dict[str, bytes], /) -> int
232-
reveal_type(c) # revealed: (Unknown, /) -> int
230+
reveal_type(c) # revealed: (dict[str, bytes], /) -> int
233231

234232
ListOrDict: TypeAlias = MyList[T] | dict[str, T]
235233

236234
def _(x: ListOrDict[int]):
237-
# TODO: should be list[int] | dict[str, int]
238-
reveal_type(x) # revealed: Unknown | dict[str, int]
235+
reveal_type(x) # revealed: list[int] | dict[str, int]
239236

240237
MyAlias7: TypeAlias = Callable[Concatenate[T, ...], None]
241238

crates/ty_python_semantic/resources/mdtest/pep695_type_aliases.md

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -223,8 +223,27 @@ T = TypeVar("T")
223223
IntAndT = TypeAliasType("IntAndT", tuple[int, T], type_params=(T,))
224224

225225
def f(x: IntAndT[str]) -> None:
226-
# TODO: This should be `tuple[int, str]`
227-
reveal_type(x) # revealed: Unknown
226+
reveal_type(x) # revealed: tuple[int, str]
227+
228+
U = TypeVar("U", default=str)
229+
230+
ListOrSet = TypeAliasType("ListOrSet", list[U] | set[U], type_params=(U,))
231+
232+
def g(
233+
list_or_set_of_int: ListOrSet[int],
234+
list_or_set_of_str: ListOrSet,
235+
) -> None:
236+
reveal_type(list_or_set_of_int) # revealed: list[int] | set[int]
237+
reveal_type(list_or_set_of_str) # revealed: list[str] | set[str]
238+
239+
MyDict = TypeAliasType("MyDict", dict[U, T], type_params=(U, T))
240+
241+
def h(
242+
dict_int_str: MyDict[int, str],
243+
dict_str_unknown: MyDict,
244+
) -> None:
245+
reveal_type(dict_int_str) # revealed: dict[int, str]
246+
reveal_type(dict_str_unknown) # revealed: dict[str, Unknown]
228247
```
229248

230249
### Error cases
@@ -241,6 +260,35 @@ def get_name() -> str:
241260
IntOrStr = TypeAliasType(get_name(), int | str)
242261
```
243262

263+
#### Type parameters argument is not a tuple
264+
265+
```py
266+
from typing_extensions import TypeAliasType, TypeVar
267+
268+
T = TypeVar("T")
269+
270+
# error: [invalid-type-alias-type] "`type_params` argument to `TypeAliasType` must be a tuple"
271+
IntAndT = TypeAliasType("IntAndT", tuple[int, T], type_params=T)
272+
273+
def _(x: IntAndT[str]) -> None:
274+
reveal_type(x) # revealed: Unknown
275+
```
276+
277+
#### Invalid type parameters entries
278+
279+
```py
280+
from typing_extensions import TypeAliasType, TypeVar
281+
282+
T = TypeVar("T")
283+
284+
# TODO: This should be an error
285+
IntAndT = TypeAliasType("IntAndT", tuple[int, T], type_params=(str,))
286+
287+
# error: [non-subscriptable] "Cannot subscript non-generic type alias"
288+
def _(x: IntAndT[str]) -> None:
289+
reveal_type(x) # revealed: Unknown
290+
```
291+
244292
## Cyclic aliases
245293

246294
### Self-referential

crates/ty_python_semantic/src/types.rs

Lines changed: 51 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12898,6 +12898,8 @@ pub struct ManualPEP695TypeAliasType<'db> {
1289812898
pub name: ast::name::Name,
1289912899
pub definition: Option<Definition<'db>>,
1290012900
pub value: Type<'db>,
12901+
generic_context: Option<GenericContext<'db>>,
12902+
specialization: Option<Specialization<'db>>,
1290112903
}
1290212904

1290312905
// The Salsa heap is tracked separately.
@@ -12912,12 +12914,50 @@ fn walk_manual_pep_695_type_alias<'db, V: visitor::TypeVisitor<'db> + ?Sized>(
1291212914
}
1291312915

1291412916
impl<'db> ManualPEP695TypeAliasType<'db> {
12917+
pub(crate) fn value_type(self, db: &'db dyn Db) -> Type<'db> {
12918+
self.apply_function_specialization(db, self.value(db))
12919+
}
12920+
12921+
fn apply_function_specialization(self, db: &'db dyn Db, ty: Type<'db>) -> Type<'db> {
12922+
if let Some(generic_context) = self.generic_context(db) {
12923+
let specialization = self
12924+
.specialization(db)
12925+
.unwrap_or_else(|| generic_context.default_specialization(db, None));
12926+
ty.apply_specialization(db, specialization)
12927+
} else {
12928+
ty
12929+
}
12930+
}
12931+
12932+
pub(crate) fn apply_specialization(
12933+
self,
12934+
db: &'db dyn Db,
12935+
f: impl FnOnce(GenericContext<'db>) -> Specialization<'db>,
12936+
) -> ManualPEP695TypeAliasType<'db> {
12937+
match self.generic_context(db) {
12938+
None => self,
12939+
Some(generic_context) => {
12940+
let specialization = f(generic_context);
12941+
ManualPEP695TypeAliasType::new(
12942+
db,
12943+
self.name(db),
12944+
self.definition(db),
12945+
self.value(db),
12946+
self.generic_context(db),
12947+
Some(specialization),
12948+
)
12949+
}
12950+
}
12951+
}
12952+
1291512953
fn normalized_impl(self, db: &'db dyn Db, visitor: &NormalizedVisitor<'db>) -> Self {
1291612954
Self::new(
1291712955
db,
1291812956
self.name(db),
1291912957
self.definition(db),
1292012958
self.value(db).normalized_impl(db, visitor),
12959+
self.generic_context(db),
12960+
self.specialization(db),
1292112961
)
1292212962
}
1292312963

@@ -12929,6 +12969,8 @@ impl<'db> ManualPEP695TypeAliasType<'db> {
1292912969
self.definition(db),
1293012970
self.value(db)
1293112971
.recursive_type_normalized_impl(db, div, true)?,
12972+
self.generic_context(db),
12973+
self.specialization(db),
1293212974
))
1293312975
}
1293412976
}
@@ -12999,7 +13041,7 @@ impl<'db> TypeAliasType<'db> {
1299913041
pub fn value_type(self, db: &'db dyn Db) -> Type<'db> {
1300013042
match self {
1300113043
TypeAliasType::PEP695(type_alias) => type_alias.value_type(db),
13002-
TypeAliasType::ManualPEP695(type_alias) => type_alias.value(db),
13044+
TypeAliasType::ManualPEP695(type_alias) => type_alias.value_type(db),
1300313045
}
1300413046
}
1300513047

@@ -13018,24 +13060,25 @@ impl<'db> TypeAliasType<'db> {
1301813060
}
1301913061

1302013062
pub(crate) fn generic_context(self, db: &'db dyn Db) -> Option<GenericContext<'db>> {
13021-
// TODO: Add support for generic non-PEP695 type aliases.
1302213063
match self {
1302313064
TypeAliasType::PEP695(type_alias) => type_alias.generic_context(db),
13024-
TypeAliasType::ManualPEP695(_) => None,
13065+
TypeAliasType::ManualPEP695(type_alias) => type_alias.generic_context(db),
1302513066
}
1302613067
}
1302713068

1302813069
pub(crate) fn specialization(self, db: &'db dyn Db) -> Option<Specialization<'db>> {
1302913070
match self {
1303013071
TypeAliasType::PEP695(type_alias) => type_alias.specialization(db),
13031-
TypeAliasType::ManualPEP695(_) => None,
13072+
TypeAliasType::ManualPEP695(type_alias) => type_alias.specialization(db),
1303213073
}
1303313074
}
1303413075

1303513076
fn apply_function_specialization(self, db: &'db dyn Db, ty: Type<'db>) -> Type<'db> {
1303613077
match self {
1303713078
TypeAliasType::PEP695(type_alias) => type_alias.apply_function_specialization(db, ty),
13038-
TypeAliasType::ManualPEP695(_) => ty,
13079+
TypeAliasType::ManualPEP695(type_alias) => {
13080+
type_alias.apply_function_specialization(db, ty)
13081+
}
1303913082
}
1304013083
}
1304113084

@@ -13048,7 +13091,9 @@ impl<'db> TypeAliasType<'db> {
1304813091
TypeAliasType::PEP695(type_alias) => {
1304913092
TypeAliasType::PEP695(type_alias.apply_specialization(db, f))
1305013093
}
13051-
TypeAliasType::ManualPEP695(_) => self,
13094+
TypeAliasType::ManualPEP695(type_alias) => {
13095+
TypeAliasType::ManualPEP695(type_alias.apply_specialization(db, f))
13096+
}
1305213097
}
1305313098
}
1305413099
}

crates/ty_python_semantic/src/types/class.rs

Lines changed: 4 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use crate::semantic_index::{
1919
use crate::types::bound_super::BoundSuperError;
2020
use crate::types::constraints::{ConstraintSet, IteratorConstraintsExtension};
2121
use crate::types::context::InferContext;
22-
use crate::types::diagnostic::{INVALID_TYPE_ALIAS_TYPE, SUPER_CALL_IN_NAMED_TUPLE_METHOD};
22+
use crate::types::diagnostic::SUPER_CALL_IN_NAMED_TUPLE_METHOD;
2323
use crate::types::enums::enum_metadata;
2424
use crate::types::function::{DataclassTransformerParams, KnownFunction};
2525
use crate::types::generics::{
@@ -35,9 +35,9 @@ use crate::types::{
3535
ApplyTypeMappingVisitor, Binding, BoundSuperType, CallableType, CallableTypes, DATACLASS_FLAGS,
3636
DataclassFlags, DataclassParams, DeprecatedInstance, FindLegacyTypeVarsVisitor,
3737
HasRelationToVisitor, IsDisjointVisitor, IsEquivalentVisitor, KnownInstanceType,
38-
ManualPEP695TypeAliasType, MaterializationKind, NormalizedVisitor, PropertyInstanceType,
39-
StringLiteralType, TypeAliasType, TypeContext, TypeMapping, TypeRelation, TypedDictParams,
40-
UnionBuilder, VarianceInferable, binding_type, declaration_type, determine_upper_bound,
38+
MaterializationKind, NormalizedVisitor, PropertyInstanceType, StringLiteralType, TypeContext,
39+
TypeMapping, TypeRelation, TypedDictParams, UnionBuilder, VarianceInferable, binding_type,
40+
declaration_type, determine_upper_bound,
4141
};
4242
use crate::{
4343
Db, FxIndexMap, FxIndexSet, FxOrderSet, Program,
@@ -5664,42 +5664,6 @@ impl KnownClass {
56645664
)));
56655665
}
56665666

5667-
KnownClass::TypeAliasType => {
5668-
let assigned_to = index
5669-
.try_expression(ast::ExprRef::from(call_expression))
5670-
.and_then(|expr| expr.assigned_to(db));
5671-
5672-
let containing_assignment = assigned_to.as_ref().and_then(|assigned_to| {
5673-
match assigned_to.node(module).targets.as_slice() {
5674-
[ast::Expr::Name(target)] => Some(index.expect_single_definition(target)),
5675-
_ => None,
5676-
}
5677-
});
5678-
5679-
let [Some(name), Some(value), ..] = overload.parameter_types() else {
5680-
return;
5681-
};
5682-
5683-
let Some(name) = name.as_string_literal() else {
5684-
if let Some(builder) =
5685-
context.report_lint(&INVALID_TYPE_ALIAS_TYPE, call_expression)
5686-
{
5687-
builder.into_diagnostic(
5688-
"The name of a `typing.TypeAlias` must be a string literal",
5689-
);
5690-
}
5691-
return;
5692-
};
5693-
overload.set_return_type(Type::KnownInstance(KnownInstanceType::TypeAliasType(
5694-
TypeAliasType::ManualPEP695(ManualPEP695TypeAliasType::new(
5695-
db,
5696-
ast::name::Name::new(name.value(db)),
5697-
containing_assignment,
5698-
value,
5699-
)),
5700-
)));
5701-
}
5702-
57035667
_ => {}
57045668
}
57055669
}

0 commit comments

Comments
 (0)