Unnamed repository; edit this file 'description' to name the repository.
fix: \`extract_function\` misses \`&mut\` for \`container[i].mut_method()\`
fixes https://github.com/rust-lang/rust-analyzer/issues/20162 \`expr_require_exclusive_access\` had no \`IndexExpr\` case, so \`container[i].mut_method()\` fell through to \`Some(false)\`. \`WRITE\` is only set on direct assignments, so method calls through an index were not detected as requiring exclusive access, and the container was extracted as \`&container\` instead of \`&mut container\`.
albab-hasan 6 weeks ago
parent b679057 · commit 256fe3e
-rw-r--r--crates/ide-assists/src/handlers/extract_function.rs59
1 files changed, 57 insertions, 2 deletions
diff --git a/crates/ide-assists/src/handlers/extract_function.rs b/crates/ide-assists/src/handlers/extract_function.rs
index 67bb7fc03e..f8572dcd3a 100644
--- a/crates/ide-assists/src/handlers/extract_function.rs
+++ b/crates/ide-assists/src/handlers/extract_function.rs
@@ -1197,7 +1197,7 @@ fn reference_is_exclusive(reference: &FileReference, ctx: &AssistContext<'_, '_>
expr_require_exclusive_access(ctx, &path).unwrap_or(false)
}
-/// checks if this expr requires `&mut` access, recurses on field access
+/// checks if this expr requires `&mut` access, recurses on field/index access
fn expr_require_exclusive_access(ctx: &AssistContext<'_, '_>, expr: &ast::Expr) -> Option<bool> {
let parent = expr.syntax().parent()?;
@@ -1220,10 +1220,17 @@ fn expr_require_exclusive_access(ctx: &AssistContext<'_, '_>, expr: &ast::Expr)
return Some(matches!(access, hir::Access::Exclusive));
}
- if let Some(field) = ast::FieldExpr::cast(parent) {
+ if let Some(field) = ast::FieldExpr::cast(parent.clone()) {
return expr_require_exclusive_access(ctx, &field.into());
}
+ // `container[i].mut_method()` needs `&mut container` but WRITE is not set (no direct assignment).
+ if let Some(index_expr) = ast::IndexExpr::cast(parent)
+ && index_expr.base().is_some_and(|base| base.syntax() == expr.syntax())
+ {
+ return expr_require_exclusive_access(ctx, &index_expr.into());
+ }
+
Some(false)
}
@@ -3251,6 +3258,54 @@ fn $0fun_name(arr: &mut [i32; 1]) {
}
#[test]
+ fn mut_index_element_method_call() {
+ check_assist(
+ extract_function,
+ r#"
+//- minicore: index
+use core::ops::{Index, IndexMut};
+struct Container([S; 2]);
+struct S;
+impl S { fn mutate(&mut self) {} }
+impl Index<usize> for Container {
+ type Output = S;
+ fn index(&self, i: usize) -> &S { &self.0[i] }
+}
+impl IndexMut<usize> for Container {
+ fn index_mut(&mut self, i: usize) -> &mut S { &mut self.0[i] }
+}
+fn foo() {
+ let mut c = Container([S, S]);
+ $0c[0].mutate();$0
+ let _ = c;
+}
+"#,
+ r#"
+use core::ops::{Index, IndexMut};
+struct Container([S; 2]);
+struct S;
+impl S { fn mutate(&mut self) {} }
+impl Index<usize> for Container {
+ type Output = S;
+ fn index(&self, i: usize) -> &S { &self.0[i] }
+}
+impl IndexMut<usize> for Container {
+ fn index_mut(&mut self, i: usize) -> &mut S { &mut self.0[i] }
+}
+fn foo() {
+ let mut c = Container([S, S]);
+ fun_name(&mut c);
+ let _ = c;
+}
+
+fn $0fun_name(c: &mut Container) {
+ c[0].mutate();
+}
+"#,
+ );
+ }
+
+ #[test]
fn mut_field_from_outer_scope() {
check_assist(
extract_function,