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 @@ -40,6 +40,15 @@ fn ref_simple() { y } } + 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: [ @@ -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]