mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-10-23 08:42:40 +00:00
Fix panics on Foo{mut x}
for destructure_struct_binding
Example --- ```rust struct Foo { x: () } struct Bar { foo: Foo } fn f(Bar { mut $0foo }: Bar) {} ``` **Before this PR**: Panic `Option::unwrap` **After this PR**: ```rust struct Foo { x: () } struct Bar { foo: Foo } fn f(Bar { foo: Foo { mut x } }: Bar) {} ```
This commit is contained in:
parent
b12a129347
commit
d4731ad9e2
1 changed files with 96 additions and 9 deletions
|
@ -7,7 +7,7 @@ use ide_db::{
|
||||||
search::{FileReference, SearchScope},
|
search::{FileReference, SearchScope},
|
||||||
};
|
};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use syntax::ast::syntax_factory::SyntaxFactory;
|
use syntax::ast::{HasName, syntax_factory::SyntaxFactory};
|
||||||
use syntax::syntax_editor::SyntaxEditor;
|
use syntax::syntax_editor::SyntaxEditor;
|
||||||
use syntax::{AstNode, Edition, SmolStr, SyntaxNode, ToSmolStr, ast};
|
use syntax::{AstNode, Edition, SmolStr, SyntaxNode, ToSmolStr, ast};
|
||||||
|
|
||||||
|
@ -71,13 +71,14 @@ fn destructure_struct_binding_impl(
|
||||||
|
|
||||||
struct StructEditData {
|
struct StructEditData {
|
||||||
ident_pat: ast::IdentPat,
|
ident_pat: ast::IdentPat,
|
||||||
|
name: ast::Name,
|
||||||
kind: hir::StructKind,
|
kind: hir::StructKind,
|
||||||
struct_def_path: hir::ModPath,
|
struct_def_path: hir::ModPath,
|
||||||
visible_fields: Vec<hir::Field>,
|
visible_fields: Vec<hir::Field>,
|
||||||
usages: Vec<FileReference>,
|
usages: Vec<FileReference>,
|
||||||
names_in_scope: FxHashSet<SmolStr>,
|
names_in_scope: FxHashSet<SmolStr>,
|
||||||
has_private_members: bool,
|
has_private_members: bool,
|
||||||
is_nested: bool,
|
need_record_field_name: bool,
|
||||||
is_ref: bool,
|
is_ref: bool,
|
||||||
edition: Edition,
|
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_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
|
let usages = ctx
|
||||||
.sema
|
.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();
|
let names_in_scope = get_names_in_scope(ctx, &ident_pat, &usages).unwrap_or_default();
|
||||||
|
|
||||||
Some(StructEditData {
|
Some(StructEditData {
|
||||||
|
name: ident_pat.name()?,
|
||||||
ident_pat,
|
ident_pat,
|
||||||
kind,
|
kind,
|
||||||
struct_def_path,
|
struct_def_path,
|
||||||
|
@ -140,7 +146,7 @@ fn collect_data(ident_pat: ast::IdentPat, ctx: &AssistContext<'_>) -> Option<Str
|
||||||
has_private_members,
|
has_private_members,
|
||||||
visible_fields,
|
visible_fields,
|
||||||
names_in_scope,
|
names_in_scope,
|
||||||
is_nested,
|
need_record_field_name,
|
||||||
is_ref,
|
is_ref,
|
||||||
edition: module.krate().edition(ctx.db()),
|
edition: module.krate().edition(ctx.db()),
|
||||||
})
|
})
|
||||||
|
@ -177,6 +183,7 @@ fn destructure_pat(
|
||||||
field_names: &[(SmolStr, SmolStr)],
|
field_names: &[(SmolStr, SmolStr)],
|
||||||
) {
|
) {
|
||||||
let ident_pat = &data.ident_pat;
|
let ident_pat = &data.ident_pat;
|
||||||
|
let name = &data.name;
|
||||||
|
|
||||||
let struct_path = mod_path_to_ast(&data.struct_def_path, data.edition);
|
let struct_path = mod_path_to_ast(&data.struct_def_path, data.edition);
|
||||||
let is_ref = ident_pat.ref_token().is_some();
|
let is_ref = ident_pat.ref_token().is_some();
|
||||||
|
@ -194,9 +201,9 @@ fn destructure_pat(
|
||||||
hir::StructKind::Record => {
|
hir::StructKind::Record => {
|
||||||
let fields = field_names.iter().map(|(old_name, new_name)| {
|
let fields = field_names.iter().map(|(old_name, new_name)| {
|
||||||
// Use shorthand syntax if possible
|
// Use shorthand syntax if possible
|
||||||
if old_name == new_name && !is_mut {
|
if old_name == new_name {
|
||||||
make.record_pat_field_shorthand(
|
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 {
|
} else {
|
||||||
make.record_pat_field(
|
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
|
// If the binding is nested inside a record, we need to wrap the new
|
||||||
// destructured pattern in a non-shorthand record field
|
// destructured pattern in a non-shorthand record field
|
||||||
let destructured_pat = if data.is_nested {
|
let destructured_pat = if data.need_record_field_name {
|
||||||
make.record_pat_field(make.name_ref(&ident_pat.to_string()), new_pat).syntax().clone()
|
make.record_pat_field(make.name_ref(&name.to_string()), new_pat).syntax().clone()
|
||||||
} else {
|
} else {
|
||||||
new_pat.syntax().clone()
|
new_pat.syntax().clone()
|
||||||
};
|
};
|
||||||
|
@ -579,7 +586,7 @@ mod tests {
|
||||||
struct Foo { bar: i32, baz: i32 }
|
struct Foo { bar: i32, baz: i32 }
|
||||||
|
|
||||||
fn main() {
|
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 bar2 = bar;
|
||||||
let baz2 = &baz;
|
let baz2 = &baz;
|
||||||
}
|
}
|
||||||
|
@ -587,6 +594,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]
|
#[test]
|
||||||
fn mut_ref() {
|
fn mut_ref() {
|
||||||
check_assist(
|
check_assist(
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue