about summary refs log tree commit diff
diff options
context:
space:
mode:
authorDylan DPC <dylan.dpc@gmail.com>2021-04-12 01:04:04 +0200
committerGitHub <noreply@github.com>2021-04-12 01:04:04 +0200
commitc905e9d0ca7fc21c5b723f66d6eb4a36ec29d730 (patch)
tree687df080ec62a1835c18d5de3d903c36a48e0554
parentb6780b3a20ad9f482911119eee84b0ad5a495af6 (diff)
parentbb502c488915253a261f837cce7a920bf49a1666 (diff)
downloadrust-c905e9d0ca7fc21c5b723f66d6eb4a36ec29d730.tar.gz
rust-c905e9d0ca7fc21c5b723f66d6eb4a36ec29d730.zip
Rollup merge of #84014 - estebank:cool-bears-hot-tip, r=varkor
Improve trait/impl method discrepancy errors

* Use more accurate spans
* Clean up some code by removing previous hack
* Provide structured suggestions

Structured suggestions are particularly useful for cases where arbitrary self types are used, like in custom `Future`s, because the way to write `self: Pin<&mut Self>` is not necessarily self-evident when first encountered.
-rw-r--r--compiler/rustc_middle/src/ty/error.rs13
-rw-r--r--compiler/rustc_middle/src/ty/relate.rs6
-rw-r--r--compiler/rustc_middle/src/ty/structural_impls.rs2
-rw-r--r--compiler/rustc_typeck/src/check/compare_method.rs168
-rw-r--r--src/test/ui/associated-types/defaults-specialization.stderr10
-rw-r--r--src/test/ui/compare-method/bad-self-type.rs26
-rw-r--r--src/test/ui/compare-method/bad-self-type.stderr46
-rw-r--r--src/test/ui/compare-method/reordered-type-param.stderr6
-rw-r--r--src/test/ui/impl-trait/impl-generic-mismatch-ab.stderr6
-rw-r--r--src/test/ui/impl-trait/trait_type.stderr7
-rw-r--r--src/test/ui/issues/issue-13033.stderr2
-rw-r--r--src/test/ui/issues/issue-20225.stderr21
-rw-r--r--src/test/ui/issues/issue-21332.stderr7
-rw-r--r--src/test/ui/issues/issue-35869.stderr20
-rw-r--r--src/test/ui/mismatched_types/E0053.stderr7
-rw-r--r--src/test/ui/mismatched_types/trait-impl-fn-incompatibility.stderr7
-rw-r--r--src/test/ui/wrong-mul-method-signature.stderr21
17 files changed, 258 insertions, 117 deletions
diff --git a/compiler/rustc_middle/src/ty/error.rs b/compiler/rustc_middle/src/ty/error.rs
index d295b17d902..008e6d015e8 100644
--- a/compiler/rustc_middle/src/ty/error.rs
+++ b/compiler/rustc_middle/src/ty/error.rs
@@ -36,6 +36,7 @@ pub enum TypeError<'tcx> {
     UnsafetyMismatch(ExpectedFound<hir::Unsafety>),
     AbiMismatch(ExpectedFound<abi::Abi>),
     Mutability,
+    ArgumentMutability(usize),
     TupleSize(ExpectedFound<usize>),
     FixedArraySize(ExpectedFound<u64>),
     ArgCount,
