about summary refs log tree commit diff
diff options
context:
space:
mode:
authorA4-Tacks <wdsjxhno1001@163.com>2025-09-20 17:04:56 +0800
committerA4-Tacks <wdsjxhno1001@163.com>2025-09-20 17:52:01 +0800
commit8eda71b98a8540f848cfd33b58f6d284edb64073 (patch)
tree8527e294f89728bb9a1419a05591341fe1aeb4e9
parent300aee8239168bfd07114322654da9840d0a2331 (diff)
downloadrust-8eda71b98a8540f848cfd33b58f6d284edb64073.tar.gz
rust-8eda71b98a8540f848cfd33b58f6d284edb64073.zip
Fix panic `!self.data().mutable` for destructure_struct_binding
When the reference type does not require adding a dereference or parentheses, it will panic

Example
---

```rust
struct Foo { bar: i32, baz: i32 }

fn main() {
    let $0foo = &Foo { bar: 1, baz: 2 };
    let _ = &foo.bar;
}
```

**Before this PR**:

Panic:
```
assertion failed: !self.data().mutable
```

**After this PR**:

```rust
struct Foo { bar: i32, baz: i32 }

fn main() {
    let Foo { bar, baz } = &Foo { bar: 1, baz: 2 };
    let _ = bar;
}
```
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_struct_binding.rs50
1 files changed, 48 insertions, 2 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..5ba2f96c01a 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
@@ -288,7 +288,7 @@ 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 new_expr = make.expr_path(ast::make::ext::ident_path(new_field_name));
+            let new_expr = ast::make::expr_path(ast::make::ext::ident_path(new_field_name));
 
             // If struct binding is a reference, we might need to deref field usages
             if data.is_ref {
@@ -298,7 +298,7 @@ fn build_usage_edit(
                     ref_data.wrap_expr(new_expr).syntax().clone_for_update(),
                 )
             } else {
-                (field_expr.syntax().clone(), new_expr.syntax().clone())
+                (field_expr.syntax().clone(), new_expr.syntax().clone_for_update())
             }
         }),
         None => Some((
@@ -611,6 +611,52 @@ mod tests {
     }
 
     #[test]
+    fn ref_not_add_parenthesis_and_deref_record() {
+        check_assist(
+            destructure_struct_binding,
+            r#"
+            struct Foo { bar: i32, baz: i32 }
+
+            fn main() {
+                let $0foo = &Foo { bar: 1, baz: 2 };
+                let _ = &foo.bar;
+            }
+            "#,
+            r#"
+            struct Foo { bar: i32, baz: i32 }
+
+            fn main() {
+                let Foo { bar, baz } = &Foo { bar: 1, baz: 2 };
+                let _ = bar;
+            }
+            "#,
+        )
+    }
+
+    #[test]
+    fn ref_not_add_parenthesis_and_deref_tuple() {
+        check_assist(
+            destructure_struct_binding,
+            r#"
+            struct Foo(i32, i32);
+
+            fn main() {
+                let $0foo = &Foo(1, 2);
+                let _ = &foo.0;
+            }
+            "#,
+            r#"
+            struct Foo(i32, i32);
+
+            fn main() {
+                let Foo(_0, _1) = &Foo(1, 2);
+                let _ = _0;
+            }
+            "#,
+        )
+    }
+
+    #[test]
     fn record_struct_name_collision() {
         check_assist(
             destructure_struct_binding,