Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/ide-assists/src/handlers/destructure_struct_binding.rs')
-rw-r--r--crates/ide-assists/src/handlers/destructure_struct_binding.rs98
1 files changed, 72 insertions, 26 deletions
diff --git a/crates/ide-assists/src/handlers/destructure_struct_binding.rs b/crates/ide-assists/src/handlers/destructure_struct_binding.rs
index c45cc9b64f..d45df2cb1f 100644
--- a/crates/ide-assists/src/handlers/destructure_struct_binding.rs
+++ b/crates/ide-assists/src/handlers/destructure_struct_binding.rs
@@ -10,7 +10,10 @@ use itertools::Itertools;
use syntax::{ast, ted, AstNode, SmolStr};
use text_edit::TextRange;
-use crate::assist_context::{AssistContext, Assists, SourceChangeBuilder};
+use crate::{
+ assist_context::{AssistContext, Assists, SourceChangeBuilder},
+ utils::ref_field_expr::determine_ref_and_parens,
+};
// Assist: destructure_struct_binding
//
@@ -58,11 +61,12 @@ fn destructure_struct_binding_impl(
builder: &mut SourceChangeBuilder,
data: &StructEditData,
) {
- let assignment_edit = build_assignment_edit(ctx, builder, data);
- let usage_edits = build_usage_edits(ctx, builder, data, &assignment_edit.field_name_map);
+ let field_names = generate_field_names(ctx, data);
+ let assignment_edit = build_assignment_edit(ctx, builder, data, &field_names);
+ let usage_edits = build_usage_edits(ctx, builder, data, &field_names.into_iter().collect());
assignment_edit.apply();
- for edit in usage_edits.unwrap_or_default() {
+ for edit in usage_edits.into_iter().flatten() {
edit.apply(builder);
}
}
@@ -74,14 +78,16 @@ struct StructEditData {
visible_fields: Vec<hir::Field>,
usages: Option<UsageSearchResult>,
names_in_scope: FxHashSet<SmolStr>, // TODO currently always empty
- add_rest: bool,
+ has_private_members: bool,
is_nested: bool,
+ is_ref: bool,
}
fn collect_data(ident_pat: ast::IdentPat, ctx: &AssistContext<'_>) -> Option<StructEditData> {
- let ty = ctx.sema.type_of_binding_in_pat(&ident_pat)?.strip_references().as_adt()?;
+ let ty = ctx.sema.type_of_binding_in_pat(&ident_pat)?;
+ let is_ref = ty.is_reference();
- let hir::Adt::Struct(struct_type) = ty else { return None };
+ let hir::Adt::Struct(struct_type) = ty.strip_references().as_adt()? else { return None };
let module = ctx.sema.scope(ident_pat.syntax())?.module();
let struct_def = hir::ModuleDef::from(struct_type);
@@ -97,8 +103,9 @@ fn collect_data(ident_pat: ast::IdentPat, ctx: &AssistContext<'_>) -> Option<Str
let visible_fields =
fields.into_iter().filter(|field| field.is_visible_from(ctx.db(), module)).collect_vec();
- let add_rest = (is_non_exhaustive && is_foreign_crate) || visible_fields.len() < n_fields;
- if !matches!(kind, hir::StructKind::Record) && add_rest {
+ let has_private_members =
+ (is_non_exhaustive && is_foreign_crate) || visible_fields.len() < n_fields;
+ if !matches!(kind, hir::StructKind::Record) && has_private_members {
return None;
}
@@ -123,17 +130,19 @@ fn collect_data(ident_pat: ast::IdentPat, ctx: &AssistContext<'_>) -> Option<Str
kind,
struct_def_path,
usages,
- add_rest,
+ has_private_members,
visible_fields,
names_in_scope: FxHashSet::default(), // TODO
is_nested,
+ is_ref,
})
}
fn build_assignment_edit(
- ctx: &AssistContext<'_>,
+ _ctx: &AssistContext<'_>,
builder: &mut SourceChangeBuilder,
data: &StructEditData,
+ field_names: &[(SmolStr, SmolStr)],
) -> AssignmentEdit {
let ident_pat = builder.make_mut(data.ident_pat.clone());
@@ -141,8 +150,6 @@ fn build_assignment_edit(
let is_ref = ident_pat.ref_token().is_some();
let is_mut = ident_pat.mut_token().is_some();
- let field_names = generate_field_names(ctx, data);
-
let new_pat = match data.kind {
hir::StructKind::Tuple => {
let ident_pats = field_names.iter().map(|(_, new_name)| {
@@ -169,7 +176,7 @@ fn build_assignment_edit(
let field_list = ast::make::record_pat_field_list(
fields,
- data.add_rest.then_some(ast::make::rest_pat()),
+ data.has_private_members.then_some(ast::make::rest_pat()),
);
ast::Pat::RecordPat(ast::make::record_pat_with_fields(struct_path, field_list))
}
@@ -185,7 +192,7 @@ fn build_assignment_edit(
NewPat::Pat(new_pat.clone_for_update())
};
- AssignmentEdit { ident_pat, new_pat, field_name_map: field_names.into_iter().collect() }
+ AssignmentEdit { ident_pat, new_pat }
}
fn generate_field_names(ctx: &AssistContext<'_>, data: &StructEditData) -> Vec<(SmolStr, SmolStr)> {
@@ -195,8 +202,8 @@ fn generate_field_names(ctx: &AssistContext<'_>, data: &StructEditData) -> Vec<(
.iter()
.enumerate()
.map(|(index, _)| {
- let new_name = format!("_{}", index);
- (index.to_string().into(), new_name.into())
+ let new_name = new_field_name((format!("_{}", index)).into(), &data.names_in_scope);
+ (index.to_string().into(), new_name)
})
.collect(),
hir::StructKind::Record => data
@@ -204,8 +211,8 @@ fn generate_field_names(ctx: &AssistContext<'_>, data: &StructEditData) -> Vec<(
.iter()
.map(|field| {
let field_name = field.name(ctx.db()).to_smol_str();
- let new_field_name = new_field_name(field_name.clone(), &data.names_in_scope);
- (field_name, new_field_name)
+ let new_name = new_field_name(field_name.clone(), &data.names_in_scope);
+ (field_name, new_name)
})
.collect(),
hir::StructKind::Unit => Vec::new(),
@@ -225,7 +232,6 @@ fn new_field_name(base_name: SmolStr, names_in_scope: &FxHashSet<SmolStr>) -> Sm
struct AssignmentEdit {
ident_pat: ast::IdentPat,
new_pat: NewPat,
- field_name_map: FxHashMap<SmolStr, SmolStr>,
}
enum NewPat {
@@ -260,14 +266,16 @@ fn build_usage_edits(
.iter()
.find_map(|(file_id, refs)| (*file_id == ctx.file_id()).then_some(refs))?
.iter()
- .filter_map(|r| build_usage_edit(builder, r, field_names))
+ .filter_map(|r| build_usage_edit(ctx, builder, data, r, field_names))
.collect_vec();
Some(edits)
}
fn build_usage_edit(
+ ctx: &AssistContext<'_>,
builder: &mut SourceChangeBuilder,
+ data: &StructEditData,
usage: &FileReference,
field_names: &FxHashMap<SmolStr, SmolStr>,
) -> Option<StructUsageEdit> {
@@ -275,11 +283,20 @@ fn build_usage_edit(
Some(field_expr) => Some({
let field_name: SmolStr = field_expr.name_ref()?.to_string().into();
let new_field_name = field_names.get(&field_name)?;
-
- let expr = builder.make_mut(field_expr).into();
- let new_expr =
- ast::make::expr_path(ast::make::ext::ident_path(new_field_name)).clone_for_update();
- StructUsageEdit::IndexField(expr, new_expr)
+ let new_expr = ast::make::expr_path(ast::make::ext::ident_path(new_field_name));
+
+ if data.is_ref {
+ let (replace_expr, ref_data) = determine_ref_and_parens(ctx, &field_expr);
+ StructUsageEdit::IndexField(
+ builder.make_mut(replace_expr),
+ ref_data.wrap_expr(new_expr).clone_for_update(),
+ )
+ } else {
+ StructUsageEdit::IndexField(
+ builder.make_mut(field_expr).into(),
+ new_expr.clone_for_update(),
+ )
+ }
}),
None => Some(StructUsageEdit::Path(usage.range)),
}
@@ -602,4 +619,33 @@ mod tests {
"#,
)
}
+
+ #[test]
+ fn mut_ref() {
+ check_assist(
+ destructure_struct_binding,
+ r#"
+ struct Foo {
+ bar: i32,
+ baz: i32
+ }
+
+ fn main() {
+ let $0foo = &mut Foo { bar: 1, baz: 2 };
+ foo.bar = 5;
+ }
+ "#,
+ r#"
+ struct Foo {
+ bar: i32,
+ baz: i32
+ }
+
+ fn main() {
+ let Foo { bar, baz } = &mut Foo { bar: 1, baz: 2 };
+ *bar = 5;
+ }
+ "#,
+ )
+ }
}