11use std:: borrow:: Cow ;
2+ use std:: cmp:: Ordering ;
23
34use rustc_hash:: FxHashMap ;
45
@@ -11,6 +12,7 @@ use crate::unpack::UnpackValue;
1112use crate :: Db ;
1213
1314use super :: context:: { InferContext , WithDiagnostics } ;
15+ use super :: diagnostic:: INVALID_ASSIGNMENT ;
1416use super :: { TupleType , UnionType } ;
1517
1618/// Unpacks the value expression type to their respective targets.
@@ -104,9 +106,46 @@ impl<'db> Unpacker<'db> {
104106 } ;
105107
106108 if let Some ( tuple_ty) = ty. into_tuple ( ) {
107- let tuple_ty_elements = self . tuple_ty_elements ( elts, tuple_ty) ;
109+ let ( tuple_ty_elements, has_starred_expression) =
110+ self . tuple_ty_elements ( elts, tuple_ty) ;
108111
109- // TODO: Add diagnostic for length mismatch
112+ match elts. len ( ) . cmp ( & tuple_ty_elements. len ( ) ) {
113+ Ordering :: Less => {
114+ self . context . report_lint (
115+ & INVALID_ASSIGNMENT ,
116+ target. into ( ) ,
117+ format_args ! (
118+ "Too many values to unpack (expected {}, got {})" ,
119+ elts. len( ) ,
120+ tuple_ty_elements. len( )
121+ ) ,
122+ ) ;
123+ }
124+ Ordering :: Greater => {
125+ if has_starred_expression {
126+ self . context . report_lint (
127+ & INVALID_ASSIGNMENT ,
128+ target. into ( ) ,
129+ format_args ! (
130+ "Not enough values to unpack (expected {} or more, got {})" ,
131+ elts. len( ) - 1 ,
132+ tuple_ty. len( self . db( ) )
133+ ) ,
134+ ) ;
135+ } else {
136+ self . context . report_lint (
137+ & INVALID_ASSIGNMENT ,
138+ target. into ( ) ,
139+ format_args ! (
140+ "Not enough values to unpack (expected {}, got {})" ,
141+ elts. len( ) ,
142+ tuple_ty_elements. len( )
143+ ) ,
144+ ) ;
145+ }
146+ }
147+ Ordering :: Equal => { }
148+ }
110149
111150 for ( index, ty) in tuple_ty_elements. iter ( ) . enumerate ( ) {
112151 if let Some ( element_types) = target_types. get_mut ( index) {
@@ -129,6 +168,7 @@ impl<'db> Unpacker<'db> {
129168 for ( index, element) in elts. iter ( ) . enumerate ( ) {
130169 // SAFETY: `target_types` is initialized with the same length as `elts`.
131170 let element_ty = match target_types[ index] . as_slice ( ) {
171+ [ ] if element. is_starred_expr ( ) => todo_type ! ( "starred unpacking" ) ,
132172 [ ] => Type :: Unknown ,
133173 types => UnionType :: from_elements ( self . db ( ) , types) ,
134174 } ;
@@ -142,29 +182,39 @@ impl<'db> Unpacker<'db> {
142182 /// Returns the [`Type`] elements inside the given [`TupleType`] taking into account that there
143183 /// can be a starred expression in the `elements`.
144184 fn tuple_ty_elements (
145- & mut self ,
185+ & self ,
146186 targets : & [ ast:: Expr ] ,
147187 tuple_ty : TupleType < ' db > ,
148- ) -> Cow < ' _ , [ Type < ' db > ] > {
149- // If there is a starred expression, it will consume all of the entries at that location.
188+ ) -> ( Cow < ' _ , [ Type < ' db > ] > , bool ) {
189+ // If there is a starred expression, it will consume all of the types at that location.
150190 let Some ( starred_index) = targets. iter ( ) . position ( ast:: Expr :: is_starred_expr) else {
151- // Otherwise, the types will be unpacked 1-1 to the elements .
152- return Cow :: Borrowed ( tuple_ty. elements ( self . db ( ) ) . as_ref ( ) ) ;
191+ // Otherwise, the types will be unpacked 1-1 to the targets .
192+ return ( Cow :: Borrowed ( tuple_ty. elements ( self . db ( ) ) . as_ref ( ) ) , false ) ;
153193 } ;
154194
155195 if tuple_ty. len ( self . db ( ) ) >= targets. len ( ) - 1 {
196+ // This branch is only taken when there are enough elements in the tuple type to
197+ // combine for the starred expression. So, the arithmetic and indexing operations are
198+ // safe to perform.
156199 let mut element_types = Vec :: with_capacity ( targets. len ( ) ) ;
200+
201+ // Insert all the elements before the starred expression.
157202 element_types. extend_from_slice (
158203 // SAFETY: Safe because of the length check above.
159204 & tuple_ty. elements ( self . db ( ) ) [ ..starred_index] ,
160205 ) ;
161206
162- // E.g., in `(a, *b, c, d) = ...`, the index of starred element `b`
163- // is 1 and the remaining elements after that are 2.
207+ // The number of target expressions that are remaining after the starred expression.
208+ // For example, in `(a, *b, c, d) = ...`, the index of starred element `b` is 1 and the
209+ // remaining elements after that are 2.
164210 let remaining = targets. len ( ) - ( starred_index + 1 ) ;
165- // This index represents the type of the last element that belongs
166- // to the starred expression, in an exclusive manner.
211+
212+ // This index represents the position of the last element that belongs to the starred
213+ // expression, in an exclusive manner. For example, in `(a, *b, c) = (1, 2, 3, 4)`, the
214+ // starred expression `b` will consume the elements `Literal[2]` and `Literal[3]` and
215+ // the index value would be 3.
167216 let starred_end_index = tuple_ty. len ( self . db ( ) ) - remaining;
217+
168218 // SAFETY: Safe because of the length check above.
169219 let _starred_element_types =
170220 & tuple_ty. elements ( self . db ( ) ) [ starred_index..starred_end_index] ;
@@ -173,19 +223,20 @@ impl<'db> Unpacker<'db> {
173223 // combine_types(starred_element_types);
174224 element_types. push ( todo_type ! ( "starred unpacking" ) ) ;
175225
226+ // Insert the types remaining that aren't consumed by the starred expression.
176227 element_types. extend_from_slice (
177228 // SAFETY: Safe because of the length check above.
178229 & tuple_ty. elements ( self . db ( ) ) [ starred_end_index..] ,
179230 ) ;
180- Cow :: Owned ( element_types)
181- } else {
231+
232+ ( Cow :: Owned ( element_types) , true )
233+ } else if starred_index < tuple_ty. len ( self . db ( ) ) {
182234 let mut element_types = tuple_ty. elements ( self . db ( ) ) . to_vec ( ) ;
183- // Subtract 1 to insert the starred expression type at the correct
184- // index.
185- element_types. resize ( targets. len ( ) - 1 , Type :: Unknown ) ;
186235 // TODO: This should be `list[Unknown]`
187236 element_types. insert ( starred_index, todo_type ! ( "starred unpacking" ) ) ;
188- Cow :: Owned ( element_types)
237+ ( Cow :: Owned ( element_types) , true )
238+ } else {
239+ ( Cow :: Borrowed ( tuple_ty. elements ( self . db ( ) ) ) , true )
189240 }
190241 }
191242
0 commit comments