[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(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(f) # revealed: @Todo(full tuple[...] support)
reveal_type(g) # revealed: @Todo(full tuple[...] support)
# TODO: support more kinds of type expressions in annotations
reveal_type(h) # revealed: @Todo(full tuple[...] support)
reveal_type(h) # revealed: tuple[@Todo(generics), @Todo(generics)]
reveal_type(i) # revealed: tuple[str | int, str | int]
reveal_type(j) # revealed: tuple[str | int]

View file

@ -6451,14 +6451,25 @@ impl<'db> TypeInferenceBuilder<'db> {
/// homogeneous tuple and a partly homogeneous tuple (respectively) due to the `...`
/// 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.
/// Even a subscript subelement could alter the type of the entire tuple
/// if the subscript is `Unpack[]` (which again, we don't yet support).
fn element_could_alter_type_of_whole_tuple(element: &ast::Expr, element_ty: Type) -> bool {
element_ty.is_todo()
&& matches!(
element,
ast::Expr::EllipsisLiteral(_) | ast::Expr::Starred(_) | ast::Expr::Subscript(_)
)
fn element_could_alter_type_of_whole_tuple(
element: &ast::Expr,
element_ty: Type,
builder: &TypeInferenceBuilder,
) -> bool {
if !element_ty.is_todo() {
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:
@ -6474,7 +6485,8 @@ impl<'db> TypeInferenceBuilder<'db> {
for element in elements {
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);
}
@ -6493,7 +6505,8 @@ impl<'db> TypeInferenceBuilder<'db> {
}
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")
} else {
TupleType::from_elements(self.db(), std::iter::once(single_element_ty))