1
+ use hir:: { PathResolution , StructKind } ;
2
+ use ide_db:: syntax_helpers:: suggest_name:: NameGenerator ;
1
3
use syntax:: {
2
4
ast:: { self , make} ,
3
5
match_ast, AstNode , ToSmolStr ,
4
6
} ;
5
7
6
8
use crate :: { AssistContext , AssistId , Assists } ;
7
9
8
- // Assist: expand_rest_pattern
10
+ pub ( crate ) fn expand_rest_pattern ( acc : & mut Assists , ctx : & AssistContext < ' _ > ) -> Option < ( ) > {
11
+ let rest_pat = ctx. find_node_at_offset :: < ast:: RestPat > ( ) ?;
12
+ let parent = rest_pat. syntax ( ) . parent ( ) ?;
13
+ match_ast ! {
14
+ match parent {
15
+ ast:: RecordPatFieldList ( it) => expand_record_rest_pattern( acc, ctx, it. syntax( ) . parent( ) . and_then( ast:: RecordPat :: cast) ?, rest_pat) ,
16
+ ast:: TupleStructPat ( it) => expand_tuple_struct_rest_pattern( acc, ctx, it, rest_pat) ,
17
+ // FIXME
18
+ // ast::TuplePat(it) => (),
19
+ // FIXME
20
+ // ast::SlicePat(it) => (),
21
+ _ => return None ,
22
+ }
23
+ }
24
+ }
25
+
26
+ // Assist: expand_record_rest_pattern
9
27
//
10
28
// Fills fields by replacing rest pattern in record patterns.
11
29
//
@@ -24,22 +42,12 @@ use crate::{AssistContext, AssistId, Assists};
24
42
// let Bar { y, z } = bar;
25
43
// }
26
44
// ```
27
- pub ( crate ) fn expand_rest_pattern ( acc : & mut Assists , ctx : & AssistContext < ' _ > ) -> Option < ( ) > {
28
- let rest_pat = ctx. find_node_at_offset :: < ast:: RestPat > ( ) ?;
29
- let parent = rest_pat. syntax ( ) . parent ( ) ?;
30
- let record_pat = match_ast ! {
31
- match parent {
32
- ast:: RecordPatFieldList ( it) => ast:: RecordPat :: cast( it. syntax( ) . parent( ) ?) ?,
33
- // ast::TupleStructPat(it) => (),
34
- // ast::TuplePat(it) => (),
35
- // ast::SlicePat(it) => (),
36
- _ => return None ,
37
- }
38
- } ;
39
-
40
- let ellipsis = record_pat. record_pat_field_list ( ) . and_then ( |r| r. rest_pat ( ) ) ?;
41
- let target_range = ellipsis. syntax ( ) . text_range ( ) ;
42
-
45
+ fn expand_record_rest_pattern (
46
+ acc : & mut Assists ,
47
+ ctx : & AssistContext < ' _ > ,
48
+ record_pat : ast:: RecordPat ,
49
+ rest_pat : ast:: RestPat ,
50
+ ) -> Option < ( ) > {
43
51
let missing_fields = ctx. sema . record_pattern_missing_fields ( & record_pat) ;
44
52
45
53
if missing_fields. is_empty ( ) {
@@ -48,6 +56,11 @@ pub(crate) fn expand_rest_pattern(acc: &mut Assists, ctx: &AssistContext<'_>) ->
48
56
}
49
57
50
58
let old_field_list = record_pat. record_pat_field_list ( ) ?;
59
+ let old_range = ctx. sema . original_range_opt ( old_field_list. syntax ( ) ) ?;
60
+ if old_range. file_id != ctx. file_id ( ) {
61
+ return None ;
62
+ }
63
+
51
64
let new_field_list =
52
65
make:: record_pat_field_list ( old_field_list. fields ( ) , None ) . clone_for_update ( ) ;
53
66
for ( f, _) in missing_fields. iter ( ) {
@@ -58,16 +71,93 @@ pub(crate) fn expand_rest_pattern(acc: &mut Assists, ctx: &AssistContext<'_>) ->
58
71
new_field_list. add_field ( field. clone_for_update ( ) ) ;
59
72
}
60
73
61
- let old_range = ctx. sema . original_range_opt ( old_field_list. syntax ( ) ) ?;
74
+ let target_range = rest_pat. syntax ( ) . text_range ( ) ;
75
+ acc. add (
76
+ AssistId ( "expand_record_rest_pattern" , crate :: AssistKind :: RefactorRewrite ) ,
77
+ "Fill struct fields" ,
78
+ target_range,
79
+ move |builder| builder. replace_ast ( old_field_list, new_field_list) ,
80
+ )
81
+ }
82
+ // Assist: expand_tuple_struct_rest_pattern
83
+ //
84
+ // Fills fields by replacing rest pattern in tuple struct patterns.
85
+ //
86
+ // ```
87
+ // struct Bar(Y, Z);
88
+ //
89
+ // fn foo(bar: Bar) {
90
+ // let Bar(..$0) = bar;
91
+ // }
92
+ // ```
93
+ // ->
94
+ // ```
95
+ // struct Bar(Y, Z);
96
+ //
97
+ // fn foo(bar: Bar) {
98
+ // let Bar(_0, _1) = bar;
99
+ // }
100
+ // ```
101
+ fn expand_tuple_struct_rest_pattern (
102
+ acc : & mut Assists ,
103
+ ctx : & AssistContext < ' _ > ,
104
+ pat : ast:: TupleStructPat ,
105
+ rest_pat : ast:: RestPat ,
106
+ ) -> Option < ( ) > {
107
+ let path = pat. path ( ) ?;
108
+ let fields = match ctx. sema . type_of_pat ( & pat. clone ( ) . into ( ) ) ?. original . as_adt ( ) ? {
109
+ hir:: Adt :: Struct ( s) if s. kind ( ctx. sema . db ) == StructKind :: Tuple => s. fields ( ctx. sema . db ) ,
110
+ hir:: Adt :: Enum ( _) => match ctx. sema . resolve_path ( & path) ? {
111
+ PathResolution :: Def ( hir:: ModuleDef :: Variant ( v) )
112
+ if v. kind ( ctx. sema . db ) == StructKind :: Tuple =>
113
+ {
114
+ v. fields ( ctx. sema . db )
115
+ }
116
+ _ => return None ,
117
+ } ,
118
+ _ => return None ,
119
+ } ;
120
+
121
+ let rest_pat = rest_pat. into ( ) ;
122
+ let mut pats = pat. fields ( ) ;
123
+ let prefix_count = pats. by_ref ( ) . position ( |p| p == rest_pat) ?;
124
+ let suffix_count = pats. count ( ) ;
125
+
126
+ if fields. len ( ) . saturating_sub ( prefix_count) . saturating_sub ( suffix_count) == 0 {
127
+ cov_mark:: hit!( no_missing_fields_tuple_struct) ;
128
+ return None ;
129
+ }
130
+
131
+ let old_range = ctx. sema . original_range_opt ( pat. syntax ( ) ) ?;
62
132
if old_range. file_id != ctx. file_id ( ) {
63
133
return None ;
64
134
}
65
135
136
+ let mut name_gen = NameGenerator :: new_from_scope_locals ( ctx. sema . scope ( pat. syntax ( ) ) ) ;
137
+ let new_pat = make:: tuple_struct_pat (
138
+ path,
139
+ pat. fields ( )
140
+ . take ( prefix_count)
141
+ . chain ( fields[ prefix_count..fields. len ( ) - suffix_count] . iter ( ) . map ( |f| {
142
+ make:: ident_pat (
143
+ false ,
144
+ false ,
145
+ match name_gen. for_type ( & f. ty ( ctx. sema . db ) , ctx. sema . db , ctx. edition ( ) ) {
146
+ Some ( name) => make:: name ( & name) ,
147
+ None => make:: name ( & format ! ( "_{}" , f. index( ) ) ) ,
148
+ } ,
149
+ )
150
+ . into ( )
151
+ } ) )
152
+ . chain ( pat. fields ( ) . skip ( prefix_count + 1 ) ) ,
153
+ ) ;
154
+
155
+ let target_range = rest_pat. syntax ( ) . text_range ( ) ;
66
156
acc. add (
67
- AssistId ( "expand_rest_pattern " , crate :: AssistKind :: RefactorRewrite ) ,
68
- "Fill structure fields" ,
157
+ AssistId ( "expand_tuple_struct_rest_pattern " , crate :: AssistKind :: RefactorRewrite ) ,
158
+ "Fill tuple struct fields" ,
69
159
target_range,
70
- move |builder| builder. replace_ast ( old_field_list , new_field_list ) ,
160
+ move |builder| builder. replace_ast ( pat , new_pat ) ,
71
161
)
72
162
}
73
163
@@ -165,6 +255,27 @@ struct Bar {
165
255
fn foo(bar: Bar) {
166
256
let Bar { y, z } = bar;
167
257
}
258
+ "# ,
259
+ ) ;
260
+ check_assist (
261
+ expand_rest_pattern,
262
+ r#"
263
+ struct Y;
264
+ struct Z;
265
+ struct Bar(Y, Z)
266
+
267
+ fn foo(bar: Bar) {
268
+ let Bar(..$0) = bar;
269
+ }
270
+ "# ,
271
+ r#"
272
+ struct Y;
273
+ struct Z;
274
+ struct Bar(Y, Z)
275
+
276
+ fn foo(bar: Bar) {
277
+ let Bar(y, z) = bar;
278
+ }
168
279
"# ,
169
280
)
170
281
}
@@ -192,6 +303,29 @@ struct Bar {
192
303
fn foo(bar: Bar) {
193
304
let Bar { y, z } = bar;
194
305
}
306
+ "# ,
307
+ ) ;
308
+ check_assist (
309
+ expand_rest_pattern,
310
+ r#"
311
+ struct X;
312
+ struct Y;
313
+ struct Z;
314
+ struct Bar(X, Y, Z)
315
+
316
+ fn foo(bar: Bar) {
317
+ let Bar(x, ..$0, z) = bar;
318
+ }
319
+ "# ,
320
+ r#"
321
+ struct X;
322
+ struct Y;
323
+ struct Z;
324
+ struct Bar(X, Y, Z)
325
+
326
+ fn foo(bar: Bar) {
327
+ let Bar(x, y, z) = bar;
328
+ }
195
329
"# ,
196
330
)
197
331
}
@@ -330,6 +464,7 @@ fn bar(foo: Foo) {
330
464
fn not_applicable_when_no_missing_fields ( ) {
331
465
// This is still possible even though it's meaningless
332
466
cov_mark:: check!( no_missing_fields) ;
467
+ cov_mark:: check!( no_missing_fields_tuple_struct) ;
333
468
check_assist_not_applicable (
334
469
expand_rest_pattern,
335
470
r#"
@@ -357,6 +492,16 @@ struct Bar {
357
492
fn foo(bar: Bar) {
358
493
let Bar { y, z, ..$0 } = bar;
359
494
}
495
+ "# ,
496
+ ) ;
497
+ check_assist_not_applicable (
498
+ expand_rest_pattern,
499
+ r#"
500
+ struct Bar(Y, Z)
501
+
502
+ fn foo(bar: Bar) {
503
+ let Bar(y, ..$0, z) = bar;
504
+ }
360
505
"# ,
361
506
) ;
362
507
}
0 commit comments