Support tuple struct patterns for expand_rest_pattern assist
This commit is contained in:
parent
43c5e95af9
commit
c315ad914f
4 changed files with 217 additions and 25 deletions
|
@ -296,6 +296,7 @@ pub enum ModuleDef {
|
|||
Function(Function),
|
||||
Adt(Adt),
|
||||
// Can't be directly declared, but can be imported.
|
||||
// FIXME: Rename to `EnumVariant`
|
||||
Variant(Variant),
|
||||
Const(Const),
|
||||
Static(Static),
|
||||
|
@ -1564,6 +1565,7 @@ impl From<&Variant> for DefWithBodyId {
|
|||
}
|
||||
}
|
||||
|
||||
// FIXME: Rename to `EnumVariant`
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct Variant {
|
||||
pub(crate) id: EnumVariantId,
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
use hir::{PathResolution, StructKind};
|
||||
use ide_db::syntax_helpers::suggest_name::NameGenerator;
|
||||
use syntax::{
|
||||
ast::{self, make},
|
||||
match_ast, AstNode, ToSmolStr,
|
||||
|
@ -5,7 +7,23 @@ use syntax::{
|
|||
|
||||
use crate::{AssistContext, AssistId, Assists};
|
||||
|
||||
// Assist: expand_rest_pattern
|
||||
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()?;
|
||||
match_ast! {
|
||||
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) => (),
|
||||
_ => return None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Assist: expand_record_rest_pattern
|
||||
//
|
||||
// Fills fields by replacing rest pattern in record patterns.
|
||||
//
|
||||
|
@ -24,22 +42,12 @@ use crate::{AssistContext, AssistId, Assists};
|
|||
// let Bar { y, z } = bar;
|
||||
// }
|
||||
// ```
|
||||
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()?;
|
||||
let record_pat = match_ast! {
|
||||
match parent {
|
||||
ast::RecordPatFieldList(it) => ast::RecordPat::cast(it.syntax().parent()?)?,
|
||||
// ast::TupleStructPat(it) => (),
|
||||
// ast::TuplePat(it) => (),
|
||||
// ast::SlicePat(it) => (),
|
||||
_ => return None,
|
||||
}
|
||||
};
|
||||
|
||||
let ellipsis = record_pat.record_pat_field_list().and_then(|r| r.rest_pat())?;
|
||||
let target_range = ellipsis.syntax().text_range();
|
||||
|
||||
fn expand_record_rest_pattern(
|
||||
acc: &mut Assists,
|
||||
ctx: &AssistContext<'_>,
|
||||
record_pat: ast::RecordPat,
|
||||
rest_pat: ast::RestPat,
|
||||
) -> Option<()> {
|
||||
let missing_fields = ctx.sema.record_pattern_missing_fields(&record_pat);
|
||||
|
||||
if missing_fields.is_empty() {
|
||||
|
@ -48,6 +56,11 @@ pub(crate) fn expand_rest_pattern(acc: &mut Assists, ctx: &AssistContext<'_>) ->
|
|||
}
|
||||
|
||||
let old_field_list = record_pat.record_pat_field_list()?;
|
||||
let old_range = ctx.sema.original_range_opt(old_field_list.syntax())?;
|
||||
if old_range.file_id != ctx.file_id() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let new_field_list =
|
||||
make::record_pat_field_list(old_field_list.fields(), None).clone_for_update();
|
||||
for (f, _) in missing_fields.iter() {
|
||||
|
@ -58,16 +71,93 @@ pub(crate) fn expand_rest_pattern(acc: &mut Assists, ctx: &AssistContext<'_>) ->
|
|||
new_field_list.add_field(field.clone_for_update());
|
||||
}
|
||||
|
||||
let old_range = ctx.sema.original_range_opt(old_field_list.syntax())?;
|
||||
let target_range = rest_pat.syntax().text_range();
|
||||
acc.add(
|
||||
AssistId("expand_record_rest_pattern", crate::AssistKind::RefactorRewrite),
|
||||
"Fill struct fields",
|
||||
target_range,
|
||||
move |builder| builder.replace_ast(old_field_list, new_field_list),
|
||||
)
|
||||
}
|
||||
// Assist: expand_tuple_struct_rest_pattern
|
||||
//
|
||||
// Fills fields by replacing rest pattern in tuple struct patterns.
|
||||
//
|
||||
// ```
|
||||
// struct Bar(Y, Z);
|
||||
//
|
||||
// fn foo(bar: Bar) {
|
||||
// let Bar(..$0) = bar;
|
||||
// }
|
||||
// ```
|
||||
// ->
|
||||
// ```
|
||||
// struct Bar(Y, Z);
|
||||
//
|
||||
// fn foo(bar: Bar) {
|
||||
// let Bar(_0, _1) = bar;
|
||||
// }
|
||||
// ```
|
||||
fn expand_tuple_struct_rest_pattern(
|
||||
acc: &mut Assists,
|
||||
ctx: &AssistContext<'_>,
|
||||
pat: ast::TupleStructPat,
|
||||
rest_pat: ast::RestPat,
|
||||
) -> Option<()> {
|
||||
let path = pat.path()?;
|
||||
let fields = match ctx.sema.type_of_pat(&pat.clone().into())?.original.as_adt()? {
|
||||
hir::Adt::Struct(s) if s.kind(ctx.sema.db) == StructKind::Tuple => s.fields(ctx.sema.db),
|
||||
hir::Adt::Enum(_) => match ctx.sema.resolve_path(&path)? {
|
||||
PathResolution::Def(hir::ModuleDef::Variant(v))
|
||||
if v.kind(ctx.sema.db) == StructKind::Tuple =>
|
||||
{
|
||||
v.fields(ctx.sema.db)
|
||||
}
|
||||
_ => return None,
|
||||
},
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
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();
|
||||
|
||||
if fields.len().saturating_sub(prefix_count).saturating_sub(suffix_count) == 0 {
|
||||
cov_mark::hit!(no_missing_fields_tuple_struct);
|
||||
return None;
|
||||
}
|
||||
|
||||
let old_range = ctx.sema.original_range_opt(pat.syntax())?;
|
||||
if old_range.file_id != ctx.file_id() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut name_gen = NameGenerator::new_from_scope_locals(ctx.sema.scope(pat.syntax()));
|
||||
let new_pat = make::tuple_struct_pat(
|
||||
path,
|
||||
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), ctx.sema.db, ctx.edition()) {
|
||||
Some(name) => make::name(&name),
|
||||
None => make::name(&format!("_{}", f.index())),
|
||||
},
|
||||
)
|
||||
.into()
|
||||
}))
|
||||
.chain(pat.fields().skip(prefix_count + 1)),
|
||||
);
|
||||
|
||||
let target_range = rest_pat.syntax().text_range();
|
||||
acc.add(
|
||||
AssistId("expand_rest_pattern", crate::AssistKind::RefactorRewrite),
|
||||
"Fill structure fields",
|
||||
AssistId("expand_tuple_struct_rest_pattern", crate::AssistKind::RefactorRewrite),
|
||||
"Fill tuple struct fields",
|
||||
target_range,
|
||||
move |builder| builder.replace_ast(old_field_list, new_field_list),
|
||||
move |builder| builder.replace_ast(pat, new_pat),
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -165,6 +255,27 @@ struct Bar {
|
|||
fn foo(bar: Bar) {
|
||||
let Bar { y, z } = bar;
|
||||
}
|
||||
"#,
|
||||
);
|
||||
check_assist(
|
||||
expand_rest_pattern,
|
||||
r#"
|
||||
struct Y;
|
||||
struct Z;
|
||||
struct Bar(Y, Z)
|
||||
|
||||
fn foo(bar: Bar) {
|
||||
let Bar(..$0) = bar;
|
||||
}
|
||||
"#,
|
||||
r#"
|
||||
struct Y;
|
||||
struct Z;
|
||||
struct Bar(Y, Z)
|
||||
|
||||
fn foo(bar: Bar) {
|
||||
let Bar(y, z) = bar;
|
||||
}
|
||||
"#,
|
||||
)
|
||||
}
|
||||
|
@ -192,6 +303,29 @@ struct Bar {
|
|||
fn foo(bar: Bar) {
|
||||
let Bar { y, z } = bar;
|
||||
}
|
||||
"#,
|
||||
);
|
||||
check_assist(
|
||||
expand_rest_pattern,
|
||||
r#"
|
||||
struct X;
|
||||
struct Y;
|
||||
struct Z;
|
||||
struct Bar(X, Y, Z)
|
||||
|
||||
fn foo(bar: Bar) {
|
||||
let Bar(x, ..$0, z) = bar;
|
||||
}
|
||||
"#,
|
||||
r#"
|
||||
struct X;
|
||||
struct Y;
|
||||
struct Z;
|
||||
struct Bar(X, Y, Z)
|
||||
|
||||
fn foo(bar: Bar) {
|
||||
let Bar(x, y, z) = bar;
|
||||
}
|
||||
"#,
|
||||
)
|
||||
}
|
||||
|
@ -330,6 +464,7 @@ fn bar(foo: Foo) {
|
|||
fn not_applicable_when_no_missing_fields() {
|
||||
// This is still possible even though it's meaningless
|
||||
cov_mark::check!(no_missing_fields);
|
||||
cov_mark::check!(no_missing_fields_tuple_struct);
|
||||
check_assist_not_applicable(
|
||||
expand_rest_pattern,
|
||||
r#"
|
||||
|
@ -357,6 +492,16 @@ struct Bar {
|
|||
fn foo(bar: Bar) {
|
||||
let Bar { y, z, ..$0 } = bar;
|
||||
}
|
||||
"#,
|
||||
);
|
||||
check_assist_not_applicable(
|
||||
expand_rest_pattern,
|
||||
r#"
|
||||
struct Bar(Y, Z)
|
||||
|
||||
fn foo(bar: Bar) {
|
||||
let Bar(y, ..$0, z) = bar;
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -956,9 +956,9 @@ pub use foo::{Bar, Baz};
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn doctest_expand_rest_pattern() {
|
||||
fn doctest_expand_record_rest_pattern() {
|
||||
check_doc_test(
|
||||
"expand_rest_pattern",
|
||||
"expand_record_rest_pattern",
|
||||
r#####"
|
||||
struct Bar { y: Y, z: Z }
|
||||
|
||||
|
@ -976,6 +976,27 @@ fn foo(bar: Bar) {
|
|||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn doctest_expand_tuple_struct_rest_pattern() {
|
||||
check_doc_test(
|
||||
"expand_tuple_struct_rest_pattern",
|
||||
r#####"
|
||||
struct Bar(Y, Z);
|
||||
|
||||
fn foo(bar: Bar) {
|
||||
let Bar(..$0) = bar;
|
||||
}
|
||||
"#####,
|
||||
r#####"
|
||||
struct Bar(Y, Z);
|
||||
|
||||
fn foo(bar: Bar) {
|
||||
let Bar(_0, _1) = bar;
|
||||
}
|
||||
"#####,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn doctest_extract_constant() {
|
||||
check_doc_test(
|
||||
|
|
|
@ -1069,8 +1069,8 @@ pub use foo::{Bar, Baz};
|
|||
```
|
||||
|
||||
|
||||
### `expand_rest_pattern`
|
||||
**Source:** [expand_rest_pattern.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/expand_rest_pattern.rs#L8)
|
||||
### `expand_record_rest_pattern`
|
||||
**Source:** [expand_rest_pattern.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/expand_rest_pattern.rs#L24)
|
||||
|
||||
Fills fields by replacing rest pattern in record patterns.
|
||||
|
||||
|
@ -1093,6 +1093,30 @@ fn foo(bar: Bar) {
|
|||
```
|
||||
|
||||
|
||||
### `expand_tuple_struct_rest_pattern`
|
||||
**Source:** [expand_rest_pattern.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/expand_rest_pattern.rs#L80)
|
||||
|
||||
Fills fields by replacing rest pattern in tuple struct patterns.
|
||||
|
||||
#### Before
|
||||
```rust
|
||||
struct Bar(Y, Z);
|
||||
|
||||
fn foo(bar: Bar) {
|
||||
let Bar(..┃) = bar;
|
||||
}
|
||||
```
|
||||
|
||||
#### After
|
||||
```rust
|
||||
struct Bar(Y, Z);
|
||||
|
||||
fn foo(bar: Bar) {
|
||||
let Bar(_0, _1) = bar;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### `extract_constant`
|
||||
**Source:** [extract_variable.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-assists/src/handlers/extract_variable.rs#L35)
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue