Repository: rust-lang/rust-analyzer (2025-08-04T12:54:39Z)

Issue:
Unexpected `&mut Self::XXX` in completion list
**rust-analyzer version**: rust-analyzer 1.90.0-nightly (9748d87 2025-07-21)

**rustc version**: rustc 1.90.0-nightly (9748d87dc 2025-07-21)

**editor or extension**: VIM-9.1

**code snippet to reproduce**:
```rust
enum State {
    Stop(()),
}

impl State {
    fn get_or_init(&mut self) -> &mut () {
        match self {
            State::Stop(_) => {
                let this = self;
                let Self::Stop(f) = this else { unreachable!() };
                *self = ;
            },
        }
    }
}
```

![Image](https://github.com/user-attachments/assets/03663e86-c6d4-4049-8ef5-e8149fd6cb56)

Why does dereference assign suggest `&mut `?



Discussion:
Looks like we don't handle assigning expressions in https://github.com/rust-lang/rust-analyzer/blob/68e7ec90bf29c9b17d0dcdb3358de5dee7f4afb5/crates/ide-completion/src/context/analysis.rs#L557, so we fall back to no expecation



Patch:
diff --git a/crates/ide-completion/src/context/analysis.rs b/crates/ide-completion/src/context/analysis.rs
index ea5fb39338b2..6c2edc384998 100644
--- a/crates/ide-completion/src/context/analysis.rs
+++ b/crates/ide-completion/src/context/analysis.rs
@@ -562,6 +562,7 @@ fn expected_type_and_name<'db>(
     token: &SyntaxToken,
     name_like: &ast::NameLike,
 ) -> (Option<Type<'db>>, Option<NameOrNameRef>) {
+    let token = prev_assign_token_at_trivia(token.clone());
     let mut node = match token.parent() {
         Some(it) => it,
         None => return (None, None),
@@ -632,6 +633,17 @@ fn expected_type_and_name<'db>(
                         .map(TypeInfo::original);
                     (ty, None)
                 },
+                ast::BinExpr(it) => {
+                    if let Some(ast::BinaryOp::Assignment { op: None }) = it.op_kind() {
+                        let ty = it.lhs()
+                            .and_then(|lhs| sema.type_of_expr(&lhs))
+                            .or_else(|| it.rhs().and_then(|rhs| sema.type_of_expr(&rhs)))
+                            .map(TypeInfo::original);
+                        (ty, None)
+                    } else {
+                        (None, None)
+                    }
+                },
                 ast::ArgList(_) => {
                     cov_mark::hit!(expected_type_fn_param);
                     ActiveParameter::at_token(
@@ -1870,3 +1882,23 @@ fn next_non_trivia_sibling(ele: SyntaxElement) -> Option<SyntaxElement> {
     }
     None
 }
+
+fn prev_assign_token_at_trivia(mut token: SyntaxToken) -> SyntaxToken {
+    while token.kind().is_trivia()
+        && let Some(prev) = token.prev_token()
+        && let T![=]
+        | T![+=]
+        | T![/=]
+        | T![*=]
+        | T![%=]
+        | T![>>=]
+        | T![<<=]
+        | T![-=]
+        | T![|=]
+        | T![&=]
+        | T![^=] = prev.kind()
+    {
+        token = prev
+    }
+    token
+}


Test patch:
diff --git a/crates/ide-completion/src/context/tests.rs b/crates/ide-completion/src/context/tests.rs
index 75c20968e1e5..7a8c70f190ef 100644
--- a/crates/ide-completion/src/context/tests.rs
+++ b/crates/ide-completion/src/context/tests.rs
@@ -434,3 +434,53 @@ fn f(thing: u32) -> &u32 {
         expect!["ty: u32, name: ?"],
     );
 }
+
+#[test]
+fn expected_type_assign() {
+    check_expected_type_and_name(
+        r#"
+enum State { Stop }
+fn foo() {
+    let x: &mut State = &mut State::Stop;
+    x = $0;
+}
+"#,
+        expect![[r#"ty: &'_ mut State, name: ?"#]],
+    );
+}
+
+#[test]
+fn expected_type_deref_assign() {
+    check_expected_type_and_name(
+        r#"
+enum State { Stop }
+fn foo() {
+    let x: &mut State = &mut State::Stop;
+    match x {
+        State::Stop => {
+            *x = $0;
+        },
+    }
+}
+"#,
+        expect![[r#"ty: State, name: ?"#]],
+    );
+}
+
+#[test]
+fn expected_type_deref_assign_at_block_end() {
+    check_expected_type_and_name(
+        r#"
+enum State { Stop }
+fn foo() {
+    let x: &mut State = &mut State::Stop;
+    match x {
+        State::Stop => {
+            *x = $0
+        },
+    }
+}
+"#,
+        expect![[r#"ty: State, name: ?"#]],
+    );
+}