Skip to content

Commit 3e81edc

Browse files
committed
Infer list[T] for starred target in unpacking
1 parent 04dc48e commit 3e81edc

File tree

3 files changed

+39
-35
lines changed

3 files changed

+39
-35
lines changed

crates/ty_python_semantic/resources/mdtest/attributes.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -302,7 +302,7 @@ class C:
302302

303303
c_instance = C()
304304
reveal_type(c_instance.a) # revealed: Unknown | Literal[1]
305-
reveal_type(c_instance.b) # revealed: Unknown
305+
reveal_type(c_instance.b) # revealed: Unknown | list[Literal[2, 3]]
306306
```
307307

308308
#### Attributes defined in for-loop (unpacking)

crates/ty_python_semantic/resources/mdtest/unpacking.md

Lines changed: 14 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -109,8 +109,7 @@ reveal_type(d) # revealed: Literal[5]
109109
# error: [invalid-assignment] "Not enough values to unpack: Expected 3 or more"
110110
[a, *b, c, d] = (1, 2)
111111
reveal_type(a) # revealed: Unknown
112-
# TODO: Should be list[Any] once support for assigning to starred expression is added
113-
reveal_type(b) # revealed: Unknown
112+
reveal_type(b) # revealed: list[Unknown]
114113
reveal_type(c) # revealed: Unknown
115114
reveal_type(d) # revealed: Unknown
116115
```
@@ -120,8 +119,7 @@ reveal_type(d) # revealed: Unknown
120119
```py
121120
[a, *b, c] = (1, 2)
122121
reveal_type(a) # revealed: Literal[1]
123-
# TODO: Should be list[Any] once support for assigning to starred expression is added
124-
reveal_type(b) # revealed: @Todo(starred unpacking)
122+
reveal_type(b) # revealed: list[Unknown]
125123
reveal_type(c) # revealed: Literal[2]
126124
```
127125

@@ -130,8 +128,7 @@ reveal_type(c) # revealed: Literal[2]
130128
```py
131129
[a, *b, c] = (1, 2, 3)
132130
reveal_type(a) # revealed: Literal[1]
133-
# TODO: Should be list[int] once support for assigning to starred expression is added
134-
reveal_type(b) # revealed: @Todo(starred unpacking)
131+
reveal_type(b) # revealed: list[Literal[2]]
135132
reveal_type(c) # revealed: Literal[3]
136133
```
137134

@@ -140,8 +137,7 @@ reveal_type(c) # revealed: Literal[3]
140137
```py
141138
[a, *b, c, d] = (1, 2, 3, 4, 5, 6)
142139
reveal_type(a) # revealed: Literal[1]
143-
# TODO: Should be list[int] once support for assigning to starred expression is added
144-
reveal_type(b) # revealed: @Todo(starred unpacking)
140+
reveal_type(b) # revealed: list[Literal[2, 3, 4]]
145141
reveal_type(c) # revealed: Literal[5]
146142
reveal_type(d) # revealed: Literal[6]
147143
```
@@ -152,8 +148,7 @@ reveal_type(d) # revealed: Literal[6]
152148
[a, b, *c] = (1, 2, 3, 4)
153149
reveal_type(a) # revealed: Literal[1]
154150
reveal_type(b) # revealed: Literal[2]
155-
# TODO: Should be list[int] once support for assigning to starred expression is added
156-
reveal_type(c) # revealed: @Todo(starred unpacking)
151+
reveal_type(c) # revealed: list[Literal[3, 4]]
157152
```
158153

159154
### Starred expression (6)
@@ -164,7 +159,7 @@ reveal_type(c) # revealed: @Todo(starred unpacking)
164159
reveal_type(a) # revealed: Unknown
165160
reveal_type(b) # revealed: Unknown
166161
reveal_type(c) # revealed: Unknown
167-
reveal_type(d) # revealed: Unknown
162+
reveal_type(d) # revealed: list[Unknown]
168163
reveal_type(e) # revealed: Unknown
169164
reveal_type(f) # revealed: Unknown
170165
```
@@ -247,8 +242,7 @@ reveal_type(b) # revealed: Unknown
247242
# error: [invalid-assignment] "Not enough values to unpack: Expected 3 or more"
248243
(a, *b, c, d) = "ab"
249244
reveal_type(a) # revealed: Unknown
250-
# TODO: Should be list[LiteralString] once support for assigning to starred expression is added
251-
reveal_type(b) # revealed: Unknown
245+
reveal_type(b) # revealed: list[Unknown]
252246
reveal_type(c) # revealed: Unknown
253247
reveal_type(d) # revealed: Unknown
254248
```
@@ -258,7 +252,7 @@ reveal_type(d) # revealed: Unknown
258252
(a, b, *c, d) = "a"
259253
reveal_type(a) # revealed: Unknown
260254
reveal_type(b) # revealed: Unknown
261-
reveal_type(c) # revealed: Unknown
255+
reveal_type(c) # revealed: list[Unknown]
262256
reveal_type(d) # revealed: Unknown
263257
```
264258

@@ -267,8 +261,7 @@ reveal_type(d) # revealed: Unknown
267261
```py
268262
(a, *b, c) = "ab"
269263
reveal_type(a) # revealed: LiteralString
270-
# TODO: Should be list[Any] once support for assigning to starred expression is added
271-
reveal_type(b) # revealed: @Todo(starred unpacking)
264+
reveal_type(b) # revealed: list[Unknown]
272265
reveal_type(c) # revealed: LiteralString
273266
```
274267

