@@ -114,9 +114,7 @@ fn expand_tuple_struct_rest_pattern(
114
114
} ;
115
115
116
116
let rest_pat = rest_pat. into ( ) ;
117
- let mut pats = pat. fields ( ) ;
118
- let prefix_count = pats. by_ref ( ) . position ( |p| p == rest_pat) ?;
119
- let suffix_count = pats. count ( ) ;
117
+ let ( prefix_count, suffix_count) = calculate_counts ( & rest_pat, pat. fields ( ) ) ?;
120
118
121
119
if fields. len ( ) . saturating_sub ( prefix_count) . saturating_sub ( suffix_count) == 0 {
122
120
cov_mark:: hit!( no_missing_fields_tuple_struct) ;
@@ -142,16 +140,7 @@ fn expand_tuple_struct_rest_pattern(
142
140
pat. fields ( )
143
141
. take ( prefix_count)
144
142
. chain ( fields[ prefix_count..fields. len ( ) - suffix_count] . iter ( ) . map ( |f| {
145
- make. ident_pat (
146
- false ,
147
- false ,
148
- match name_gen. for_type ( & f. ty ( ctx. sema . db ) , ctx. sema . db , ctx. edition ( ) )
149
- {
150
- Some ( name) => make. name ( & name) ,
151
- None => make. name ( & format ! ( "_{}" , f. index( ) ) ) ,
152
- } ,
153
- )
154
- . into ( )
143
+ gen_unnamed_pat ( ctx, & make, & mut name_gen, & f. ty ( ctx. db ( ) ) , f. index ( ) )
155
144
} ) )
156
145
. chain ( pat. fields ( ) . skip ( prefix_count + 1 ) ) ,
157
146
) ;
@@ -164,22 +153,175 @@ fn expand_tuple_struct_rest_pattern(
164
153
)
165
154
}
166
155
156
+ // Assist: expand_tuple_rest_pattern
157
+ //
158
+ // Fills fields by replacing rest pattern in tuple patterns.
159
+ //
160
+ // ```
161
+ // fn foo(bar: (char, i32, i32)) {
162
+ // let (ch, ..$0) = bar;
163
+ // }
164
+ // ```
165
+ // ->
166
+ // ```
167
+ // fn foo(bar: (char, i32, i32)) {
168
+ // let (ch, _1, _2) = bar;
169
+ // }
170
+ // ```
171
+ fn expand_tuple_rest_pattern (
172
+ acc : & mut Assists ,
173
+ ctx : & AssistContext < ' _ > ,
174
+ pat : ast:: TuplePat ,
175
+ rest_pat : ast:: RestPat ,
176
+ ) -> Option < ( ) > {
177
+ let fields = ctx. sema . type_of_pat ( & pat. clone ( ) . into ( ) ) ?. original . tuple_fields ( ctx. db ( ) ) ;
178
+ let len = fields. len ( ) ;
179
+
180
+ let rest_pat = rest_pat. into ( ) ;
181
+ let ( prefix_count, suffix_count) = calculate_counts ( & rest_pat, pat. fields ( ) ) ?;
182
+
183
+ if len. saturating_sub ( prefix_count) . saturating_sub ( suffix_count) == 0 {
184
+ cov_mark:: hit!( no_missing_fields_tuple) ;
185
+ return None ;
186
+ }
187
+
188
+ let old_range = ctx. sema . original_range_opt ( pat. syntax ( ) ) ?;
189
+ if old_range. file_id != ctx. file_id ( ) {
190
+ return None ;
191
+ }
192
+
193
+ acc. add (
194
+ AssistId :: refactor_rewrite ( "expand_tuple_rest_pattern" ) ,
195
+ "Fill tuple fields" ,
196
+ rest_pat. syntax ( ) . text_range ( ) ,
197
+ |builder| {
198
+ let make = SyntaxFactory :: with_mappings ( ) ;
199
+ let mut editor = builder. make_editor ( rest_pat. syntax ( ) ) ;
200
+
201
+ let mut name_gen = NameGenerator :: new_from_scope_locals ( ctx. sema . scope ( pat. syntax ( ) ) ) ;
202
+ let new_pat = make. tuple_pat (
203
+ pat. fields ( )
204
+ . take ( prefix_count)
205
+ . chain ( fields[ prefix_count..len - suffix_count] . iter ( ) . enumerate ( ) . map (
206
+ |( index, ty) | {
207
+ gen_unnamed_pat ( ctx, & make, & mut name_gen, ty, prefix_count + index)
208
+ } ,
209
+ ) )
210
+ . chain ( pat. fields ( ) . skip ( prefix_count + 1 ) ) ,
211
+ ) ;
212
+
213
+ editor. replace ( pat. syntax ( ) , new_pat. syntax ( ) ) ;
214
+
215
+ editor. add_mappings ( make. finish_with_mappings ( ) ) ;
216
+ builder. add_file_edits ( ctx. vfs_file_id ( ) , editor) ;
217
+ } ,
218
+ )
219
+ }
220
+
221
+ // Assist: expand_slice_rest_pattern
222
+ //
223
+ // Fills fields by replacing rest pattern in slice patterns.
224
+ //
225
+ // ```
226
+ // fn foo(bar: [i32; 3]) {
227
+ // let [first, ..$0] = bar;
228
+ // }
229
+ // ```
230
+ // ->
231
+ // ```
232
+ // fn foo(bar: [i32; 3]) {
233
+ // let [first, _1, _2] = bar;
234
+ // }
235
+ // ```
236
+ fn expand_slice_rest_pattern (
237
+ acc : & mut Assists ,
238
+ ctx : & AssistContext < ' _ > ,
239
+ pat : ast:: SlicePat ,
240
+ rest_pat : ast:: RestPat ,
241
+ ) -> Option < ( ) > {
242
+ let ( ty, len) = ctx. sema . type_of_pat ( & pat. clone ( ) . into ( ) ) ?. original . as_array ( ctx. db ( ) ) ?;
243
+
244
+ let rest_pat = rest_pat. into ( ) ;
245
+ let ( prefix_count, suffix_count) = calculate_counts ( & rest_pat, pat. pats ( ) ) ?;
246
+
247
+ if len. saturating_sub ( prefix_count) . saturating_sub ( suffix_count) == 0 {
248
+ cov_mark:: hit!( no_missing_fields_slice) ;
249
+ return None ;
250
+ }
251
+
252
+ let old_range = ctx. sema . original_range_opt ( pat. syntax ( ) ) ?;
253
+ if old_range. file_id != ctx. file_id ( ) {
254
+ return None ;
255
+ }
256
+
257
+ acc. add (
258
+ AssistId :: refactor_rewrite ( "expand_slice_rest_pattern" ) ,
259
+ "Fill slice fields" ,
260
+ rest_pat. syntax ( ) . text_range ( ) ,
261
+ |builder| {
262
+ let make = SyntaxFactory :: with_mappings ( ) ;
263
+ let mut editor = builder. make_editor ( rest_pat. syntax ( ) ) ;
264
+
265
+ let mut name_gen = NameGenerator :: new_from_scope_locals ( ctx. sema . scope ( pat. syntax ( ) ) ) ;
266
+ let new_pat = make. slice_pat (
267
+ pat. pats ( )
268
+ . take ( prefix_count)
269
+ . chain (
270
+ ( prefix_count..len - suffix_count)
271
+ . map ( |index| gen_unnamed_pat ( ctx, & make, & mut name_gen, & ty, index) ) ,
272
+ )
273
+ . chain ( pat. pats ( ) . skip ( prefix_count + 1 ) ) ,
274
+ ) ;
275
+
276
+ editor. replace ( pat. syntax ( ) , new_pat. syntax ( ) ) ;
277
+
278
+ editor. add_mappings ( make. finish_with_mappings ( ) ) ;
279
+ builder. add_file_edits ( ctx. vfs_file_id ( ) , editor) ;
280
+ } ,
281
+ )
282
+ }
283
+
167
284
pub ( crate ) fn expand_rest_pattern ( acc : & mut Assists , ctx : & AssistContext < ' _ > ) -> Option < ( ) > {
168
285
let rest_pat = ctx. find_node_at_offset :: < ast:: RestPat > ( ) ?;
169
286
let parent = rest_pat. syntax ( ) . parent ( ) ?;
170
287
match_ast ! {
171
288
match parent {
172
289
ast:: RecordPatFieldList ( it) => expand_record_rest_pattern( acc, ctx, it. syntax( ) . parent( ) . and_then( ast:: RecordPat :: cast) ?, rest_pat) ,
173
290
ast:: TupleStructPat ( it) => expand_tuple_struct_rest_pattern( acc, ctx, it, rest_pat) ,
174
- // FIXME
175
- // ast::TuplePat(it) => (),
176
- // FIXME
177
- // ast::SlicePat(it) => (),
291
+ ast:: TuplePat ( it) => expand_tuple_rest_pattern( acc, ctx, it, rest_pat) ,
292
+ ast:: SlicePat ( it) => expand_slice_rest_pattern( acc, ctx, it, rest_pat) ,
178
293
_ => None ,
179
294
}
180
295
}
181
296
}
182
297
298
+ fn gen_unnamed_pat (
299
+ ctx : & AssistContext < ' _ > ,
300
+ make : & SyntaxFactory ,
301
+ name_gen : & mut NameGenerator ,
302
+ ty : & hir:: Type < ' _ > ,
303
+ index : usize ,
304
+ ) -> ast:: Pat {
305
+ make. ident_pat (
306
+ false ,
307
+ false ,
308
+ match name_gen. for_type ( & ty, ctx. sema . db , ctx. edition ( ) ) {
309
+ Some ( name) => make. name ( & name) ,
310
+ None => make. name ( & format ! ( "_{index}" ) ) ,
311
+ } ,
312
+ )
313
+ . into( )
314
+ }
315
+
316
+ fn calculate_counts (
317
+ rest_pat : & ast:: Pat ,
318
+ mut pats : ast:: AstChildren < ast:: Pat > ,
319
+ ) -> Option < ( usize , usize ) > {
320
+ let prefix_count = pats. by_ref ( ) . position ( |p| p == * rest_pat) ?;
321
+ let suffix_count = pats. count ( ) ;
322
+ Some ( ( prefix_count, suffix_count) )
323
+ }
324
+
183
325
#[ cfg( test) ]
184
326
mod tests {
185
327
use super :: * ;
@@ -349,6 +491,79 @@ fn foo(bar: Bar) {
349
491
)
350
492
}
351
493
494
+ #[ test]
495
+ fn fill_tuple_with_fields ( ) {
496
+ check_assist (
497
+ expand_rest_pattern,
498
+ r#"
499
+ fn foo(bar: (char, i32, i32)) {
500
+ let (ch, ..$0) = bar;
501
+ }
502
+ "# ,
503
+
CDAC
r#"
504
+ fn foo(bar: (char, i32, i32)) {
505
+ let (ch, _1, _2) = bar;
506
+ }
507
+ "# ,
508
+ ) ;
509
+ check_assist (
510
+ expand_rest_pattern,
511
+ r#"
512
+ fn foo(bar: (char, i32, i32)) {
513
+ let (ch, ..$0, end) = bar;
514
+ }
515
+ "# ,
516
+ r#"
517
+ fn foo(bar: (char, i32, i32)) {
518
+ let (ch, _1, end) = bar;
519
+ }
520
+ "# ,
521
+ ) ;
522
+ }
523
+
524
+ #[ test]
525
+ fn fill_array_with_fields ( ) {
526
+ check_assist (
527
+ expand_rest_pattern,
528
+ r#"
529
+ fn foo(bar: [i32; 4]) {
530
+ let [first, ..$0] = bar;
531
+ }
532
+ "# ,
533
+ r#"
534
+ fn foo(bar: [i32; 4]) {
535
+ let [first, _1, _2, _3] = bar;
536
+ }
537
+ "# ,
538
+ ) ;
539
+ check_assist (
540
+ expand_rest_pattern,
541
+ r#"
542
+ fn foo(bar: [i32; 4]) {
543
+ let [first, second, ..$0] = bar;
544
+ }
545
+ "# ,
546
+ r#"
547
+ fn foo(bar: [i32; 4]) {
548
+ let [first, second, _2, _3] = bar;
549
+ }
550
+ "# ,
551
+ ) ;
552
+ check_assist (
553
+ expand_rest_pattern,
554
+ r#"
555
+ fn foo(bar: [i32; 4]) {
556
+ let [first, second, ..$0, end] = bar;
557
+ }
558
+ "# ,
559
+ r#"
560
+ fn foo(bar: [i32; 4]) {
561
+ let [first, second, _2, end] = bar;
562
+ }
563
+ "# ,
564
+ ) ;
565
+ }
566
+
352
567
#[ test]
353
568
fn fill_fields_struct_generated_by_macro ( ) {
354
569
check_assist (
@@ -484,6 +699,8 @@ fn bar(foo: Foo) {
484
699
// This is still possible even though it's meaningless
485
700
cov_mark:: check!( no_missing_fields) ;
486
701
cov_mark:: check!( no_missing_fields_tuple_struct) ;
702
+ cov_mark:: check!( no_missing_fields_tuple) ;
703
+ cov_mark:: check!( no_missing_fields_slice) ;
487
704
check_assist_not_applicable (
488
705
expand_rest_pattern,
489
706
r#"
@@ -521,6 +738,22 @@ struct Bar(Y, Z)
521
738
fn foo(bar: Bar) {
522
739
let Bar(y, ..$0, z) = bar;
523
740
}
741
+ "# ,
742
+ ) ;
743
+ check_assist_not_applicable (
744
+ expand_rest_pattern,
745
+ r#"
746
+ fn foo(bar: (i32, i32)) {
747
+ let (y, ..$0, z) = bar;
748
+ }
749
+ "# ,
750
+ ) ;
751
+ check_assist_not_applicable (
752
+ expand_rest_pattern,
753
+ r#"
754
+ fn foo(bar: [i32; 2]) {
755
+ let [y, ..$0, z] = bar;
756
+ }
524
757
"# ,
525
758
) ;
526
759
}
0 commit comments