diff options
| -rw-r--r-- | src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_struct_binding.rs | 105 |
1 files changed, 96 insertions, 9 deletions
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_struct_binding.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_struct_binding.rs index 397327cb4ff..63a41ae5dc8 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_struct_binding.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_struct_binding.rs @@ -7,7 +7,7 @@ use ide_db::{ search::{FileReference, SearchScope}, }; use itertools::Itertools; -use syntax::ast::syntax_factory::SyntaxFactory; +use syntax::ast::{HasName, syntax_factory::SyntaxFactory}; use syntax::syntax_editor::SyntaxEditor; use syntax::{AstNode, Edition, SmolStr, SyntaxNode, ToSmolStr, ast}; @@ -71,13 +71,14 @@ fn destructure_struct_binding_impl( struct StructEditData { ident_pat: ast::IdentPat, + name: ast::Name, kind: hir::StructKind, struct_def_path: hir::ModPath, visible_fields: Vec<hir::Field>, usages: Vec<FileReference>, names_in_scope: FxHashSet<SmolStr>, has_private_members: bool, - is_nested: bool, + need_record_field_name: bool, is_ref: bool, edition: Edition, } @@ -114,7 +115,11 @@ fn collect_data(ident_pat: ast::IdentPat, ctx: &AssistContext<'_>) -> Option<Str } let is_ref = ty.is_reference(); - let is_nested = ident_pat.syntax().parent().and_then(ast::RecordPatField::cast).is_some(); + let need_record_field_name = ident_pat + .syntax() + .parent() + .and_then(ast::RecordPatField::cast) + .is_some_and(|field| field.colon_token().is_none()); let usages = ctx .sema @@ -133,6 +138,7 @@ fn collect_data(ident_pat: ast::IdentPat, ctx: &AssistContext<'_>) -> Option<Str let names_in_scope = get_names_in_scope(ctx, &ident_pat, &usages).unwrap_or_default(); Some(StructEditData { + name: ident_pat.name()?, ident_pat, kind, struct_def_path, @@ -140,7 +146,7 @@ fn collect_data(ident_pat: ast::IdentPat, ctx: &AssistContext<'_>) -> Option<Str has_private_members, visible_fields, names_in_scope, - is_nested, + need_record_field_name, is_ref, edition: module.krate().edition(ctx.db()), }) @@ -177,6 +183,7 @@ fn destructure_pat( field_names: &[(SmolStr, SmolStr)], ) { let ident_pat = &data.ident_pat; + let name = &data.name; let struct_path = mod_path_to_ast(&data.struct_def_path, data.edition); let is_ref = ident_pat.ref_token().is_some(); @@ -194,9 +201,9 @@ fn destructure_pat( hir::StructKind::Record => { let fields = field_names.iter().map(|(old_name, new_name)| { // Use shorthand syntax if possible - if old_name == new_name && !is_mut { + if old_name == new_name { make.record_pat_field_shorthand( - make.ident_pat(false, false, make.name(old_name)).into(), + make.ident_pat(is_ref, is_mut, make.name(old_name)).into(), ) } else { make.record_pat_field( @@ -215,8 +222,8 @@ fn destructure_pat( // If the binding is nested inside a record, we need to wrap the new // destructured pattern in a non-shorthand record field - let destructured_pat = if data.is_nested { - make.record_pat_field(make.name_ref(&ident_pat.to_string()), new_pat).syntax().clone() + let destructured_pat = if data.need_record_field_name { + make.record_pat_field(make.name_ref(&name.to_string()), new_pat).syntax().clone() } else { new_pat.syntax().clone() }; @@ -579,7 +586,7 @@ mod tests { struct Foo { bar: i32, baz: i32 } fn main() { - let Foo { bar: mut bar, baz: mut baz } = Foo { bar: 1, baz: 2 }; + let Foo { mut bar, mut baz } = Foo { bar: 1, baz: 2 }; let bar2 = bar; let baz2 = &baz; } @@ -588,6 +595,86 @@ mod tests { } #[test] + fn mut_record_field() { + check_assist( + destructure_struct_binding, + r#" + struct Foo { x: () } + struct Bar { foo: Foo } + fn f(Bar { mut $0foo }: Bar) {} + "#, + r#" + struct Foo { x: () } + struct Bar { foo: Foo } + fn f(Bar { foo: Foo { mut x } }: Bar) {} + "#, + ) + } + + #[test] + fn ref_record_field() { + check_assist( + destructure_struct_binding, + r#" + struct Foo { x: () } + struct Bar { foo: Foo } + fn f(Bar { ref $0foo }: Bar) { + let _ = foo.x; + } + "#, + r#" + struct Foo { x: () } + struct Bar { foo: Foo } + fn f(Bar { foo: Foo { ref x } }: Bar) { + let _ = *x; + } + "#, + ) + } + + #[test] + fn ref_mut_record_field() { + check_assist( + destructure_struct_binding, + r#" + struct Foo { x: () } + struct Bar { foo: Foo } + fn f(Bar { ref mut $0foo }: Bar) { + let _ = foo.x; + } + "#, + r#" + struct Foo { x: () } + struct Bar { foo: Foo } + fn f(Bar { foo: Foo { ref mut x } }: Bar) { + let _ = *x; + } + "#, + ) + } + + #[test] + fn ref_mut_record_renamed_field() { + check_assist( + destructure_struct_binding, + r#" + struct Foo { x: () } + struct Bar { foo: Foo } + fn f(Bar { foo: ref mut $0foo1 }: Bar) { + let _ = foo1.x; + } + "#, + r#" + struct Foo { x: () } + struct Bar { foo: Foo } + fn f(Bar { foo: Foo { ref mut x } }: Bar) { + let _ = *x; + } + "#, + ) + } + + #[test] fn mut_ref() { check_assist( destructure_struct_binding, |
