From f30eced2e3910139108f5751ea1f6a22829358c6 Mon Sep 17 00:00:00 2001 From: reddevilmidzy Date: Thu, 20 Nov 2025 02:06:38 +0900 Subject: [PATCH 1/2] Wrap binding name in parentheses in for-loop mut suggestion --- .../src/diagnostics/conflict_errors.rs | 16 ++++++++++++++- ...rowck-for-loop-deref-pattern-assignment.rs | 10 ++++++++++ ...k-for-loop-deref-pattern-assignment.stderr | 20 +++++++++++++++++++ 3 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 tests/ui/borrowck/borrowck-for-loop-deref-pattern-assignment.rs create mode 100644 tests/ui/borrowck/borrowck-for-loop-deref-pattern-assignment.stderr diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 9a8927c102973..7bb50a3ec4d57 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -3940,12 +3940,26 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { if let Some(decl) = local_decl && decl.can_be_made_mutable() { + let message = if matches!( + decl.local_info(), + LocalInfo::User(BindingForm::Var(VarBindingForm { + opt_match_place: Some((_, match_span)), + .. + })) if matches!(match_span.desugaring_kind(), Some(DesugaringKind::ForLoop)) + ) && let Ok(binding_name) = + self.infcx.tcx.sess.source_map().span_to_snippet(decl.source_info.span) + { + format!("(mut {}) ", binding_name) + } else { + "mut ".to_string() + }; err.span_suggestion_verbose( decl.source_info.span.shrink_to_lo(), "consider making this binding mutable", - "mut ".to_string(), + message, Applicability::MachineApplicable, ); + if !from_arg && matches!( decl.local_info(), diff --git a/tests/ui/borrowck/borrowck-for-loop-deref-pattern-assignment.rs b/tests/ui/borrowck/borrowck-for-loop-deref-pattern-assignment.rs new file mode 100644 index 0000000000000..fc4f1e4eacb95 --- /dev/null +++ b/tests/ui/borrowck/borrowck-for-loop-deref-pattern-assignment.rs @@ -0,0 +1,10 @@ +//! regression test for +//! Ensure the diagnostic suggests `for &(mut x) ...` (parenthesized) instead of `&mut x`. + +fn main() { + let nums: &[u32] = &[1, 2, 3]; + for &num in nums { + num *= 2; //~ ERROR cannot assign twice to immutable variable `num` + println!("{num}"); + } +} diff --git a/tests/ui/borrowck/borrowck-for-loop-deref-pattern-assignment.stderr b/tests/ui/borrowck/borrowck-for-loop-deref-pattern-assignment.stderr new file mode 100644 index 0000000000000..cd53e297e3483 --- /dev/null +++ b/tests/ui/borrowck/borrowck-for-loop-deref-pattern-assignment.stderr @@ -0,0 +1,20 @@ +error[E0384]: cannot assign twice to immutable variable `num` + --> $DIR/borrowck-for-loop-deref-pattern-assignment.rs:7:9 + | +LL | for &num in nums { + | --- first assignment to `num` +LL | num *= 2; + | ^^^^^^^^ cannot assign twice to immutable variable + | +help: consider making this binding mutable + | +LL | for &(mut num) num in nums { + | +++++++++ +help: to modify the original value, take a borrow instead + | +LL | for &ref mut num in nums { + | +++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0384`. From d49075f083d39c1ab91256c6945cc5c854ecee84 Mon Sep 17 00:00:00 2001 From: reddevilmidzy Date: Thu, 4 Dec 2025 15:13:40 +0900 Subject: [PATCH 2/2] Suppress `ref mut` suggestion for for-loop bindings --- .../src/diagnostics/conflict_errors.rs | 19 +++++++++++-------- ...k-for-loop-deref-pattern-assignment.stderr | 4 ---- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 7bb50a3ec4d57..f8a6fafbe78ac 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -3940,14 +3940,16 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { if let Some(decl) = local_decl && decl.can_be_made_mutable() { - let message = if matches!( - decl.local_info(), - LocalInfo::User(BindingForm::Var(VarBindingForm { - opt_match_place: Some((_, match_span)), - .. - })) if matches!(match_span.desugaring_kind(), Some(DesugaringKind::ForLoop)) - ) && let Ok(binding_name) = - self.infcx.tcx.sess.source_map().span_to_snippet(decl.source_info.span) + let is_for_loop = matches!( + decl.local_info(), + LocalInfo::User(BindingForm::Var(VarBindingForm { + opt_match_place: Some((_, match_span)), + .. + })) if matches!(match_span.desugaring_kind(), Some(DesugaringKind::ForLoop)) + ); + let message = if is_for_loop + && let Ok(binding_name) = + self.infcx.tcx.sess.source_map().span_to_snippet(decl.source_info.span) { format!("(mut {}) ", binding_name) } else { @@ -3961,6 +3963,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { ); if !from_arg + && !is_for_loop && matches!( decl.local_info(), LocalInfo::User(BindingForm::Var(VarBindingForm { diff --git a/tests/ui/borrowck/borrowck-for-loop-deref-pattern-assignment.stderr b/tests/ui/borrowck/borrowck-for-loop-deref-pattern-assignment.stderr index cd53e297e3483..fa230134df555 100644 --- a/tests/ui/borrowck/borrowck-for-loop-deref-pattern-assignment.stderr +++ b/tests/ui/borrowck/borrowck-for-loop-deref-pattern-assignment.stderr @@ -10,10 +10,6 @@ help: consider making this binding mutable | LL | for &(mut num) num in nums { | +++++++++ -help: to modify the original value, take a borrow instead - | -LL | for &ref mut num in nums { - | +++++++ error: aborting due to 1 previous error