Infer list[T] for starred target in unpacking (#18401)
Some checks are pending
CI / Determine changes (push) Waiting to run
CI / cargo fmt (push) Waiting to run
CI / cargo clippy (push) Blocked by required conditions
CI / cargo test (linux) (push) Blocked by required conditions
CI / cargo test (linux, release) (push) Blocked by required conditions
CI / cargo test (windows) (push) Blocked by required conditions
CI / cargo test (wasm) (push) Blocked by required conditions
CI / cargo build (release) (push) Waiting to run
CI / cargo build (msrv) (push) Blocked by required conditions
CI / cargo fuzz build (push) Blocked by required conditions
CI / fuzz parser (push) Blocked by required conditions
CI / test scripts (push) Blocked by required conditions
CI / mkdocs (push) Waiting to run
CI / ecosystem (push) Blocked by required conditions
CI / Fuzz for new ty panics (push) Blocked by required conditions
CI / cargo shear (push) Blocked by required conditions
CI / python package (push) Waiting to run
CI / pre-commit (push) Waiting to run
CI / formatter instabilities and black similarity (push) Blocked by required conditions
CI / test ruff-lsp (push) Blocked by required conditions
CI / check playground (push) Blocked by required conditions
CI / benchmarks (push) Blocked by required conditions
[ty Playground] Release / publish (push) Waiting to run

## Summary

Closes: astral-sh/ty#191

## Test Plan

Update existing tests.
This commit is contained in:
Dhruv Manilawala 2025-06-03 07:25:07 +05:30 committed by GitHub
parent 14c42a8ddf
commit 2289187b74
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 39 additions and 35 deletions

View file

@ -302,7 +302,7 @@ class C:
c_instance = C()
reveal_type(c_instance.a) # revealed: Unknown | Literal[1]
reveal_type(c_instance.b) # revealed: Unknown
reveal_type(c_instance.b) # revealed: Unknown | list[Literal[2, 3]]
```
#### Attributes defined in for-loop (unpacking)

View file

@ -109,8 +109,7 @@ reveal_type(d) # revealed: Literal[5]
# error: [invalid-assignment] "Not enough values to unpack: Expected 3 or more"
[a, *b, c, d] = (1, 2)
reveal_type(a) # revealed: Unknown
# TODO: Should be list[Any] once support for assigning to starred expression is added
reveal_type(b) # revealed: Unknown
reveal_type(b) # revealed: list[Unknown]
reveal_type(c) # revealed: Unknown
reveal_type(d) # revealed: Unknown
```
@ -120,8 +119,7 @@ reveal_type(d) # revealed: Unknown
```py
[a, *b, c] = (1, 2)
reveal_type(a) # revealed: Literal[1]
# TODO: Should be list[Any] once support for assigning to starred expression is added
reveal_type(b) # revealed: @Todo(starred unpacking)
reveal_type(b) # revealed: list[Unknown]
reveal_type(c) # revealed: Literal[2]
```
@ -130,8 +128,7 @@ reveal_type(c) # revealed: Literal[2]
```py
[a, *b, c] = (1, 2, 3)
reveal_type(a) # revealed: Literal[1]
# TODO: Should be list[int] once support for assigning to starred expression is added
reveal_type(b) # revealed: @Todo(starred unpacking)
reveal_type(b) # revealed: list[Literal[2]]
reveal_type(c) # revealed: Literal[3]
```
@ -140,8 +137,7 @@ reveal_type(c) # revealed: Literal[3]
```py
[a, *b, c, d] = (1, 2, 3, 4, 5, 6)
reveal_type(a) # revealed: Literal[1]
# TODO: Should be list[int] once support for assigning to starred expression is added
reveal_type(b) # revealed: @Todo(starred unpacking)
reveal_type(b) # revealed: list[Literal[2, 3, 4]]
reveal_type(c) # revealed: Literal[5]
reveal_type(d) # revealed: Literal[6]
```
@ -152,8 +148,7 @@ reveal_type(d) # revealed: Literal[6]
[a, b, *c] = (1, 2, 3, 4)
reveal_type(a) # revealed: Literal[1]
reveal_type(b) # revealed: Literal[2]
# TODO: Should be list[int] once support for assigning to starred expression is added
reveal_type(c) # revealed: @Todo(starred unpacking)
reveal_type(c) # revealed: list[Literal[3, 4]]
```
### Starred expression (6)
@ -164,7 +159,7 @@ reveal_type(c) # revealed: @Todo(starred unpacking)
reveal_type(a) # revealed: Unknown
reveal_type(b) # revealed: Unknown
reveal_type(c) # revealed: Unknown
reveal_type(d) # revealed: Unknown
reveal_type(d) # revealed: list[Unknown]
reveal_type(e) # revealed: Unknown
reveal_type(f) # revealed: Unknown
```
@ -247,8 +242,7 @@ reveal_type(b) # revealed: Unknown
# error: [invalid-assignment] "Not enough values to unpack: Expected 3 or more"
(a, *b, c, d) = "ab"
reveal_type(a) # revealed: Unknown
# TODO: Should be list[LiteralString] once support for assigning to starred expression is added
reveal_type(b) # revealed: Unknown
reveal_type(b) # revealed: list[Unknown]
reveal_type(c) # revealed: Unknown
reveal_type(d) # revealed: Unknown
```
@ -258,7 +252,7 @@ reveal_type(d) # revealed: Unknown
(a, b, *c, d) = "a"
reveal_type(a) # revealed: Unknown
reveal_type(b) # revealed: Unknown
reveal_type(c) # revealed: Unknown
reveal_type(c) # revealed: list[Unknown]
reveal_type(d) # revealed: Unknown
```
@ -267,8 +261,7 @@ reveal_type(d) # revealed: Unknown
```py
(a, *b, c) = "ab"
reveal_type(a) # revealed: LiteralString
# TODO: Should be list[Any] once support for assigning to starred expression is added
reveal_type(b) # revealed: @Todo(starred unpacking)
reveal_type(b) # revealed: list[Unknown]
reveal_type(c) # revealed: LiteralString
```
@ -277,8 +270,7 @@ reveal_type(c) # revealed: LiteralString
```py
(a, *b, c) = "abc"
reveal_type(a) # revealed: LiteralString
# TODO: Should be list[LiteralString] once support for assigning to starred expression is added
reveal_type(b) # revealed: @Todo(starred unpacking)
reveal_type(b) # revealed: list[LiteralString]
reveal_type(c) # revealed: LiteralString
```
@ -287,8 +279,7 @@ reveal_type(c) # revealed: LiteralString
```py
(a, *b, c, d) = "abcdef"
reveal_type(a) # revealed: LiteralString
# TODO: Should be list[LiteralString] once support for assigning to starred expression is added
reveal_type(b) # revealed: @Todo(starred unpacking)
reveal_type(b) # revealed: list[LiteralString]
reveal_type(c) # revealed: LiteralString
reveal_type(d) # revealed: LiteralString
```
@ -299,8 +290,7 @@ reveal_type(d) # revealed: LiteralString
(a, b, *c) = "abcd"
reveal_type(a) # revealed: LiteralString
reveal_type(b) # revealed: LiteralString
# TODO: Should be list[int] once support for assigning to starred expression is added
reveal_type(c) # revealed: @Todo(starred unpacking)
reveal_type(c) # revealed: list[LiteralString]
```
### Unicode
@ -411,8 +401,7 @@ def _(arg: tuple[int, tuple[str, bytes]] | tuple[tuple[int, bytes], Literal["ab"
def _(arg: tuple[int, bytes, int] | tuple[int, int, str, int, bytes]):
a, *b, c = arg
reveal_type(a) # revealed: int
# TODO: Should be `list[bytes | int | str]`
reveal_type(b) # revealed: @Todo(starred unpacking)
reveal_type(b) # revealed: list[bytes] | list[int | str]
reveal_type(c) # revealed: int | bytes
```
@ -676,8 +665,7 @@ class ContextManager:
with ContextManager() as (a, *b):
reveal_type(a) # revealed: int
# TODO: Should be list[int] once support for assigning to starred expression is added
reveal_type(b) # revealed: @Todo(starred unpacking)
reveal_type(b) # revealed: list[int]
```
### Unbound context manager expression

View file

@ -8,12 +8,12 @@ use ruff_python_ast::{self as ast, AnyNodeRef};
use crate::Db;
use crate::semantic_index::ast_ids::{HasScopedExpressionId, ScopedExpressionId};
use crate::semantic_index::symbol::ScopeId;
use crate::types::{Type, TypeCheckDiagnostics, infer_expression_types, todo_type};
use crate::types::{Type, TypeCheckDiagnostics, infer_expression_types};
use crate::unpack::{UnpackKind, UnpackValue};
use super::context::InferContext;
use super::diagnostic::INVALID_ASSIGNMENT;
use super::{TupleType, UnionType};
use super::{KnownClass, TupleType, UnionType};
/// Unpacks the value expression type to their respective targets.
pub(crate) struct Unpacker<'db> {
@ -253,12 +253,17 @@ impl<'db> Unpacker<'db> {
let starred_end_index = tuple_ty.len(self.db()) - remaining;
// SAFETY: Safe because of the length check above.
let _starred_element_types =
let starred_element_types =
&tuple_ty.elements(self.db())[starred_index..starred_end_index];
// TODO: Combine the types into a list type. If the
// starred_element_types is empty, then it should be `List[Any]`.
// combine_types(starred_element_types);
element_types.push(todo_type!("starred unpacking"));
element_types.push(KnownClass::List.to_specialized_instance(
self.db(),
[if starred_element_types.is_empty() {
Type::unknown()
} else {
UnionType::from_elements(self.db(), starred_element_types)
}],
));
// Insert the types remaining that aren't consumed by the starred expression.
element_types.extend_from_slice(
@ -278,7 +283,18 @@ impl<'db> Unpacker<'db> {
);
}
Cow::Owned(vec![Type::unknown(); targets.len()])
Cow::Owned(
targets
.iter()
.map(|target| {
if target.is_starred_expr() {
KnownClass::List.to_specialized_instance(self.db(), [Type::unknown()])
} else {
Type::unknown()
}
})
.collect(),
)
}
}