@@ -113,9 +113,7 @@ fn expand_tuple_struct_rest_pattern(
113
113
} ;
114
114
115
115
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 ( ) ) ?;
119
117
120
118
if fields. len ( ) . saturating_sub ( prefix_count) . saturating_sub ( suffix_count) == 0 {
121
119
cov_mark:: hit!( no_missing_fields_tuple_struct) ;
@@ -141,19 +139,13 @@ fn expand_tuple_struct_rest_pattern(
141
139
pat. fields ( )
142
140
. take ( prefix_count)
143
141
. 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 ( ) ,
155
148
)
156
- . into ( )
157
149
} ) )
158
150
. chain ( pat. fields ( ) . skip ( prefix_count + 1 ) ) ,
159
151
) ;
@@ -166,22 +158,175 @@ fn expand_tuple_struct_rest_pattern(
166
158
)
167
159
}
168
160
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
+
169
289
pub ( crate ) fn expand_rest_pattern ( acc : & mut Assists , ctx : & AssistContext < ' _ > ) -> Option < ( ) > {
170
290
let rest_pat = ctx. find_node_at_offset :: < ast:: RestPat > ( ) ?;
171
291
let parent = rest_pat. syntax ( ) . parent ( ) ?;
172
292
match_ast ! {
173
293
match parent {
174
294
ast:: RecordPatFieldList ( it) => expand_record_rest_pattern( acc, ctx, it. syntax( ) . parent( ) . and_then( ast:: RecordPat :: cast) ?, rest_pat) ,
175
295
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) ,
180
298
_ => None ,
181
299
}
182
300
}
183
301
}
184
302
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
+
185
330
#[ cfg( test) ]
186
331
mod tests {
187
332
use super :: * ;
@@ -351,6 +496,79 @@ fn foo(bar: Bar) {
351
496
)
352
497
}
353
498
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
+
354
572
#[ test]
355
573
fn fill_fields_struct_generated_by_macro ( ) {
356
574
check_assist (
@@ -486,6 +704,8 @@ fn bar(foo: Foo) {
486
704
// This is still possible even though it's meaningless
487
705
cov_mark:: check!( no_missing_fields) ;
488
706
cov_mark:: check!( no_missing_fields_tuple_struct) ;
707
+ cov_mark:: check!( no_missing_fields_tuple) ;
708
+ cov_mark:: check!( no_missing_fields_slice) ;
489
709
check_assist_not_applicable (
490
710
expand_rest_pattern,
491
711
r#"
@@ -523,6 +743,22 @@ struct Bar(Y, Z)
523
743
fn foo(bar: Bar) {
524
744
let Bar(y, ..$0, z) = bar;
525
745
}
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
+ }
526
762
"# ,
527
763
) ;
528
764
}
0 commit comments