borrowck: suggest &mut *x for pattern reborrows
This commit is contained in:
parent
c58d9f9f82
commit
0de7415875
7 changed files with 175 additions and 23 deletions
|
|
@ -354,14 +354,71 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
self.infcx.tcx.sess.source_map().span_to_snippet(source_info.span)
|
||||
{
|
||||
if snippet.starts_with("&mut ") {
|
||||
// We don't have access to the HIR to get accurate spans, but we can
|
||||
// give a best effort structured suggestion.
|
||||
err.span_suggestion_verbose(
|
||||
source_info.span.with_hi(source_info.span.lo() + BytePos(5)),
|
||||
"if there is only one mutable reborrow, remove the `&mut`",
|
||||
"",
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
// In calls, `&mut &mut T` may be deref-coerced to `&mut T`, and
|
||||
// removing the extra `&mut` is the most direct suggestion. But for
|
||||
// pattern-matching expressions (`match`, `if let`, `while let`), that
|
||||
// can easily turn into a move, so prefer suggesting an explicit
|
||||
// reborrow via `&mut *x` instead.
|
||||
let mut in_pat_scrutinee = false;
|
||||
let mut is_deref_coerced = false;
|
||||
if let Some(expr) = self.find_expr(source_info.span) {
|
||||
let tcx = self.infcx.tcx;
|
||||
let span = expr.span.source_callsite();
|
||||
for (_, node) in tcx.hir_parent_iter(expr.hir_id) {
|
||||
if let Node::Expr(parent_expr) = node {
|
||||
match parent_expr.kind {
|
||||
ExprKind::Match(scrutinee, ..)
|
||||
if scrutinee
|
||||
.span
|
||||
.source_callsite()
|
||||
.contains(span) =>
|
||||
{
|
||||
in_pat_scrutinee = true;
|
||||
break;
|
||||
}
|
||||
ExprKind::Let(let_expr)
|
||||
if let_expr
|
||||
.init
|
||||
.span
|
||||
.source_callsite()
|
||||
.contains(span) =>
|
||||
{
|
||||
in_pat_scrutinee = true;
|
||||
break;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let typeck = tcx.typeck(expr.hir_id.owner.def_id);
|
||||
is_deref_coerced =
|
||||
typeck.expr_adjustments(expr).iter().any(|adj| {
|
||||
matches!(adj.kind, ty::adjustment::Adjust::Deref(_))
|
||||
});
|
||||
}
|
||||
|
||||
if in_pat_scrutinee {
|
||||
// Best-effort structured suggestion: insert `*` after `&mut `.
|
||||
err.span_suggestion_verbose(
|
||||
source_info
|
||||
.span
|
||||
.with_lo(source_info.span.lo() + BytePos(5))
|
||||
.shrink_to_lo(),
|
||||
"to reborrow the mutable reference, add `*`",
|
||||
"*",
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
} else if is_deref_coerced {
|
||||
// We don't have access to the HIR to get accurate spans, but we
|
||||
// can give a best effort structured suggestion.
|
||||
err.span_suggestion_verbose(
|
||||
source_info.span.with_hi(source_info.span.lo() + BytePos(5)),
|
||||
"if there is only one mutable reborrow, remove the `&mut`",
|
||||
"",
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// This can occur with things like `(&mut self).foo()`.
|
||||
err.span_help(source_info.span, "try removing `&mut` here");
|
||||
|
|
|
|||
37
tests/ui/borrowck/mut-borrow-of-mut-ref.fixed
Normal file
37
tests/ui/borrowck/mut-borrow-of-mut-ref.fixed
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
// Suggest not mutably borrowing a mutable reference
|
||||
//@ run-rustfix
|
||||
#![crate_type = "rlib"]
|
||||
|
||||
pub fn f(mut b: &mut i32) {
|
||||
//~^ 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(b);
|
||||
//~^ 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
|
||||
}
|
||||
|
||||
pub fn g(mut 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
|
||||
}
|
||||
|
||||
pub fn h(_: &mut i32) {}
|
||||
|
||||
trait Foo {
|
||||
fn bar(&mut self);
|
||||
}
|
||||
|
||||
impl Foo for &mut String {
|
||||
fn bar(&mut self) {}
|
||||
}
|
||||
|
||||
pub fn baz(mut f: &mut String) { //~ HELP consider making the binding mutable
|
||||
f.bar(); //~ ERROR cannot borrow `f` as mutable, as it is not declared as mutable
|
||||
//~^ NOTE cannot borrow as mutable
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
// Suggest not mutably borrowing a mutable reference
|
||||
//@ run-rustfix
|
||||
#![crate_type = "rlib"]
|
||||
|
||||
pub fn f(b: &mut i32) {
|
||||
|
|
@ -11,7 +12,6 @@ pub fn f(b: &mut i32) {
|
|||
//~| HELP: if there is only one mutable reborrow, remove the `&mut`
|
||||
g(&mut &mut b);
|
||||
//~^ 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
|
||||
|
|
@ -19,7 +19,6 @@ pub fn g(b: &mut i32) { //~ NOTE: the binding is already a mutable borrow
|
|||
h(&mut &mut b);
|
||||
//~^ ERROR: cannot borrow
|
||||
//~| NOTE: cannot borrow as mutable
|
||||
//~| HELP: if there is only one mutable reborrow, remove the `&mut`
|
||||
}
|
||||
|
||||
pub fn h(_: &mut i32) {}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
error[E0596]: cannot borrow `b` as mutable, as it is not declared as mutable
|
||||
--> $DIR/mut-borrow-of-mut-ref.rs:4:10
|
||||
--> $DIR/mut-borrow-of-mut-ref.rs:5:10
|
||||
|
|
||||
LL | pub fn f(b: &mut i32) {
|
||||
| ^ not mutable
|
||||
|
|
@ -11,7 +11,7 @@ LL | g(&mut &mut b);
|
|||
| ------ cannot borrow as mutable
|
||||
|
|
||||
note: the binding is already a mutable borrow
|
||||
--> $DIR/mut-borrow-of-mut-ref.rs:4:13
|
||||
--> $DIR/mut-borrow-of-mut-ref.rs:5:13
|
||||
|
|
||||
LL | pub fn f(b: &mut i32) {
|
||||
| ^^^^^^^^
|
||||
|
|
@ -24,11 +24,6 @@ help: if there is only one mutable reborrow, remove the `&mut`
|
|||
LL - h(&mut b);
|
||||
LL + h(b);
|
||||
|
|
||||
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:19:12
|
||||
|
|
@ -45,14 +40,9 @@ 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:36:5
|
||||
--> $DIR/mut-borrow-of-mut-ref.rs:35:5
|
||||
|
|
||||
LL | f.bar();
|
||||
| ^ cannot borrow as mutable
|
||||
|
|
|
|||
23
tests/ui/borrowck/reborrow-in-match-suggest-deref.fixed
Normal file
23
tests/ui/borrowck/reborrow-in-match-suggest-deref.fixed
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
// Regression test for #81059.
|
||||
// edition:2024
|
||||
//@ run-rustfix
|
||||
|
||||
#![allow(unused)]
|
||||
|
||||
fn test(mut outer: &mut Option<i32>) {
|
||||
//~^ NOTE: the binding is already a mutable borrow
|
||||
//~| HELP: consider making the binding mutable if you need to reborrow multiple times
|
||||
match (&mut *outer, 23) {
|
||||
//~^ ERROR: cannot borrow `outer` as mutable, as it is not declared as mutable
|
||||
//~| NOTE: cannot borrow as mutable
|
||||
//~| HELP: to reborrow the mutable reference, add `*`
|
||||
(Some(inner), _) => {
|
||||
*inner = 17;
|
||||
}
|
||||
_ => {
|
||||
*outer = Some(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
23
tests/ui/borrowck/reborrow-in-match-suggest-deref.rs
Normal file
23
tests/ui/borrowck/reborrow-in-match-suggest-deref.rs
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
// Regression test for #81059.
|
||||
// edition:2024
|
||||
//@ run-rustfix
|
||||
|
||||
#![allow(unused)]
|
||||
|
||||
fn test(outer: &mut Option<i32>) {
|
||||
//~^ NOTE: the binding is already a mutable borrow
|
||||
//~| HELP: consider making the binding mutable if you need to reborrow multiple times
|
||||
match (&mut outer, 23) {
|
||||
//~^ ERROR: cannot borrow `outer` as mutable, as it is not declared as mutable
|
||||
//~| NOTE: cannot borrow as mutable
|
||||
//~| HELP: to reborrow the mutable reference, add `*`
|
||||
(Some(inner), _) => {
|
||||
*inner = 17;
|
||||
}
|
||||
_ => {
|
||||
*outer = Some(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
23
tests/ui/borrowck/reborrow-in-match-suggest-deref.stderr
Normal file
23
tests/ui/borrowck/reborrow-in-match-suggest-deref.stderr
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
error[E0596]: cannot borrow `outer` as mutable, as it is not declared as mutable
|
||||
--> $DIR/reborrow-in-match-suggest-deref.rs:10:12
|
||||
|
|
||||
LL | match (&mut outer, 23) {
|
||||
| ^^^^^^^^^^ cannot borrow as mutable
|
||||
|
|
||||
note: the binding is already a mutable borrow
|
||||
--> $DIR/reborrow-in-match-suggest-deref.rs:7:16
|
||||
|
|
||||
LL | fn test(outer: &mut Option<i32>) {
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
help: consider making the binding mutable if you need to reborrow multiple times
|
||||
|
|
||||
LL | fn test(mut outer: &mut Option<i32>) {
|
||||
| +++
|
||||
help: to reborrow the mutable reference, add `*`
|
||||
|
|
||||
LL | match (&mut *outer, 23) {
|
||||
| +
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0596`.
|
||||
Loading…
Add table
Add a link
Reference in a new issue