Skip to content

Commit d8c5a61

Browse files
committed
Auto merge of #16651 - dfireBird:new_assist_fill_fields, r=Veykril
Add assist for filling fields by replacing ellipsis in record syntax I'm not sure if the tests cover the most cases, I'll add more if suggested.
2 parents ea82cc4 + 8fa903a commit d8c5a61

File tree

3 files changed

+377
-0
lines changed

3 files changed

+377
-0
lines changed
Lines changed: 354 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,354 @@
1+
use syntax::{
2+
ast::{self, make},
3+
AstNode,
4+
};
5+
6+
use crate::{AssistContext, AssistId, Assists};
7+
8+
// Assist: fill_record_pattern_fields
9+
//
10+
// Fills fields by replacing rest pattern in record patterns.
11+
//
12+
// ```
13+
// struct Bar { y: Y, z: Z }
14+
//
15+
// fn foo(bar: Bar) {
16+
// let Bar { ..$0 } = bar;
17+
// }
18+
// ```
19+
// ->
20+
// ```
21+
// struct Bar { y: Y, z: Z }
22+
//
23+
// fn foo(bar: Bar) {
24+
// let Bar { y, z } = bar;
25+
// }
26+
// ```
27+
pub(crate) fn fill_record_pattern_fields(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
28+
let record_pat = ctx.find_node_at_offset::<ast::RecordPat>()?;
29+
30+
let ellipsis = record_pat.record_pat_field_list().and_then(|r| r.rest_pat())?;
31+
if !ellipsis.syntax().text_range().contains_inclusive(ctx.offset()) {
32+
return None;
33+
}
34+
35+
let target_range = ellipsis.syntax().text_range();
36+
37+
let missing_fields = ctx.sema.record_pattern_missing_fields(&record_pat);
38+
39+
if missing_fields.is_empty() {
40+
cov_mark::hit!(no_missing_fields);
41+
return None;
42+
}
43+
44+
let old_field_list = record_pat.record_pat_field_list()?;
45+
let new_field_list = make::record_pat_field_list(old_field_list.fields()).clone_for_update();
46+
for (f, _) in missing_fields.iter() {
47+
let field =
48+
make::record_pat_field_shorthand(make::name_ref(&f.name(ctx.sema.db).to_smol_str()));
49+
new_field_list.add_field(field.clone_for_update());
50+
}
51+
52+
let old_range = ctx.sema.original_range_opt(old_field_list.syntax())?;
53+
if old_range.file_id != ctx.file_id() {
54+
return None;
55+
}
56+
57+
acc.add(
58+
AssistId("fill_record_pattern_fields", crate::AssistKind::RefactorRewrite),
59+
"Fill structure fields",
60+
target_range,
61+
move |builder| builder.replace_ast(old_field_list, new_field_list),
62+
)
63+
}
64+
65+
#[cfg(test)]
66+
mod tests {
67+
use super::*;
68+
use crate::tests::{check_assist, check_assist_not_applicable};
69+
70+
#[test]
71+
fn fill_fields_enum_with_only_ellipsis() {
72+
check_assist(
73+
fill_record_pattern_fields,
74+
r#"
75+
enum Foo {
76+
A(X),
77+
B{y: Y, z: Z}
78+
}
79+
80+
fn bar(foo: Foo) {
81+
match foo {
82+
Foo::A(_) => false,
83+
Foo::B{ ..$0 } => true,
84+
};
85+
}
86+
"#,
87+
r#"
88+
enum Foo {
89+
A(X),
90+
B{y: Y, z: Z}
91+
}
92+
93+
fn bar(foo: Foo) {
94+
match foo {
95+
Foo::A(_) => false,
96+
Foo::B{ y, z } => true,
97+
};
98+
}
99+
"#,
100+
)
101+
}
102+
103+
#[test]
104+
fn fill_fields_enum_with_fields() {
105+
check_assist(
106+
fill_record_pattern_fields,
107+
r#"
108+
enum Foo {
109+
A(X),
110+
B{y: Y, z: Z}
111+
}
112+
113+
fn bar(foo: Foo) {
114+
match foo {
115+
Foo::A(_) => false,
116+
Foo::B{ y, ..$0 } => true,
117+
};
118+
}
119+
"#,
120+
r#"
121+
enum Foo {
122+
A(X),
123+
B{y: Y, z: Z}
124+
}
125+
126+
fn bar(foo: Foo) {
127+
match foo {
128+
Foo::A(_) => false,
129+
Foo::B{ y, z } => true,
130+
};
131+
}
132+
"#,
133+
)
134+
}
135+
136+
#[test]
137+
fn fill_fields_struct_with_only_ellipsis() {
138+
check_assist(
139+
fill_record_pattern_fields,
140+
r#"
141+
struct Bar {
142+
y: Y,
143+
z: Z,
144+
}
145+
146+
fn foo(bar: Bar) {
147+
let Bar { ..$0 } = bar;
148+
}
149+
"#,
150+
r#"
151+
struct Bar {
152+
y: Y,
153+
z: Z,
154+
}
155+
156+
fn foo(bar: Bar) {
157+
let Bar { y, z } = bar;
158+
}
159+
"#,
160+
)
161+
}
162+
163+
#[test]
164+
fn fill_fields_struct_with_fields() {
165+
check_assist(
166+
fill_record_pattern_fields,
167+
r#"
168+
struct Bar {
169+
y: Y,
170+
z: Z,
171+
}
172+
173+
fn foo(bar: Bar) {
174+
let Bar { y, ..$0 } = bar;
175+
}
176+
"#,
177+
r#"
178+
struct Bar {
179+
y: Y,
180+
z: Z,
181+
}
182+
183+
fn foo(bar: Bar) {
184+
let Bar { y, z } = bar;
185+
}
186+
"#,
187+
)
188+
}
189+
190+
#[test]
191+
fn fill_fields_struct_generated_by_macro() {
192+
check_assist(
193+
fill_record_pattern_fields,
194+
r#"
195+
macro_rules! position {
196+
($t: ty) => {
197+
struct Pos {x: $t, y: $t}
198+
};
199+
}
200+
201+
position!(usize);
202+
203+
fn macro_call(pos: Pos) {
204+
let Pos { ..$0 } = pos;
205+
}
206+
"#,
207+
r#"
208+
macro_rules! position {
209+
($t: ty) => {
210+
struct Pos {x: $t, y: $t}
211+
};
212+
}
213+
214+
position!(usize);
215+
216+
fn macro_call(pos: Pos) {
217+
let Pos { x, y } = pos;
218+
}
219+
"#,
220+
);
221+
}
222+
223+
#[test]
224+
fn fill_fields_enum_generated_by_macro() {
225+
check_assist(
226+
fill_record_pattern_fields,
227+
r#"
228+
macro_rules! enum_gen {
229+
($t: ty) => {
230+
enum Foo {
231+
A($t),
232+
B{x: $t, y: $t},
233+
}
234+
};
235+
}
236+
237+
enum_gen!(usize);
238+
239+
fn macro_call(foo: Foo) {
240+
match foo {
241+
Foo::A(_) => false,
242+
Foo::B{ ..$0 } => true,
243+
}
244+
}
245+
"#,
246+
r#"
247+
macro_rules! enum_gen {
248+
($t: ty) => {
249+
enum Foo {
250+
A($t),
251+
B{x: $t, y: $t},
252+
}
253+
};
254+
}
255+
256+
enum_gen!(usize);
257+
258+
fn macro_call(foo: Foo) {
259+
match foo {
260+
Foo::A(_) => false,
261+
Foo::B{ x, y } => true,
262+
}
263+
}
264+
"#,
265+
);
266+
}
267+
268+
#[test]
269+
fn not_applicable_when_not_in_ellipsis() {
270+
check_assist_not_applicable(
271+
fill_record_pattern_fields,
272+
r#"
273+
enum Foo {
274+
A(X),
275+
B{y: Y, z: Z}
276+
}
277+
278+
fn bar(foo: Foo) {
279+
match foo {
280+
Foo::A(_) => false,
281+
Foo::B{..}$0 => true,
282+
};
283+
}
284+
"#,
285+
);
286+
check_assist_not_applicable(
287+
fill_record_pattern_fields,
288+
r#"
289+
enum Foo {
290+
A(X),
291+
B{y: Y, z: Z}
292+
}
293+
294+
fn bar(foo: Foo) {
295+
match foo {
296+
Foo::A(_) => false,
297+
Foo::B$0{..} => true,
298+
};
299+
}
300+
"#,
301+
);
302+
check_assist_not_applicable(
303+
fill_record_pattern_fields,
304+
r#"
305+
enum Foo {
306+
A(X),
307+
B{y: Y, z: Z}
308+
}
309+
310+
fn bar(foo: Foo) {
311+
match foo {
312+
Foo::A(_) => false,
313+
Foo::$0B{..} => true,
314+
};
315+
}
316+
"#,
317+
);
318+
}
319+
320+
#[test]
321+
fn not_applicable_when_no_missing_fields() {
322+
// This is still possible even though it's meaningless
323+
cov_mark::check!(no_missing_fields);
324+
check_assist_not_applicable(
325+
fill_record_pattern_fields,
326+
r#"
327+
enum Foo {
328+
A(X),
329+
B{y: Y, z: Z}
330+
}
331+
332+
fn bar(foo: Foo) {
333+
match foo {
334+
Foo::A(_) => false,
335+
Foo::B{y, z, ..$0} => true,
336+
};
337+
}
338+
"#,
339+
);
340+
check_assist_not_applicable(
341+
fill_record_pattern_fields,
342+
r#"
343+
struct Bar {
344+
y: Y,
345+
z: Z,
346+
}
347+
348+
fn foo(bar: Bar) {
349+
let Bar { y, z, ..$0 } = bar;
350+
}
351+
"#,
352+
);
353+
}
354+
}

