Unnamed repository; edit this file 'description' to name the repository.
-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 28cb301f3e..6927d89d63 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 31b6765a7a..0db4edeb69 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 db923cb0fe..e161c94a0e 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 8c4ca23e06..3847e4d30e 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]