about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEsteban Küber <esteban@kuber.com.ar>2025-07-10 17:15:42 +0000
committerEsteban Küber <esteban@kuber.com.ar>2025-07-10 17:23:29 +0000
commit7dfc3e9af45ad54832c083baf0d2d0ca8e0d979a (patch)
tree402cbafb83b5b24077b444f4e71f38d0a613a088
parent78a6e132984dba0303ebad7dcfd1305c93ad5835 (diff)
downloadrust-7dfc3e9af45ad54832c083baf0d2d0ca8e0d979a.tar.gz
rust-7dfc3e9af45ad54832c083baf0d2d0ca8e0d979a.zip
Rework borrowing suggestions to use `Expr` instead of just `Span`
In the suggestion machinery for borrowing expressions and types, always use the available obligation `Span` to find the appropriate `Expr` to perform appropriateness checks no the `ExprKind` instead of on the textual snippet corresponding to the `Span`.

Unify the logic for the case where `&` *and* `&mut` are appropriate with the logic for only one of those cases.

Handle the case when `S::foo()` should have been `<&S>::foo()` (instead of suggesting the prior `&S::foo()`.
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs228
-rw-r--r--tests/ui/consts/const-size_of_val-align_of_val-extern-type.stderr20
-rw-r--r--tests/ui/derives/deriving-copyclone.stderr30
-rw-r--r--tests/ui/extern/unsized-extern-derefmove.stderr8
-rw-r--r--tests/ui/for/issue-20605.current.stderr9
-rw-r--r--tests/ui/for/issue-20605.next.stderr9
-rw-r--r--tests/ui/self/arbitrary_self_types_generic_over_receiver.stderr16
-rw-r--r--tests/ui/suggestions/imm-ref-trait-object-literal.stderr10
-rw-r--r--tests/ui/suggestions/issue-104961.stderr10
-rw-r--r--tests/ui/suggestions/issue-62843.stderr10
-rw-r--r--tests/ui/suggestions/issue-84973-2.stderr10
-rw-r--r--tests/ui/suggestions/issue-84973-negative.stderr10
-rw-r--r--tests/ui/suggestions/issue-84973.stderr10
-rw-r--r--tests/ui/suggestions/suggest-adding-reference-to-trait-assoc-item.stderr20
-rw-r--r--tests/ui/suggestions/suggest-imm-mut-trait-implementations.stderr20
-rw-r--r--tests/ui/traits/negative-impls/negated-auto-traits-error.stderr10
16 files changed, 217 insertions, 213 deletions
diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs
index 3e64573aa03..8c9eb41568f 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs
@@ -1321,7 +1321,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
             let imm_ref_self_ty_satisfies_pred = mk_result(trait_pred_and_imm_ref);
             let mut_ref_self_ty_satisfies_pred = mk_result(trait_pred_and_mut_ref);
 
-            let (ref_inner_ty_satisfies_pred, ref_inner_ty_mut) =
+            let (ref_inner_ty_satisfies_pred, ref_inner_ty_is_mut) =
                 if let ObligationCauseCode::WhereClauseInExpr(..) = obligation.cause.code()
                     && let ty::Ref(_, ty, mutability) = old_pred.self_ty().skip_binder().kind()
                 {
@@ -1333,117 +1333,139 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                     (false, false)
                 };
 
-            if imm_ref_self_ty_satisfies_pred
-                || mut_ref_self_ty_satisfies_pred
-                || ref_inner_ty_satisfies_pred
-            {
-                if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
-                    // We don't want a borrowing suggestion on the fields in structs,
-                    // ```
-                    // struct Foo {
-                    //  the_foos: Vec<Foo>
-                    // }
-                    // ```
-                    if !matches!(
-                        span.ctxt().outer_expn_data().kind,
-                        ExpnKind::Root | ExpnKind::Desugaring(DesugaringKind::ForLoop)
-                    ) {
-                        return false;
-                    }
-                    if snippet.starts_with('&') {
-                        // This is already a literal borrow and the obligation is failing
-                        // somewhere else in the obligation chain. Do not suggest non-sense.
-                        return false;
-                    }
-                    // We have a very specific type of error, where just borrowing this argument
-                    // might solve the problem. In cases like this, the important part is the
-                    // original type obligation, not the last one that failed, which is arbitrary.
-                    // Because of this, we modify the error to refer to the original obligation and
-                    // return early in the caller.
-
-                    let msg = format!(
-                        "the trait bound `{}` is not satisfied",
-                        self.tcx.short_string(old_pred, err.long_ty_path()),
-                    );
-                    let self_ty_str =
-                        self.tcx.short_string(old_pred.self_ty().skip_binder(), err.long_ty_path());
-                    if has_custom_message {
-                        err.note(msg);
-                    } else {
-                        err.messages = vec![(rustc_errors::DiagMessage::from(msg), Style::NoStyle)];
-                    }
-                    err.span_label(
-                        span,
-                        format!(
-                            "the trait `{}` is not implemented for `{self_ty_str}`",
-                            old_pred.print_modifiers_and_trait_path()
-                        ),
-                    );
+            let is_immut = imm_ref_self_ty_satisfies_pred
+                || (ref_inner_ty_satisfies_pred && !ref_inner_ty_is_mut);
+            let is_mut = mut_ref_self_ty_satisfies_pred || ref_inner_ty_is_mut;
+            if !is_immut && !is_mut {
+                return false;
+            }
+            let Ok(_snippet) = self.tcx.sess.source_map().span_to_snippet(span) else {
+                return false;
+            };
+            // We don't want a borrowing suggestion on the fields in structs
+            // ```
+            // #[derive(Clone)]
+            // struct Foo {
+            //     the_foos: Vec<Foo>
+            // }
+            // ```
+            if !matches!(
+                span.ctxt().outer_expn_data().kind,
+                ExpnKind::Root | ExpnKind::Desugaring(DesugaringKind::ForLoop)
+            ) {
+                return false;
+            }
+            // We have a very specific type of error, where just borrowing this argument
+            // might solve the problem. In cases like this, the important part is the
+            // original type obligation, not the last one that failed, which is arbitrary.
+            // Because of this, we modify the error to refer to the original obligation and
+            // return early in the caller.
 
-                    if imm_ref_self_ty_satisfies_pred && mut_ref_self_ty_satisfies_pred {
-                        err.span_suggestions(
-                            span.shrink_to_lo(),
-                            "consider borrowing here",
-                            ["&".to_string(), "&mut ".to_string()],
-                            Applicability::MaybeIncorrect,
-                        );
-                    } else {
-                        let is_mut = mut_ref_self_ty_satisfies_pred || ref_inner_ty_mut;
-                        let sugg_prefix = format!("&{}", if is_mut { "mut " } else { "" });
-                        let sugg_msg = format!(
-                            "consider{} borrowing here",
-                            if is_mut { " mutably" } else { "" }
-                        );
+            let mut label = || {
+                let msg = format!(
+                    "the trait bound `{}` is not satisfied",
+                    self.tcx.short_string(old_pred, err.long_ty_path()),
+                );
+                let self_ty_str =
+                    self.tcx.short_string(old_pred.self_ty().skip_binder(), err.long_ty_path());
+                if has_custom_message {
+                    err.note(msg);
+                } else {
+                    err.messages = vec![(rustc_errors::DiagMessage::from(msg), Style::NoStyle)];
+                }
+                err.span_label(
+                    span,
+                    format!(
+                        "the trait `{}` is not implemented for `{self_ty_str}`",
+                        old_pred.print_modifiers_and_trait_path()
+                    ),
+                );
+            };
 
-                        // Issue #109436, we need to add parentheses properly for method calls
-                        // for example, `foo.into()` should be `(&foo).into()`
-                        if let Some(_) =
-                            self.tcx.sess.source_map().span_look_ahead(span, ".", Some(50))
-                        {
-                            err.multipart_suggestion_verbose(
-                                sugg_msg,
-                                vec![
-                                    (span.shrink_to_lo(), format!("({sugg_prefix}")),
-                                    (span.shrink_to_hi(), ")".to_string()),
-                                ],
-                                Applicability::MaybeIncorrect,
-                            );
-                            return true;
-                        }
+            let mut sugg_prefixes = vec![];
+            if is_immut {
+                sugg_prefixes.push("&");
+            }
+            if is_mut {
+                sugg_prefixes.push("&mut ");
+            }
+            let sugg_msg = format!(
+                "consider{} borrowing here",
+                if is_mut && !is_immut { " mutably" } else { "" },
+            );
 
-                        // Issue #104961, we need to add parentheses properly for compound expressions
-                        // for example, `x.starts_with("hi".to_string() + "you")`
-                        // should be `x.starts_with(&("hi".to_string() + "you"))`
-                        let Some(body) = self.tcx.hir_maybe_body_owned_by(obligation.cause.body_id)
-                        else {
-                            return false;
-                        };
-                        let mut expr_finder = FindExprBySpan::new(span, self.tcx);
-                        expr_finder.visit_expr(body.value);
-                        let Some(expr) = expr_finder.result else {
-                            return false;
-                        };
-                        let needs_parens = expr_needs_parens(expr);
+            // Issue #104961, we need to add parentheses properly for compound expressions
+            // for example, `x.starts_with("hi".to_string() + "you")`
+            // should be `x.starts_with(&("hi".to_string() + "you"))`
+            let Some(body) = self.tcx.hir_maybe_body_owned_by(obligation.cause.body_id) else {
+                return false;
+            };
+            let mut expr_finder = FindExprBySpan::new(span, self.tcx);
+            expr_finder.visit_expr(body.value);
 
-                        let span = if needs_parens { span } else { span.shrink_to_lo() };
-                        let suggestions = if !needs_parens {
-                            vec![(span.shrink_to_lo(), sugg_prefix)]
-                        } else {
+            if let Some(ty) = expr_finder.ty_result {
+                if let hir::Node::Expr(expr) = self.tcx.parent_hir_node(ty.hir_id)
+                    && let hir::ExprKind::Path(hir::QPath::TypeRelative(_, _)) = expr.kind
+                    && ty.span == span
+                {
+                    // We've encountered something like `str::from("")`, where the intended code
+                    // was likely `<&str>::from("")`. #143393.
+                    label();
+                    err.multipart_suggestions(
+                        sugg_msg,
+                        sugg_prefixes.into_iter().map(|sugg_prefix| {
                             vec![
-                                (span.shrink_to_lo(), format!("{sugg_prefix}(")),
-                                (span.shrink_to_hi(), ")".to_string()),
+                                (span.shrink_to_lo(), format!("<{sugg_prefix}")),
+                                (span.shrink_to_hi(), ">".to_string()),
                             ]
-                        };
-                        err.multipart_suggestion_verbose(
-                            sugg_msg,
-                            suggestions,
-                            Applicability::MaybeIncorrect,
-                        );
-                    }
+                        }),
+                        Applicability::MaybeIncorrect,
+                    );
                     return true;
                 }
+                return false;
             }
-            return false;
+            let Some(expr) = expr_finder.result else {
+                return false;
+            };
+            if let hir::ExprKind::AddrOf(_, _, _) = expr.kind {
+                return false;
+            }
+            let needs_parens_post = expr_needs_parens(expr);
+            let needs_parens_pre = match self.tcx.parent_hir_node(expr.hir_id) {
+                Node::Expr(e)
+                    if let hir::ExprKind::MethodCall(_, base, _, _) = e.kind
+                        && base.hir_id == expr.hir_id =>
+                {
+                    true
+                }
+                _ => false,
+            };
+
+            label();
+            let suggestions = sugg_prefixes.into_iter().map(|sugg_prefix| {
+                match (needs_parens_pre, needs_parens_post) {
+                    (false, false) => vec![(span.shrink_to_lo(), sugg_prefix.to_string())],
+                    // We have something like `foo.bar()`, where we want to bororw foo, so we need
+                    // to suggest `(&mut foo).bar()`.
+                    (false, true) => vec![
+                        (span.shrink_to_lo(), format!("{sugg_prefix}(")),
+                        (span.shrink_to_hi(), ")".to_string()),
+                    ],
+                    // Issue #109436, we need to add parentheses properly for method calls
+                    // for example, `foo.into()` should be `(&foo).into()`
+                    (true, false) => vec![
+                        (span.shrink_to_lo(), format!("({sugg_prefix}")),
+                        (span.shrink_to_hi(), ")".to_string()),
+                    ],
+                    (true, true) => vec![
+                        (span.shrink_to_lo(), format!("({sugg_prefix}(")),
+                        (span.shrink_to_hi(), "))".to_string()),
+                    ],
+                }
+            });
+            err.multipart_suggestions(sugg_msg, suggestions, Applicability::MaybeIncorrect);
+            return true;
         };
 
         if let ObligationCauseCode::ImplDerived(cause) = &*code {
diff --git a/tests/ui/consts/const-size_of_val-align_of_val-extern-type.stderr b/tests/ui/consts/const-size_of_val-align_of_val-extern-type.stderr
index 6d6bc157771..825b9e94158 100644
--- a/tests/ui/consts/const-size_of_val-align_of_val-extern-type.stderr
+++ b/tests/ui/consts/const-size_of_val-align_of_val-extern-type.stderr
@@ -2,25 +2,37 @@ error[E0277]: the size for values of type `Opaque` cannot be known
   --> $DIR/const-size_of_val-align_of_val-extern-type.rs:10:43
    |
 LL | const _SIZE: usize = unsafe { size_of_val(&4 as *const i32 as *const Opaque) };
-   |                               ----------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a known size
+   |                               ----------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `MetaSized` is not implemented for `Opaque`
    |                               |
    |                               required by a bound introduced by this call
    |
-   = help: the trait `MetaSized` is not implemented for `Opaque`
+   = note: the trait bound `Opaque: MetaSized` is not satisfied
 note: required by a bound in `std::intrinsics::size_of_val`
   --> $SRC_DIR/core/src/intrinsics/mod.rs:LL:COL
+help: consider borrowing here
+   |
+LL | const _SIZE: usize = unsafe { size_of_val(&(&4 as *const i32 as *const Opaque)) };
+   |                                           ++                                 +
+LL | const _SIZE: usize = unsafe { size_of_val(&mut (&4 as *const i32 as *const Opaque)) };
+   |                                           ++++++                                 +
 
 error[E0277]: the size for values of type `Opaque` cannot be known
   --> $DIR/const-size_of_val-align_of_val-extern-type.rs:12:45
    |
 LL | const _ALIGN: usize = unsafe { align_of_val(&4 as *const i32 as *const Opaque) };
-   |                                ------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a known size
+   |                                ------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `MetaSized` is not implemented for `Opaque`
    |                                |
    |                                required by a bound introduced by this call
    |
-   = help: the trait `MetaSized` is not implemented for `Opaque`
+   = note: the trait bound `Opaque: MetaSized` is not satisfied
 note: required by a bound in `std::intrinsics::align_of_val`
   --> $SRC_DIR/core/src/intrinsics/mod.rs:LL:COL
+help: consider borrowing here
+   |
+LL | const _ALIGN: usize = unsafe { align_of_val(&(&4 as *const i32 as *const Opaque)) };
+   |                                             ++                                 +
+LL | const _ALIGN: usize = unsafe { align_of_val(&mut (&4 as *const i32 as *const Opaque)) };
+   |                                             ++++++                                 +
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/derives/deriving-copyclone.stderr b/tests/ui/derives/deriving-copyclone.stderr
index befff880280..707a63ba28d 100644
--- a/tests/ui/derives/deriving-copyclone.stderr
+++ b/tests/ui/derives/deriving-copyclone.stderr
@@ -2,8 +2,10 @@ error[E0277]: the trait bound `B<C>: Copy` is not satisfied
   --> $DIR/deriving-copyclone.rs:31:26
    |
 LL |     is_copy(B { a: 1, b: C });
-   |     -------              ^ the trait `Copy` is not implemented for `B<C>`
-   |     |
+   |     -------              ^
+   |     |                    |
+   |     |                    the trait `Copy` is not implemented for `B<C>`
+   |     |                    help: consider borrowing here: `&`
    |     required by a bound introduced by this call
    |
 note: required for `B<C>` to implement `Copy`
@@ -16,17 +18,15 @@ note: required by a bound in `is_copy`
    |
 LL | fn is_copy<T: Copy>(_: T) {}
    |               ^^^^ required by this bound in `is_copy`
-help: consider borrowing here
-   |
-LL |     is_copy(B { a: 1, b: &C });
-   |                          +
 
 error[E0277]: the trait bound `B<C>: Clone` is not satisfied
   --> $DIR/deriving-copyclone.rs:32:27
    |
 LL |     is_clone(B { a: 1, b: C });
-   |     --------              ^ the trait `Clone` is not implemented for `B<C>`
-   |     |
+   |     --------              ^
+   |     |                     |
+   |     |                     the trait `Clone` is not implemented for `B<C>`
+   |     |                     help: consider borrowing here: `&`
    |     required by a bound introduced by this call
    |
 note: required for `B<C>` to implement `Clone`
@@ -39,17 +39,15 @@ note: required by a bound in `is_clone`
    |
 LL | fn is_clone<T: Clone>(_: T) {}
    |                ^^^^^ required by this bound in `is_clone`
-help: consider borrowing here
-   |
-LL |     is_clone(B { a: 1, b: &C });
-   |                           +
 
 error[E0277]: the trait bound `B<D>: Copy` is not satisfied
   --> $DIR/deriving-copyclone.rs:35:26
    |
 LL |     is_copy(B { a: 1, b: D });
-   |     -------              ^ the trait `Copy` is not implemented for `B<D>`
-   |     |
+   |     -------              ^
+   |     |                    |
+   |     |                    the trait `Copy` is not implemented for `B<D>`
+   |     |                    help: consider borrowing here: `&`
    |     required by a bound introduced by this call
    |
 note: required for `B<D>` to implement `Copy`
@@ -62,10 +60,6 @@ note: required by a bound in `is_copy`
    |
 LL | fn is_copy<T: Copy>(_: T) {}
    |               ^^^^ required by this bound in `is_copy`
-help: consider borrowing here
-   |
-LL |     is_copy(B { a: 1, b: &D });
-   |                          +
 
 error: aborting due to 3 previous errors
 
diff --git a/tests/ui/extern/unsized-extern-derefmove.stderr b/tests/ui/extern/unsized-extern-derefmove.stderr
index d6be76a9d62..a9efc2e66e3 100644
--- a/tests/ui/extern/unsized-extern-derefmove.stderr
+++ b/tests/ui/extern/unsized-extern-derefmove.stderr
@@ -21,10 +21,10 @@ note: required by a bound in `Box::<T>::from_raw`
   --> $SRC_DIR/alloc/src/boxed.rs:LL:COL
 help: consider borrowing here
    |
-LL |     Box::from_raw(&0 as *mut _)
-   |                   +
-LL |     Box::from_raw(&mut 0 as *mut _)
-   |                   ++++
+LL |     Box::from_raw(&(0 as *mut _))
+   |                   ++           +
+LL |     Box::from_raw(&mut (0 as *mut _))
+   |                   ++++++           +
 
 error[E0277]: the size for values of type `Device` cannot be known
   --> $DIR/unsized-extern-derefmove.rs:11:5
diff --git a/tests/ui/for/issue-20605.current.stderr b/tests/ui/for/issue-20605.current.stderr
index 1a66cb41464..289dca289ae 100644
--- a/tests/ui/for/issue-20605.current.stderr
+++ b/tests/ui/for/issue-20605.current.stderr
@@ -2,14 +2,13 @@ error[E0277]: `dyn Iterator<Item = &'a mut u8>` is not an iterator
   --> $DIR/issue-20605.rs:6:17
    |
 LL |     for item in *things { *item = 0 }
-   |                 ^^^^^^^ the trait `IntoIterator` is not implemented for `dyn Iterator<Item = &'a mut u8>`
+   |                 -^^^^^^
+   |                 |
+   |                 the trait `IntoIterator` is not implemented for `dyn Iterator<Item = &'a mut u8>`
+   |                 help: consider mutably borrowing here: `&mut`
    |
    = note: the trait bound `dyn Iterator<Item = &'a mut u8>: IntoIterator` is not satisfied
    = note: required for `dyn Iterator<Item = &'a mut u8>` to implement `IntoIterator`
-help: consider mutably borrowing here
-   |
-LL |     for item in &mut *things { *item = 0 }
-   |                 ++++
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/for/issue-20605.next.stderr b/tests/ui/for/issue-20605.next.stderr
index 1a66cb41464..289dca289ae 100644
--- a/tests/ui/for/issue-20605.next.stderr
+++ b/tests/ui/for/issue-20605.next.stderr
@@ -2,14 +2,13 @@ error[E0277]: `dyn Iterator<Item = &'a mut u8>` is not an iterator
   --> $DIR/issue-20605.rs:6:17
    |
 LL |     for item in *things { *item = 0 }
-   |                 ^^^^^^^ the trait `IntoIterator` is not implemented for `dyn Iterator<Item = &'a mut u8>`
+   |                 -^^^^^^
+   |                 |
+   |                 the trait `IntoIterator` is not implemented for `dyn Iterator<Item = &'a mut u8>`
+   |                 help: consider mutably borrowing here: `&mut`
    |
    = note: the trait bound `dyn Iterator<Item = &'a mut u8>: IntoIterator` is not satisfied
    = note: required for `dyn Iterator<Item = &'a mut u8>` to implement `IntoIterator`
-help: consider mutably borrowing here
-   |
-LL |     for item in &mut *things { *item = 0 }
-   |                 ++++
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/self/arbitrary_self_types_generic_over_receiver.stderr b/tests/ui/self/arbitrary_self_types_generic_over_receiver.stderr
index 2da3925341e..7a3d8b43c2e 100644
--- a/tests/ui/self/arbitrary_self_types_generic_over_receiver.stderr
+++ b/tests/ui/self/arbitrary_self_types_generic_over_receiver.stderr
@@ -30,10 +30,10 @@ LL |     fn a(self: impl Receiver<Target=Self>) -> u32 {
    |                     ^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Foo::a`
 help: consider borrowing here
    |
-LL |     &foo.a();
-   |     +
-LL |     &mut foo.a();
-   |     ++++
+LL |     (&foo).a();
+   |     ++   +
+LL |     (&mut foo).a();
+   |     +++++    +
 
 error[E0277]: the trait bound `Foo: Deref` is not satisfied
   --> $DIR/arbitrary_self_types_generic_over_receiver.rs:21:9
@@ -48,10 +48,10 @@ LL |     fn b(self: impl Deref<Target=Self>) -> u32 {
    |                     ^^^^^^^^^^^^^^^^^^ required by this bound in `Foo::b`
 help: consider borrowing here
    |
-LL |     &foo.b();
-   |     +
-LL |     &mut foo.b();
-   |     ++++
+LL |     (&foo).b();
+   |     ++   +
+LL |     (&mut foo).b();
+   |     +++++    +
 
 error: aborting due to 4 previous errors
 
diff --git a/tests/ui/suggestions/imm-ref-trait-object-literal.stderr b/tests/ui/suggestions/imm-ref-trait-object-literal.stderr
index 4b770d572c5..62f1c03e9aa 100644
--- a/tests/ui/suggestions/imm-ref-trait-object-literal.stderr
+++ b/tests/ui/suggestions/imm-ref-trait-object-literal.stderr
@@ -21,8 +21,10 @@ error[E0277]: the trait bound `S: Trait` is not satisfied
   --> $DIR/imm-ref-trait-object-literal.rs:13:7
    |
 LL |   foo(s);
-   |   --- ^ the trait `Trait` is not implemented for `S`
-   |   |
+   |   --- ^
+   |   |   |
+   |   |   the trait `Trait` is not implemented for `S`
+   |   |   help: consider mutably borrowing here: `&mut`
    |   required by a bound introduced by this call
    |
 note: required by a bound in `foo`
@@ -30,10 +32,6 @@ note: required by a bound in `foo`
    |
 LL | fn foo<X: Trait>(_: X) {}
    |           ^^^^^ required by this bound in `foo`
-help: consider mutably borrowing here
-   |
-LL |   foo(&mut s);
-   |       ++++
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/suggestions/issue-104961.stderr b/tests/ui/suggestions/issue-104961.stderr
index 0d229e6dada..6f02c125cad 100644
--- a/tests/ui/suggestions/issue-104961.stderr
+++ b/tests/ui/suggestions/issue-104961.stderr
@@ -18,17 +18,15 @@ error[E0277]: the trait bound `String: Pattern` is not satisfied
   --> $DIR/issue-104961.rs:9:19
    |
 LL |     x.starts_with("hi".to_string())
-   |       ----------- ^^^^^^^^^^^^^^^^ the trait `Pattern` is not implemented for `String`
-   |       |
+   |       ----------- -^^^^^^^^^^^^^^^
+   |       |           |
+   |       |           the trait `Pattern` is not implemented for `String`
+   |       |           help: consider borrowing here: `&`
    |       required by a bound introduced by this call
    |
    = note: required for `String` to implement `Pattern`
 note: required by a bound in `core::str::<impl str>::starts_with`
   --> $SRC_DIR/core/src/str/mod.rs:LL:COL
-help: consider borrowing here
-   |
-LL |     x.starts_with(&"hi".to_string())
-   |                   +
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/suggestions/issue-62843.stderr b/tests/ui/suggestions/issue-62843.stderr
index c3c0360b3a9..c3480a8e396 100644
--- a/tests/ui/suggestions/issue-62843.stderr
+++ b/tests/ui/suggestions/issue-62843.stderr
@@ -2,17 +2,15 @@ error[E0277]: the trait bound `String: Pattern` is not satisfied
   --> $DIR/issue-62843.rs:4:32
    |
 LL |     println!("{:?}", line.find(pattern));
-   |                           ---- ^^^^^^^ the trait `Pattern` is not implemented for `String`
-   |                           |
+   |                           ---- -^^^^^^
+   |                           |    |
+   |                           |    the trait `Pattern` is not implemented for `String`
+   |                           |    help: consider borrowing here: `&`
    |                           required by a bound introduced by this call
    |
    = note: required for `String` to implement `Pattern`
 note: required by a bound in `core::str::<impl str>::find`
   --> $SRC_DIR/core/src/str/mod.rs:LL:COL
-help: consider borrowing here
-   |
-LL |     println!("{:?}", line.find(&pattern));
-   |                                +
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/suggestions/issue-84973-2.stderr b/tests/ui/suggestions/issue-84973-2.stderr
index 74995a0500a..914307008ef 100644
--- a/tests/ui/suggestions/issue-84973-2.stderr
+++ b/tests/ui/suggestions/issue-84973-2.stderr
@@ -2,8 +2,10 @@ error[E0277]: the trait bound `i32: Tr` is not satisfied
   --> $DIR/issue-84973-2.rs:11:9
    |
 LL |     foo(a);
-   |     --- ^ the trait `Tr` is not implemented for `i32`
-   |     |
+   |     --- ^
+   |     |   |
+   |     |   the trait `Tr` is not implemented for `i32`
+   |     |   help: consider mutably borrowing here: `&mut`
    |     required by a bound introduced by this call
    |
 note: required by a bound in `foo`
@@ -11,10 +13,6 @@ note: required by a bound in `foo`
    |
 LL | fn foo<T: Tr>(i: T) {}
    |           ^^ required by this bound in `foo`
-help: consider mutably borrowing here
-   |
-LL |     foo(&mut a);
-   |         ++++
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/suggestions/issue-84973-negative.stderr b/tests/ui/suggestions/issue-84973-negative.stderr
index ce838bce09e..e3e6296890f 100644
--- a/tests/ui/suggestions/issue-84973-negative.stderr
+++ b/tests/ui/suggestions/issue-84973-negative.stderr
@@ -17,8 +17,10 @@ error[E0277]: the trait bound `f32: Tr` is not satisfied
   --> $DIR/issue-84973-negative.rs:11:9
    |
 LL |     bar(b);
-   |     --- ^ the trait `Tr` is not implemented for `f32`
-   |     |
+   |     --- ^
+   |     |   |
+   |     |   the trait `Tr` is not implemented for `f32`
+   |     |   help: consider borrowing here: `&`
    |     required by a bound introduced by this call
    |
 note: required by a bound in `bar`
@@ -26,10 +28,6 @@ note: required by a bound in `bar`
    |
 LL | fn bar<T: Tr>(t: T) {}
    |           ^^ required by this bound in `bar`
-help: consider borrowing here
-   |
-LL |     bar(&b);
-   |         +
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/suggestions/issue-84973.stderr b/tests/ui/suggestions/issue-84973.stderr
index c5e1958e030..5ca91c544db 100644
--- a/tests/ui/suggestions/issue-84973.stderr
+++ b/tests/ui/suggestions/issue-84973.stderr
@@ -2,8 +2,10 @@ error[E0277]: the trait bound `Fancy: SomeTrait` is not satisfied
   --> $DIR/issue-84973.rs:6:24
    |
 LL |     let o = Other::new(f);
-   |             ---------- ^ the trait `SomeTrait` is not implemented for `Fancy`
-   |             |
+   |             ---------- ^
+   |             |          |
+   |             |          the trait `SomeTrait` is not implemented for `Fancy`
+   |             |          help: consider borrowing here: `&`
    |             required by a bound introduced by this call
    |
 note: required by a bound in `Other::<'a, G>::new`
@@ -14,10 +16,6 @@ LL |     G: SomeTrait,
 LL | {
 LL |     pub fn new(g: G) -> Self {
    |            --- required by a bound in this associated function
-help: consider borrowing here
-   |
-LL |     let o = Other::new(&f);
-   |                        +
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/suggestions/suggest-adding-reference-to-trait-assoc-item.stderr b/tests/ui/suggestions/suggest-adding-reference-to-trait-assoc-item.stderr
index 485015a98f2..146d6705243 100644
--- a/tests/ui/suggestions/suggest-adding-reference-to-trait-assoc-item.stderr
+++ b/tests/ui/suggestions/suggest-adding-reference-to-trait-assoc-item.stderr
@@ -2,23 +2,19 @@ error[E0277]: the trait bound `&mut usize: Default` is not satisfied
   --> $DIR/suggest-adding-reference-to-trait-assoc-item.rs:13:9
    |
 LL |     foo(Default::default());
-   |         ^^^^^^^^^^^^^^^^^^ the trait `Default` is not implemented for `&mut usize`
-   |
-help: consider mutably borrowing here
-   |
-LL |     foo(&mut Default::default());
-   |         ++++
+   |         -^^^^^^^^^^^^^^^^^
+   |         |
+   |         the trait `Default` is not implemented for `&mut usize`
+   |         help: consider mutably borrowing here: `&mut`
 
 error[E0277]: the trait bound `&usize: Default` is not satisfied
   --> $DIR/suggest-adding-reference-to-trait-assoc-item.rs:14:9
    |
 LL |     bar(Default::default());
-   |         ^^^^^^^^^^^^^^^^^^ the trait `Default` is not implemented for `&usize`
-   |
-help: consider borrowing here
-   |
-LL |     bar(&Default::default());
-   |         +
+   |         -^^^^^^^^^^^^^^^^^
+   |         |
+   |         the trait `Default` is not implemented for `&usize`
+   |         help: consider borrowing here: `&`
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/suggestions/suggest-imm-mut-trait-implementations.stderr b/tests/ui/suggestions/suggest-imm-mut-trait-implementations.stderr
index f2eb651eaa4..b3a0c68450e 100644
--- a/tests/ui/suggestions/suggest-imm-mut-trait-implementations.stderr
+++ b/tests/ui/suggestions/suggest-imm-mut-trait-implementations.stderr
@@ -22,8 +22,10 @@ error[E0277]: the trait bound `B: Trait` is not satisfied
   --> $DIR/suggest-imm-mut-trait-implementations.rs:21:9
    |
 LL |     foo(b);
-   |     --- ^ the trait `Trait` is not implemented for `B`
-   |     |
+   |     --- ^
+   |     |   |
+   |     |   the trait `Trait` is not implemented for `B`
+   |     |   help: consider borrowing here: `&`
    |     required by a bound introduced by this call
    |
 note: required by a bound in `foo`
@@ -31,17 +33,15 @@ note: required by a bound in `foo`
    |
 LL | fn foo<X: Trait>(_: X) {}
    |           ^^^^^ required by this bound in `foo`
-help: consider borrowing here
-   |
-LL |     foo(&b);
-   |         +
 
 error[E0277]: the trait bound `C: Trait` is not satisfied
   --> $DIR/suggest-imm-mut-trait-implementations.rs:22:9
    |
 LL |     foo(c);
-   |     --- ^ the trait `Trait` is not implemented for `C`
-   |     |
+   |     --- ^
+   |     |   |
+   |     |   the trait `Trait` is not implemented for `C`
+   |     |   help: consider mutably borrowing here: `&mut`
    |     required by a bound introduced by this call
    |
 note: required by a bound in `foo`
@@ -49,10 +49,6 @@ note: required by a bound in `foo`
    |
 LL | fn foo<X: Trait>(_: X) {}
    |           ^^^^^ required by this bound in `foo`
-help: consider mutably borrowing here
-   |
-LL |     foo(&mut c);
-   |         ++++
 
 error: aborting due to 3 previous errors
 
diff --git a/tests/ui/traits/negative-impls/negated-auto-traits-error.stderr b/tests/ui/traits/negative-impls/negated-auto-traits-error.stderr
index 8f5b937e586..cc5ca471788 100644
--- a/tests/ui/traits/negative-impls/negated-auto-traits-error.stderr
+++ b/tests/ui/traits/negative-impls/negated-auto-traits-error.stderr
@@ -61,8 +61,10 @@ error[E0277]: `dummy2::TestType` cannot be sent between threads safely
   --> $DIR/negated-auto-traits-error.rs:48:13
    |
 LL |     is_send(Box::new(TestType));
-   |     ------- ^^^^^^^^^^^^^^^^^^ the trait `Send` is not implemented for `Unique<dummy2::TestType>`
-   |     |
+   |     ------- -^^^^^^^^^^^^^^^^^
+   |     |       |
+   |     |       the trait `Send` is not implemented for `Unique<dummy2::TestType>`
+   |     |       help: consider borrowing here: `&`
    |     required by a bound introduced by this call
    |
    = note: the trait bound `Unique<dummy2::TestType>: Send` is not satisfied
@@ -74,10 +76,6 @@ note: required by a bound in `is_send`
    |
 LL | fn is_send<T: Send>(_: T) {}
    |               ^^^^ required by this bound in `is_send`
-help: consider borrowing here
-   |
-LL |     is_send(&Box::new(TestType));
-   |             +
 
 error[E0277]: `dummy3::TestType` cannot be sent between threads safely
   --> $DIR/negated-auto-traits-error.rs:56:13