@@ -113,9 +113,7 @@ fn expand_tuple_struct_rest_pattern(
113113 } ;
114114
115115 let rest_pat = rest_pat. into ( ) ;
116- let mut pats = pat. fields ( ) ;
117- let prefix_count = pats. by_ref ( ) . position ( |p| p == rest_pat) ?;
118- let suffix_count = pats. count ( ) ;
116+ let ( prefix_count, suffix_count) = calculate_counts ( & rest_pat, pat. fields ( ) ) ?;
119117
120118 if fields. len ( ) . saturating_sub ( prefix_count) . saturating_sub ( suffix_count) == 0 {
121119 cov_mark:: hit!( no_missing_fields_tuple_struct) ;
@@ -141,19 +139,13 @@ fn expand_tuple_struct_rest_pattern(
141139 pat. fields ( )
142140 . take ( prefix_count)
143141 . chain ( fields[ prefix_count..fields. len ( ) - suffix_count] . iter ( ) . map ( |f| {
144- make. ident_pat (
145- false ,
146- false ,
147- match name_gen. for_type (
148- & f. ty ( ctx. sema . db ) . to_type ( ctx. sema . db ) ,
149- ctx. sema . db ,
150- ctx. edition ( ) ,
151- ) {
152- Some ( name) => make. name ( & name) ,
153- None => make. name ( & format ! ( "_{}" , f. index( ) ) ) ,
154- } ,
142+ gen_unnamed_pat (
143+ ctx,
144+ & make,
145+ & mut name_gen,
146+ & f. ty ( ctx. db ( ) ) . to_type ( ctx. sema . db ) ,
147+ f. index ( ) ,
155148 )
156- . into ( )
157149 } ) )
158150 . chain ( pat. fields ( ) . skip ( prefix_count + 1 ) ) ,
159151 ) ;
@@ -166,22 +158,175 @@ fn expand_tuple_struct_rest_pattern(
166158 )
167159}
168160
161+ // Assist: expand_tuple_rest_pattern
162+ //
163+ // Fills fields by replacing rest pattern in tuple patterns.
164+ //
165+ // ```
166+ // fn foo(bar: (char, i32, i32)) {
167+ // let (ch, ..$0) = bar;
168+ // }
169+ // ```
170+ // ->
171+ // ```
172+ // fn foo(bar: (char, i32, i32)) {
173+ // let (ch, _1, _2) = bar;
174+ // }
175+ // ```
176+ fn expand_tuple_rest_pattern (
177+ acc : & mut Assists ,
178+ ctx : & AssistContext < ' _ > ,
179+ pat : ast:: TuplePat ,
180+ rest_pat : ast:: RestPat ,
181+ ) -> Option < ( ) > {
182+ let fields = ctx. sema . type_of_pat ( & pat. clone ( ) . into ( ) ) ?. original . tuple_fields ( ctx. db ( ) ) ;
183+ let len = fields. len ( ) ;
184+
185+ let rest_pat = rest_pat. into ( ) ;
186+ let ( prefix_count, suffix_count) = calculate_counts ( & rest_pat, pat. fields ( ) ) ?;
187+
188+ if len. saturating_sub ( prefix_count) . saturating_sub ( suffix_count) == 0 {
189+ cov_mark:: hit!( no_missing_fields_tuple) ;
190+ return None ;
191+ }
192+
193+ let old_range = ctx. sema . original_range_opt ( pat. syntax ( ) ) ?;
194+ if old_range. file_id != ctx. file_id ( ) {
195+ return None ;
196+ }
197+
198+ acc. add (
199+ AssistId :: refactor_rewrite ( "expand_tuple_rest_pattern" ) ,
200+ "Fill tuple fields" ,
201+ rest_pat. syntax ( ) . text_range ( ) ,
202+ |builder| {
203+ let make = SyntaxFactory :: with_mappings ( ) ;
204+ let mut editor = builder. make_editor ( rest_pat. syntax ( ) ) ;
205+
206+ let mut name_gen = NameGenerator :: new_from_scope_locals ( ctx. sema . scope ( pat. syntax ( ) ) ) ;
207+ let new_pat = make. tuple_pat (
208+ pat. fields ( )
209+ . take ( prefix_count)
210+ . chain ( fields[ prefix_count..len - suffix_count] . iter ( ) . enumerate ( ) . map (
211+ |( index, ty) | {
212+ gen_unnamed_pat ( ctx, & make, & mut name_gen, ty, prefix_count + index)
213+ } ,
214+ ) )
215+ . chain ( pat. fields ( ) . skip ( prefix_count + 1 ) ) ,
216+ ) ;
217+
218+ editor. replace ( pat. syntax ( ) , new_pat. syntax ( ) ) ;
219+
220+ editor. add_mappings ( make. finish_with_mappings ( ) ) ;
221+ builder. add_file_edits ( ctx. vfs_file_id ( ) , editor) ;
222+ } ,
223+ )
224+ }
225+
226+ // Assist: expand_slice_rest_pattern
227+ //
228+ // Fills fields by replacing rest pattern in slice patterns.
229+ //
230+ // ```
231+ // fn foo(bar: [i32; 3]) {
232+ // let [first, ..$0] = bar;
233+ // }
234+ // ```
235+ // ->
236+ // ```
237+ // fn foo(bar: [i32; 3]) {
238+ // let [first, _1, _2] = bar;
239+ // }
240+ // ```
241+ fn expand_slice_rest_pattern (
242+ acc : & mut Assists ,
243+ ctx : & AssistContext < ' _ > ,
244+ pat : ast:: SlicePat ,
245+ rest_pat : ast:: RestPat ,
246+ ) -> Option < ( ) > {
247+ let ( ty, len) = ctx. sema . type_of_pat ( & pat. clone ( ) . into ( ) ) ?. original . as_array ( ctx. db ( ) ) ?;
248+
249+ let rest_pat = rest_pat. into ( ) ;
250+ let ( prefix_count, suffix_count) = calculate_counts ( & rest_pat, pat. pats ( ) ) ?;
251+
252+ if len. saturating_sub ( prefix_count) . saturating_sub ( suffix_count) == 0 {
253+ cov_mark:: hit!( no_missing_fields_slice) ;
254+ return None ;
255+ }
256+
257+ let old_range = ctx. sema . original_range_opt ( pat. syntax ( ) ) ?;
258+ if old_range. file_id != ctx. file_id ( ) {
259+ return None ;
260+ }
261+
262+ acc. add (
263+ AssistId :: refactor_rewrite ( "expand_slice_rest_pattern" ) ,
264+ "Fill slice fields" ,
265+ rest_pat. syntax ( ) . text_range ( ) ,
266+ |builder| {
267+ let make = SyntaxFactory :: with_mappings ( ) ;
268+ let mut editor = builder. make_editor ( rest_pat. syntax ( ) ) ;
269+
270+ let mut name_gen = NameGenerator :: new_from_scope_locals ( ctx. sema . scope ( pat. syntax ( ) ) ) ;
271+ let new_pat = make. slice_pat (
272+ pat. pats ( )
273+ . take ( prefix_count)
274+ . chain (
275+ ( prefix_count..len - suffix_count)
276+ . map ( |index| gen_unnamed_pat ( ctx, & make, & mut name_gen, & ty, index) ) ,
277+ )
278+ . chain ( pat. pats ( ) . skip ( prefix_count + 1 ) ) ,
279+ ) ;
280+
281+ editor. replace ( pat. syntax ( ) , new_pat. syntax ( ) ) ;
282+
283+ editor. add_mappings ( make. finish_with_mappings ( ) ) ;
284+ builder. add_file_edits ( ctx. vfs_file_id ( ) , editor) ;
285+ } ,
286+ )
287+ }
288+
169289pub ( crate ) fn expand_rest_pattern ( acc : & mut Assists , ctx : & AssistContext < ' _ > ) -> Option < ( ) > {
170290 let rest_pat = ctx. find_node_at_offset :: < ast:: RestPat > ( ) ?;
171291 let parent = rest_pat. syntax ( ) . parent ( ) ?;
172292 match_ast ! {
173293 match parent {
174294 ast:: RecordPatFieldList ( it) => expand_record_rest_pattern( acc, ctx, it. syntax( ) . parent( ) . and_then( ast:: RecordPat :: cast) ?, rest_pat) ,
175295 ast:: TupleStructPat ( it) => expand_tuple_struct_rest_pattern( acc, ctx, it, rest_pat) ,
176- // FIXME
177- // ast::TuplePat(it) => (),
178- // FIXME
179- // ast::SlicePat(it) => (),
296+ ast:: TuplePat ( it) => expand_tuple_rest_pattern( acc, ctx, it, rest_pat) ,
297+ ast:: SlicePat ( it) => expand_slice_rest_pattern( acc, ctx, it, rest_pat) ,
180298 _ => None ,
181299 }
182300 }
183301}
184302
303+ fn gen_unnamed_pat (
304+ ctx : & AssistContext < ' _ > ,
305+ make : & SyntaxFactory ,
306+ name_gen : & mut NameGenerator ,
307+ ty : & hir:: Type < ' _ > ,
308+ index : usize ,
309+ ) -> ast:: Pat {
310+ make. ident_pat (
311+ false ,
312+ false ,
313+ match name_gen. for_type ( ty, ctx. sema . db , ctx. edition ( ) ) {
314+ Some ( name) => make. name ( & name) ,
315+ None => make. name ( & format ! ( "_{index}" ) ) ,
316+ } ,
317+ )
318+ . into ( )
319+ }
320+
321+ fn calculate_counts (
322+ rest_pat : & ast:: Pat ,
323+ mut pats : ast:: AstChildren < ast:: Pat > ,
324+ ) -> Option < ( usize , usize ) > {
325+ let prefix_count = pats. by_ref ( ) . position ( |p| p == * rest_pat) ?;
326+ let suffix_count = pats. count ( ) ;
327+ Some ( ( prefix_count, suffix_count) )
328+ }
329+
185330#[ cfg( test) ]
186331mod tests {
187332 use super :: * ;
@@ -351,6 +496,79 @@ fn foo(bar: Bar) {
351496 )
352497 }
353498
499+ #[ test]
500+ fn fill_tuple_with_fields ( ) {
501+ check_assist (
502+ expand_rest_pattern,
503+ r#"
504+ fn foo(bar: (char, i32, i32)) {
505+ let (ch, ..$0) = bar;
506+ }
507+ "# ,
508+ r#"
509+ fn foo(bar: (char, i32, i32)) {
510+ let (ch, _1, _2) = bar;
511+ }
512+ "# ,
513+ ) ;
514+ check_assist (
515+ expand_rest_pattern,
516+ r#"
517+ fn foo(bar: (char, i32, i32)) {
518+ let (ch, ..$0, end) = bar;
519+ }
520+ "# ,
521+ r#"
522+ fn foo(bar: (char, i32, i32)) {
523+ let (ch, _1, end) = bar;
524+ }
525+ "# ,
526+ ) ;
527+ }
528+
529+ #[ test]
530+ fn fill_array_with_fields ( ) {
531+ check_assist (
532+ expand_rest_pattern,
533+ r#"
534+ fn foo(bar: [i32; 4]) {
535+ let [first, ..$0] = bar;
536+ }
537+ "# ,
538+ r#"
539+ fn foo(bar: [i32; 4]) {
540+ let [first, _1, _2, _3] = bar;
541+ }
542+ "# ,
543+ ) ;
544+ check_assist (
545+ expand_rest_pattern,
546+ r#"
547+ fn foo(bar: [i32; 4]) {
548+ let [first, second, ..$0] = bar;
549+ }
550+ "# ,
551+ r#"
552+ fn foo(bar: [i32; 4]) {
553+ let [first, second, _2, _3] = bar;
554+ }
555+ "# ,
556+ ) ;
557+ check_assist (
558+ expand_rest_pattern,
559+ r#"
560+ fn foo(bar: [i32; 4]) {
561+ let [first, second, ..$0, end] = bar;
562+ }
563+ "# ,
564+ r#"
565+ fn foo(bar: [i32; 4]) {
566+ let [first, second, _2, end] = bar;
567+ }
568+ "# ,
569+ ) ;
570+ }
571+
354572 #[ test]
355573 fn fill_fields_struct_generated_by_macro ( ) {
356574 check_assist (
@@ -486,6 +704,8 @@ fn bar(foo: Foo) {
486704 // This is still possible even though it's meaningless
487705 cov_mark:: check!( no_missing_fields) ;
488706 cov_mark:: check!( no_missing_fields_tuple_struct) ;
707+ cov_mark:: check!( no_missing_fields_tuple) ;
708+ cov_mark:: check!( no_missing_fields_slice) ;
489709 check_assist_not_applicable (
490710 expand_rest_pattern,
491711 r#"
@@ -523,6 +743,22 @@ struct Bar(Y, Z)
523743fn foo(bar: Bar) {
524744 let Bar(y, ..$0, z) = bar;
525745}
746+ "# ,
747+ ) ;
748+ check_assist_not_applicable (
749+ expand_rest_pattern,
750+ r#"
751+ fn foo(bar: (i32, i32)) {
752+ let (y, ..$0, z) = bar;
753+ }
754+ "# ,
755+ ) ;
756+ check_assist_not_applicable (
757+ expand_rest_pattern,
758+ r#"
759+ fn foo(bar: [i32; 2]) {
760+ let [y, ..$0, z] = bar;
761+ }
526762"# ,
527763 ) ;
528764 }
0 commit comments