mirror of https://github.com/astral-sh/ruff
[ty] Generic "manual" PEP 695 type aliases
This commit is contained in:
parent
fdcb5a7e73
commit
e700c86857
|
|
@ -740,8 +740,26 @@ mod tests {
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
|
|
||||||
// TODO: This should jump to the definition of `Alias` above.
|
assert_snapshot!(test.goto_type_definition(), @r#"
|
||||||
assert_snapshot!(test.goto_type_definition(), @"No type definitions found");
|
info[goto-type-definition]: Type definition
|
||||||
|
--> main.py:4:1
|
||||||
|
|
|
||||||
|
2 | from typing_extensions import TypeAliasType
|
||||||
|
3 |
|
||||||
|
4 | Alias = TypeAliasType("Alias", tuple[int, int])
|
||||||
|
| ^^^^^
|
||||||
|
5 |
|
||||||
|
6 | Alias
|
||||||
|
|
|
||||||
|
info: Source
|
||||||
|
--> main.py:6:1
|
||||||
|
|
|
||||||
|
4 | Alias = TypeAliasType("Alias", tuple[int, int])
|
||||||
|
5 |
|
||||||
|
6 | Alias
|
||||||
|
| ^^^^^
|
||||||
|
|
|
||||||
|
"#);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
||||||
|
|
@ -217,8 +217,7 @@ MyList = TypeAliasType("MyList", list[T], type_params=(T,))
|
||||||
MyAlias5 = Callable[[MyList[T]], int]
|
MyAlias5 = Callable[[MyList[T]], int]
|
||||||
|
|
||||||
def _(c: MyAlias5[int]):
|
def _(c: MyAlias5[int]):
|
||||||
# TODO: should be (list[int], /) -> int
|
reveal_type(c) # revealed: (list[int], /) -> int
|
||||||
reveal_type(c) # revealed: (Unknown, /) -> int
|
|
||||||
|
|
||||||
K = TypeVar("K")
|
K = TypeVar("K")
|
||||||
V = TypeVar("V")
|
V = TypeVar("V")
|
||||||
|
|
@ -228,14 +227,12 @@ MyDict = TypeAliasType("MyDict", dict[K, V], type_params=(K, V))
|
||||||
MyAlias6 = Callable[[MyDict[K, V]], int]
|
MyAlias6 = Callable[[MyDict[K, V]], int]
|
||||||
|
|
||||||
def _(c: MyAlias6[str, bytes]):
|
def _(c: MyAlias6[str, bytes]):
|
||||||
# TODO: should be (dict[str, bytes], /) -> int
|
reveal_type(c) # revealed: (dict[str, bytes], /) -> int
|
||||||
reveal_type(c) # revealed: (Unknown, /) -> int
|
|
||||||
|
|
||||||
ListOrDict: TypeAlias = MyList[T] | dict[str, T]
|
ListOrDict: TypeAlias = MyList[T] | dict[str, T]
|
||||||
|
|
||||||
def _(x: ListOrDict[int]):
|
def _(x: ListOrDict[int]):
|
||||||
# TODO: should be list[int] | dict[str, int]
|
reveal_type(x) # revealed: list[int] | dict[str, int]
|
||||||
reveal_type(x) # revealed: Unknown | dict[str, int]
|
|
||||||
|
|
||||||
MyAlias7: TypeAlias = Callable[Concatenate[T, ...], None]
|
MyAlias7: TypeAlias = Callable[Concatenate[T, ...], None]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -223,8 +223,27 @@ T = TypeVar("T")
|
||||||
IntAndT = TypeAliasType("IntAndT", tuple[int, T], type_params=(T,))
|
IntAndT = TypeAliasType("IntAndT", tuple[int, T], type_params=(T,))
|
||||||
|
|
||||||
def f(x: IntAndT[str]) -> None:
|
def f(x: IntAndT[str]) -> None:
|
||||||
# TODO: This should be `tuple[int, str]`
|
reveal_type(x) # revealed: tuple[int, str]
|
||||||
reveal_type(x) # revealed: Unknown
|
|
||||||
|
U = TypeVar("U", default=str)
|
||||||
|
|
||||||
|
ListOrSet = TypeAliasType("ListOrSet", list[U] | set[U], type_params=(U,))
|
||||||
|
|
||||||
|
def g(
|
||||||
|
list_or_set_of_int: ListOrSet[int],
|
||||||
|
list_or_set_of_str: ListOrSet,
|
||||||
|
) -> None:
|
||||||
|
reveal_type(list_or_set_of_int) # revealed: list[int] | set[int]
|
||||||
|
reveal_type(list_or_set_of_str) # revealed: list[str] | set[str]
|
||||||
|
|
||||||
|
MyDict = TypeAliasType("MyDict", dict[U, T], type_params=(U, T))
|
||||||
|
|
||||||
|
def h(
|
||||||
|
dict_int_str: MyDict[int, str],
|
||||||
|
dict_str_unknown: MyDict,
|
||||||
|
) -> None:
|
||||||
|
reveal_type(dict_int_str) # revealed: dict[int, str]
|
||||||
|
reveal_type(dict_str_unknown) # revealed: dict[str, Unknown]
|
||||||
```
|
```
|
||||||
|
|
||||||
### Error cases
|
### Error cases
|
||||||
|
|
@ -241,6 +260,35 @@ def get_name() -> str:
|
||||||
IntOrStr = TypeAliasType(get_name(), int | str)
|
IntOrStr = TypeAliasType(get_name(), int | str)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### Type parameters argument is not a tuple
|
||||||
|
|
||||||
|
```py
|
||||||
|
from typing_extensions import TypeAliasType, TypeVar
|
||||||
|
|
||||||
|
T = TypeVar("T")
|
||||||
|
|
||||||
|
# error: [invalid-type-alias-type] "`type_params` argument to `TypeAliasType` must be a tuple"
|
||||||
|
IntAndT = TypeAliasType("IntAndT", tuple[int, T], type_params=T)
|
||||||
|
|
||||||
|
def _(x: IntAndT[str]) -> None:
|
||||||
|
reveal_type(x) # revealed: Unknown
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Invalid type parameters entries
|
||||||
|
|
||||||
|
```py
|
||||||
|
from typing_extensions import TypeAliasType, TypeVar
|
||||||
|
|
||||||
|
T = TypeVar("T")
|
||||||
|
|
||||||
|
# TODO: This should be an error
|
||||||
|
IntAndT = TypeAliasType("IntAndT", tuple[int, T], type_params=(str,))
|
||||||
|
|
||||||
|
# error: [non-subscriptable] "Cannot subscript non-generic type alias"
|
||||||
|
def _(x: IntAndT[str]) -> None:
|
||||||
|
reveal_type(x) # revealed: Unknown
|
||||||
|
```
|
||||||
|
|
||||||
## Cyclic aliases
|
## Cyclic aliases
|
||||||
|
|
||||||
### Self-referential
|
### Self-referential
|
||||||
|
|
|
||||||
|
|
@ -12898,6 +12898,8 @@ pub struct ManualPEP695TypeAliasType<'db> {
|
||||||
pub name: ast::name::Name,
|
pub name: ast::name::Name,
|
||||||
pub definition: Option<Definition<'db>>,
|
pub definition: Option<Definition<'db>>,
|
||||||
pub value: Type<'db>,
|
pub value: Type<'db>,
|
||||||
|
generic_context: Option<GenericContext<'db>>,
|
||||||
|
specialization: Option<Specialization<'db>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// The Salsa heap is tracked separately.
|
// The Salsa heap is tracked separately.
|
||||||
|
|
@ -12912,12 +12914,50 @@ fn walk_manual_pep_695_type_alias<'db, V: visitor::TypeVisitor<'db> + ?Sized>(
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'db> ManualPEP695TypeAliasType<'db> {
|
impl<'db> ManualPEP695TypeAliasType<'db> {
|
||||||
|
pub(crate) fn value_type(self, db: &'db dyn Db) -> Type<'db> {
|
||||||
|
self.apply_function_specialization(db, self.value(db))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn apply_function_specialization(self, db: &'db dyn Db, ty: Type<'db>) -> Type<'db> {
|
||||||
|
if let Some(generic_context) = self.generic_context(db) {
|
||||||
|
let specialization = self
|
||||||
|
.specialization(db)
|
||||||
|
.unwrap_or_else(|| generic_context.default_specialization(db, None));
|
||||||
|
ty.apply_specialization(db, specialization)
|
||||||
|
} else {
|
||||||
|
ty
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn apply_specialization(
|
||||||
|
self,
|
||||||
|
db: &'db dyn Db,
|
||||||
|
f: impl FnOnce(GenericContext<'db>) -> Specialization<'db>,
|
||||||
|
) -> ManualPEP695TypeAliasType<'db> {
|
||||||
|
match self.generic_context(db) {
|
||||||
|
None => self,
|
||||||
|
Some(generic_context) => {
|
||||||
|
let specialization = f(generic_context);
|
||||||
|
ManualPEP695TypeAliasType::new(
|
||||||
|
db,
|
||||||
|
self.name(db),
|
||||||
|
self.definition(db),
|
||||||
|
self.value(db),
|
||||||
|
self.generic_context(db),
|
||||||
|
Some(specialization),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn normalized_impl(self, db: &'db dyn Db, visitor: &NormalizedVisitor<'db>) -> Self {
|
fn normalized_impl(self, db: &'db dyn Db, visitor: &NormalizedVisitor<'db>) -> Self {
|
||||||
Self::new(
|
Self::new(
|
||||||
db,
|
db,
|
||||||
self.name(db),
|
self.name(db),
|
||||||
self.definition(db),
|
self.definition(db),
|
||||||
self.value(db).normalized_impl(db, visitor),
|
self.value(db).normalized_impl(db, visitor),
|
||||||
|
self.generic_context(db),
|
||||||
|
self.specialization(db),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -12929,6 +12969,8 @@ impl<'db> ManualPEP695TypeAliasType<'db> {
|
||||||
self.definition(db),
|
self.definition(db),
|
||||||
self.value(db)
|
self.value(db)
|
||||||
.recursive_type_normalized_impl(db, div, true)?,
|
.recursive_type_normalized_impl(db, div, true)?,
|
||||||
|
self.generic_context(db),
|
||||||
|
self.specialization(db),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -12999,7 +13041,7 @@ impl<'db> TypeAliasType<'db> {
|
||||||
pub fn value_type(self, db: &'db dyn Db) -> Type<'db> {
|
pub fn value_type(self, db: &'db dyn Db) -> Type<'db> {
|
||||||
match self {
|
match self {
|
||||||
TypeAliasType::PEP695(type_alias) => type_alias.value_type(db),
|
TypeAliasType::PEP695(type_alias) => type_alias.value_type(db),
|
||||||
TypeAliasType::ManualPEP695(type_alias) => type_alias.value(db),
|
TypeAliasType::ManualPEP695(type_alias) => type_alias.value_type(db),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -13018,24 +13060,25 @@ impl<'db> TypeAliasType<'db> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn generic_context(self, db: &'db dyn Db) -> Option<GenericContext<'db>> {
|
pub(crate) fn generic_context(self, db: &'db dyn Db) -> Option<GenericContext<'db>> {
|
||||||
// TODO: Add support for generic non-PEP695 type aliases.
|
|
||||||
match self {
|
match self {
|
||||||
TypeAliasType::PEP695(type_alias) => type_alias.generic_context(db),
|
TypeAliasType::PEP695(type_alias) => type_alias.generic_context(db),
|
||||||
TypeAliasType::ManualPEP695(_) => None,
|
TypeAliasType::ManualPEP695(type_alias) => type_alias.generic_context(db),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn specialization(self, db: &'db dyn Db) -> Option<Specialization<'db>> {
|
pub(crate) fn specialization(self, db: &'db dyn Db) -> Option<Specialization<'db>> {
|
||||||
match self {
|
match self {
|
||||||
TypeAliasType::PEP695(type_alias) => type_alias.specialization(db),
|
TypeAliasType::PEP695(type_alias) => type_alias.specialization(db),
|
||||||
TypeAliasType::ManualPEP695(_) => None,
|
TypeAliasType::ManualPEP695(type_alias) => type_alias.specialization(db),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_function_specialization(self, db: &'db dyn Db, ty: Type<'db>) -> Type<'db> {
|
fn apply_function_specialization(self, db: &'db dyn Db, ty: Type<'db>) -> Type<'db> {
|
||||||
match self {
|
match self {
|
||||||
TypeAliasType::PEP695(type_alias) => type_alias.apply_function_specialization(db, ty),
|
TypeAliasType::PEP695(type_alias) => type_alias.apply_function_specialization(db, ty),
|
||||||
TypeAliasType::ManualPEP695(_) => ty,
|
TypeAliasType::ManualPEP695(type_alias) => {
|
||||||
|
type_alias.apply_function_specialization(db, ty)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -13048,7 +13091,9 @@ impl<'db> TypeAliasType<'db> {
|
||||||
TypeAliasType::PEP695(type_alias) => {
|
TypeAliasType::PEP695(type_alias) => {
|
||||||
TypeAliasType::PEP695(type_alias.apply_specialization(db, f))
|
TypeAliasType::PEP695(type_alias.apply_specialization(db, f))
|
||||||
}
|
}
|
||||||
TypeAliasType::ManualPEP695(_) => self,
|
TypeAliasType::ManualPEP695(type_alias) => {
|
||||||
|
TypeAliasType::ManualPEP695(type_alias.apply_specialization(db, f))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ use crate::semantic_index::{
|
||||||
use crate::types::bound_super::BoundSuperError;
|
use crate::types::bound_super::BoundSuperError;
|
||||||
use crate::types::constraints::{ConstraintSet, IteratorConstraintsExtension};
|
use crate::types::constraints::{ConstraintSet, IteratorConstraintsExtension};
|
||||||
use crate::types::context::InferContext;
|
use crate::types::context::InferContext;
|
||||||
use crate::types::diagnostic::{INVALID_TYPE_ALIAS_TYPE, SUPER_CALL_IN_NAMED_TUPLE_METHOD};
|
use crate::types::diagnostic::SUPER_CALL_IN_NAMED_TUPLE_METHOD;
|
||||||
use crate::types::enums::enum_metadata;
|
use crate::types::enums::enum_metadata;
|
||||||
use crate::types::function::{DataclassTransformerParams, KnownFunction};
|
use crate::types::function::{DataclassTransformerParams, KnownFunction};
|
||||||
use crate::types::generics::{
|
use crate::types::generics::{
|
||||||
|
|
@ -35,9 +35,9 @@ use crate::types::{
|
||||||
ApplyTypeMappingVisitor, Binding, BoundSuperType, CallableType, CallableTypes, DATACLASS_FLAGS,
|
ApplyTypeMappingVisitor, Binding, BoundSuperType, CallableType, CallableTypes, DATACLASS_FLAGS,
|
||||||
DataclassFlags, DataclassParams, DeprecatedInstance, FindLegacyTypeVarsVisitor,
|
DataclassFlags, DataclassParams, DeprecatedInstance, FindLegacyTypeVarsVisitor,
|
||||||
HasRelationToVisitor, IsDisjointVisitor, IsEquivalentVisitor, KnownInstanceType,
|
HasRelationToVisitor, IsDisjointVisitor, IsEquivalentVisitor, KnownInstanceType,
|
||||||
ManualPEP695TypeAliasType, MaterializationKind, NormalizedVisitor, PropertyInstanceType,
|
MaterializationKind, NormalizedVisitor, PropertyInstanceType, StringLiteralType, TypeContext,
|
||||||
StringLiteralType, TypeAliasType, TypeContext, TypeMapping, TypeRelation, TypedDictParams,
|
TypeMapping, TypeRelation, TypedDictParams, UnionBuilder, VarianceInferable, binding_type,
|
||||||
UnionBuilder, VarianceInferable, binding_type, declaration_type, determine_upper_bound,
|
declaration_type, determine_upper_bound,
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
Db, FxIndexMap, FxIndexSet, FxOrderSet, Program,
|
Db, FxIndexMap, FxIndexSet, FxOrderSet, Program,
|
||||||
|
|
@ -5664,42 +5664,6 @@ impl KnownClass {
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
KnownClass::TypeAliasType => {
|
|
||||||
let assigned_to = index
|
|
||||||
.try_expression(ast::ExprRef::from(call_expression))
|
|
||||||
.and_then(|expr| expr.assigned_to(db));
|
|
||||||
|
|
||||||
let containing_assignment = assigned_to.as_ref().and_then(|assigned_to| {
|
|
||||||
match assigned_to.node(module).targets.as_slice() {
|
|
||||||
[ast::Expr::Name(target)] => Some(index.expect_single_definition(target)),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
let [Some(name), Some(value), ..] = overload.parameter_types() else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
let Some(name) = name.as_string_literal() else {
|
|
||||||
if let Some(builder) =
|
|
||||||
context.report_lint(&INVALID_TYPE_ALIAS_TYPE, call_expression)
|
|
||||||
{
|
|
||||||
builder.into_diagnostic(
|
|
||||||
"The name of a `typing.TypeAlias` must be a string literal",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
overload.set_return_type(Type::KnownInstance(KnownInstanceType::TypeAliasType(
|
|
||||||
TypeAliasType::ManualPEP695(ManualPEP695TypeAliasType::new(
|
|
||||||
db,
|
|
||||||
ast::name::Name::new(name.value(db)),
|
|
||||||
containing_assignment,
|
|
||||||
value,
|
|
||||||
)),
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
|
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -61,12 +61,12 @@ use crate::types::diagnostic::{
|
||||||
INVALID_ARGUMENT_TYPE, INVALID_ASSIGNMENT, INVALID_ATTRIBUTE_ACCESS, INVALID_BASE,
|
INVALID_ARGUMENT_TYPE, INVALID_ASSIGNMENT, INVALID_ATTRIBUTE_ACCESS, INVALID_BASE,
|
||||||
INVALID_DECLARATION, INVALID_GENERIC_CLASS, INVALID_KEY, INVALID_LEGACY_TYPE_VARIABLE,
|
INVALID_DECLARATION, INVALID_GENERIC_CLASS, INVALID_KEY, INVALID_LEGACY_TYPE_VARIABLE,
|
||||||
INVALID_METACLASS, INVALID_NAMED_TUPLE, INVALID_NEWTYPE, INVALID_OVERLOAD,
|
INVALID_METACLASS, INVALID_NAMED_TUPLE, INVALID_NEWTYPE, INVALID_OVERLOAD,
|
||||||
INVALID_PARAMETER_DEFAULT, INVALID_PARAMSPEC, INVALID_PROTOCOL, INVALID_TYPE_ARGUMENTS,
|
INVALID_PARAMETER_DEFAULT, INVALID_PARAMSPEC, INVALID_PROTOCOL, INVALID_TYPE_ALIAS_TYPE,
|
||||||
INVALID_TYPE_FORM, INVALID_TYPE_GUARD_CALL, INVALID_TYPE_VARIABLE_CONSTRAINTS,
|
INVALID_TYPE_ARGUMENTS, INVALID_TYPE_FORM, INVALID_TYPE_GUARD_CALL,
|
||||||
IncompatibleBases, NON_SUBSCRIPTABLE, POSSIBLY_MISSING_ATTRIBUTE,
|
INVALID_TYPE_VARIABLE_CONSTRAINTS, IncompatibleBases, NON_SUBSCRIPTABLE,
|
||||||
POSSIBLY_MISSING_IMPLICIT_CALL, POSSIBLY_MISSING_IMPORT, SUBCLASS_OF_FINAL_CLASS,
|
POSSIBLY_MISSING_ATTRIBUTE, POSSIBLY_MISSING_IMPLICIT_CALL, POSSIBLY_MISSING_IMPORT,
|
||||||
UNDEFINED_REVEAL, UNRESOLVED_ATTRIBUTE, UNRESOLVED_GLOBAL, UNRESOLVED_IMPORT,
|
SUBCLASS_OF_FINAL_CLASS, UNDEFINED_REVEAL, UNRESOLVED_ATTRIBUTE, UNRESOLVED_GLOBAL,
|
||||||
UNRESOLVED_REFERENCE, UNSUPPORTED_OPERATOR, USELESS_OVERLOAD_BODY,
|
UNRESOLVED_IMPORT, UNRESOLVED_REFERENCE, UNSUPPORTED_OPERATOR, USELESS_OVERLOAD_BODY,
|
||||||
hint_if_stdlib_attribute_exists_on_other_versions,
|
hint_if_stdlib_attribute_exists_on_other_versions,
|
||||||
hint_if_stdlib_submodule_exists_on_other_versions, report_attempted_protocol_instantiation,
|
hint_if_stdlib_submodule_exists_on_other_versions, report_attempted_protocol_instantiation,
|
||||||
report_bad_dunder_set_call, report_cannot_pop_required_field_on_typed_dict,
|
report_bad_dunder_set_call, report_cannot_pop_required_field_on_typed_dict,
|
||||||
|
|
@ -105,13 +105,13 @@ use crate::types::visitor::any_over_type;
|
||||||
use crate::types::{
|
use crate::types::{
|
||||||
BoundTypeVarInstance, CallDunderError, CallableBinding, CallableType, CallableTypes,
|
BoundTypeVarInstance, CallDunderError, CallableBinding, CallableType, CallableTypes,
|
||||||
ClassLiteral, ClassType, DataclassParams, DynamicType, InternedType, IntersectionBuilder,
|
ClassLiteral, ClassType, DataclassParams, DynamicType, InternedType, IntersectionBuilder,
|
||||||
IntersectionType, KnownClass, KnownInstanceType, LintDiagnosticGuard, MemberLookupPolicy,
|
IntersectionType, KnownClass, KnownInstanceType, LintDiagnosticGuard,
|
||||||
MetaclassCandidate, PEP695TypeAliasType, ParameterForm, SpecialFormType, SubclassOfType,
|
ManualPEP695TypeAliasType, MemberLookupPolicy, MetaclassCandidate, PEP695TypeAliasType,
|
||||||
TrackedConstraintSet, Truthiness, Type, TypeAliasType, TypeAndQualifiers, TypeContext,
|
ParameterForm, SpecialFormType, SubclassOfType, TrackedConstraintSet, Truthiness, Type,
|
||||||
TypeQualifiers, TypeVarBoundOrConstraints, TypeVarBoundOrConstraintsEvaluation,
|
TypeAliasType, TypeAndQualifiers, TypeContext, TypeQualifiers, TypeVarBoundOrConstraints,
|
||||||
TypeVarDefaultEvaluation, TypeVarIdentity, TypeVarInstance, TypeVarKind, TypeVarVariance,
|
TypeVarBoundOrConstraintsEvaluation, TypeVarDefaultEvaluation, TypeVarIdentity,
|
||||||
TypedDictType, UnionBuilder, UnionType, UnionTypeInstance, binding_type, infer_scope_types,
|
TypeVarInstance, TypeVarKind, TypeVarVariance, TypedDictType, UnionBuilder, UnionType,
|
||||||
overrides, todo_type,
|
UnionTypeInstance, binding_type, infer_scope_types, overrides, todo_type,
|
||||||
};
|
};
|
||||||
use crate::types::{ClassBase, add_inferred_python_version_hint_to_diagnostic};
|
use crate::types::{ClassBase, add_inferred_python_version_hint_to_diagnostic};
|
||||||
use crate::unpack::{EvaluationMode, UnpackPosition};
|
use crate::unpack::{EvaluationMode, UnpackPosition};
|
||||||
|
|
@ -4843,6 +4843,9 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
Some(KnownClass::NewType) => {
|
Some(KnownClass::NewType) => {
|
||||||
self.infer_newtype_expression(target, call_expr, definition)
|
self.infer_newtype_expression(target, call_expr, definition)
|
||||||
}
|
}
|
||||||
|
Some(KnownClass::TypeAliasType) => {
|
||||||
|
self.infer_type_alias_type_expression(target, call_expr, definition)
|
||||||
|
}
|
||||||
Some(_) | None => {
|
Some(_) | None => {
|
||||||
self.infer_call_expression_impl(call_expr, callable_type, tcx)
|
self.infer_call_expression_impl(call_expr, callable_type, tcx)
|
||||||
}
|
}
|
||||||
|
|
@ -5373,6 +5376,129 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Infer a `TypeAliasType(name, value, type_params=(...))` call expression.
|
||||||
|
fn infer_type_alias_type_expression(
|
||||||
|
&mut self,
|
||||||
|
target: &ast::Expr,
|
||||||
|
call_expr: &ast::ExprCall,
|
||||||
|
definition: Definition<'db>,
|
||||||
|
) -> Type<'db> {
|
||||||
|
fn error<'db>(
|
||||||
|
context: &InferContext<'db, '_>,
|
||||||
|
message: impl std::fmt::Display,
|
||||||
|
node: impl Ranged,
|
||||||
|
) -> Type<'db> {
|
||||||
|
if let Some(builder) = context.report_lint(&INVALID_TYPE_ALIAS_TYPE, node) {
|
||||||
|
builder.into_diagnostic(message);
|
||||||
|
}
|
||||||
|
Type::unknown()
|
||||||
|
}
|
||||||
|
|
||||||
|
let db = self.db();
|
||||||
|
let arguments = &call_expr.arguments;
|
||||||
|
|
||||||
|
// Extract positional arguments: name, value
|
||||||
|
let positional_args: Vec<_> = arguments
|
||||||
|
.args
|
||||||
|
.iter()
|
||||||
|
.filter(|arg| !arg.is_starred_expr())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
if positional_args.len() < 2 {
|
||||||
|
return error(
|
||||||
|
&self.context,
|
||||||
|
format!(
|
||||||
|
"Wrong number of arguments in `TypeAliasType` creation, expected at least 2, found {}",
|
||||||
|
positional_args.len()
|
||||||
|
),
|
||||||
|
call_expr,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// First argument: name (string literal)
|
||||||
|
let name_param_ty = self.infer_expression(positional_args[0], TypeContext::default());
|
||||||
|
let Some(name) = name_param_ty.as_string_literal().map(|n| n.value(db)) else {
|
||||||
|
return error(
|
||||||
|
&self.context,
|
||||||
|
"The name of a `typing.TypeAlias` must be a string literal",
|
||||||
|
positional_args[0],
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Validate that the target is a simple name and matches
|
||||||
|
let ast::Expr::Name(ast::ExprName {
|
||||||
|
id: target_name, ..
|
||||||
|
}) = target
|
||||||
|
else {
|
||||||
|
return error(
|
||||||
|
&self.context,
|
||||||
|
"A `TypeAliasType` definition must be a simple variable assignment",
|
||||||
|
target,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
if name != target_name {
|
||||||
|
return error(
|
||||||
|
&self.context,
|
||||||
|
format_args!(
|
||||||
|
"The name of a `TypeAliasType` (`{name}`) must match \
|
||||||
|
the name of the variable it is assigned to (`{target_name}`)"
|
||||||
|
),
|
||||||
|
target,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Second argument: value
|
||||||
|
let value_ty = self.infer_type_expression(positional_args[1]);
|
||||||
|
|
||||||
|
// Optional keyword argument: type_params
|
||||||
|
let generic_context = if let Some(type_params) = arguments
|
||||||
|
.keywords
|
||||||
|
.iter()
|
||||||
|
.find(|kw| kw.arg.as_ref().is_some_and(|arg| arg.id == "type_params"))
|
||||||
|
{
|
||||||
|
let type_params_ty = self.infer_expression(&type_params.value, TypeContext::default());
|
||||||
|
let Some(tuple_spec) = type_params_ty.tuple_instance_spec(db) else {
|
||||||
|
return error(
|
||||||
|
&self.context,
|
||||||
|
"`type_params` argument to `TypeAliasType` must be a tuple",
|
||||||
|
&type_params.value,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut bound_typevars = tuple_spec
|
||||||
|
.all_elements()
|
||||||
|
.filter_map(|element| {
|
||||||
|
if let Type::KnownInstance(KnownInstanceType::TypeVar(typevar)) = element {
|
||||||
|
Some(typevar.with_binding_context(db, definition))
|
||||||
|
} else {
|
||||||
|
// TODO: Emit a diagnostic if this is not a valid TypeVar, ParamSpec, or TypeVarTuple.
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.peekable();
|
||||||
|
|
||||||
|
if bound_typevars.peek().is_some() {
|
||||||
|
Some(GenericContext::from_typevar_instances(db, bound_typevars))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
Type::KnownInstance(KnownInstanceType::TypeAliasType(
|
||||||
|
TypeAliasType::ManualPEP695(ManualPEP695TypeAliasType::new(
|
||||||
|
db,
|
||||||
|
ast::name::Name::new(name),
|
||||||
|
Some(definition),
|
||||||
|
value_ty,
|
||||||
|
generic_context,
|
||||||
|
None,
|
||||||
|
)),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
fn infer_assignment_deferred(&mut self, value: &ast::Expr) {
|
fn infer_assignment_deferred(&mut self, value: &ast::Expr) {
|
||||||
// Infer deferred bounds/constraints/defaults of a legacy TypeVar / ParamSpec / NewType.
|
// Infer deferred bounds/constraints/defaults of a legacy TypeVar / ParamSpec / NewType.
|
||||||
let ast::Expr::Call(ast::ExprCall {
|
let ast::Expr::Call(ast::ExprCall {
|
||||||
|
|
@ -11020,19 +11146,6 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Type::KnownInstance(KnownInstanceType::TypeAliasType(TypeAliasType::ManualPEP695(
|
|
||||||
_,
|
|
||||||
))) => {
|
|
||||||
let slice_ty = self.infer_expression(slice, TypeContext::default());
|
|
||||||
let mut variables = FxOrderSet::default();
|
|
||||||
slice_ty.bind_and_find_all_legacy_typevars(
|
|
||||||
self.db(),
|
|
||||||
self.typevar_binding_context,
|
|
||||||
&mut variables,
|
|
||||||
);
|
|
||||||
let generic_context = GenericContext::from_typevar_instances(self.db(), variables);
|
|
||||||
return Type::Dynamic(DynamicType::UnknownGeneric(generic_context));
|
|
||||||
}
|
|
||||||
Type::KnownInstance(KnownInstanceType::TypeAliasType(type_alias)) => {
|
Type::KnownInstance(KnownInstanceType::TypeAliasType(type_alias)) => {
|
||||||
if let Some(generic_context) = type_alias.generic_context(self.db()) {
|
if let Some(generic_context) = type_alias.generic_context(self.db()) {
|
||||||
return self.infer_explicit_type_alias_type_specialization(
|
return self.infer_explicit_type_alias_type_specialization(
|
||||||
|
|
@ -11042,6 +11155,14 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
generic_context,
|
generic_context,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.infer_expression(slice, TypeContext::default());
|
||||||
|
if let Some(builder) = self.context.report_lint(&NON_SUBSCRIPTABLE, subscript) {
|
||||||
|
builder
|
||||||
|
.into_diagnostic(format_args!("Cannot subscript non-generic type alias"));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Type::unknown();
|
||||||
}
|
}
|
||||||
Type::SpecialForm(SpecialFormType::Tuple) => {
|
Type::SpecialForm(SpecialFormType::Tuple) => {
|
||||||
return tuple_generic_alias(self.db(), self.infer_tuple_type_expression(slice));
|
return tuple_generic_alias(self.db(), self.infer_tuple_type_expression(slice));
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,8 @@ use crate::types::tuple::{TupleSpecBuilder, TupleType};
|
||||||
use crate::types::visitor::any_over_type;
|
use crate::types::visitor::any_over_type;
|
||||||
use crate::types::{
|
use crate::types::{
|
||||||
BindingContext, CallableType, DynamicType, GenericContext, IntersectionBuilder, KnownClass,
|
BindingContext, CallableType, DynamicType, GenericContext, IntersectionBuilder, KnownClass,
|
||||||
KnownInstanceType, LintDiagnosticGuard, SpecialFormType, SubclassOfType, Type, TypeAliasType,
|
KnownInstanceType, LintDiagnosticGuard, SpecialFormType, SubclassOfType, Type, TypeContext,
|
||||||
TypeContext, TypeIsType, TypeMapping, UnionBuilder, UnionType, todo_type,
|
TypeIsType, TypeMapping, UnionBuilder, UnionType, todo_type,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Type expressions
|
/// Type expressions
|
||||||
|
|
@ -911,7 +911,7 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
|
||||||
}
|
}
|
||||||
Type::unknown()
|
Type::unknown()
|
||||||
}
|
}
|
||||||
KnownInstanceType::TypeAliasType(type_alias @ TypeAliasType::PEP695(_)) => {
|
KnownInstanceType::TypeAliasType(type_alias) => {
|
||||||
match type_alias.generic_context(self.db()) {
|
match type_alias.generic_context(self.db()) {
|
||||||
Some(generic_context) => {
|
Some(generic_context) => {
|
||||||
let specialized_type_alias = self
|
let specialized_type_alias = self
|
||||||
|
|
@ -945,19 +945,6 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
KnownInstanceType::TypeAliasType(TypeAliasType::ManualPEP695(_)) => {
|
|
||||||
// TODO: support generic "manual" PEP 695 type aliases
|
|
||||||
let slice_ty = self.infer_expression(slice, TypeContext::default());
|
|
||||||
let mut variables = FxOrderSet::default();
|
|
||||||
slice_ty.bind_and_find_all_legacy_typevars(
|
|
||||||
self.db(),
|
|
||||||
self.typevar_binding_context,
|
|
||||||
&mut variables,
|
|
||||||
);
|
|
||||||
let generic_context =
|
|
||||||
GenericContext::from_typevar_instances(self.db(), variables);
|
|
||||||
Type::Dynamic(DynamicType::UnknownGeneric(generic_context))
|
|
||||||
}
|
|
||||||
KnownInstanceType::LiteralStringAlias(_) => {
|
KnownInstanceType::LiteralStringAlias(_) => {
|
||||||
self.infer_type_expression(slice);
|
self.infer_type_expression(slice);
|
||||||
todo_type!("Generic stringified PEP-613 type alias")
|
todo_type!("Generic stringified PEP-613 type alias")
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue