about summary refs log tree commit diff
path: root/src/tools/rust-analyzer
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2024-10-14 12:07:31 +0000
committerbors <bors@rust-lang.org>2024-10-14 12:07:31 +0000
commitb85026b15be4cffc80ca1716b24470cda1efbe50 (patch)
tree9f7467743b9035b4f75038075645bc26d9f68f4b /src/tools/rust-analyzer
parenta672968113c065aae5ea19dcbfd3f0e7f5810db4 (diff)
parent3eea7afd98835b7325439a16d564d8a1d0d5005c (diff)
downloadrust-b85026b15be4cffc80ca1716b24470cda1efbe50.tar.gz
rust-b85026b15be4cffc80ca1716b24470cda1efbe50.zip
Auto merge of #18252 - ShoyuVanilla:issue-15799, r=Veykril
fix: Do not consider mutable usage of deref to `*mut T` as deref_mut

Fixes #15799

We are doing some heuristics for deciding whether the given deref is deref or deref_mut here;

https://github.com/rust-lang/rust-analyzer/blob/5982d9c420d0dc90739171829f0d2e9c80d98979/crates/hir-ty/src/infer/mutability.rs#L182-L200

But this heuristic is erroneous if we are dereferencing to a mut ptr and normally those cases are filtered out here as builtin;

https://github.com/rust-lang/rust-analyzer/blob/5982d9c420d0dc90739171829f0d2e9c80d98979/crates/hir-ty/src/mir/lower/as_place.rs#L165-L177

Howerver, this works not so well if the given dereferencing is double dereferencings like the case in the #15799.

```rust
struct WrapPtr(*mut u32);

impl core::ops::Deref for WrapPtr {
    type Target = *mut u32;
    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

fn main() {
    let mut x = 0u32;
    let wrap = WrapPtr(&mut x);
    unsafe {
        **wrap = 6;
    }
}
```

Here are two - outer and inner - dereferences here, and the outer dereference is marked as deref_mut because there is an assignment operation.
And this deref_mut marking is propagated into the inner dereferencing.
In the later MIR lowering, the outer dereference is filtered out as it's expr type is `*mut u32`, but the expr type in the inner dereference is an ADT, so this false-mutablility is not filtered out.

This PR cuts propagation of this false mutablilty chain if the expr type is mut ptr.
Since this happens before the resolve_all, it may have some limitations when the expr type is determined as mut ptr at the very end of inferencing, but I couldn't find simple fix for it 🤔
Diffstat (limited to 'src/tools/rust-analyzer')
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs13
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs25
2 files changed, 37 insertions, 1 deletions
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs
index 8e52725e536..8dcaa9c581e 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs
@@ -180,6 +180,7 @@ impl InferenceContext<'_> {
                 self.infer_mut_expr(index, Mutability::Not);
             }
             Expr::UnaryOp { expr, op: UnaryOp::Deref } => {
+                let mut mutability = mutability;
                 if let Some((f, _)) = self.result.method_resolutions.get_mut(&tgt_expr) {
                     if mutability == Mutability::Mut {
                         if let Some(deref_trait) = self
@@ -187,7 +188,17 @@ impl InferenceContext<'_> {
                             .lang_item(self.table.trait_env.krate, LangItem::DerefMut)
                             .and_then(|l| l.as_trait())
                         {
-                            if let Some(deref_fn) = self
+                            let ty = self.result.type_of_expr.get(*expr);
+                            let is_mut_ptr = ty.is_some_and(|ty| {
+                                let ty = self.table.resolve_ty_shallow(ty);
+                                matches!(
+                                    ty.kind(Interner),
+                                    chalk_ir::TyKind::Raw(Mutability::Mut, _)
+                                )
+                            });
+                            if is_mut_ptr {
+                                mutability = Mutability::Not;
+                            } else if let Some(deref_fn) = self
                                 .db
                                 .trait_data(deref_trait)
                                 .method_by_name(&Name::new_symbol_root(sym::deref_mut.clone()))
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs
index 95542793915..6fa0e7a5a89 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs
@@ -1258,4 +1258,29 @@ pub unsafe fn foo(a: *mut A) {
 "#,
         );
     }
+
+    #[test]
+    fn regression_15799() {
+        check_diagnostics(
+            r#"
+//- minicore: deref_mut
+struct WrapPtr(*mut u32);
+
+impl core::ops::Deref for WrapPtr {
+    type Target = *mut u32;
+    fn deref(&self) -> &Self::Target {
+        &self.0
+    }
+}
+
+fn main() {
+    let mut x = 0u32;
+    let wrap = WrapPtr(&mut x);
+    unsafe {
+        **wrap = 6;
+    }
+}
+"#,
+        );
+    }
 }