mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-10-27 18:26:19 +00:00
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;
}
```
This commit is contained in:
parent
a96d92e9e9
commit
6d85fd739f
2 changed files with 289 additions and 19 deletions
|
|
@ -113,9 +113,7 @@ fn expand_tuple_struct_rest_pattern(
|
|||
};
|
||||
|
||||
let rest_pat = rest_pat.into();
|
||||
let mut pats = pat.fields();
|
||||
let prefix_count = pats.by_ref().position(|p| p == rest_pat)?;
|
||||
let suffix_count = pats.count();
|
||||
let (prefix_count, suffix_count) = calculate_counts(&rest_pat, pat.fields())?;
|
||||
|
||||
if fields.len().saturating_sub(prefix_count).saturating_sub(suffix_count) == 0 {
|
||||
cov_mark::hit!(no_missing_fields_tuple_struct);
|
||||
|
|
@ -141,19 +139,13 @@ fn expand_tuple_struct_rest_pattern(
|
|||
pat.fields()
|
||||
.take(prefix_count)
|
||||
.chain(fields[prefix_count..fields.len() - suffix_count].iter().map(|f| {
|
||||
make.ident_pat(
|
||||
false,
|
||||
false,
|
||||
match name_gen.for_type(
|
||||
&f.ty(ctx.sema.db).to_type(ctx.sema.db),
|
||||
ctx.sema.db,
|
||||
ctx.edition(),
|
||||
) {
|
||||
Some(name) => make.name(&name),
|
||||
None => make.name(&format!("_{}", f.index())),
|
||||
},
|
||||
gen_unnamed_pat(
|
||||
ctx,
|
||||
&make,
|
||||
&mut name_gen,
|
||||
&f.ty(ctx.db()).to_type(ctx.sema.db),
|
||||
f.index(),
|
||||
)
|
||||
.into()
|
||||
}))
|
||||
.chain(pat.fields().skip(prefix_count + 1)),
|
||||
);
|
||||
|
|
@ -166,6 +158,134 @@ fn expand_tuple_struct_rest_pattern(
|
|||
)
|
||||
}
|
||||
|
||||
// Assist: expand_tuple_rest_pattern
|
||||
//
|
||||
// Fills fields by replacing rest pattern in tuple patterns.
|
||||
//
|
||||
// ```
|
||||
// fn foo(bar: (char, i32, i32)) {
|
||||
// let (ch, ..$0) = bar;
|
||||
// }
|
||||
// ```
|
||||
// ->
|
||||
// ```
|
||||
// fn foo(bar: (char, i32, i32)) {
|
||||
// let (ch, _1, _2) = bar;
|
||||
// }
|
||||
// ```
|
||||
fn expand_tuple_rest_pattern(
|
||||
acc: &mut Assists,
|
||||
ctx: &AssistContext<'_>,
|
||||
pat: ast::TuplePat,
|
||||
rest_pat: ast::RestPat,
|
||||
) -> Option<()> {
|
||||
let fields = ctx.sema.type_of_pat(&pat.clone().into())?.original.tuple_fields(ctx.db());
|
||||
let len = fields.len();
|
||||
|
||||
let rest_pat = rest_pat.into();
|
||||
let (prefix_count, suffix_count) = calculate_counts(&rest_pat, pat.fields())?;
|
||||
|
||||
if len.saturating_sub(prefix_count).saturating_sub(suffix_count) == 0 {
|
||||
cov_mark::hit!(no_missing_fields_tuple);
|
||||
return None;
|
||||
}
|
||||
|
||||
let old_range = ctx.sema.original_range_opt(pat.syntax())?;
|
||||
if old_range.file_id != ctx.file_id() {
|
||||
return None;
|
||||
}
|
||||
|
||||
acc.add(
|
||||
AssistId::refactor_rewrite("expand_tuple_rest_pattern"),
|
||||
"Fill tuple fields",
|
||||
rest_pat.syntax().text_range(),
|
||||
|builder| {
|
||||
let make = SyntaxFactory::with_mappings();
|
||||
let mut editor = builder.make_editor(rest_pat.syntax());
|
||||
|
||||
let mut name_gen = NameGenerator::new_from_scope_locals(ctx.sema.scope(pat.syntax()));
|
||||
let new_pat = make.tuple_pat(
|
||||
pat.fields()
|
||||
.take(prefix_count)
|
||||
.chain(fields[prefix_count..len - suffix_count].iter().enumerate().map(
|
||||
|(index, ty)| {
|
||||
gen_unnamed_pat(ctx, &make, &mut name_gen, ty, prefix_count + index)
|
||||
},
|
||||
))
|
||||
.chain(pat.fields().skip(prefix_count + 1)),
|
||||
);
|
||||
|
||||
editor.replace(pat.syntax(), new_pat.syntax());
|
||||
|
||||
editor.add_mappings(make.finish_with_mappings());
|
||||
builder.add_file_edits(ctx.vfs_file_id(), editor);
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// Assist: expand_slice_rest_pattern
|
||||
//
|
||||
// Fills fields by replacing rest pattern in slice patterns.
|
||||
//
|
||||
// ```
|
||||
// fn foo(bar: [i32; 3]) {
|
||||
// let [first, ..$0] = bar;
|
||||
// }
|
||||
// ```
|
||||
// ->
|
||||
// ```
|
||||
// fn foo(bar: [i32; 3]) {
|
||||
// let [first, _1, _2] = bar;
|
||||
// }
|
||||
// ```
|
||||
fn expand_slice_rest_pattern(
|
||||
acc: &mut Assists,
|
||||
ctx: &AssistContext<'_>,
|
||||
pat: ast::SlicePat,
|
||||
rest_pat: ast::RestPat,
|
||||
) -> Option<()> {
|
||||
let (ty, len) = ctx.sema.type_of_pat(&pat.clone().into())?.original.as_array(ctx.db())?;
|
||||
|
||||
let rest_pat = rest_pat.into();
|
||||
let (prefix_count, suffix_count) = calculate_counts(&rest_pat, pat.pats())?;
|
||||
|
||||
if len.saturating_sub(prefix_count).saturating_sub(suffix_count) == 0 {
|
||||
cov_mark::hit!(no_missing_fields_slice);
|
||||
return None;
|
||||
}
|
||||
|
||||
let old_range = ctx.sema.original_range_opt(pat.syntax())?;
|
||||
if old_range.file_id != ctx.file_id() {
|
||||
return None;
|
||||
}
|
||||
|
||||
acc.add(
|
||||
AssistId::refactor_rewrite("expand_slice_rest_pattern"),
|
||||
"Fill slice fields",
|
||||
rest_pat.syntax().text_range(),
|
||||
|builder| {
|
||||
let make = SyntaxFactory::with_mappings();
|
||||
let mut editor = builder.make_editor(rest_pat.syntax());
|
||||
|
||||
let mut name_gen = NameGenerator::new_from_scope_locals(ctx.sema.scope(pat.syntax()));
|
||||
let new_pat = make.slice_pat(
|
||||
pat.pats()
|
||||
.take(prefix_count)
|
||||
.chain(
|
||||
(prefix_count..len - suffix_count)
|
||||
.map(|index| gen_unnamed_pat(ctx, &make, &mut name_gen, &ty, index)),
|
||||
)
|
||||
.chain(pat.pats().skip(prefix_count + 1)),
|
||||
);
|
||||
|
||||
editor.replace(pat.syntax(), new_pat.syntax());
|
||||
|
||||
editor.add_mappings(make.finish_with_mappings());
|
||||
builder.add_file_edits(ctx.vfs_file_id(), editor);
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn expand_rest_pattern(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
|
||||
let rest_pat = ctx.find_node_at_offset::<ast::RestPat>()?;
|
||||
let parent = rest_pat.syntax().parent()?;
|
||||
|
|
@ -173,15 +293,40 @@ pub(crate) fn expand_rest_pattern(acc: &mut Assists, ctx: &AssistContext<'_>) ->
|
|||
match parent {
|
||||
ast::RecordPatFieldList(it) => expand_record_rest_pattern(acc, ctx, it.syntax().parent().and_then(ast::RecordPat::cast)?, rest_pat),
|
||||
ast::TupleStructPat(it) => expand_tuple_struct_rest_pattern(acc, ctx, it, rest_pat),
|
||||
// FIXME
|
||||
// ast::TuplePat(it) => (),
|
||||
// FIXME
|
||||
// ast::SlicePat(it) => (),
|
||||
ast::TuplePat(it) => expand_tuple_rest_pattern(acc, ctx, it, rest_pat),
|
||||
ast::SlicePat(it) => expand_slice_rest_pattern(acc, ctx, it, rest_pat),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn gen_unnamed_pat(
|
||||
ctx: &AssistContext<'_>,
|
||||
make: &SyntaxFactory,
|
||||
name_gen: &mut NameGenerator,
|
||||
ty: &hir::Type<'_>,
|
||||
index: usize,
|
||||
) -> ast::Pat {
|
||||
make.ident_pat(
|
||||
false,
|
||||
false,
|
||||
match name_gen.for_type(ty, ctx.sema.db, ctx.edition()) {
|
||||
Some(name) => make.name(&name),
|
||||
None => make.name(&format!("_{index}")),
|
||||
},
|
||||
)
|
||||
.into()
|
||||
}
|
||||
|
||||
fn calculate_counts(
|
||||
rest_pat: &ast::Pat,
|
||||
mut pats: ast::AstChildren<ast::Pat>,
|
||||
) -> Option<(usize, usize)> {
|
||||
let prefix_count = pats.by_ref().position(|p| p == *rest_pat)?;
|
||||
let suffix_count = pats.count();
|
||||
Some((prefix_count, suffix_count))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
|
@ -351,6 +496,79 @@ fn foo(bar: Bar) {
|
|||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fill_tuple_with_fields() {
|
||||
check_assist(
|
||||
expand_rest_pattern,
|
||||
r#"
|
||||
fn foo(bar: (char, i32, i32)) {
|
||||
let (ch, ..$0) = bar;
|
||||
}
|
||||
"#,
|
||||
r#"
|
||||
fn foo(bar: (char, i32, i32)) {
|
||||
let (ch, _1, _2) = bar;
|
||||
}
|
||||
"#,
|
||||
);
|
||||
check_assist(
|
||||
expand_rest_pattern,
|
||||
r#"
|
||||
fn foo(bar: (char, i32, i32)) {
|
||||
let (ch, ..$0, end) = bar;
|
||||
}
|
||||
"#,
|
||||
r#"
|
||||
fn foo(bar: (char, i32, i32)) {
|
||||
let (ch, _1, end) = bar;
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fill_array_with_fields() {
|
||||
check_assist(
|
||||
expand_rest_pattern,
|
||||
r#"
|
||||
fn foo(bar: [i32; 4]) {
|
||||
let [first, ..$0] = bar;
|
||||
}
|
||||
"#,
|
||||
r#"
|
||||
fn foo(bar: [i32; 4]) {
|
||||
let [first, _1, _2, _3] = bar;
|
||||
}
|
||||
"#,
|
||||
);
|
||||
check_assist(
|
||||
expand_rest_pattern,
|
||||
r#"
|
||||
fn foo(bar: [i32; 4]) {
|
||||
let [first, second, ..$0] = bar;
|
||||
}
|
||||
"#,
|
||||
r#"
|
||||
fn foo(bar: [i32; 4]) {
|
||||
let [first, second, _2, _3] = bar;
|
||||
}
|
||||
"#,
|
||||
);
|
||||
check_assist(
|
||||
expand_rest_pattern,
|
||||
r#"
|
||||
fn foo(bar: [i32; 4]) {
|
||||
let [first, second, ..$0, end] = bar;
|
||||
}
|
||||
"#,
|
||||
r#"
|
||||
fn foo(bar: [i32; 4]) {
|
||||
let [first, second, _2, end] = bar;
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fill_fields_struct_generated_by_macro() {
|
||||
check_assist(
|
||||
|
|
@ -486,6 +704,8 @@ fn bar(foo: Foo) {
|
|||
// This is still possible even though it's meaningless
|
||||
cov_mark::check!(no_missing_fields);
|
||||
cov_mark::check!(no_missing_fields_tuple_struct);
|
||||
cov_mark::check!(no_missing_fields_tuple);
|
||||
cov_mark::check!(no_missing_fields_slice);
|
||||
check_assist_not_applicable(
|
||||
expand_rest_pattern,
|
||||
r#"
|
||||
|
|
@ -523,6 +743,22 @@ struct Bar(Y, Z)
|
|||
fn foo(bar: Bar) {
|
||||
let Bar(y, ..$0, z) = bar;
|
||||
}
|
||||
"#,
|
||||
);
|
||||
check_assist_not_applicable(
|
||||
expand_rest_pattern,
|
||||
r#"
|
||||
fn foo(bar: (i32, i32)) {
|
||||
let (y, ..$0, z) = bar;
|
||||
}
|
||||
"#,
|
||||
);
|
||||
check_assist_not_applicable(
|
||||
expand_rest_pattern,
|
||||
r#"
|
||||
fn foo(bar: [i32; 2]) {
|
||||
let [y, ..$0, z] = bar;
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1041,6 +1041,40 @@ fn foo(bar: Bar) {
|
|||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn doctest_expand_slice_rest_pattern() {
|
||||
check_doc_test(
|
||||
"expand_slice_rest_pattern",
|
||||
r#####"
|
||||
fn foo(bar: [i32; 3]) {
|
||||
let [first, ..$0] = bar;
|
||||
}
|
||||
"#####,
|
||||
r#####"
|
||||
fn foo(bar: [i32; 3]) {
|
||||
let [first, _1, _2] = bar;
|
||||
}
|
||||
"#####,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn doctest_expand_tuple_rest_pattern() {
|
||||
check_doc_test(
|
||||
"expand_tuple_rest_pattern",
|
||||
r#####"
|
||||
fn foo(bar: (char, i32, i32)) {
|
||||
let (ch, ..$0) = bar;
|
||||
}
|
||||
"#####,
|
||||
r#####"
|
||||
fn foo(bar: (char, i32, i32)) {
|
||||
let (ch, _1, _2) = bar;
|
||||
}
|
||||
"#####,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn doctest_expand_tuple_struct_rest_pattern() {
|
||||
check_doc_test(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue