Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 57 additions & 2 deletions crates/ide-assists/src/handlers/extract_function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()?;

Expand All @@ -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)
}

Expand Down Expand Up @@ -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<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(
Expand Down
Loading