improvements

This commit is contained in:
Alex Waygood 2025-11-27 18:03:29 +00:00
parent 7d7a3a883c
commit 263877718f
3 changed files with 44 additions and 13 deletions

View File

@ -42,14 +42,11 @@ reveal_type(len(())) # revealed: Literal[0]
reveal_type(len((1,))) # revealed: Literal[1] reveal_type(len((1,))) # revealed: Literal[1]
reveal_type(len((1, 2))) # revealed: Literal[2] reveal_type(len((1, 2))) # revealed: Literal[2]
reveal_type(len(tuple())) # revealed: Literal[0] reveal_type(len(tuple())) # revealed: Literal[0]
reveal_type(len((*[],))) # revealed: Literal[0]
# could also be `Literal[0]`, but `int` is accurate
reveal_type(len((*[],))) # revealed: int
# fmt: off # fmt: off
# could also be `Literal[1]`, but `int` is accurate reveal_type(len( # revealed: Literal[1]
reveal_type(len( # revealed: int
( (
*[], *[],
1, 1,
@ -58,11 +55,8 @@ reveal_type(len( # revealed: int
# fmt: on # fmt: on
# Could also be `Literal[2]`, but `int` is accurate reveal_type(len((*[], 1, 2))) # revealed: Literal[2]
reveal_type(len((*[], 1, 2))) # revealed: int reveal_type(len((*[], *{}))) # revealed: Literal[0]
# Could also be `Literal[0]`, but `int` is accurate
reveal_type(len((*[], *{}))) # revealed: int
``` ```
Tuple subclasses: Tuple subclasses:

View File

@ -534,6 +534,8 @@ reveal_type(x) # revealed: list[Literal[1, 2, 3]]
## Tuples with starred elements ## Tuples with starred elements
```py ```py
from typing import Literal, Sequence
x = (1, *range(3), 3) x = (1, *range(3), 3)
reveal_type(x) # revealed: tuple[Literal[1], *tuple[int, ...], Literal[3]] reveal_type(x) # revealed: tuple[Literal[1], *tuple[int, ...], Literal[3]]
@ -542,7 +544,21 @@ y = 1, 2
reveal_type(("foo", *y)) # revealed: tuple[Literal["foo"], Literal[1], Literal[2]] reveal_type(("foo", *y)) # revealed: tuple[Literal["foo"], Literal[1], Literal[2]]
aa: tuple[list[int], ...] = ([42], *{[56], [78]}, [100]) aa: tuple[list[int], ...] = ([42], *{[56], [78]}, [100])
reveal_type(aa) # revealed: tuple[list[int], *tuple[list[int], ...], list[int]] reveal_type(aa) # revealed: tuple[list[int], list[int], list[int], list[int]]
bb: tuple[list[Literal[42, 56]], ...] = ([42], *{[56, 42], [42]}, [42, 42, 56])
reveal_type(bb) # revealed: tuple[list[Literal[42, 56]], list[Literal[42, 56]], list[Literal[42, 56]], list[Literal[42, 56]]]
reveal_type((*[],)) # revealed: tuple[()]
reveal_type((42, *[], 56, *[])) # revealed: tuple[Literal[42], Literal[56]]
tup: Sequence[str] = (*{"foo": 42, "bar": 56},)
# TODO: `tuple[str, str]` would be better, given the type annotation
reveal_type(tup) # revealed: tuple[Unknown | str, Unknown | str]
def f(x: list[int]):
reveal_type((42, 56, *x, 97)) # revealed: tuple[Literal[42], Literal[56], *tuple[int, ...], Literal[97]]
``` ```
[not a singleton type]: https://discuss.python.org/t/should-we-specify-in-the-language-reference-that-the-empty-tuple-is-a-singleton/67957 [not a singleton type]: https://discuss.python.org/t/should-we-specify-in-the-language-reference-that-the-empty-tuple-is-a-singleton/67957

View File

@ -7334,12 +7334,33 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
let mut builder = TupleSpecBuilder::with_capacity(elts.len()); let mut builder = TupleSpecBuilder::with_capacity(elts.len());
for element in elts { for element in elts {
if element.is_starred_expr() { if let ast::Expr::Starred(starred) = element {
let element_type = infer_element(element); let element_type = infer_element(element);
// Fine to use `iterate` rather than `try_iterate` here: // Fine to use `iterate` rather than `try_iterate` here:
// errors from iterating over something not iterable will have been // errors from iterating over something not iterable will have been
// emitted in the `infer_element` call above. // emitted in the `infer_element` call above.
builder = builder.concat(db, &element_type.iterate(db)); let mut spec = element_type.iterate(db).into_owned();
let known_length = match &*starred.value {
ast::Expr::List(ast::ExprList { elts, .. })
| ast::Expr::Set(ast::ExprSet { elts, .. }) => elts
.iter()
.all(|elt| !elt.is_starred_expr())
.then_some(elts.len()),
ast::Expr::Dict(ast::ExprDict { items, .. }) => items
.iter()
.all(|item| item.key.is_some())
.then_some(items.len()),
_ => None,
};
if let Some(known_length) = known_length {
spec = spec
.resize(db, TupleLength::Fixed(known_length))
.unwrap_or(spec);
}
builder = builder.concat(db, &spec);
} else { } else {
builder.push(infer_element(element)); builder.push(infer_element(element));
} }