Skip to content

Commit e81202c

Browse files
authored
Merge pull request #20731 from A4-Tacks/expand-rest-pat-in-tuple-slice-pat
Fix expand rest pattern in tuple and slice pattern
2 parents 997f3ec + 6d85fd7 commit e81202c

File tree

2 files changed

+289
-19
lines changed

2 files changed

+289
-19
lines changed

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

Lines changed: 255 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -113,9 +113,7 @@ fn expand_tuple_struct_rest_pattern(
113113
};
114114

115115
let rest_pat = rest_pat.into();
116-
let mut pats = pat.fields();
117-
let prefix_count = pats.by_ref().position(|p| p == rest_pat)?;
118-
let suffix_count = pats.count();
116+
let (prefix_count, suffix_count) = calculate_counts(&rest_pat, pat.fields())?;
119117

120118
if fields.len().saturating_sub(prefix_count).saturating_sub(suffix_count) == 0 {
121119
cov_mark::hit!(no_missing_fields_tuple_struct);
@@ -141,19 +139,13 @@ fn expand_tuple_struct_rest_pattern(
141139
pat.fields()
142140
.take(prefix_count)
143141
.chain(fields[prefix_count..fields.len() - suffix_count].iter().map(|f| {
144-
make.ident_pat(
145-
false,
146-
false,
147-
match name_gen.for_type(
148-
&f.ty(ctx.sema.db).to_type(ctx.sema.db),
149-
ctx.sema.db,
150-
ctx.edition(),
151-
) {
152-
Some(name) => make.name(&name),
153-
None => make.name(&format!("_{}", f.index())),
154-
},
142+
gen_unnamed_pat(
143+
ctx,
144+
&make,
145+
&mut name_gen,
146+
&f.ty(ctx.db()).to_type(ctx.sema.db),
147+
f.index(),
155148
)
156-
.into()
157149
}))
158150
.chain(pat.fields().skip(prefix_count + 1)),
159151
);
@@ -166,22 +158,175 @@ fn expand_tuple_struct_rest_pattern(
166158
)
167159
}
168160

161+
// Assist: expand_tuple_rest_pattern
162+
//
163+
// Fills fields by replacing rest pattern in tuple patterns.
164+
//
165+
// ```
166+
// fn foo(bar: (char, i32, i32)) {
167+
// let (ch, ..$0) = bar;
168+
// }
169+
// ```
170+
// ->
171+
// ```
172+
// fn foo(bar: (char, i32, i32)) {
173+
// let (ch, _1, _2) = bar;
174+
// }
175+
// ```
176+
fn expand_tuple_rest_pattern(
177+
acc: &mut Assists,
178+
ctx: &AssistContext<'_>,
179+
pat: ast::TuplePat,
180+
rest_pat: ast::RestPat,
181+
) -> Option<()> {
182+
let fields = ctx.sema.type_of_pat(&pat.clone().into())?.original.tuple_fields(ctx.db());
183+
let len = fields.len();
184+
185+
let rest_pat = rest_pat.into();
186+
let (prefix_count, suffix_count) = calculate_counts(&rest_pat, pat.fields())?;
187+
188+
if len.saturating_sub(prefix_count).saturating_sub(suffix_count) == 0 {
189+
cov_mark::hit!(no_missing_fields_tuple);
190+
return None;
191+
}
192+
193+
let old_range = ctx.sema.original_range_opt(pat.syntax())?;
194+
if old_range.file_id != ctx.file_id() {
195+
return None;
196+
}
197+
198+
acc.add(
199+
AssistId::refactor_rewrite("expand_tuple_rest_pattern"),
200+
"Fill tuple fields",
201+
rest_pat.syntax().text_range(),
202+
|builder| {
203+
let make = SyntaxFactory::with_mappings();
204+
let mut editor = builder.make_editor(rest_pat.syntax());
205+
206+
let mut name_gen = NameGenerator::new_from_scope_locals(ctx.sema.scope(pat.syntax()));
207+
let new_pat = make.tuple_pat(
208+
pat.fields()
209+
.take(prefix_count)
210+
.chain(fields[prefix_count..len - suffix_count].iter().enumerate().map(
211+
|(index, ty)| {
212+
gen_unnamed_pat(ctx, &make, &mut name_gen, ty, prefix_count + index)
213+
},
214+
))
215+
.chain(pat.fields().skip(prefix_count + 1)),
216+
);
217+
218+
editor.replace(pat.syntax(), new_pat.syntax());
219+
220+
editor.add_mappings(make.finish_with_mappings());
221+
builder.add_file_edits(ctx.vfs_file_id(), editor);
222+
},
223+
)
224+
}
225+
226+
// Assist: expand_slice_rest_pattern
227+
//
228+
// Fills fields by replacing rest pattern in slice patterns.
229+
//
230+
// ```
231+
// fn foo(bar: [i32; 3]) {
232+
// let [first, ..$0] = bar;
233+
// }
234+
// ```
235+
// ->
236+
// ```
237+
// fn foo(bar: [i32; 3]) {
238+
// let [first, _1, _2] = bar;
239+
// }
240+
// ```
241+
fn expand_slice_rest_pattern(
242+
acc: &mut Assists,
243+
ctx: &AssistContext<'_>,
244+
pat: ast::SlicePat,
245+
rest_pat: ast::RestPat,
246+
) -> Option<()> {
247+
let (ty, len) = ctx.sema.type_of_pat(&pat.clone().into())?.original.as_array(ctx.db())?;
248+
249+
let rest_pat = rest_pat.into();
250+
let (prefix_count, suffix_count) = calculate_counts(&rest_pat, pat.pats())?;
251+
252+
if len.saturating_sub(prefix_count).saturating_sub(suffix_count) == 0 {
253+
cov_mark::hit!(no_missing_fields_slice);
254+
return None;
255+
}
256+
257+
let old_range = ctx.sema.original_range_opt(pat.syntax())?;
258+
if old_range.file_id != ctx.file_id() {
259+
return None;
260+
}
261+
262+
acc.add(
263+
AssistId::refactor_rewrite("expand_slice_rest_pattern"),
264+
"Fill slice fields",
265+
rest_pat.syntax().text_range(),
266+
|builder| {
267+
let make = SyntaxFactory::with_mappings();
268+
let mut editor = builder.make_editor(rest_pat.syntax());
269+
270+
let mut name_gen = NameGenerator::new_from_scope_locals(ctx.sema.scope(pat.syntax()));
271+
let new_pat = make.slice_pat(
272+
pat.pats()
273+
.take(prefix_count)
274+
.chain(
275+
(prefix_count..len - suffix_count)
276+
.map(|index| gen_unnamed_pat(ctx, &make, &mut name_gen, &ty, index)),
277+
)
278+
.chain(pat.pats().skip(prefix_count + 1)),
279+
);
280+
281+
editor.replace(pat.syntax(), new_pat.syntax());
282+
283+
editor.add_mappings(make.finish_with_mappings());
284+
builder.add_file_edits(ctx.vfs_file_id(), editor);
285+
},
286+
)
287+
}
288+
169289
pub(crate) fn expand_rest_pattern(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
170290
let rest_pat = ctx.find_node_at_offset::<ast::RestPat>()?;
171291
let parent = rest_pat.syntax().parent()?;
172292
match_ast! {
173293
match parent {
174294
ast::RecordPatFieldList(it) => expand_record_rest_pattern(acc, ctx, it.syntax().parent().and_then(ast::RecordPat::cast)?, rest_pat),
175295
ast::TupleStructPat(it) => expand_tuple_struct_rest_pattern(acc, ctx, it, rest_pat),
176-
// FIXME
177-
// ast::TuplePat(it) => (),
178-
// FIXME
179-
// ast::SlicePat(it) => (),
296+
ast::TuplePat(it) => expand_tuple_rest_pattern(acc, ctx, it, rest_pat),
297+
ast::SlicePat(it) => expand_slice_rest_pattern(acc, ctx, it, rest_pat),
180298
_ => None,
181299
}
182300
}
183301
}
184302

303+
fn gen_unnamed_pat(
304+
ctx: &AssistContext<'_>,
305+
make: &SyntaxFactory,
306+
name_gen: &mut NameGenerator,
307+
ty: &hir::Type<'_>,
308+
index: usize,
309+
) -> ast::Pat {
310+
make.ident_pat(
311+
false,
312+
false,
313+
match name_gen.for_type(ty, ctx.sema.db, ctx.edition()) {
314+
Some(name) => make.name(&name),
315+
None => make.name(&format!("_{index}")),
316+
},
317+
)
318+
.into()
319+
}
320+
321+
fn calculate_counts(
322+
rest_pat: &ast::Pat,
323+
mut pats: ast::AstChildren<ast::Pat>,
324+
) -> Option<(usize, usize)> {
325+
let prefix_count = pats.by_ref().position(|p| p == *rest_pat)?;
326+
let suffix_count = pats.count();
327+
Some((prefix_count, suffix_count))
328+
}
329+
185330
#[cfg(test)]
186331
mod tests {
187332
use super::*;
@@ -351,6 +496,79 @@ fn foo(bar: Bar) {
351496
)
352497
}
353498

499+
#[test]
500+
fn fill_tuple_with_fields() {
501+
check_assist(
502+
expand_rest_pattern,
503+
r#"
504+
fn foo(bar: (char, i32, i32)) {
505+
let (ch, ..$0) = bar;
506+
}
507+
"#,
508+
r#"
509+
fn foo(bar: (char, i32, i32)) {
510+
let (ch, _1, _2) = bar;
511+
}
512+
"#,
513+
);
514+
check_assist(
515+
expand_rest_pattern,
516+
r#"
517+
fn foo(bar: (char, i32, i32)) {
518+
let (ch, ..$0, end) = bar;
519+
}
520+
"#,
521+
r#"
522+
fn foo(bar: (char, i32, i32)) {
523+
let (ch, _1, end) = bar;
524+
}
525+
"#,
526+
);
527+
}
528+
529+
#[test]
530+
fn fill_array_with_fields() {
531+
check_assist(
532+
expand_rest_pattern,
533+
r#"
534+
fn foo(bar: [i32; 4]) {
535+
let [first, ..$0] = bar;
536+
}
537+
"#,
538+
r#"
539+
fn foo(bar: [i32; 4]) {
540+
let [first, _1, _2, _3] = bar;
541+
}
542+
"#,
543+
);
544+
check_assist(
545+
expand_rest_pattern,
546+
r#"
547+
fn foo(bar: [i32; 4]) {
548+
let [first, second, ..$0] = bar;
549+
}
550+
"#,
551+
r#"
552+
fn foo(bar: [i32; 4]) {
553+
let [first, second, _2, _3] = bar;
554+
}
555+
"#,
556+
);
557+
check_assist(
558+
expand_rest_pattern,
559+
r#"
560+
fn foo(bar: [i32; 4]) {
561+
let [first, second, ..$0, end] = bar;
562+
}
563+
"#,
564+
r#"
565+
fn foo(bar: [i32; 4]) {
566+
let [first, second, _2, end] = bar;
567+
}
568+
"#,
569+
);
570+
}
571+
354572
#[test]
355573
fn fill_fields_struct_generated_by_macro() {
356574
check_assist(
@@ -486,6 +704,8 @@ fn bar(foo: Foo) {
486704
// This is still possible even though it's meaningless
487705
cov_mark::check!(no_missing_fields);
488706
cov_mark::check!(no_missing_fields_tuple_struct);
707+
cov_mark::check!(no_missing_fields_tuple);
708+
cov_mark::check!(no_missing_fields_slice);
489709
check_assist_not_applicable(
490710
expand_rest_pattern,
491711
r#"
@@ -523,6 +743,22 @@ struct Bar(Y, Z)
523743
fn foo(bar: Bar) {
524744
let Bar(y, ..$0, z) = bar;
525745
}
746+
"#,
747+
);
748+
check_assist_not_applicable(
749+
expand_rest_pattern,
750+
r#"
751+
fn foo(bar: (i32, i32)) {
752+
let (y, ..$0, z) = bar;
753+
}
754+
"#,
755+
);
756+
check_assist_not_applicable(
757+
expand_rest_pattern,
758+
r#"
759+
fn foo(bar: [i32; 2]) {
760+
let [y, ..$0, z] = bar;
761+
}
526762
"#,
527763
);
528764
}

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)