@@ -10,7 +10,10 @@ use itertools::Itertools;
10
10
use syntax:: { ast, ted, AstNode , SmolStr } ;
11
11
use text_edit:: TextRange ;
12
12
13
- use crate :: assist_context:: { AssistContext , Assists , SourceChangeBuilder } ;
13
+ use crate :: {
14
+ assist_context:: { AssistContext , Assists , SourceChangeBuilder } ,
15
+ utils:: ref_field_expr:: determine_ref_and_parens,
16
+ } ;
14
17
15
18
// Assist: destructure_struct_binding
16
19
//
@@ -58,11 +61,12 @@ fn destructure_struct_binding_impl(
58
61
builder : & mut SourceChangeBuilder ,
59
62
data : & StructEditData ,
60
63
) {
61
- let assignment_edit = build_assignment_edit ( ctx, builder, data) ;
62
- let usage_edits = build_usage_edits ( ctx, builder, data, & assignment_edit. field_name_map ) ;
64
+ let field_names = generate_field_names ( ctx, data) ;
65
+ let assignment_edit = build_assignment_edit ( ctx, builder, data, & field_names) ;
66
+ let usage_edits = build_usage_edits ( ctx, builder, data, & field_names. into_iter ( ) . collect ( ) ) ;
63
67
64
68
assignment_edit. apply ( ) ;
65
- for edit in usage_edits. unwrap_or_default ( ) {
69
+ for edit in usage_edits. into_iter ( ) . flatten ( ) {
66
70
edit. apply ( builder) ;
67
71
}
68
72
}
@@ -74,14 +78,16 @@ struct StructEditData {
74
78
visible_fields : Vec < hir:: Field > ,
75
79
usages : Option < UsageSearchResult > ,
76
80
names_in_scope : FxHashSet < SmolStr > , // TODO currently always empty
77
- add_rest : bool ,
81
+ has_private_members : bool ,
78
82
is_nested : bool ,
83
+ is_ref : bool ,
79
84
}
80
85
81
86
fn collect_data ( ident_pat : ast:: IdentPat , ctx : & AssistContext < ' _ > ) -> Option < StructEditData > {
82
- let ty = ctx. sema . type_of_binding_in_pat ( & ident_pat) ?. strip_references ( ) . as_adt ( ) ?;
87
+ let ty = ctx. sema . type_of_binding_in_pat ( & ident_pat) ?;
88
+ let is_ref = ty. is_reference ( ) ;
83
89
84
- let hir:: Adt :: Struct ( struct_type) = ty else { return None } ;
90
+ let hir:: Adt :: Struct ( struct_type) = ty. strip_references ( ) . as_adt ( ) ? else { return None } ;
85
91
86
92
let module = ctx. sema . scope ( ident_pat. syntax ( ) ) ?. module ( ) ;
87
93
let struct_def = hir:: ModuleDef :: from ( struct_type) ;
@@ -97,8 +103,9 @@ fn collect_data(ident_pat: ast::IdentPat, ctx: &AssistContext<'_>) -> Option<Str
97
103
let visible_fields =
98
104
fields. into_iter ( ) . filter ( |field| field. is_visible_from ( ctx. db ( ) , module) ) . collect_vec ( ) ;
99
105
100
- let add_rest = ( is_non_exhaustive && is_foreign_crate) || visible_fields. len ( ) < n_fields;
101
- if !matches ! ( kind, hir:: StructKind :: Record ) && add_rest {
106
+ let has_private_members =
107
+ ( is_non_exhaustive && is_foreign_crate) || visible_fields. len ( ) < n_fields;
108
+ if !matches ! ( kind, hir:: StructKind :: Record ) && has_private_members {
102
109
return None ;
103
110
}
104
111
@@ -123,26 +130,26 @@ fn collect_data(ident_pat: ast::IdentPat, ctx: &AssistContext<'_>) -> Option<Str
123
130
kind,
124
131
struct_def_path,
125
132
usages,
126
- add_rest ,
133
+ has_private_members ,
127
134
visible_fields,
128
135
names_in_scope : FxHashSet :: default ( ) , // TODO
129
136
is_nested,
137
+ is_ref,
130
138
} )
131
139
}
132
140
133
141
fn build_assignment_edit (
134
- ctx : & AssistContext < ' _ > ,
142
+ _ctx : & AssistContext < ' _ > ,
135
143
builder : & mut SourceChangeBuilder ,
136
144
data : & StructEditData ,
145
+ field_names : & [ ( SmolStr , SmolStr ) ] ,
137
146
) -> AssignmentEdit {
138
147
let ident_pat = builder. make_mut ( data. ident_pat . clone ( ) ) ;
139
148
140
149
let struct_path = mod_path_to_ast ( & data. struct_def_path ) ;
141
150
let is_ref = ident_pat. ref_token ( ) . is_some ( ) ;
142
151
let is_mut = ident_pat. mut_token ( ) . is_some ( ) ;
143
152
144
- let field_names = generate_field_names ( ctx, data) ;
145
-
146
153
let new_pat = match data. kind {
147
154
hir:: StructKind :: Tuple => {
148
155
let ident_pats = field_names. iter ( ) . map ( |( _, new_name) | {
@@ -169,7 +176,7 @@ fn build_assignment_edit(
169
176
170
177
let field_list = ast:: make:: record_pat_field_list (
171
178
fields,
172
- data. add_rest . then_some ( ast:: make:: rest_pat ( ) ) ,
179
+ data. has_private_members . then_some ( ast:: make:: rest_pat ( ) ) ,
173
180
) ;
174
181
ast:: Pat :: RecordPat ( ast:: make:: record_pat_with_fields ( struct_path, field_list) )
175
182
}
@@ -185,7 +192,7 @@ fn build_assignment_edit(
185
192
NewPat :: Pat ( new_pat. clone_for_update ( ) )
186
193
} ;
187
194
188
- AssignmentEdit { ident_pat, new_pat, field_name_map : field_names . into_iter ( ) . collect ( ) }
195
+ AssignmentEdit { ident_pat, new_pat }
189
196
}
190
197
191
198
fn generate_field_names ( ctx : & AssistContext < ' _ > , data : & StructEditData ) -> Vec < ( SmolStr , SmolStr ) > {
@@ -195,17 +202,17 @@ fn generate_field_names(ctx: &AssistContext<'_>, data: &StructEditData) -> Vec<(
195
202
. iter ( )
196
203
. enumerate ( )
197
204
. map ( |( index, _) | {
198
- let new_name = format ! ( "_{}" , index) ;
199
- ( index. to_string ( ) . into ( ) , new_name. into ( ) )
205
+ let new_name = new_field_name ( ( format ! ( "_{}" , index) ) . into ( ) , & data . names_in_scope ) ;
206
+ ( index. to_string ( ) . into ( ) , new_name)
200
207
} )
201
208
. collect ( ) ,
202
209
hir:: StructKind :: Record => data
203
210
. visible_fields
204
211
. iter ( )
205
212
. map ( |field| {
206
213
let field_name = field. name ( ctx. db ( ) ) . to_smol_str ( ) ;
207
- let new_field_name = new_field_name ( field_name. clone ( ) , & data. names_in_scope ) ;
208
- ( field_name, new_field_name )
214
+ let new_name = new_field_name ( field_name. clone ( ) , & data. names_in_scope ) ;
215
+ ( field_name, new_name )
209
216
} )
210
217
. collect ( ) ,
211
218
hir:: StructKind :: Unit => Vec :: new ( ) ,
@@ -225,7 +232,6 @@ fn new_field_name(base_name: SmolStr, names_in_scope: &FxHashSet<SmolStr>) -> Sm
225
232
struct AssignmentEdit {
226
233
ident_pat : ast:: IdentPat ,
227
234
new_pat : NewPat ,
228
- field_name_map : FxHashMap < SmolStr , SmolStr > ,
229
235
}
230
236
231
237
enum NewPat {
@@ -260,26 +266,37 @@ fn build_usage_edits(
260
266
. iter ( )
261
267
. find_map ( |( file_id, refs) | ( * file_id == ctx. file_id ( ) ) . then_some ( refs) ) ?
262
268
. iter ( )
263
- . filter_map ( |r| build_usage_edit ( builder, r, field_names) )
269
+ . filter_map ( |r| build_usage_edit ( ctx , builder, data , r, field_names) )
264
270
. collect_vec ( ) ;
265
271
266
272
Some ( edits)
267
273
}
268
274
269
275
fn build_usage_edit (
276
+ ctx : & AssistContext < ' _ > ,
270
277
builder : & mut SourceChangeBuilder ,
278
+ data : & StructEditData ,
271
279
usage : & FileReference ,
272
280
field_names : & FxHashMap < SmolStr , SmolStr > ,
273
281
) -> Option < StructUsageEdit > {
274
282
match usage. name . syntax ( ) . ancestors ( ) . find_map ( ast:: FieldExpr :: cast) {
275
283
Some ( field_expr) => Some ( {
276
284
let field_name: SmolStr = field_expr. name_ref ( ) ?. to_string ( ) . into ( ) ;
277
285
let new_field_name = field_names. get ( & field_name) ?;
278
-
279
- let expr = builder. make_mut ( field_expr) . into ( ) ;
280
- let new_expr =
281
- ast:: make:: expr_path ( ast:: make:: ext:: ident_path ( new_field_name) ) . clone_for_update ( ) ;
282
- StructUsageEdit :: IndexField ( expr, new_expr)
286
+ let new_expr = ast:: make:: expr_path ( ast:: make:: ext:: ident_path ( new_field_name) ) ;
287
+
288
+ if data. is_ref {
289
+ let ( replace_expr, ref_data) = determine_ref_and_parens ( ctx, & field_expr) ;
290
+ StructUsageEdit :: IndexField (
291
+ builder. make_mut ( replace_expr) ,
292
+ ref_data. wrap_expr ( new_expr) . clone_for_update ( ) ,
293
+ )
294
+ } else {
295
+ StructUsageEdit :: IndexField (
296
+ builder. make_mut ( field_expr) . into ( ) ,
297
+ new_expr. clone_for_update ( ) ,
298
+ )
299
+ }
283
300
} ) ,
284
301
None => Some ( StructUsageEdit :: Path ( usage. range ) ) ,
285
302
}
@@ -602,4 +619,33 @@ mod tests {
602
619
"# ,
603
620
)
604
621
}
622
+
623
+ #[ test]
624
+ fn mut_ref ( ) {
625
+ check_assist (
626
+ destructure_struct_binding,
627
+ r#"
628
+ struct Foo {
629
+ bar: i32,
630
+ baz: i32
631
+ }
632
+
633
+ fn main() {
634
+ let $0foo = &mut Foo { bar: 1, baz: 2 };
635
+ foo.bar = 5;
636
+ }
637
+ "# ,
638
+ r#"
639
+ struct Foo {
640
+ bar: i32,
641
+ baz: i32
642
+ }
643
+
644
+ fn main() {
645
+ let Foo { bar, baz } = &mut Foo { bar: 1, baz: 2 };
646
+ *bar = 5;
647
+ }
648
+ "# ,
649
+ )
650
+ }
605
651
}
0 commit comments