mirror of https://github.com/astral-sh/ruff
[ty] Default specialize generic type aliases
This commit is contained in:
parent
508c0a0861
commit
e4833614c2
|
|
@ -190,14 +190,10 @@ def _(
|
||||||
reveal_type(type_of_str_or_int) # revealed: type[str] | int
|
reveal_type(type_of_str_or_int) # revealed: type[str] | int
|
||||||
reveal_type(int_or_callable) # revealed: int | ((str, /) -> bytes)
|
reveal_type(int_or_callable) # revealed: int | ((str, /) -> bytes)
|
||||||
reveal_type(callable_or_int) # revealed: ((str, /) -> bytes) | int
|
reveal_type(callable_or_int) # revealed: ((str, /) -> bytes) | int
|
||||||
# TODO should be Unknown | int
|
reveal_type(type_var_or_int) # revealed: Unknown | int
|
||||||
reveal_type(type_var_or_int) # revealed: T@TypeVarOrInt | int
|
reveal_type(int_or_type_var) # revealed: int | Unknown
|
||||||
# TODO should be int | Unknown
|
reveal_type(type_var_or_none) # revealed: Unknown | None
|
||||||
reveal_type(int_or_type_var) # revealed: int | T@IntOrTypeVar
|
reveal_type(none_or_type_var) # revealed: None | Unknown
|
||||||
# TODO should be Unknown | None
|
|
||||||
reveal_type(type_var_or_none) # revealed: T@TypeVarOrNone | None
|
|
||||||
# TODO should be None | Unknown
|
|
||||||
reveal_type(none_or_type_var) # revealed: None | T@NoneOrTypeVar
|
|
||||||
```
|
```
|
||||||
|
|
||||||
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
|
||||||
|
|
@ -529,28 +525,18 @@ def _(
|
||||||
annotated_unknown: AnnotatedType,
|
annotated_unknown: AnnotatedType,
|
||||||
optional_unknown: MyOptional,
|
optional_unknown: MyOptional,
|
||||||
):
|
):
|
||||||
# TODO: This should be `list[Unknown]`
|
reveal_type(list_unknown) # revealed: list[Unknown]
|
||||||
reveal_type(list_unknown) # revealed: list[T@MyList]
|
reveal_type(dict_unknown) # revealed: dict[Unknown, Unknown]
|
||||||
# TODO: This should be `dict[Unknown, Unknown]`
|
reveal_type(subclass_of_unknown) # revealed: type[Unknown]
|
||||||
reveal_type(dict_unknown) # revealed: dict[T@MyDict, U@MyDict]
|
reveal_type(int_and_unknown) # revealed: tuple[int, Unknown]
|
||||||
# TODO: Should be `type[Unknown]`
|
reveal_type(pair_of_unknown) # revealed: tuple[Unknown, Unknown]
|
||||||
reveal_type(subclass_of_unknown) # revealed: type[T@MyType]
|
reveal_type(unknown_and_unknown) # revealed: tuple[Unknown, Unknown]
|
||||||
# TODO: Should be `tuple[int, Unknown]`
|
reveal_type(list_or_tuple) # revealed: list[Unknown] | tuple[Unknown, ...]
|
||||||
reveal_type(int_and_unknown) # revealed: tuple[int, T@IntAndType]
|
reveal_type(list_or_tuple_legacy) # revealed: list[Unknown] | tuple[Unknown, ...]
|
||||||
# TODO: Should be `tuple[Unknown, Unknown]`
|
|
||||||
reveal_type(pair_of_unknown) # revealed: tuple[T@Pair, T@Pair]
|
|
||||||
# TODO: Should be `tuple[Unknown, Unknown]`
|
|
||||||
reveal_type(unknown_and_unknown) # revealed: tuple[T@Sum, U@Sum]
|
|
||||||
# TODO: Should be `list[Unknown] | tuple[Unknown, ...]`
|
|
||||||
reveal_type(list_or_tuple) # revealed: list[T@ListOrTuple] | tuple[T@ListOrTuple, ...]
|
|
||||||
# TODO: Should be `list[Unknown] | tuple[Unknown, ...]`
|
|
||||||
reveal_type(list_or_tuple_legacy) # revealed: list[T@ListOrTupleLegacy] | tuple[T@ListOrTupleLegacy, ...]
|
|
||||||
# TODO: Should be `(...) -> Unknown`
|
# TODO: Should be `(...) -> Unknown`
|
||||||
reveal_type(my_callable) # revealed: @Todo(Callable[..] specialized with ParamSpec)
|
reveal_type(my_callable) # revealed: @Todo(Callable[..] specialized with ParamSpec)
|
||||||
# TODO: Should be `Unknown`
|
reveal_type(annotated_unknown) # revealed: Unknown
|
||||||
reveal_type(annotated_unknown) # revealed: T@AnnotatedType
|
reveal_type(optional_unknown) # revealed: Unknown | None
|
||||||
# TODO: Should be `Unknown | None`
|
|
||||||
reveal_type(optional_unknown) # revealed: T@MyOptional | None
|
|
||||||
```
|
```
|
||||||
|
|
||||||
For a type variable with a default, we use the default type:
|
For a type variable with a default, we use the default type:
|
||||||
|
|
@ -565,8 +551,7 @@ def _(
|
||||||
list_of_int: MyListWithDefault,
|
list_of_int: MyListWithDefault,
|
||||||
):
|
):
|
||||||
reveal_type(list_of_str) # revealed: list[str]
|
reveal_type(list_of_str) # revealed: list[str]
|
||||||
# TODO: this should be `list[int]`
|
reveal_type(list_of_int) # revealed: list[int]
|
||||||
reveal_type(list_of_int) # revealed: list[T_default@MyListWithDefault]
|
|
||||||
```
|
```
|
||||||
|
|
||||||
(Generic) implicit type aliases can be used as base classes:
|
(Generic) implicit type aliases can be used as base classes:
|
||||||
|
|
|
||||||
|
|
@ -7540,6 +7540,15 @@ impl<'db> Type<'db> {
|
||||||
self.apply_type_mapping_impl(db, type_mapping, tcx, &ApplyTypeMappingVisitor::default())
|
self.apply_type_mapping_impl(db, type_mapping, tcx, &ApplyTypeMappingVisitor::default())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Erase all free type variables in this type, replacing them with their defaults
|
||||||
|
/// or `Unknown` if no default exists.
|
||||||
|
///
|
||||||
|
/// This is used when an implicit type alias containing free type variables is used
|
||||||
|
/// in a type expression without explicit type arguments.
|
||||||
|
pub(crate) fn erase_free_typevars(self, db: &'db dyn Db) -> Type<'db> {
|
||||||
|
self.apply_type_mapping(db, &TypeMapping::EraseTypevars, TypeContext::default())
|
||||||
|
}
|
||||||
|
|
||||||
fn apply_type_mapping_impl<'a>(
|
fn apply_type_mapping_impl<'a>(
|
||||||
self,
|
self,
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
|
|
@ -7571,7 +7580,8 @@ impl<'db> Type<'db> {
|
||||||
TypeMapping::ReplaceSelf { .. } |
|
TypeMapping::ReplaceSelf { .. } |
|
||||||
TypeMapping::Materialize(_) |
|
TypeMapping::Materialize(_) |
|
||||||
TypeMapping::ReplaceParameterDefaults |
|
TypeMapping::ReplaceParameterDefaults |
|
||||||
TypeMapping::EagerExpansion => self,
|
TypeMapping::EagerExpansion |
|
||||||
|
TypeMapping::EraseTypevars => self,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
KnownInstanceType::UnionType(instance) => {
|
KnownInstanceType::UnionType(instance) => {
|
||||||
|
|
@ -7748,6 +7758,7 @@ impl<'db> Type<'db> {
|
||||||
TypeMapping::Materialize(_) |
|
TypeMapping::Materialize(_) |
|
||||||
TypeMapping::ReplaceParameterDefaults |
|
TypeMapping::ReplaceParameterDefaults |
|
||||||
TypeMapping::EagerExpansion |
|
TypeMapping::EagerExpansion |
|
||||||
|
TypeMapping::EraseTypevars |
|
||||||
TypeMapping::PromoteLiterals(PromoteLiteralsMode::Off) => self,
|
TypeMapping::PromoteLiterals(PromoteLiteralsMode::Off) => self,
|
||||||
TypeMapping::PromoteLiterals(PromoteLiteralsMode::On) => self.promote_literals_impl(db, tcx)
|
TypeMapping::PromoteLiterals(PromoteLiteralsMode::On) => self.promote_literals_impl(db, tcx)
|
||||||
}
|
}
|
||||||
|
|
@ -7760,7 +7771,8 @@ impl<'db> Type<'db> {
|
||||||
TypeMapping::ReplaceSelf { .. } |
|
TypeMapping::ReplaceSelf { .. } |
|
||||||
TypeMapping::PromoteLiterals(_) |
|
TypeMapping::PromoteLiterals(_) |
|
||||||
TypeMapping::ReplaceParameterDefaults |
|
TypeMapping::ReplaceParameterDefaults |
|
||||||
TypeMapping::EagerExpansion => self,
|
TypeMapping::EagerExpansion |
|
||||||
|
TypeMapping::EraseTypevars => self,
|
||||||
TypeMapping::Materialize(materialization_kind) => match materialization_kind {
|
TypeMapping::Materialize(materialization_kind) => match materialization_kind {
|
||||||
MaterializationKind::Top => Type::object(),
|
MaterializationKind::Top => Type::object(),
|
||||||
MaterializationKind::Bottom => Type::Never,
|
MaterializationKind::Bottom => Type::Never,
|
||||||
|
|
@ -8434,6 +8446,9 @@ pub enum TypeMapping<'a, 'db> {
|
||||||
/// Apply eager expansion to the type.
|
/// Apply eager expansion to the type.
|
||||||
/// In the case of recursive type aliases, this will diverge, so that part will be replaced with `Divergent`.
|
/// In the case of recursive type aliases, this will diverge, so that part will be replaced with `Divergent`.
|
||||||
EagerExpansion,
|
EagerExpansion,
|
||||||
|
/// Replace all type variables with `Unknown`. This is used when an implicit type alias containing
|
||||||
|
/// free type variables is used in a type expression without explicit type arguments.
|
||||||
|
EraseTypevars,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'db> TypeMapping<'_, 'db> {
|
impl<'db> TypeMapping<'_, 'db> {
|
||||||
|
|
@ -8450,7 +8465,8 @@ impl<'db> TypeMapping<'_, 'db> {
|
||||||
| TypeMapping::BindLegacyTypevars(_)
|
| TypeMapping::BindLegacyTypevars(_)
|
||||||
| TypeMapping::Materialize(_)
|
| TypeMapping::Materialize(_)
|
||||||
| TypeMapping::ReplaceParameterDefaults
|
| TypeMapping::ReplaceParameterDefaults
|
||||||
| TypeMapping::EagerExpansion => context,
|
| TypeMapping::EagerExpansion
|
||||||
|
| TypeMapping::EraseTypevars => context,
|
||||||
TypeMapping::BindSelf { .. } => GenericContext::from_typevar_instances(
|
TypeMapping::BindSelf { .. } => GenericContext::from_typevar_instances(
|
||||||
db,
|
db,
|
||||||
context
|
context
|
||||||
|
|
@ -8487,7 +8503,8 @@ impl<'db> TypeMapping<'_, 'db> {
|
||||||
| TypeMapping::BindSelf { .. }
|
| TypeMapping::BindSelf { .. }
|
||||||
| TypeMapping::ReplaceSelf { .. }
|
| TypeMapping::ReplaceSelf { .. }
|
||||||
| TypeMapping::ReplaceParameterDefaults
|
| TypeMapping::ReplaceParameterDefaults
|
||||||
| TypeMapping::EagerExpansion => self.clone(),
|
| TypeMapping::EagerExpansion
|
||||||
|
| TypeMapping::EraseTypevars => self.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -9866,6 +9883,12 @@ impl<'db> BoundTypeVarInstance<'db> {
|
||||||
| TypeMapping::ReplaceParameterDefaults
|
| TypeMapping::ReplaceParameterDefaults
|
||||||
| TypeMapping::BindLegacyTypevars(_)
|
| TypeMapping::BindLegacyTypevars(_)
|
||||||
| TypeMapping::EagerExpansion => Type::TypeVar(self),
|
| TypeMapping::EagerExpansion => Type::TypeVar(self),
|
||||||
|
TypeMapping::EraseTypevars => {
|
||||||
|
// Replace the type variable with its default, or Unknown if no default exists.
|
||||||
|
self.default_type(db)
|
||||||
|
.unwrap_or_else(Type::unknown)
|
||||||
|
.apply_type_mapping(db, type_mapping, TypeContext::default())
|
||||||
|
}
|
||||||
TypeMapping::Materialize(materialization_kind) => {
|
TypeMapping::Materialize(materialization_kind) => {
|
||||||
Type::TypeVar(self.materialize_impl(db, *materialization_kind, visitor))
|
Type::TypeVar(self.materialize_impl(db, *materialization_kind, visitor))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -143,20 +143,27 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
|
||||||
TypeQualifiers::INIT_VAR,
|
TypeQualifiers::INIT_VAR,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
_ => TypeAndQualifiers::declared(
|
_ => {
|
||||||
ty.in_type_expression(
|
// When a name or attribute expression resolves to a type containing free
|
||||||
builder.db(),
|
// type variables (like a GenericAlias from an implicit type alias), we need
|
||||||
builder.scope(),
|
// to erase them since the alias is being used without explicit type arguments.
|
||||||
builder.typevar_binding_context,
|
let erased = ty.erase_free_typevars(builder.db());
|
||||||
|
TypeAndQualifiers::declared(
|
||||||
|
erased
|
||||||
|
.in_type_expression(
|
||||||
|
builder.db(),
|
||||||
|
builder.scope(),
|
||||||
|
builder.typevar_binding_context,
|
||||||
|
)
|
||||||
|
.unwrap_or_else(|error| {
|
||||||
|
error.into_fallback_type(
|
||||||
|
&builder.context,
|
||||||
|
annotation,
|
||||||
|
builder.is_reachable(annotation),
|
||||||
|
)
|
||||||
|
}),
|
||||||
)
|
)
|
||||||
.unwrap_or_else(|error| {
|
}
|
||||||
error.into_fallback_type(
|
|
||||||
&builder.context,
|
|
||||||
annotation,
|
|
||||||
builder.is_reachable(annotation),
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -89,16 +89,22 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
|
||||||
// https://typing.python.org/en/latest/spec/annotations.html#grammar-token-expression-grammar-type_expression
|
// https://typing.python.org/en/latest/spec/annotations.html#grammar-token-expression-grammar-type_expression
|
||||||
match expression {
|
match expression {
|
||||||
ast::Expr::Name(name) => match name.ctx {
|
ast::Expr::Name(name) => match name.ctx {
|
||||||
ast::ExprContext::Load => self
|
ast::ExprContext::Load => {
|
||||||
.infer_name_expression(name)
|
// When a name expression resolves to a type containing free type variables
|
||||||
.in_type_expression(self.db(), self.scope(), self.typevar_binding_context)
|
// (like a GenericAlias from an implicit type alias), we need to erase them
|
||||||
.unwrap_or_else(|error| {
|
// since the alias is being used without explicit type arguments.
|
||||||
error.into_fallback_type(
|
let ty = self.infer_name_expression(name);
|
||||||
&self.context,
|
let erased = ty.erase_free_typevars(self.db());
|
||||||
expression,
|
erased
|
||||||
self.is_reachable(expression),
|
.in_type_expression(self.db(), self.scope(), self.typevar_binding_context)
|
||||||
)
|
.unwrap_or_else(|error| {
|
||||||
}),
|
error.into_fallback_type(
|
||||||
|
&self.context,
|
||||||
|
expression,
|
||||||
|
self.is_reachable(expression),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
ast::ExprContext::Invalid => Type::unknown(),
|
ast::ExprContext::Invalid => Type::unknown(),
|
||||||
ast::ExprContext::Store | ast::ExprContext::Del => {
|
ast::ExprContext::Store | ast::ExprContext::Del => {
|
||||||
todo_type!("Name expression annotation in Store/Del context")
|
todo_type!("Name expression annotation in Store/Del context")
|
||||||
|
|
@ -106,16 +112,20 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
|
||||||
},
|
},
|
||||||
|
|
||||||
ast::Expr::Attribute(attribute_expression) => match attribute_expression.ctx {
|
ast::Expr::Attribute(attribute_expression) => match attribute_expression.ctx {
|
||||||
ast::ExprContext::Load => self
|
ast::ExprContext::Load => {
|
||||||
.infer_attribute_expression(attribute_expression)
|
// Same as name expressions - erase free type variables in attribute access
|
||||||
.in_type_expression(self.db(), self.scope(), self.typevar_binding_context)
|
let ty = self.infer_attribute_expression(attribute_expression);
|
||||||
.unwrap_or_else(|error| {
|
let erased = ty.erase_free_typevars(self.db());
|
||||||
error.into_fallback_type(
|
erased
|
||||||
&self.context,
|
.in_type_expression(self.db(), self.scope(), self.typevar_binding_context)
|
||||||
expression,
|
.unwrap_or_else(|error| {
|
||||||
self.is_reachable(expression),
|
error.into_fallback_type(
|
||||||
)
|
&self.context,
|
||||||
}),
|
expression,
|
||||||
|
self.is_reachable(expression),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
ast::ExprContext::Invalid => Type::unknown(),
|
ast::ExprContext::Invalid => Type::unknown(),
|
||||||
ast::ExprContext::Store | ast::ExprContext::Del => {
|
ast::ExprContext::Store | ast::ExprContext::Del => {
|
||||||
todo_type!("Attribute expression annotation in Store/Del context")
|
todo_type!("Attribute expression annotation in Store/Del context")
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue