about summary refs log tree commit diff
diff options
context:
space:
mode:
authorhkalbasi <hamidrezakalbasi@protonmail.com>2023-04-14 15:32:40 +0330
committerhkalbasi <hamidrezakalbasi@protonmail.com>2023-04-14 15:32:40 +0330
commit7cb4318331e0040165ff7b30903b9ea1164c114c (patch)
treec69bfe4c30def8438f90dbedb6cf267a33fa889c
parent1ee88db4121bfd4bad1dcfdb64922f126d93e286 (diff)
downloadrust-7cb4318331e0040165ff7b30903b9ea1164c114c.tar.gz
rust-7cb4318331e0040165ff7b30903b9ea1164c114c.zip
Fix explicit deref problems in closure capture
-rw-r--r--crates/hir-ty/src/infer/closure.rs33
-rw-r--r--crates/hir-ty/src/layout/tests/closure.rs19
-rw-r--r--crates/hir/src/lib.rs5
-rw-r--r--crates/ide-diagnostics/src/handlers/mutability_errors.rs37
4 files changed, 90 insertions, 4 deletions
diff --git a/crates/hir-ty/src/infer/closure.rs b/crates/hir-ty/src/infer/closure.rs
index 28cb301f3e4..6927d89d63d 100644
--- a/crates/hir-ty/src/infer/closure.rs
+++ b/crates/hir-ty/src/infer/closure.rs
@@ -190,6 +190,16 @@ impl InferenceContext<'_> {
                 }
                 return Some(place);
             }
+            Expr::UnaryOp { expr, op: UnaryOp::Deref } => {
+                if matches!(
+                    self.expr_ty_after_adjustments(*expr).kind(Interner),
+                    TyKind::Ref(..) | TyKind::Raw(..)
+                ) {
+                    let mut place = self.place_of_expr(*expr)?;
+                    place.projections.push(ProjectionElem::Deref);
+                    return Some(place);
+                }
+            }
             _ => (),
         }
         None
@@ -371,7 +381,12 @@ impl InferenceContext<'_> {
             }
             Expr::Field { expr, name: _ } => self.select_from_expr(*expr),
             Expr::UnaryOp { expr, op: UnaryOp::Deref } => {
-                if let Some((f, _)) = self.result.method_resolution(tgt_expr) {
+                if matches!(
+                    self.expr_ty_after_adjustments(*expr).kind(Interner),
+                    TyKind::Ref(..) | TyKind::Raw(..)
+                ) {
+                    self.select_from_expr(*expr);
+                } else if let Some((f, _)) = self.result.method_resolution(tgt_expr) {
                     let mutability = 'b: {
                         if let Some(deref_trait) =
                             self.resolve_lang_item(LangItem::DerefMut).and_then(|x| x.as_trait())
@@ -461,10 +476,20 @@ impl InferenceContext<'_> {
         }
     }
 
-    fn expr_ty(&mut self, expr: ExprId) -> Ty {
+    fn expr_ty(&self, expr: ExprId) -> Ty {
         self.result[expr].clone()
     }
 
+    fn expr_ty_after_adjustments(&self, e: ExprId) -> Ty {
+        let mut ty = None;
+        if let Some(x) = self.result.expr_adjustments.get(&e) {
+            if let Some(x) = x.last() {
+                ty = Some(x.target.clone());
+            }
+        }
+        ty.unwrap_or_else(|| self.expr_ty(e))
+    }
+
     fn is_upvar(&self, place: &HirPlace) -> bool {
         let b = &self.body[place.local];
         if let Some(c) = self.current_closure {
@@ -701,7 +726,9 @@ impl InferenceContext<'_> {
         };
         self.consume_expr(*body);
         for item in &self.current_captures {
-            if matches!(item.kind, CaptureKind::ByRef(BorrowKind::Mut { .. })) {
+            if matches!(item.kind, CaptureKind::ByRef(BorrowKind::Mut { .. }))
+                && !item.place.projections.contains(&ProjectionElem::Deref)
+            {
                 // FIXME: remove the `mutated_bindings_in_closure` completely and add proper fake reads in
                 // MIR. I didn't do that due duplicate diagnostics.
                 self.result.mutated_bindings_in_closure.insert(item.place.local);
diff --git a/crates/hir-ty/src/layout/tests/closure.rs b/crates/hir-ty/src/layout/tests/closure.rs
index 31b6765a7a2..0db4edeb698 100644
--- a/crates/hir-ty/src/layout/tests/closure.rs
+++ b/crates/hir-ty/src/layout/tests/closure.rs
@@ -41,6 +41,15 @@ fn ref_simple() {
         }
     }
     size_and_align_expr! {
+        minicore: copy, deref_mut;
+        stmts: [
+            let y: &mut i32 = &mut 5;
+        ]
+        |x: i32| {
+            *y += x;
+        }
+    }
+    size_and_align_expr! {
         minicore: copy;
         stmts: [
             struct X(i32, i64);
@@ -50,6 +59,16 @@ fn ref_simple() {
             x
         }
     }
+    size_and_align_expr! {
+        minicore: copy, deref_mut;
+        stmts: [
+            struct X(i32, i64);
+            let x: &mut X = &mut X(2, 6);
+        ]
+        || {
+            (*x).0 as i64 + x.1
+        }
+    }
 }
 
 #[test]
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index db923cb0fe2..e161c94a0ee 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -1564,7 +1564,10 @@ impl DefWithBody {
                         }
                         (mir::MutabilityReason::Not, true) => {
                             if !infer.mutated_bindings_in_closure.contains(&binding_id) {
-                                acc.push(UnusedMut { local }.into())
+                                let should_ignore = matches!(body[binding_id].name.as_str(), Some(x) if x.starts_with("_"));
+                                if !should_ignore {
+                                    acc.push(UnusedMut { local }.into())
+                                }
                             }
                         }
                     }
diff --git a/crates/ide-diagnostics/src/handlers/mutability_errors.rs b/crates/ide-diagnostics/src/handlers/mutability_errors.rs
index 8c4ca23e06e..3847e4d30e8 100644
--- a/crates/ide-diagnostics/src/handlers/mutability_errors.rs
+++ b/crates/ide-diagnostics/src/handlers/mutability_errors.rs
@@ -851,6 +851,43 @@ fn f() {
 }
             "#,
         );
+        check_diagnostics(
+            r#"
+        //- minicore: copy, fn, deref_mut
+        struct X(i32, i64);
+
+        fn f() {
+            let mut x = &mut 5;
+              //^^^^^ 💡 weak: variable does not need to be mutable
+            let closure1 = || { *x = 2; };
+            let _ = closure1();
+                  //^^^^^^^^ 💡 error: cannot mutate immutable variable `closure1`
+            let mut x = &mut 5;
+              //^^^^^ 💡 weak: variable does not need to be mutable
+            let closure1 = move || { *x = 2; };
+            let _ = closure1();
+                  //^^^^^^^^ 💡 error: cannot mutate immutable variable `closure1`
+            let mut x = &mut X(1, 2);
+              //^^^^^ 💡 weak: variable does not need to be mutable
+            let closure1 = || { x.0 = 2; };
+            let _ = closure1();
+                  //^^^^^^^^ 💡 error: cannot mutate immutable variable `closure1`
+        }
+                    "#,
+        );
+    }
+
+    #[test]
+    fn allow_unused_mut_for_identifiers_starting_with_underline() {
+        check_diagnostics(
+            r#"
+fn f(_: i32) {}
+fn main() {
+    let mut _x = 2;
+    f(_x);
+}
+"#,
+        );
     }
 
     #[test]