diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs index b861a4f2861..27f700ebbda 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs @@ -1241,35 +1241,41 @@ fn suggest_ampmut<'tcx>( } } - let (suggestibility, highlight_span) = match opt_ty_info { + let (binding_exists, span) = match opt_ty_info { // if this is a variable binding with an explicit type, - // try to highlight that for the suggestion. + // then we will suggest changing it to be mutable. + // this is `Applicability::MachineApplicable`. Some(ty_span) => (true, ty_span), - // otherwise, just highlight the span associated with - // the (MIR) LocalDecl. + // otherwise, we'll suggest *adding* an annotated type, we'll suggest + // the RHS's type for that. + // this is `Applicability::HasPlaceholders`. None => (false, local_decl.source_info.span), }; - if let Ok(src) = tcx.sess.source_map().span_to_snippet(highlight_span) - && let (true, Some(ws_pos)) = (src.starts_with("&'"), src.find(char::is_whitespace)) + // if the binding already exists and is a reference with a explicit + // lifetime, then we can suggest adding ` mut`. this is special-cased from + // the path without a explicit lifetime. + if let Ok(src) = tcx.sess.source_map().span_to_snippet(span) + && src.starts_with("&'") + // note that `& 'a T` is invalid so this is correct. + && let Some(ws_pos) = src.find(char::is_whitespace) { - let lt_name = &src[1..ws_pos]; - let ty = &src[ws_pos..]; - return (true, highlight_span, format!("&{lt_name} mut{ty}")); + let span = span.with_lo(span.lo() + BytePos(ws_pos as u32)).shrink_to_lo(); + (true, span, " mut".to_owned()) + } else { + let ty_mut = local_decl.ty.builtin_deref(true).unwrap(); + assert_eq!(ty_mut.mutbl, hir::Mutability::Not); + ( + binding_exists, + span, + if local_decl.ty.is_ref() { + format!("&mut {}", ty_mut.ty) + } else { + format!("*mut {}", ty_mut.ty) + }, + ) } - - let ty_mut = local_decl.ty.builtin_deref(true).unwrap(); - assert_eq!(ty_mut.mutbl, hir::Mutability::Not); - ( - suggestibility, - highlight_span, - if local_decl.ty.is_ref() { - format!("&mut {}", ty_mut.ty) - } else { - format!("*mut {}", ty_mut.ty) - }, - ) } fn is_closure_or_generator(ty: Ty<'_>) -> bool { diff --git a/tests/ui/did_you_mean/issue-39544.stderr b/tests/ui/did_you_mean/issue-39544.stderr index 8dc0512a945..97f0ec60603 100644 --- a/tests/ui/did_you_mean/issue-39544.stderr +++ b/tests/ui/did_you_mean/issue-39544.stderr @@ -73,7 +73,7 @@ LL | let _ = &mut self.x; help: consider changing this to be a mutable reference | LL | fn foo3<'a>(self: &'a mut Self, other: &Z) { - | ~~~~~~~~~~~~ + | +++ error[E0596]: cannot borrow `other.x` as mutable, as it is behind a `&` reference --> $DIR/issue-39544.rs:31:17 diff --git a/tests/ui/issues/issue-61623.stderr b/tests/ui/issues/issue-61623.stderr index 5fcc338557c..bedea3890a3 100644 --- a/tests/ui/issues/issue-61623.stderr +++ b/tests/ui/issues/issue-61623.stderr @@ -7,7 +7,7 @@ LL | f2(|| x.0, f1(x.1)) help: consider changing this to be a mutable reference | LL | fn f3<'a>(x: &'a mut ((), &'a mut ())) { - | ~~~~~~~~~~~~~~~~~~~~~~~~ + | +++ error: aborting due to previous error diff --git a/tests/ui/span/borrowck-borrow-overloaded-auto-deref-mut.stderr b/tests/ui/span/borrowck-borrow-overloaded-auto-deref-mut.stderr index 570328fc211..e640d40913e 100644 --- a/tests/ui/span/borrowck-borrow-overloaded-auto-deref-mut.stderr +++ b/tests/ui/span/borrowck-borrow-overloaded-auto-deref-mut.stderr @@ -50,7 +50,7 @@ LL | x.y = 3; help: consider changing this to be a mutable reference | LL | fn assign_field2<'a>(x: &'a mut Own) { - | ~~~~~~~~~~~~~~~~~~ + | +++ error[E0499]: cannot borrow `*x` as mutable more than once at a time --> $DIR/borrowck-borrow-overloaded-auto-deref-mut.rs:101:5 @@ -104,7 +104,7 @@ LL | *x.y_mut() = 3; help: consider changing this to be a mutable reference | LL | fn assign_method2<'a>(x: &'a mut Own) { - | ~~~~~~~~~~~~~~~~~~ + | +++ error: aborting due to 10 previous errors diff --git a/tests/ui/span/borrowck-borrow-overloaded-deref-mut.stderr b/tests/ui/span/borrowck-borrow-overloaded-deref-mut.stderr index 3fed7b3f4dc..dbd52dc2d38 100644 --- a/tests/ui/span/borrowck-borrow-overloaded-deref-mut.stderr +++ b/tests/ui/span/borrowck-borrow-overloaded-deref-mut.stderr @@ -18,7 +18,7 @@ LL | &mut **x help: consider changing this to be a mutable reference | LL | fn deref_extend_mut1<'a>(x: &'a mut Own) -> &'a mut isize { - | ~~~~~~~~~~~~~~~~~~ + | +++ error[E0596]: cannot borrow `x` as mutable, as it is not declared as mutable --> $DIR/borrowck-borrow-overloaded-deref-mut.rs:49:6 @@ -40,7 +40,7 @@ LL | **x = 3; help: consider changing this to be a mutable reference | LL | fn assign2<'a>(x: &'a mut Own) { - | ~~~~~~~~~~~~~~~~~~ + | +++ error: aborting due to 4 previous errors diff --git a/tests/ui/span/mut-arg-hint.stderr b/tests/ui/span/mut-arg-hint.stderr index 96ce4d5bc6c..9b82012291c 100644 --- a/tests/ui/span/mut-arg-hint.stderr +++ b/tests/ui/span/mut-arg-hint.stderr @@ -18,7 +18,7 @@ LL | a.push_str("foo"); help: consider changing this to be a mutable reference | LL | pub fn foo<'a>(mut a: &'a mut String) { - | ~~~~~~~~~~~~~~ + | +++ error[E0596]: cannot borrow `*a` as mutable, as it is behind a `&` reference --> $DIR/mut-arg-hint.rs:15:9 diff --git a/tests/ui/trivial-bounds/trivial-bounds-inconsistent-copy-reborrow.stderr b/tests/ui/trivial-bounds/trivial-bounds-inconsistent-copy-reborrow.stderr index 39b60c31197..c054ddb893d 100644 --- a/tests/ui/trivial-bounds/trivial-bounds-inconsistent-copy-reborrow.stderr +++ b/tests/ui/trivial-bounds/trivial-bounds-inconsistent-copy-reborrow.stderr @@ -7,7 +7,7 @@ LL | *t help: consider changing this to be a mutable reference | LL | fn reborrow_mut<'a>(t: &'a mut &'a mut i32) -> &'a mut i32 where &'a mut i32: Copy { - | ~~~~~~~~~~~~~~~~~~~ + | +++ error[E0596]: cannot borrow `**t` as mutable, as it is behind a `&` reference --> $DIR/trivial-bounds-inconsistent-copy-reborrow.rs:10:6 @@ -18,7 +18,7 @@ LL | {*t} help: consider changing this to be a mutable reference | LL | fn copy_reborrow_mut<'a>(t: &'a mut &'a mut i32) -> &'a mut i32 where &'a mut i32: Copy { - | ~~~~~~~~~~~~~~~~~~~ + | +++ error: aborting due to 2 previous errors