Skip to content

Commit b86bbdd

Browse files
committed
Fix expand rest pattern in tuple and slice pattern
Assist: expand_tuple_rest_pattern Fills fields by replacing rest pattern in tuple patterns. Example --- ``` fn foo(bar: (char, i32, i32)) { let (ch, ..$0) = bar; } ``` -> ``` fn foo(bar: (char, i32, i32)) { let (ch, _1, _2) = bar; } ``` --- Assist: expand_slice_rest_pattern Fills fields by replacing rest pattern in slice patterns. Example --- ``` fn foo(bar: [i32; 3]) { let [first, ..$0] = bar; } ``` -> ``` fn foo(bar: [i32; 3]) { let [first, _1, _2] = bar; } ```
1 parent c1499a2 commit b86bbdd

File tree

2 files changed

+284
-17
lines changed

2 files changed

+284
-17
lines changed

crates/ide-assists/src/handlers/expand_rest_pattern.rs

Lines changed: 250 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -114,9 +114,7 @@ fn expand_tuple_struct_rest_pattern(
114114
};
115115

116116
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())?;
120118

121119
if fields.len().saturating_sub(prefix_count).saturating_sub(suffix_count) == 0 {
122120
cov_mark::hit!(no_missing_fields_tuple_struct);
@@ -142,16 +140,7 @@ fn expand_tuple_struct_rest_pattern(
142140
pat.fields()
143141
.take(prefix_count)
144142
.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())
155144
}))
156145
.chain(pat.fields().skip(prefix_count + 1)),
157146
);
@@ -164,22 +153,175 @@ fn expand_tuple_struct_rest_pattern(
164153
)
165154
}
166155

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+
167284
pub(crate) fn expand_rest_pattern(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
168285
let rest_pat = ctx.find_node_at_offset::<ast::RestPat>()?;
169286
let parent = rest_pat.syntax().parent()?;
170287
match_ast! {
171288
match parent {
172289
ast::RecordPatFieldList(it) => expand_record_rest_pattern(acc, ctx, it.syntax().parent().and_then(ast::RecordPat::cast)?, rest_pat),
173290
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),
178293
_ => None,
179294
}
180295
}
181296
}
182297

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+
183325
#[cfg(test)]
184326
mod tests {
185327
use super::*;
@@ -349,6 +491,79 @@ fn foo(bar: Bar) {
349491
)
350492
}
351493

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+
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+
352567
#[test]
353568
fn fill_fields_struct_generated_by_macro() {
354569
check_assist(
@@ -484,6 +699,8 @@ fn bar(foo: Foo) {
484699
// This is still possible even though it's meaningless
485700
cov_mark::check!(no_missing_fields);
486701
cov_mark::check!(no_missing_fields_tuple_struct);
702+
cov_mark::check!(no_missing_fields_tuple);
703+
cov_mark::check!(no_missing_fields_slice);
487704
check_assist_not_applicable(
488705
expand_rest_pattern,
489706
r#"
@@ -521,6 +738,22 @@ struct Bar(Y, Z)
521738
fn foo(bar: Bar) {
522739
let Bar(y, ..$0, z) = bar;
523740
}
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+
}
524757
"#,
525758
);
526759
}

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

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1041,6 +1041,40 @@ fn foo(bar: Bar) {
10411041
)
10421042
}
10431043

1044+
#[test]
1045+
fn doctest_expand_slice_rest_pattern() {
1046+
check_doc_test(
1047+
"expand_slice_rest_pattern",
1048+
r#####"
1049+
fn foo(bar: [i32; 3]) {
1050+
let [first, ..$0] = bar;
1051+
}
1052+
"#####,
1053+
r#####"
1054+
fn foo(bar: [i32; 3]) {
1055+
let [first, _1, _2] = bar;
1056+
}
1057+
"#####,
1058+
)
1059+
}
1060+
1061+
#[test]
1062+
fn doctest_expand_tuple_rest_pattern() {
1063+
check_doc_test(
1064+
"expand_tuple_rest_pattern",
1065+
r#####"
1066+
fn foo(bar: (char, i32, i32)) {
1067+
let (ch, ..$0) = bar;
1068+
}
1069+
"#####,
1070+
r#####"
1071+
fn foo(bar: (char, i32, i32)) {
1072+
let (ch, _1, _2) = bar;
1073+
}
1074+
"#####,
1075+
)
1076+
}
1077+
10441078
#[test]
10451079
fn doctest_expand_tuple_struct_rest_pattern() {
10461080
check_doc_test(

0 commit comments

Comments
 (0)