@@ -46,6 +47,7 @@ pub enum TypeError<'tcx> {
     RegionsPlaceholderMismatch,
 
     Sorts(ExpectedFound<Ty<'tcx>>),
+    ArgumentSorts(ExpectedFound<Ty<'tcx>>, usize),
     IntMismatch(ExpectedFound<ty::IntVarValue>),
     FloatMismatch(ExpectedFound<ty::FloatTy>),
     Traits(ExpectedFound<DefId>),
@@ -110,7 +112,7 @@ impl<'tcx> fmt::Display for TypeError<'tcx> {
             AbiMismatch(values) => {
                 write!(f, "expected {} fn, found {} fn", values.expected, values.found)
             }
-            Mutability => write!(f, "types differ in mutability"),
+            ArgumentMutability(_) | Mutability => write!(f, "types differ in mutability"),
             TupleSize(values) => write!(
                 f,
                 "expected a tuple with {} element{}, \
@@ -142,7 +144,7 @@ impl<'tcx> fmt::Display for TypeError<'tcx> {
                 br_string(br)
             ),
             RegionsPlaceholderMismatch => write!(f, "one type is more general than the other"),
-            Sorts(values) => ty::tls::with(|tcx| {
+            ArgumentSorts(values, _) | Sorts(values) => ty::tls::with(|tcx| {
                 report_maybe_different(
                     f,
                     &values.expected.sort_string(tcx),
@@ -199,10 +201,11 @@ impl<'tcx> TypeError<'tcx> {
         use self::TypeError::*;
         match self {
             CyclicTy(_) | CyclicConst(_) | UnsafetyMismatch(_) | Mismatch | AbiMismatch(_)
-            | FixedArraySize(_) | Sorts(_) | IntMismatch(_) | FloatMismatch(_)
-            | VariadicMismatch(_) | TargetFeatureCast(_) => false,
+            | FixedArraySize(_) | ArgumentSorts(..) | Sorts(_) | IntMismatch(_)
+            | FloatMismatch(_) | VariadicMismatch(_) | TargetFeatureCast(_) => false,
 
             Mutability
+            | ArgumentMutability(_)
             | TupleSize(_)
             | ArgCount
             | RegionsDoesNotOutlive(..)
@@ -339,7 +342,7 @@ impl<'tcx> TyCtxt<'tcx> {
         use self::TypeError::*;
         debug!("note_and_explain_type_err err={:?} cause={:?}", err, cause);
         match err {
-            Sorts(values) => {
+            ArgumentSorts(values, _) | Sorts(values) => {
                 match (values.expected.kind(), values.found.kind()) {
                     (ty::Closure(..), ty::Closure(..)) => {
                         db.note("no two closures, even if identical, have the same type");
diff --git a/compiler/rustc_middle/src/ty/relate.rs b/compiler/rustc_middle/src/ty/relate.rs
index ca60339da0d..b6f93c9bd59 100644
--- a/compiler/rustc_middle/src/ty/relate.rs
+++ b/compiler/rustc_middle/src/ty/relate.rs
@@ -179,6 +179,12 @@ impl<'tcx> Relate<'tcx> for ty::FnSig<'tcx> {
                 } else {
                     relation.relate_with_variance(ty::Contravariant, a, b)
                 }
+            })
+            .enumerate()
+            .map(|(i, r)| match r {
+                Err(TypeError::Sorts(exp_found)) => Err(TypeError::ArgumentSorts(exp_found, i)),
+                Err(TypeError::Mutability) => Err(TypeError::ArgumentMutability(i)),
+                r => r,
             });
         Ok(ty::FnSig {
             inputs_and_output: tcx.mk_type_list(inputs_and_output)?,
diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs
index a969626b370..7290c41d615 100644
--- a/compiler/rustc_middle/src/ty/structural_impls.rs
+++ b/compiler/rustc_middle/src/ty/structural_impls.rs
@@ -587,6 +587,7 @@ impl<'a, 'tcx> Lift<'tcx> for ty::error::TypeError<'a> {
             UnsafetyMismatch(x) => UnsafetyMismatch(x),
             AbiMismatch(x) => AbiMismatch(x),
             Mutability => Mutability,
+            ArgumentMutability(i) => ArgumentMutability(i),
             TupleSize(x) => TupleSize(x),
             FixedArraySize(x) => FixedArraySize(x),
             ArgCount => ArgCount,
@@ -607,6 +608,7 @@ impl<'a, 'tcx> Lift<'tcx> for ty::error::TypeError<'a> {
             CyclicTy(t) => return tcx.lift(t).map(|t| CyclicTy(t)),
             CyclicConst(ct) => return tcx.lift(ct).map(|ct| CyclicConst(ct)),
             ProjectionMismatched(x) => ProjectionMismatched(x),
+            ArgumentSorts(x, i) => return tcx.lift(x).map(|x| ArgumentSorts(x, i)),
             Sorts(x) => return tcx.lift(x).map(Sorts),
             ExistentialMismatch(x) => return tcx.lift(x).map(ExistentialMismatch),
             ConstMismatch(x) => return tcx.lift(x).map(ConstMismatch),
diff --git a/compiler/rustc_typeck/src/check/compare_method.rs b/compiler/rustc_typeck/src/check/compare_method.rs
index f044daa4509..60ca562f992 100644
--- a/compiler/rustc_typeck/src/check/compare_method.rs
+++ b/compiler/rustc_typeck/src/check/compare_method.rs
@@ -278,9 +278,8 @@ fn compare_predicate_entailment<'tcx>(
         if let Err(terr) = sub_result {
             debug!("sub_types failed: impl ty {:?}, trait ty {:?}", impl_fty, trait_fty);
 
-            let (impl_err_span, trait_err_span) = extract_spans_for_error_reporting(
-                &infcx, param_env, &terr, &cause, impl_m, impl_sig, trait_m, trait_sig,
-            );
+            let (impl_err_span, trait_err_span) =
+                extract_spans_for_error_reporting(&infcx, &terr, &cause, impl_m, trait_m);
 
             cause.make_mut().span = impl_err_span;
 
@@ -291,18 +290,79 @@ fn compare_predicate_entailment<'tcx>(
                 "method `{}` has an incompatible type for trait",
                 trait_m.ident
             );
-            if let TypeError::Mutability = terr {
-                if let Some(trait_err_span) = trait_err_span {
-                    if let Ok(trait_err_str) = tcx.sess.source_map().span_to_snippet(trait_err_span)
+            match &terr {
+                TypeError::ArgumentMutability(0) | TypeError::ArgumentSorts(_, 0)
+                    if trait_m.fn_has_self_parameter =>
+                {
+                    let ty = trait_sig.inputs()[0];
+                    let sugg = match ExplicitSelf::determine(ty, |_| ty == impl_trait_ref.self_ty())
                     {
+                        ExplicitSelf::ByValue => "self".to_owned(),
+                        ExplicitSelf::ByReference(_, hir::Mutability::Not) => "&self".to_owned(),
+                        ExplicitSelf::ByReference(_, hir::Mutability::Mut) => {
+                            "&mut self".to_owned()
+                        }
+                        _ => format!("self: {}", ty),
+                    };
+
+                    // When the `impl` receiver is an arbitrary self type, like `self: Box<Self>`, the
+                    // span points only at the type `Box<Self`>, but we want to cover the whole
+                    // argument pattern and type.
+                    let impl_m_hir_id =
+                        tcx.hir().local_def_id_to_hir_id(impl_m.def_id.expect_local());
+                    let span = match tcx.hir().expect_impl_item(impl_m_hir_id).kind {
+                        ImplItemKind::Fn(ref sig, body) => tcx
+                            .hir()
+                            .body_param_names(body)
+                            .zip(sig.decl.inputs.iter())
+                            .map(|(param, ty)| param.span.to(ty.span))
+                            .next()
+                            .unwrap_or(impl_err_span),
+                        _ => bug!("{:?} is not a method", impl_m),
+                    };
+
+                    diag.span_suggestion(
+                        span,
+                        "change the self-receiver type to match the trait",
+                        sugg,
+                        Applicability::MachineApplicable,
+                    );
+                }
+                TypeError::ArgumentMutability(i) | TypeError::ArgumentSorts(_, i) => {
+                    if trait_sig.inputs().len() == *i {
+                        // Suggestion to change output type. We do not suggest in `async` functions
+                        // to avoid complex logic or incorrect output.
+                        let impl_m_hir_id =
+                            tcx.hir().local_def_id_to_hir_id(impl_m.def_id.expect_local());
+                        match tcx.hir().expect_impl_item(impl_m_hir_id).kind {
+                            ImplItemKind::Fn(ref sig, _)
+                                if sig.header.asyncness == hir::IsAsync::NotAsync =>
+                            {
+                                let msg = "change the output type to match the trait";
+                                let ap = Applicability::MachineApplicable;
+                                match sig.decl.output {
+                                    hir::FnRetTy::DefaultReturn(sp) => {
+                                        let sugg = format!("-> {} ", trait_sig.output());
+                                        diag.span_suggestion_verbose(sp, msg, sugg, ap);
+                                    }
+                                    hir::FnRetTy::Return(hir_ty) => {
+                                        let sugg = trait_sig.output().to_string();
+                                        diag.span_suggestion(hir_ty.span, msg, sugg, ap);
+                                    }
+                                };
+                            }
+                            _ => {}
+                        };
+                    } else if let Some(trait_ty) = trait_sig.inputs().get(*i) {
                         diag.span_suggestion(
                             impl_err_span,
-                            "consider changing the mutability to match the trait",
-                            trait_err_str,
+                            "change the parameter type to match the trait",
+                            trait_ty.to_string(),
                             Applicability::MachineApplicable,
                         );
                     }
                 }
+                _ => {}
             }
 
             infcx.note_type_err(
@@ -385,86 +445,35 @@ fn check_region_bounds_on_impl_item<'tcx>(
 
 fn extract_spans_for_error_reporting<'a, 'tcx>(
     infcx: &infer::InferCtxt<'a, 'tcx>,
-    param_env: ty::ParamEnv<'tcx>,
     terr: &TypeError<'_>,
     cause: &ObligationCause<'tcx>,
     impl_m: &ty::AssocItem,
-    impl_sig: ty::FnSig<'tcx>,
     trait_m: &ty::AssocItem,
-    trait_sig: ty::FnSig<'tcx>,
 ) -> (Span, Option<Span>) {
     let tcx = infcx.tcx;
     let impl_m_hir_id = tcx.hir().local_def_id_to_hir_id(impl_m.def_id.expect_local());
-    let (impl_m_output, impl_m_iter) = match tcx.hir().expect_impl_item(impl_m_hir_id).kind {
-        ImplItemKind::Fn(ref impl_m_sig, _) => {
-            (&impl_m_sig.decl.output, impl_m_sig.decl.inputs.iter())
+    let mut impl_args = match tcx.hir().expect_impl_item(impl_m_hir_id).kind {
+        ImplItemKind::Fn(ref sig, _) => {
+            sig.decl.inputs.iter().map(|t| t.span).chain(iter::once(sig.decl.output.span()))
         }
         _ => bug!("{:?} is not a method", impl_m),
     };
-
-    match *terr {
-        TypeError::Mutability => {
-            if let Some(def_id) = trait_m.def_id.as_local() {
-                let trait_m_hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
-                let trait_m_iter = match tcx.hir().expect_trait_item(trait_m_hir_id).kind {
-                    TraitItemKind::Fn(ref trait_m_sig, _) => trait_m_sig.decl.inputs.iter(),
-                    _ => bug!("{:?} is not a TraitItemKind::Fn", trait_m),
-                };
-
-                iter::zip(impl_m_iter, trait_m_iter)
-                    .find(|&(ref impl_arg, ref trait_arg)| {
-                        match (&impl_arg.kind, &trait_arg.kind) {
-                            (
-                                &hir::TyKind::Rptr(_, ref impl_mt),
-                                &hir::TyKind::Rptr(_, ref trait_mt),
-                            )
-                            | (&hir::TyKind::Ptr(ref impl_mt), &hir::TyKind::Ptr(ref trait_mt)) => {
-                                impl_mt.mutbl != trait_mt.mutbl
-                            }
-                            _ => false,
-                        }
-                    })
-                    .map(|(ref impl_arg, ref trait_arg)| (impl_arg.span, Some(trait_arg.span)))
-                    .unwrap_or_else(|| (cause.span(tcx), tcx.hir().span_if_local(trait_m.def_id)))
-            } else {
-                (cause.span(tcx), tcx.hir().span_if_local(trait_m.def_id))
+    let trait_args = trait_m.def_id.as_local().map(|def_id| {
+        let trait_m_hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
+        match tcx.hir().expect_trait_item(trait_m_hir_id).kind {
+            TraitItemKind::Fn(ref sig, _) => {
+                sig.decl.inputs.iter().map(|t| t.span).chain(iter::once(sig.decl.output.span()))
             }
+            _ => bug!("{:?} is not a TraitItemKind::Fn", trait_m),
         }
-        TypeError::Sorts(ExpectedFound { .. }) => {
-            if let Some(def_id) = trait_m.def_id.as_local() {
-                let trait_m_hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
-                let (trait_m_output, trait_m_iter) =
-                    match tcx.hir().expect_trait_item(trait_m_hir_id).kind {
-                        TraitItemKind::Fn(ref trait_m_sig, _) => {
-                            (&trait_m_sig.decl.output, trait_m_sig.decl.inputs.iter())
-                        }
-                        _ => bug!("{:?} is not a TraitItemKind::Fn", trait_m),
-                    };
+    });
 
-                let impl_iter = impl_sig.inputs().iter();
-                let trait_iter = trait_sig.inputs().iter();
-                iter::zip(iter::zip(impl_iter, trait_iter), iter::zip(impl_m_iter, trait_m_iter))
-                    .find_map(|((&impl_arg_ty, &trait_arg_ty), (impl_arg, trait_arg))| match infcx
-                        .at(&cause, param_env)
-                        .sub(trait_arg_ty, impl_arg_ty)
-                    {
-                        Ok(_) => None,
-                        Err(_) => Some((impl_arg.span, Some(trait_arg.span))),
-                    })
-                    .unwrap_or_else(|| {
-                        if infcx
-                            .at(&cause, param_env)
-                            .sup(trait_sig.output(), impl_sig.output())
-                            .is_err()
-                        {
-                            (impl_m_output.span(), Some(trait_m_output.span()))
-                        } else {
-                            (cause.span(tcx), tcx.hir().span_if_local(trait_m.def_id))
-                        }
-                    })
-            } else {
-                (cause.span(tcx), tcx.hir().span_if_local(trait_m.def_id))
-            }
+    match *terr {
+        TypeError::ArgumentMutability(i) => {
+            (impl_args.nth(i).unwrap(), trait_args.and_then(|mut args| args.nth(i)))
+        }
+        TypeError::ArgumentSorts(ExpectedFound { .. }, i) => {
+            (impl_args.nth(i).unwrap(), trait_args.and_then(|mut args| args.nth(i)))
         }
         _ => (cause.span(tcx), tcx.hir().span_if_local(trait_m.def_id)),
     }
@@ -514,8 +523,7 @@ fn compare_self_type<'tcx>(
                 tcx.sess,
                 impl_m_span,
                 E0185,
-                "method `{}` has a `{}` declaration in the impl, but \
-                                            not in the trait",
+                "method `{}` has a `{}` declaration in the impl, but not in the trait",
                 trait_m.ident,
                 self_descr
             );
@@ -535,8 +543,7 @@ fn compare_self_type<'tcx>(
                 tcx.sess,
                 impl_m_span,
                 E0186,
-                "method `{}` has a `{}` declaration in the trait, but \
-                                            not in the impl",
+                "method `{}` has a `{}` declaration in the trait, but not in the impl",
                 trait_m.ident,
                 self_descr
             );
@@ -993,8 +1000,7 @@ crate fn compare_const_impl<'tcx>(
                 tcx.sess,
                 cause.span,
                 E0326,
-                "implemented const `{}` has an incompatible type for \
-                                             trait",
+                "implemented const `{}` has an incompatible type for trait",
                 trait_c.ident
             );
 
diff --git a/src/test/ui/associated-types/defaults-specialization.stderr b/src/test/ui/associated-types/defaults-specialization.stderr
index 920f8322813..3c7dc1fc3c9 100644
--- a/src/test/ui/associated-types/defaults-specialization.stderr
+++ b/src/test/ui/associated-types/defaults-specialization.stderr
@@ -15,7 +15,10 @@ LL |     fn make() -> Self::Ty {
    |                  -------- type in trait
 ...
 LL |     fn make() -> u8 { 0 }
-   |                  ^^ expected associated type, found `u8`
+   |                  ^^
+   |                  |
+   |                  expected associated type, found `u8`
+   |                  help: change the output type to match the trait: `<A<T> as Tr>::Ty`
    |
    = note: expected fn pointer `fn() -> <A<T> as Tr>::Ty`
               found fn pointer `fn() -> u8`
@@ -30,7 +33,10 @@ LL |     default type Ty = bool;
    |     ----------------------- expected this associated type
 LL | 
 LL |     fn make() -> bool { true }
-   |                  ^^^^ expected associated type, found `bool`
+   |                  ^^^^
+   |                  |
+   |                  expected associated type, found `bool`
+   |                  help: change the output type to match the trait: `<B<T> as Tr>::Ty`
    |
    = note: expected fn pointer `fn() -> <B<T> as Tr>::Ty`
               found fn pointer `fn() -> bool`
diff --git a/src/test/ui/compare-method/bad-self-type.rs b/src/test/ui/compare-method/bad-self-type.rs
new file mode 100644
index 00000000000..f42a9e49abd
--- /dev/null
+++ b/src/test/ui/compare-method/bad-self-type.rs
@@ -0,0 +1,26 @@
+use std::future::Future;
+use std::task::{Context, Poll};
+
+fn main() {}
+
+struct MyFuture {}
+
+impl Future for MyFuture {
+    type Output = ();
+    fn poll(self, _: &mut Context<'_>) -> Poll<()> {
+    //~^ ERROR method `poll` has an incompatible type for trait
+        todo!()
+    }
+}
+
+trait T {
+    fn foo(self);
+    fn bar(self) -> Option<()>;
+}
+
+impl T for MyFuture {
+    fn foo(self: Box<Self>) {}
+    //~^ ERROR method `foo` has an incompatible type for trait
+    fn bar(self) {}
+    //~^ ERROR method `bar` has an incompatible type for trait
+}
diff --git a/src/test/ui/compare-method/bad-self-type.stderr b/src/test/ui/compare-method/bad-self-type.stderr
new file mode 100644
index 00000000000..76f91fbf241
--- /dev/null
+++ b/src/test/ui/compare-method/bad-self-type.stderr
@@ -0,0 +1,46 @@
+error[E0053]: method `poll` has an incompatible type for trait
+  --> $DIR/bad-self-type.rs:10:13
+   |
+LL |     fn poll(self, _: &mut Context<'_>) -> Poll<()> {
+   |             ^^^^
+   |             |
+   |             expected struct `Pin`, found struct `MyFuture`
+   |             help: change the self-receiver type to match the trait: `self: Pin<&mut MyFuture>`
+   |
+   = note: expected fn pointer `fn(Pin<&mut MyFuture>, &mut Context<'_>) -> Poll<_>`
+              found fn pointer `fn(MyFuture, &mut Context<'_>) -> Poll<_>`
+
+error[E0053]: method `foo` has an incompatible type for trait
+  --> $DIR/bad-self-type.rs:22:18
+   |
+LL |     fn foo(self);
+   |            ---- type in trait
+...
+LL |     fn foo(self: Box<Self>) {}
+   |            ------^^^^^^^^^
+   |            |     |
+   |            |     expected struct `MyFuture`, found struct `Box`
+   |            help: change the self-receiver type to match the trait: `self`
+   |
+   = note: expected fn pointer `fn(MyFuture)`
+              found fn pointer `fn(Box<MyFuture>)`
+
+error[E0053]: method `bar` has an incompatible type for trait
+  --> $DIR/bad-self-type.rs:24:18
+   |
+LL |     fn bar(self) -> Option<()>;
+   |                     ---------- type in trait
+...
+LL |     fn bar(self) {}
+   |                  ^ expected enum `Option`, found `()`
+   |
+   = note: expected fn pointer `fn(MyFuture) -> Option<()>`
+              found fn pointer `fn(MyFuture)`
+help: change the output type to match the trait
+   |
+LL |     fn bar(self) -> Option<()> {}
+   |                  ^^^^^^^^^^^^^
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0053`.
diff --git a/src/test/ui/compare-method/reordered-type-param.stderr b/src/test/ui/compare-method/reordered-type-param.stderr
index f1f8a663f21..d581628ea48 100644
--- a/src/test/ui/compare-method/reordered-type-param.stderr
+++ b/src/test/ui/compare-method/reordered-type-param.stderr
@@ -5,8 +5,10 @@ LL |   fn b<C:Clone,D>(&self, x: C) -> C;
    |                             - type in trait
 ...
 LL |   fn b<F:Clone,G>(&self, _x: G) -> G { panic!() }
-   |        -       -             ^ expected type parameter `F`, found type parameter `G`
-   |        |       |
+   |        -       -             ^
+   |        |       |             |
+   |        |       |             expected type parameter `F`, found type parameter `G`
+   |        |       |             help: change the parameter type to match the trait: `F`
    |        |       found type parameter
    |        expected type parameter
    |
diff --git a/src/test/ui/impl-trait/impl-generic-mismatch-ab.stderr b/src/test/ui/impl-trait/impl-generic-mismatch-ab.stderr
index 638a0093fb2..d37670db085 100644
--- a/src/test/ui/impl-trait/impl-generic-mismatch-ab.stderr
+++ b/src/test/ui/impl-trait/impl-generic-mismatch-ab.stderr
@@ -5,8 +5,10 @@ LL |     fn foo<A: Debug>(&self, a: &A, b: &impl Debug);
    |                                -- type in trait
 ...
 LL |     fn foo<B: Debug>(&self, a: &impl Debug, b: &B) { }
-   |            -                   ^^^^^^^^^^^ expected type parameter `B`, found type parameter `impl Debug`
-   |            |
+   |            -                   ^^^^^^^^^^^
+   |            |                   |
+   |            |                   expected type parameter `B`, found type parameter `impl Debug`
+   |            |                   help: change the parameter type to match the trait: `&B`
    |            expected type parameter
    |
    = note: expected fn pointer `fn(&(), &B, &impl Debug)`
diff --git a/src/test/ui/impl-trait/trait_type.stderr b/src/test/ui/impl-trait/trait_type.stderr
index 961bb735118..bea24339837 100644
--- a/src/test/ui/impl-trait/trait_type.stderr
+++ b/src/test/ui/impl-trait/trait_type.stderr
@@ -1,8 +1,11 @@
 error[E0053]: method `fmt` has an incompatible type for trait
-  --> $DIR/trait_type.rs:7:4
+  --> $DIR/trait_type.rs:7:21
    |
 LL |    fn fmt(&self, x: &str) -> () { }
-   |    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ types differ in mutability
+   |                     ^^^^
+   |                     |
+   |                     types differ in mutability
+   |                     help: change the parameter type to match the trait: `&mut Formatter<'_>`
    |
    = note: expected fn pointer `fn(&MyType, &mut Formatter<'_>) -> Result<(), std::fmt::Error>`
               found fn pointer `fn(&MyType, &str)`
diff --git a/src/test/ui/issues/issue-13033.stderr b/src/test/ui/issues/issue-13033.stderr
index 57447fa48aa..6c3651ff121 100644
--- a/src/test/ui/issues/issue-13033.stderr
+++ b/src/test/ui/issues/issue-13033.stderr
@@ -8,7 +8,7 @@ LL |     fn bar(&mut self, other: &dyn Foo) {}
    |                              ^^^^^^^^
    |                              |
    |                              types differ in mutability
-   |                              help: consider changing the mutability to match the trait: `&mut dyn Foo`
+   |                              help: change the parameter type to match the trait: `&mut dyn Foo`
    |
    = note: expected fn pointer `fn(&mut Baz, &mut dyn Foo)`
               found fn pointer `fn(&mut Baz, &dyn Foo)`
diff --git a/src/test/ui/issues/issue-20225.stderr b/src/test/ui/issues/issue-20225.stderr
index 3bcc50ded84..6f4813ca623 100644
--- a/src/test/ui/issues/issue-20225.stderr
+++ b/src/test/ui/issues/issue-20225.stderr
@@ -1,33 +1,42 @@
 error[E0053]: method `call` has an incompatible type for trait
-  --> $DIR/issue-20225.rs:6:3
+  --> $DIR/issue-20225.rs:6:43
    |
 LL | impl<'a, T> Fn<(&'a T,)> for Foo {
    |          - this type parameter
 LL |   extern "rust-call" fn call(&self, (_,): (T,)) {}
-   |   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `&T`, found type parameter `T`
+   |                                           ^^^^
+   |                                           |
+   |                                           expected `&T`, found type parameter `T`
+   |                                           help: change the parameter type to match the trait: `(&'a T,)`
    |
    = note: expected fn pointer `extern "rust-call" fn(&Foo, (&'a T,))`
               found fn pointer `extern "rust-call" fn(&Foo, (T,))`
 
 error[E0053]: method `call_mut` has an incompatible type for trait
-  --> $DIR/issue-20225.rs:11:3
+  --> $DIR/issue-20225.rs:11:51
    |
 LL | impl<'a, T> FnMut<(&'a T,)> for Foo {
    |          - this type parameter
 LL |   extern "rust-call" fn call_mut(&mut self, (_,): (T,)) {}
-   |   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `&T`, found type parameter `T`
+   |                                                   ^^^^
+   |                                                   |
+   |                                                   expected `&T`, found type parameter `T`
+   |                                                   help: change the parameter type to match the trait: `(&'a T,)`
    |
    = note: expected fn pointer `extern "rust-call" fn(&mut Foo, (&'a T,))`
               found fn pointer `extern "rust-call" fn(&mut Foo, (T,))`
 
 error[E0053]: method `call_once` has an incompatible type for trait
-  --> $DIR/issue-20225.rs:18:3
+  --> $DIR/issue-20225.rs:18:47
    |
 LL | impl<'a, T> FnOnce<(&'a T,)> for Foo {
    |          - this type parameter
 ...
 LL |   extern "rust-call" fn call_once(self, (_,): (T,)) {}
-   |   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `&T`, found type parameter `T`
+   |                                               ^^^^
+   |                                               |
+   |                                               expected `&T`, found type parameter `T`
+   |                                               help: change the parameter type to match the trait: `(&'a T,)`
    |
    = note: expected fn pointer `extern "rust-call" fn(Foo, (&'a T,))`
               found fn pointer `extern "rust-call" fn(Foo, (T,))`
diff --git a/src/test/ui/issues/issue-21332.stderr b/src/test/ui/issues/issue-21332.stderr
index 35863fbebe3..d92966da17c 100644
--- a/src/test/ui/issues/issue-21332.stderr
+++ b/src/test/ui/issues/issue-21332.stderr
@@ -1,8 +1,11 @@
 error[E0053]: method `next` has an incompatible type for trait
-  --> $DIR/issue-21332.rs:5:5
+  --> $DIR/issue-21332.rs:5:27
    |
 LL |     fn next(&mut self) -> Result<i32, i32> { Ok(7) }
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected enum `Option`, found enum `Result`
+   |                           ^^^^^^^^^^^^^^^^
+   |                           |
+   |                           expected enum `Option`, found enum `Result`
+   |                           help: change the output type to match the trait: `Option<i32>`
    |
    = note: expected fn pointer `fn(&mut S) -> Option<i32>`
               found fn pointer `fn(&mut S) -> Result<i32, i32>`
diff --git a/src/test/ui/issues/issue-35869.stderr b/src/test/ui/issues/issue-35869.stderr
index f80561bf6be..71b2a9df095 100644
--- a/src/test/ui/issues/issue-35869.stderr
+++ b/src/test/ui/issues/issue-35869.stderr
@@ -5,7 +5,10 @@ LL |     fn foo(_: fn(u8) -> ());
    |               ------------ type in trait
 ...
 LL |     fn foo(_: fn(u16) -> ()) {}
-   |               ^^^^^^^^^^^^^ expected `u8`, found `u16`
+   |               ^^^^^^^^^^^^^
+   |               |
+   |               expected `u8`, found `u16`
+   |               help: change the parameter type to match the trait: `fn(u8)`
    |
    = note: expected fn pointer `fn(fn(u8))`
               found fn pointer `fn(fn(u16))`
@@ -17,7 +20,10 @@ LL |     fn bar(_: Option<u8>);
    |               ---------- type in trait
 ...
 LL |     fn bar(_: Option<u16>) {}
-   |               ^^^^^^^^^^^ expected `u8`, found `u16`
+   |               ^^^^^^^^^^^
+   |               |
+   |               expected `u8`, found `u16`
+   |               help: change the parameter type to match the trait: `Option<u8>`
    |
    = note: expected fn pointer `fn(Option<u8>)`
               found fn pointer `fn(Option<u16>)`
@@ -29,7 +35,10 @@ LL |     fn baz(_: (u8, u16));
    |               --------- type in trait
 ...
 LL |     fn baz(_: (u16, u16)) {}
-   |               ^^^^^^^^^^ expected `u8`, found `u16`
+   |               ^^^^^^^^^^
+   |               |
+   |               expected `u8`, found `u16`
+   |               help: change the parameter type to match the trait: `(u8, u16)`
    |
    = note: expected fn pointer `fn((u8, _))`
               found fn pointer `fn((u16, _))`
@@ -41,7 +50,10 @@ LL |     fn qux() -> u8;
    |                 -- type in trait
 ...
 LL |     fn qux() -> u16 { 5u16 }
-   |                 ^^^ expected `u8`, found `u16`
+   |                 ^^^
+   |                 |
+   |                 expected `u8`, found `u16`
+   |                 help: change the output type to match the trait: `u8`
    |
    = note: expected fn pointer `fn() -> u8`
               found fn pointer `fn() -> u16`
diff --git a/src/test/ui/mismatched_types/E0053.stderr b/src/test/ui/mismatched_types/E0053.stderr
index e0a3ce922b9..6ce8126b9f9 100644
--- a/src/test/ui/mismatched_types/E0053.stderr
+++ b/src/test/ui/mismatched_types/E0053.stderr
@@ -5,7 +5,10 @@ LL |     fn foo(x: u16);
    |               --- type in trait
 ...
 LL |     fn foo(x: i16) { }
-   |               ^^^ expected `u16`, found `i16`
+   |               ^^^
+   |               |
+   |               expected `u16`, found `i16`
+   |               help: change the parameter type to match the trait: `u16`
    |
    = note: expected fn pointer `fn(u16)`
               found fn pointer `fn(i16)`
@@ -20,7 +23,7 @@ LL |     fn bar(&mut self) { }
    |            ^^^^^^^^^
    |            |
    |            types differ in mutability
-   |            help: consider changing the mutability to match the trait: `&self`
+   |            help: change the self-receiver type to match the trait: `self: &Bar`
    |
    = note: expected fn pointer `fn(&Bar)`
               found fn pointer `fn(&mut Bar)`
diff --git a/src/test/ui/mismatched_types/trait-impl-fn-incompatibility.stderr b/src/test/ui/mismatched_types/trait-impl-fn-incompatibility.stderr
index 161843473b6..2ac4d1c33a9 100644
--- a/src/test/ui/mismatched_types/trait-impl-fn-incompatibility.stderr
+++ b/src/test/ui/mismatched_types/trait-impl-fn-incompatibility.stderr
@@ -5,7 +5,10 @@ LL |     fn foo(x: u16);
    |               --- type in trait
 ...
 LL |     fn foo(x: i16) { }
-   |               ^^^ expected `u16`, found `i16`
+   |               ^^^
+   |               |
+   |               expected `u16`, found `i16`
+   |               help: change the parameter type to match the trait: `u16`
    |
    = note: expected fn pointer `fn(u16)`
               found fn pointer `fn(i16)`
@@ -20,7 +23,7 @@ LL |     fn bar(&mut self, bar: &Bar) { }
    |                            ^^^^
    |                            |
    |                            types differ in mutability
-   |                            help: consider changing the mutability to match the trait: `&mut Bar`
+   |                            help: change the parameter type to match the trait: `&mut Bar`
    |
    = note: expected fn pointer `fn(&mut Bar, &mut Bar)`
               found fn pointer `fn(&mut Bar, &Bar)`
diff --git a/src/test/ui/wrong-mul-method-signature.stderr b/src/test/ui/wrong-mul-method-signature.stderr
index 4c367fb9e9c..9f8896f01ee 100644
--- a/src/test/ui/wrong-mul-method-signature.stderr
+++ b/src/test/ui/wrong-mul-method-signature.stderr
@@ -1,26 +1,35 @@
 error[E0053]: method `mul` has an incompatible type for trait
-  --> $DIR/wrong-mul-method-signature.rs:16:5
+  --> $DIR/wrong-mul-method-signature.rs:16:21
    |
 LL |     fn mul(self, s: &f64) -> Vec1 {
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `f64`, found `&f64`
+   |                     ^^^^
+   |                     |
+   |                     expected `f64`, found `&f64`
+   |                     help: change the parameter type to match the trait: `f64`
    |
    = note: expected fn pointer `fn(Vec1, f64) -> Vec1`
               found fn pointer `fn(Vec1, &f64) -> Vec1`
 
 error[E0053]: method `mul` has an incompatible type for trait
-  --> $DIR/wrong-mul-method-signature.rs:33:5
+  --> $DIR/wrong-mul-method-signature.rs:33:21
    |
 LL |     fn mul(self, s: f64) -> Vec2 {
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `Vec2`, found `f64`
+   |                     ^^^
+   |                     |
+   |                     expected struct `Vec2`, found `f64`
+   |                     help: change the parameter type to match the trait: `Vec2`
    |
    = note: expected fn pointer `fn(Vec2, Vec2) -> f64`
               found fn pointer `fn(Vec2, f64) -> Vec2`
 
 error[E0053]: method `mul` has an incompatible type for trait
-  --> $DIR/wrong-mul-method-signature.rs:52:5
+  --> $DIR/wrong-mul-method-signature.rs:52:29
    |
 LL |     fn mul(self, s: f64) -> f64 {
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `i32`, found `f64`
+   |                             ^^^
+   |                             |
+   |                             expected `i32`, found `f64`
+   |                             help: change the output type to match the trait: `i32`
    |
    = note: expected fn pointer `fn(Vec3, _) -> i32`
               found fn pointer `fn(Vec3, _) -> f64`