crates/ide-assists/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ mod handlers {
137137
mod extract_struct_from_enum_variant;
138138
mod extract_type_alias;
139139
mod extract_variable;
140+
mod fill_record_pattern_fields;
140141
mod fix_visibility;
141142
mod flip_binexpr;
142143
mod flip_comma;
@@ -254,6 +255,7 @@ mod handlers {
254255
extract_expressions_from_format_string::extract_expressions_from_format_string,
255256
extract_struct_from_enum_variant::extract_struct_from_enum_variant,
256257
extract_type_alias::extract_type_alias,
258+
fill_record_pattern_fields::fill_record_pattern_fields,
257259
fix_visibility::fix_visibility,
258260
flip_binexpr::flip_binexpr,
259261
flip_comma::flip_comma,

crates/ide-assists/src/tests/generated.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -909,6 +909,27 @@ fn main() {
909909
)
910910
}
911911

912+
#[test]
913+
fn doctest_fill_record_pattern_fields() {
914+
check_doc_test(
915+
"fill_record_pattern_fields",
916+
r#####"
917+
struct Bar { y: Y, z: Z }
918+
919+
fn foo(bar: Bar) {
920+
let Bar { ..$0 } = bar;
921+
}
922+
"#####,
923+
r#####"
924+
struct Bar { y: Y, z: Z }
925+
926+
fn foo(bar: Bar) {
927+
let Bar { y, z } = bar;
928+
}
929+
"#####,
930+
)
931+
}
932+
912933
#[test]
913934
fn doctest_fix_visibility() {
914935
check_doc_test(

0 commit comments

Comments
 (0)