From 256fe3e7798ad74641aeeebbe34dde0479729d15 Mon Sep 17 00:00:00 2001 From: albab-hasan Date: Wed, 3 Jun 2026 20:48:11 +0600 Subject: [PATCH] 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\`. --- .../src/handlers/extract_function.rs | 59 ++++++++++++++++++- 1 file 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 67bb7fc03ee5..f8572dcd3a85 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 { 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) } @@ -3250,6 +3257,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 for Container { + type Output = S; + fn index(&self, i: usize) -> &S { &self.0[i] } +} +impl IndexMut 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 for Container { + type Output = S; + fn index(&self, i: usize) -> &S { &self.0[i] } +} +impl IndexMut 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(