mirror of https://github.com/astral-sh/ruff
use `infer_annotation_expression` for `TypedDict` fields
This commit is contained in:
parent
6fdd4c3334
commit
6981500d93
|
|
@ -5656,7 +5656,10 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
continue;
|
||||
};
|
||||
|
||||
let field_ty = self.infer_typed_dict_field_type_expression(&item.value);
|
||||
// TODO: The only qualifiers supported on `TypedDict` are `Required`, `NotRequired`,
|
||||
// and `ReadOnly`. We may want to error if others are used.
|
||||
let field_ty =
|
||||
self.infer_annotation_expression(&item.value, DeferredExpressionState::None);
|
||||
|
||||
let is_required = if field_ty.qualifiers.contains(TypeQualifiers::REQUIRED) {
|
||||
// Explicit Required[T] annotation - always required
|
||||
|
|
@ -5689,85 +5692,6 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
)))
|
||||
}
|
||||
|
||||
fn infer_typed_dict_field_type_expression(
|
||||
&mut self,
|
||||
expr: &ast::Expr,
|
||||
) -> TypeAndQualifiers<'db> {
|
||||
let ty = match expr {
|
||||
ast::Expr::Subscript(subscript @ ast::ExprSubscript { value, slice, .. }) => {
|
||||
let value_ty = self.infer_expression(value, TypeContext::default());
|
||||
let slice = &**slice;
|
||||
|
||||
// Unlike other type-form expressions, `TypedDict` constructor literals support
|
||||
// the `Required`, `NotRequired`, and `ReadOnly` qualifiers.
|
||||
match value_ty {
|
||||
Type::SpecialForm(
|
||||
type_qualifier @ (SpecialFormType::Required
|
||||
| SpecialFormType::NotRequired
|
||||
| SpecialFormType::ReadOnly),
|
||||
) => {
|
||||
let arguments = if let ast::Expr::Tuple(tuple) = slice {
|
||||
&*tuple.elts
|
||||
} else {
|
||||
std::slice::from_ref(slice)
|
||||
};
|
||||
|
||||
let num_arguments = arguments.len();
|
||||
let type_and_qualifiers = if num_arguments == 1 {
|
||||
let mut type_and_qualifiers =
|
||||
self.infer_typed_dict_field_type_expression(slice);
|
||||
|
||||
match type_qualifier {
|
||||
SpecialFormType::Required => {
|
||||
type_and_qualifiers.add_qualifier(TypeQualifiers::REQUIRED);
|
||||
}
|
||||
SpecialFormType::NotRequired => {
|
||||
type_and_qualifiers.add_qualifier(TypeQualifiers::NOT_REQUIRED);
|
||||
}
|
||||
SpecialFormType::ReadOnly => {
|
||||
type_and_qualifiers.add_qualifier(TypeQualifiers::READ_ONLY);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
||||
type_and_qualifiers
|
||||
} else {
|
||||
for element in arguments {
|
||||
self.infer_typed_dict_field_type_expression(element);
|
||||
}
|
||||
|
||||
if let Some(builder) =
|
||||
self.context.report_lint(&INVALID_TYPE_FORM, subscript)
|
||||
{
|
||||
builder.into_diagnostic(format_args!(
|
||||
"Type qualifier `{type_qualifier}` expected exactly 1 argument, \
|
||||
got {num_arguments}",
|
||||
));
|
||||
}
|
||||
|
||||
Type::unknown().into()
|
||||
};
|
||||
|
||||
if slice.is_tuple_expr() {
|
||||
self.store_expression_type(slice, type_and_qualifiers.inner_type());
|
||||
}
|
||||
|
||||
type_and_qualifiers
|
||||
}
|
||||
|
||||
_ => self
|
||||
.infer_subscript_type_expression_no_store(subscript, slice, value_ty)
|
||||
.into(),
|
||||
}
|
||||
}
|
||||
|
||||
type_expr => self.infer_type_expression_no_store(type_expr).into(),
|
||||
};
|
||||
|
||||
self.store_expression_type(expr, ty.inner_type());
|
||||
ty
|
||||
}
|
||||
|
||||
/// Infer the type of the `iter` expression of the first comprehension.
|
||||
fn infer_first_comprehension_iter(&mut self, comprehensions: &[ast::Comprehension]) {
|
||||
let mut comprehensions_iter = comprehensions.iter();
|
||||
|
|
|
|||
Loading…
Reference in New Issue