Skip to content

Commit 81ea457

Browse files
authored
Merge pull request #20708 from A4-Tacks/destruct-ref-mut-panics
Fix panics on `Foo{mut x}` for destructure_struct_binding
2 parents 0c62c01 + d4731ad commit 81ea457

File tree

1 file changed

+96
-9
lines changed

1 file changed

+96
-9
lines changed

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

Lines changed: 96 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use ide_db::{
77
search::{FileReference, SearchScope},
88
};
99
use itertools::Itertools;
10-
use syntax::ast::syntax_factory::SyntaxFactory;
10+
use syntax::ast::{HasName, syntax_factory::SyntaxFactory};
1111
use syntax::syntax_editor::SyntaxEditor;
1212
use syntax::{AstNode, Edition, SmolStr, SyntaxNode, ToSmolStr, ast};
1313

@@ -71,13 +71,14 @@ fn destructure_struct_binding_impl(
7171

7272
struct StructEditData {
7373
ident_pat: ast::IdentPat,
74+
name: ast::Name,
7475
kind: hir::StructKind,
7576
struct_def_path: hir::ModPath,
7677
visible_fields: Vec<hir::Field>,
7778
usages: Vec<FileReference>,
7879
names_in_scope: FxHashSet<SmolStr>,
7980
has_private_members: bool,
80-
is_nested: bool,
81+
need_record_field_name: bool,
8182
is_ref: bool,
8283
edition: Edition,
8384
}
@@ -114,7 +115,11 @@ fn collect_data(ident_pat: ast::IdentPat, ctx: &AssistContext<'_>) -> Option<Str
114115
}
115116

116117
let is_ref = ty.is_reference();
117-
let is_nested = ident_pat.syntax().parent().and_then(ast::RecordPatField::cast).is_some();
118+
let need_record_field_name = ident_pat
119+
.syntax()
120+
.parent()
121+
.and_then(ast::RecordPatField::cast)
122+
.is_some_and(|field| field.colon_token().is_none());
118123

119124
let usages = ctx
120125
.sema
@@ -133,14 +138,15 @@ fn collect_data(ident_pat: ast::IdentPat, ctx: &AssistContext<'_>) -> Option<Str
133138
let names_in_scope = get_names_in_scope(ctx, &ident_pat, &usages).unwrap_or_default();
134139

135140
Some(StructEditData {
141+
name: ident_pat.name()?,
136142
ident_pat,
137143
kind,
138144
struct_def_path,
139145
usages,
140146
has_private_members,
141147
visible_fields,
142148
names_in_scope,
143-
is_nested,
149+
need_record_field_name,
144150
is_ref,
145151
edition: module.krate().edition(ctx.db()),
146152
})
@@ -177,6 +183,7 @@ fn destructure_pat(
177183
field_names: &[(SmolStr, SmolStr)],
178184
) {
179185
let ident_pat = &data.ident_pat;
186+
let name = &data.name;
180187

181188
let struct_path = mod_path_to_ast(&data.struct_def_path, data.edition);
182189
let is_ref = ident_pat.ref_token().is_some();
@@ -194,9 +201,9 @@ fn destructure_pat(
194201
hir::StructKind::Record => {
195202
let fields = field_names.iter().map(|(old_name, new_name)| {
196203
// Use shorthand syntax if possible
197-
if old_name == new_name && !is_mut {
204+
if old_name == new_name {
198205
make.record_pat_field_shorthand(
199-
make.ident_pat(false, false, make.name(old_name)).into(),
206+
make.ident_pat(is_ref, is_mut, make.name(old_name)).into(),
200207
)
201208
} else {
202209
make.record_pat_field(
@@ -215,8 +222,8 @@ fn destructure_pat(
215222

216223
// If the binding is nested inside a record, we need to wrap the new
217224
// destructured pattern in a non-shorthand record field
218-
let destructured_pat = if data.is_nested {
219-
make.record_pat_field(make.name_ref(&ident_pat.to_string()), new_pat).syntax().clone()
225+
let destructured_pat = if data.need_record_field_name {
226+
make.record_pat_field(make.name_ref(&name.to_string()), new_pat).syntax().clone()
220227
} else {
221228
new_pat.syntax().clone()
222229
};
@@ -579,14 +586,94 @@ mod tests {
579586
struct Foo { bar: i32, baz: i32 }
580587
581588
fn main() {
582-
let Foo { bar: mut bar, baz: mut baz } = Foo { bar: 1, baz: 2 };
589+
let Foo { mut bar, mut baz } = Foo { bar: 1, baz: 2 };
583590
let bar2 = bar;
584591
let baz2 = &baz;
585592
}
586593
"#,
587594
)
588595
}
589596

597+
#[test]
598+
fn mut_record_field() {
599+
check_assist(
600+
destructure_struct_binding,
601+
r#"
602+
struct Foo { x: () }
603+
struct Bar { foo: Foo }
604+
fn f(Bar { mut $0foo }: Bar) {}
605+
"#,
606+
r#"
607+
struct Foo { x: () }
608+
struct Bar { foo: Foo }
609+
fn f(Bar { foo: Foo { mut x } }: Bar) {}
610+
"#,
611+
)
612+
}
613+
614+
#[test]
615+
fn ref_record_field() {
616+
check_assist(
617+
destructure_struct_binding,
618+
r#"
619+
struct Foo { x: () }
620+
struct Bar { foo: Foo }
621+
fn f(Bar { ref $0foo }: Bar) {
622+
let _ = foo.x;
623+
}
624+
"#,
625+
r#"
626+
struct Foo { x: () }
627+
struct Bar { foo: Foo }
628+
fn f(Bar { foo: Foo { ref x } }: Bar) {
629+
let _ = *x;
630+
}
631+
"#,
632+
)
633+
}
634+
635+
#[test]
636+
fn ref_mut_record_field() {
637+
check_assist(
638+
destructure_struct_binding,
639+
r#"
640+
struct Foo { x: () }
641+
struct Bar { foo: Foo }
642+
fn f(Bar { ref mut $0foo }: Bar) {
643+
let _ = foo.x;
644+
}
645+
"#,
646+
r#"
647+
struct Foo { x: () }
648+
struct Bar { foo: Foo }
649+
fn f(Bar { foo: Foo { ref mut x } }: Bar) {
650+
let _ = *x;
651+
}
652+
"#,
653+
)
654+
}
655+
656+
#[test]
657+
fn ref_mut_record_renamed_field() {
658+
check_assist(
659+
destructure_struct_binding,
660+
r#"
661+
struct Foo { x: () }
662+
struct Bar { foo: Foo }
663+
fn f(Bar { foo: ref mut $0foo1 }: Bar) {
664+
let _ = foo1.x;
665+
}
666+
"#,
667+
r#"
668+
struct Foo { x: () }
669+
struct Bar { foo: Foo }
670+
fn f(Bar { foo: Foo { ref mut x } }: Bar) {
671+
let _ = *x;
672+
}
673+
"#,
674+
)
675+
}
676+
590677
#[test]
591678
fn mut_ref() {
592679
check_assist(

0 commit comments

Comments
 (0)