From 516a273144114fd502ae837ae9be95a5ecd372bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Mon, 29 Sep 2025 02:58:25 +0000 Subject: [PATCH] Suggest making binding `mut` on `&mut` reborrow When a binding needs to be mutably reborrowed multiple times, suggesting removing `&mut` will lead to follow up errors. Instead suggest both making the binding mutable and removing the reborrow. ``` error[E0596]: cannot borrow `outer` as mutable, as it is not declared as mutable --> f14.rs:2:12 | 2 | match (&mut outer, 23) { | ^^^^^^^^^^ cannot borrow as mutable | note: the binding is already a mutable borrow --> f14.rs:1:16 | 1 | fn test(outer: &mut Option) { | ^^^^^^^^^^^^^^^^ help: consider making the binding mutable if you need to reborrow multiple times | 1 | fn test(mut outer: &mut Option) { | +++ help: if there is only one mutable reborrow, remove the `&mut` | 2 - match (&mut outer, 23) { 2 + match (outer, 23) { | ``` --- .../src/diagnostics/mutability_errors.rs | 12 ++++++++-- tests/ui/borrowck/mut-borrow-of-mut-ref.rs | 24 ++++++++++--------- .../ui/borrowck/mut-borrow-of-mut-ref.stderr | 20 +++++++++++----- tests/ui/did_you_mean/issue-31424.rs | 11 +++++---- tests/ui/did_you_mean/issue-31424.stderr | 8 +++++-- tests/ui/did_you_mean/issue-34126.rs | 6 ++--- tests/ui/did_you_mean/issue-34126.stderr | 2 +- tests/ui/nll/issue-51191.rs | 19 ++++++++------- tests/ui/nll/issue-51191.stderr | 20 +++++++++------- 9 files changed, 75 insertions(+), 47 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs index b4990ffc7739c..6bd24122a0c79 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs @@ -335,10 +335,18 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { LocalInfo::User(BindingForm::Var(mir::VarBindingForm { binding_mode: BindingMode(ByRef::No, Mutability::Not), opt_ty_info: Some(sp), + pat_span, .. })) => { if suggest { err.span_note(sp, "the binding is already a mutable borrow"); + err.span_suggestion_verbose( + pat_span.shrink_to_lo(), + "consider making the binding mutable if you need to reborrow \ + multiple times", + "mut ".to_string(), + Applicability::MaybeIncorrect, + ); } } _ => { @@ -356,9 +364,9 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { // give a best effort structured suggestion. err.span_suggestion_verbose( source_info.span.with_hi(source_info.span.lo() + BytePos(5)), - "try removing `&mut` here", + "if there is only one mutable reborrow, remove the `&mut`", "", - Applicability::MachineApplicable, + Applicability::MaybeIncorrect, ); } else { // This can occur with things like `(&mut self).foo()`. diff --git a/tests/ui/borrowck/mut-borrow-of-mut-ref.rs b/tests/ui/borrowck/mut-borrow-of-mut-ref.rs index 477a2aa48d5dc..33954592bf651 100644 --- a/tests/ui/borrowck/mut-borrow-of-mut-ref.rs +++ b/tests/ui/borrowck/mut-borrow-of-mut-ref.rs @@ -2,22 +2,24 @@ #![crate_type = "rlib"] pub fn f(b: &mut i32) { - //~^ ERROR cannot borrow - //~| NOTE not mutable - //~| NOTE the binding is already a mutable borrow + //~^ ERROR: cannot borrow + //~| NOTE: not mutable + //~| NOTE: the binding is already a mutable borrow + //~| HELP: consider making the binding mutable if you need to reborrow multiple times h(&mut b); - //~^ NOTE cannot borrow as mutable - //~| HELP try removing `&mut` here + //~^ NOTE: cannot borrow as mutable + //~| HELP: if there is only one mutable reborrow, remove the `&mut` g(&mut &mut b); - //~^ NOTE cannot borrow as mutable - //~| HELP try removing `&mut` here + //~^ NOTE: cannot borrow as mutable + //~| HELP: if there is only one mutable reborrow, remove the `&mut` } -pub fn g(b: &mut i32) { //~ NOTE the binding is already a mutable borrow +pub fn g(b: &mut i32) { //~ NOTE: the binding is already a mutable borrow + //~^ HELP: consider making the binding mutable if you need to reborrow multiple times h(&mut &mut b); - //~^ ERROR cannot borrow - //~| NOTE cannot borrow as mutable - //~| HELP try removing `&mut` here + //~^ ERROR: cannot borrow + //~| NOTE: cannot borrow as mutable + //~| HELP: if there is only one mutable reborrow, remove the `&mut` } pub fn h(_: &mut i32) {} diff --git a/tests/ui/borrowck/mut-borrow-of-mut-ref.stderr b/tests/ui/borrowck/mut-borrow-of-mut-ref.stderr index f448e009b0e1d..4c4a5e7183935 100644 --- a/tests/ui/borrowck/mut-borrow-of-mut-ref.stderr +++ b/tests/ui/borrowck/mut-borrow-of-mut-ref.stderr @@ -15,36 +15,44 @@ note: the binding is already a mutable borrow | LL | pub fn f(b: &mut i32) { | ^^^^^^^^ -help: try removing `&mut` here +help: consider making the binding mutable if you need to reborrow multiple times + | +LL | pub fn f(mut b: &mut i32) { + | +++ +help: if there is only one mutable reborrow, remove the `&mut` | LL - h(&mut b); LL + h(b); | -help: try removing `&mut` here +help: if there is only one mutable reborrow, remove the `&mut` | LL - g(&mut &mut b); LL + g(&mut b); | error[E0596]: cannot borrow `b` as mutable, as it is not declared as mutable - --> $DIR/mut-borrow-of-mut-ref.rs:17:12 + --> $DIR/mut-borrow-of-mut-ref.rs:19:12 | LL | h(&mut &mut b); | ^^^^^^ cannot borrow as mutable | note: the binding is already a mutable borrow - --> $DIR/mut-borrow-of-mut-ref.rs:16:13 + --> $DIR/mut-borrow-of-mut-ref.rs:17:13 | LL | pub fn g(b: &mut i32) { | ^^^^^^^^ -help: try removing `&mut` here +help: consider making the binding mutable if you need to reborrow multiple times + | +LL | pub fn g(mut b: &mut i32) { + | +++ +help: if there is only one mutable reborrow, remove the `&mut` | LL - h(&mut &mut b); LL + h(&mut b); | error[E0596]: cannot borrow `f` as mutable, as it is not declared as mutable - --> $DIR/mut-borrow-of-mut-ref.rs:34:5 + --> $DIR/mut-borrow-of-mut-ref.rs:36:5 | LL | f.bar(); | ^ cannot borrow as mutable diff --git a/tests/ui/did_you_mean/issue-31424.rs b/tests/ui/did_you_mean/issue-31424.rs index 2821d5b9d8b31..3b3820bfc4af3 100644 --- a/tests/ui/did_you_mean/issue-31424.rs +++ b/tests/ui/did_you_mean/issue-31424.rs @@ -4,17 +4,18 @@ struct Struct; impl Struct { fn foo(&mut self) { - (&mut self).bar(); //~ ERROR cannot borrow - //~^ HELP try removing `&mut` here + (&mut self).bar(); //~ ERROR: cannot borrow + //~^ HELP: try removing `&mut` here } // In this case we could keep the suggestion, but to distinguish the // two cases is pretty hard. It's an obscure case anyway. fn bar(self: &mut Self) { - //~^ WARN function cannot return without recursing - //~^^ HELP a `loop` may express intention better if this is on purpose + //~^ WARN: function cannot return without recursing + //~| HELP: a `loop` may express intention better if this is on purpose + //~| HELP: consider making the binding mutable if you need to reborrow multiple times (&mut self).bar(); //~ ERROR cannot borrow - //~^ HELP try removing `&mut` here + //~^ HELP: try removing `&mut` here } } diff --git a/tests/ui/did_you_mean/issue-31424.stderr b/tests/ui/did_you_mean/issue-31424.stderr index 8fe38bf697287..5752397098cf6 100644 --- a/tests/ui/did_you_mean/issue-31424.stderr +++ b/tests/ui/did_you_mean/issue-31424.stderr @@ -28,7 +28,7 @@ LL | (&mut self).bar(); = note: `#[warn(unconditional_recursion)]` on by default error[E0596]: cannot borrow `self` as mutable, as it is not declared as mutable - --> $DIR/issue-31424.rs:16:9 + --> $DIR/issue-31424.rs:17:9 | LL | (&mut self).bar(); | ^^^^^^^^^^^ cannot borrow as mutable @@ -39,10 +39,14 @@ note: the binding is already a mutable borrow LL | fn bar(self: &mut Self) { | ^^^^^^^^^ help: try removing `&mut` here - --> $DIR/issue-31424.rs:16:9 + --> $DIR/issue-31424.rs:17:9 | LL | (&mut self).bar(); | ^^^^^^^^^^^ +help: consider making the binding mutable if you need to reborrow multiple times + | +LL | fn bar(mut self: &mut Self) { + | +++ error: aborting due to 2 previous errors; 1 warning emitted diff --git a/tests/ui/did_you_mean/issue-34126.rs b/tests/ui/did_you_mean/issue-34126.rs index 53516f4f24713..82ffadbf7d3f7 100644 --- a/tests/ui/did_you_mean/issue-34126.rs +++ b/tests/ui/did_you_mean/issue-34126.rs @@ -3,9 +3,9 @@ struct Z { } impl Z { fn run(&self, z: &mut Z) { } fn start(&mut self) { - self.run(&mut self); //~ ERROR cannot borrow - //~| ERROR cannot borrow - //~| HELP try removing `&mut` here + self.run(&mut self); //~ ERROR: cannot borrow + //~| ERROR: cannot borrow + //~| HELP: if there is only one mutable reborrow, remove the `&mut` } } diff --git a/tests/ui/did_you_mean/issue-34126.stderr b/tests/ui/did_you_mean/issue-34126.stderr index 9f79207062872..ec02dfefaca18 100644 --- a/tests/ui/did_you_mean/issue-34126.stderr +++ b/tests/ui/did_you_mean/issue-34126.stderr @@ -9,7 +9,7 @@ note: the binding is already a mutable borrow | LL | fn start(&mut self) { | ^^^^^^^^^ -help: try removing `&mut` here +help: if there is only one mutable reborrow, remove the `&mut` | LL - self.run(&mut self); LL + self.run(self); diff --git a/tests/ui/nll/issue-51191.rs b/tests/ui/nll/issue-51191.rs index 836587d93b84b..c0b0c5eda139f 100644 --- a/tests/ui/nll/issue-51191.rs +++ b/tests/ui/nll/issue-51191.rs @@ -2,16 +2,17 @@ struct Struct; impl Struct { fn bar(self: &mut Self) { - //~^ WARN function cannot return without recursing - //~^^ HELP a `loop` may express intention better if this is on purpose + //~^ WARN: function cannot return without recursing + //~| HELP: a `loop` may express intention better if this is on purpose + //~| HELP: consider making the binding mutable if you need to reborrow multiple times (&mut self).bar(); - //~^ ERROR cannot borrow `self` as mutable, as it is not declared as mutable [E0596] - //~^^ HELP try removing `&mut` here + //~^ ERROR: cannot borrow `self` as mutable, as it is not declared as mutable [E0596] + //~| HELP: try removing `&mut` here } fn imm(self) { //~ HELP consider changing this to be mutable (&mut self).bar(); - //~^ ERROR cannot borrow `self` as mutable, as it is not declared as mutable [E0596] + //~^ ERROR: cannot borrow `self` as mutable, as it is not declared as mutable [E0596] } fn mtbl(mut self) { @@ -20,14 +21,14 @@ impl Struct { fn immref(&self) { (&mut self).bar(); - //~^ ERROR cannot borrow `self` as mutable, as it is not declared as mutable [E0596] - //~^^ ERROR cannot borrow data in a `&` reference as mutable [E0596] + //~^ ERROR: cannot borrow `self` as mutable, as it is not declared as mutable [E0596] + //~| ERROR: cannot borrow data in a `&` reference as mutable [E0596] } fn mtblref(&mut self) { (&mut self).bar(); - //~^ ERROR cannot borrow `self` as mutable, as it is not declared as mutable [E0596] - //~^^ HELP try removing `&mut` here + //~^ ERROR: cannot borrow `self` as mutable, as it is not declared as mutable [E0596] + //~| HELP: try removing `&mut` here } } diff --git a/tests/ui/nll/issue-51191.stderr b/tests/ui/nll/issue-51191.stderr index c14056c3a834b..73f8aab9d1417 100644 --- a/tests/ui/nll/issue-51191.stderr +++ b/tests/ui/nll/issue-51191.stderr @@ -11,7 +11,7 @@ LL | (&mut self).bar(); = note: `#[warn(unconditional_recursion)]` on by default error[E0596]: cannot borrow `self` as mutable, as it is not declared as mutable - --> $DIR/issue-51191.rs:7:9 + --> $DIR/issue-51191.rs:8:9 | LL | (&mut self).bar(); | ^^^^^^^^^^^ cannot borrow as mutable @@ -22,13 +22,17 @@ note: the binding is already a mutable borrow LL | fn bar(self: &mut Self) { | ^^^^^^^^^ help: try removing `&mut` here - --> $DIR/issue-51191.rs:7:9 + --> $DIR/issue-51191.rs:8:9 | LL | (&mut self).bar(); | ^^^^^^^^^^^ +help: consider making the binding mutable if you need to reborrow multiple times + | +LL | fn bar(mut self: &mut Self) { + | +++ error[E0596]: cannot borrow `self` as mutable, as it is not declared as mutable - --> $DIR/issue-51191.rs:13:9 + --> $DIR/issue-51191.rs:14:9 | LL | (&mut self).bar(); | ^^^^^^^^^^^ cannot borrow as mutable @@ -39,30 +43,30 @@ LL | fn imm(mut self) { | +++ error[E0596]: cannot borrow `self` as mutable, as it is not declared as mutable - --> $DIR/issue-51191.rs:22:9 + --> $DIR/issue-51191.rs:23:9 | LL | (&mut self).bar(); | ^^^^^^^^^^^ cannot borrow as mutable error[E0596]: cannot borrow data in a `&` reference as mutable - --> $DIR/issue-51191.rs:22:9 + --> $DIR/issue-51191.rs:23:9 | LL | (&mut self).bar(); | ^^^^^^^^^^^ cannot borrow as mutable error[E0596]: cannot borrow `self` as mutable, as it is not declared as mutable - --> $DIR/issue-51191.rs:28:9 + --> $DIR/issue-51191.rs:29:9 | LL | (&mut self).bar(); | ^^^^^^^^^^^ cannot borrow as mutable | note: the binding is already a mutable borrow - --> $DIR/issue-51191.rs:27:16 + --> $DIR/issue-51191.rs:28:16 | LL | fn mtblref(&mut self) { | ^^^^^^^^^ help: try removing `&mut` here - --> $DIR/issue-51191.rs:28:9 + --> $DIR/issue-51191.rs:29:9 | LL | (&mut self).bar(); | ^^^^^^^^^^^