[red-knot] Don't infer Todo for quite so many tuple type expressions (#17116)

## Summary

I noticed we were inferring `Todo` as the declared type for annotations
such as `x: tuple[list[int], list[int]]`. This PR reworks our annotation
parsing so that we instead infer `tuple[Todo, Todo]` for this
annotation, which is quite a bit more precise.

## Test Plan

Existing mdtest updated.
This commit is contained in:
Alex Waygood 2025-04-01 15:44:02 +01:00 committed by GitHub
parent 8e6a83b33e
commit 49c25993eb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 25 additions and 14 deletions

View file

@ -52,13 +52,11 @@ reveal_type(b) # revealed: tuple[int]
reveal_type(c) # revealed: tuple[str, int] reveal_type(c) # revealed: tuple[str, int]
reveal_type(d) # revealed: tuple[tuple[str, str], tuple[int, int]] reveal_type(d) # revealed: tuple[tuple[str, str], tuple[int, int]]
# TODO: homogeneous tuples, PEP-646 tuples # TODO: homogeneous tuples, PEP-646 tuples, generics
reveal_type(e) # revealed: @Todo(full tuple[...] support) reveal_type(e) # revealed: @Todo(full tuple[...] support)
reveal_type(f) # revealed: @Todo(full tuple[...] support) reveal_type(f) # revealed: @Todo(full tuple[...] support)
reveal_type(g) # revealed: @Todo(full tuple[...] support) reveal_type(g) # revealed: @Todo(full tuple[...] support)
reveal_type(h) # revealed: tuple[@Todo(generics), @Todo(generics)]
# TODO: support more kinds of type expressions in annotations
reveal_type(h) # revealed: @Todo(full tuple[...] support)
reveal_type(i) # revealed: tuple[str | int, str | int] reveal_type(i) # revealed: tuple[str | int, str | int]
reveal_type(j) # revealed: tuple[str | int] reveal_type(j) # revealed: tuple[str | int]

View file

@ -6451,15 +6451,26 @@ impl<'db> TypeInferenceBuilder<'db> {
/// homogeneous tuple and a partly homogeneous tuple (respectively) due to the `...` /// homogeneous tuple and a partly homogeneous tuple (respectively) due to the `...`
/// and the starred expression (respectively), Neither is supported by us right now, /// and the starred expression (respectively), Neither is supported by us right now,
/// so we should infer `Todo` for the *entire* tuple if we encounter one of those elements. /// so we should infer `Todo` for the *entire* tuple if we encounter one of those elements.
/// Even a subscript subelement could alter the type of the entire tuple fn element_could_alter_type_of_whole_tuple(
/// if the subscript is `Unpack[]` (which again, we don't yet support). element: &ast::Expr,
fn element_could_alter_type_of_whole_tuple(element: &ast::Expr, element_ty: Type) -> bool { element_ty: Type,
element_ty.is_todo() builder: &TypeInferenceBuilder,
&& matches!( ) -> bool {
element, if !element_ty.is_todo() {
ast::Expr::EllipsisLiteral(_) | ast::Expr::Starred(_) | ast::Expr::Subscript(_) return false;
}
match element {
ast::Expr::EllipsisLiteral(_) | ast::Expr::Starred(_) => true,
ast::Expr::Subscript(ast::ExprSubscript { value, .. }) => {
matches!(
builder.expression_type(value),
Type::KnownInstance(KnownInstanceType::Unpack)
) )
} }
_ => false,
}
}
// TODO: // TODO:
// - homogeneous tuples // - homogeneous tuples
@ -6474,7 +6485,8 @@ impl<'db> TypeInferenceBuilder<'db> {
for element in elements { for element in elements {
let element_ty = self.infer_type_expression(element); let element_ty = self.infer_type_expression(element);
return_todo |= element_could_alter_type_of_whole_tuple(element, element_ty); return_todo |=
element_could_alter_type_of_whole_tuple(element, element_ty, self);
element_types.push(element_ty); element_types.push(element_ty);
} }
@ -6493,7 +6505,8 @@ impl<'db> TypeInferenceBuilder<'db> {
} }
single_element => { single_element => {
let single_element_ty = self.infer_type_expression(single_element); let single_element_ty = self.infer_type_expression(single_element);
if element_could_alter_type_of_whole_tuple(single_element, single_element_ty) { if element_could_alter_type_of_whole_tuple(single_element, single_element_ty, self)
{
todo_type!("full tuple[...] support") todo_type!("full tuple[...] support")
} else { } else {
TupleType::from_elements(self.db(), std::iter::once(single_element_ty)) TupleType::from_elements(self.db(), std::iter::once(single_element_ty))