1+ use hir:: { PathResolution , StructKind } ;
2+ use ide_db:: syntax_helpers:: suggest_name:: NameGenerator ;
13use syntax:: {
24 ast:: { self , make} ,
35 match_ast, AstNode , ToSmolStr ,
46} ;
57
68use crate :: { AssistContext , AssistId , Assists } ;
79
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
927//
1028// Fills fields by replacing rest pattern in record patterns.
1129//
@@ -24,22 +42,12 @@ use crate::{AssistContext, AssistId, Assists};
2442// let Bar { y, z } = bar;
2543// }
2644// ```
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 < ( ) > {
4351 let missing_fields = ctx. sema . record_pattern_missing_fields ( & record_pat) ;
4452
4553 if missing_fields. is_empty ( ) {
@@ -48,6 +56,11 @@ pub(crate) fn expand_rest_pattern(acc: &mut Assists, ctx: &AssistContext<'_>) ->
4856 }
4957
5058 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+
5164 let new_field_list =
5265 make:: record_pat_field_list ( old_field_list. fields ( ) , None ) . clone_for_update ( ) ;
5366 for ( f, _) in missing_fields. iter ( ) {
@@ -58,16 +71,93 @@ pub(crate) fn expand_rest_pattern(acc: &mut Assists, ctx: &AssistContext<'_>) ->
5871 new_field_list. add_field ( field. clone_for_update ( ) ) ;
5972 }
6073
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 ( ) ) ?;
62132 if old_range. file_id != ctx. file_id ( ) {
63133 return None ;
64134 }
65135
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 ( ) ;
66156 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" ,
69159 target_range,
70- move |builder| builder. replace_ast ( old_field_list , new_field_list ) ,
160+ move |builder| builder. replace_ast ( pat , new_pat ) ,
71161 )
72162}
73163
@@ -165,6 +255,27 @@ struct Bar {
165255fn foo(bar: Bar) {
166256 let Bar { y, z } = bar;
167257}
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+ }
168279"# ,
169280 )
170281 }
@@ -192,6 +303,29 @@ struct Bar {
192303fn foo(bar: Bar) {
193304 let Bar { y, z } = bar;
194305}
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+ }
195329"# ,
196330 )
197331 }
@@ -330,6 +464,7 @@ fn bar(foo: Foo) {
330464 fn not_applicable_when_no_missing_fields ( ) {
331465 // This is still possible even though it's meaningless
332466 cov_mark:: check!( no_missing_fields) ;
467+ cov_mark:: check!( no_missing_fields_tuple_struct) ;
333468 check_assist_not_applicable (
334469 expand_rest_pattern,
335470 r#"
@@ -357,6 +492,16 @@ struct Bar {
357492fn foo(bar: Bar) {
358493 let Bar { y, z, ..$0 } = bar;
359494}
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+ }
360505"# ,
361506 ) ;
362507 }
0 commit comments