@@ -277,8 +270,7 @@ reveal_type(c) # revealed: LiteralString
277270
```py
278271
(a, *b, c) = "abc"
279272
reveal_type(a) # revealed: LiteralString
280-
# TODO: Should be list[LiteralString] once support for assigning to starred expression is added
281-
reveal_type(b) # revealed: @Todo(starred unpacking)
273+
reveal_type(b) # revealed: list[LiteralString]
282274
reveal_type(c) # revealed: LiteralString
283275
```
284276

@@ -287,8 +279,7 @@ reveal_type(c) # revealed: LiteralString
287279
```py
288280
(a, *b, c, d) = "abcdef"
289281
reveal_type(a) # revealed: LiteralString
290-
# TODO: Should be list[LiteralString] once support for assigning to starred expression is added
291-
reveal_type(b) # revealed: @Todo(starred unpacking)
282+
reveal_type(b) # revealed: list[LiteralString]
292283
reveal_type(c) # revealed: LiteralString
293284
reveal_type(d) # revealed: LiteralString
294285
```
@@ -299,8 +290,7 @@ reveal_type(d) # revealed: LiteralString
299290
(a, b, *c) = "abcd"
300291
reveal_type(a) # revealed: LiteralString
301292
reveal_type(b) # revealed: LiteralString
302-
# TODO: Should be list[int] once support for assigning to starred expression is added
303-
reveal_type(c) # revealed: @Todo(starred unpacking)
293+
reveal_type(c) # revealed: list[LiteralString]
304294
```
305295

306296
### Unicode
@@ -411,8 +401,7 @@ def _(arg: tuple[int, tuple[str, bytes]] | tuple[tuple[int, bytes], Literal["ab"
411401
def _(arg: tuple[int, bytes, int] | tuple[int, int, str, int, bytes]):
412402
a, *b, c = arg
413403
reveal_type(a) # revealed: int
414-
# TODO: Should be `list[bytes | int | str]`
415-
reveal_type(b) # revealed: @Todo(starred unpacking)
404+
reveal_type(b) # revealed: list[bytes] | list[int | str]
416405
reveal_type(c) # revealed: int | bytes
417406
```
418407

@@ -676,8 +665,7 @@ class ContextManager:
676665

677666
with ContextManager() as (a, *b):
678667
reveal_type(a) # revealed: int
679-
# TODO: Should be list[int] once support for assigning to starred expression is added
680-
reveal_type(b) # revealed: @Todo(starred unpacking)
668+
reveal_type(b) # revealed: list[int]
681669
```
682670

683671
### Unbound context manager expression

crates/ty_python_semantic/src/types/unpacker.rs

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,12 @@ use ruff_python_ast::{self as ast, AnyNodeRef};
88
use crate::Db;
99
use crate::semantic_index::ast_ids::{HasScopedExpressionId, ScopedExpressionId};
1010
use crate::semantic_index::symbol::ScopeId;
11-
use crate::types::{Type, TypeCheckDiagnostics, infer_expression_types, todo_type};
11+
use crate::types::{Type, TypeCheckDiagnostics, infer_expression_types};
1212
use crate::unpack::{UnpackKind, UnpackValue};
1313

1414
use super::context::InferContext;
1515
use super::diagnostic::INVALID_ASSIGNMENT;
16-
use super::{TupleType, UnionType};
16+
use super::{KnownClass, TupleType, UnionType};
1717

1818
/// Unpacks the value expression type to their respective targets.
1919
pub(crate) struct Unpacker<'db> {
@@ -253,12 +253,17 @@ impl<'db> Unpacker<'db> {
253253
let starred_end_index = tuple_ty.len(self.db()) - remaining;
254254

255255
// SAFETY: Safe because of the length check above.
256-
let _starred_element_types =
256+
let starred_element_types =
257257
&tuple_ty.elements(self.db())[starred_index..starred_end_index];
258-
// TODO: Combine the types into a list type. If the
259-
// starred_element_types is empty, then it should be `List[Any]`.
260-
// combine_types(starred_element_types);
261-
element_types.push(todo_type!("starred unpacking"));
258+
259+
element_types.push(KnownClass::List.to_specialized_instance(
260+
self.db(),
261+
[if starred_element_types.is_empty() {
262+
Type::unknown()
263+
} else {
264+
UnionType::from_elements(self.db(), starred_element_types)
265+
}],
266+
));
262267

263268
// Insert the types remaining that aren't consumed by the starred expression.
264269
element_types.extend_from_slice(
@@ -278,7 +283,18 @@ impl<'db> Unpacker<'db> {
278283
);
279284
}
280285

281-
Cow::Owned(vec![Type::unknown(); targets.len()])
286+
Cow::Owned(
287+
targets
288+
.iter()
289+
.map(|target| {
290+
if target.is_starred_expr() {
291+
KnownClass::List.to_specialized_instance(self.db(), [Type::unknown()])
292+
} else {
293+
Type::unknown()
294+
}
295+
})
296+
.collect(),
297+
)
282298
}
283299
}
284300

0 commit comments

Comments
 (0)