about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMatthias Krüger <matthias.krueger@famsik.de>2022-08-31 07:58:00 +0200
committerGitHub <noreply@github.com>2022-08-31 07:58:00 +0200
commitb8b2f88a044fdd20be4925eb222a0572f359de2f (patch)
tree02d970e49d571307c52d391ba951b72fbb1422f3
parent78e5d05ea02565ee2ecc8c394137b0ba3e86372e (diff)
parent1256530643fdd4762de8de5a47041fc2cf700828 (diff)
downloadrust-b8b2f88a044fdd20be4925eb222a0572f359de2f.tar.gz
rust-b8b2f88a044fdd20be4925eb222a0572f359de2f.zip
Rollup merge of #101100 - compiler-errors:generalize-call-suggestions, r=petrochenkov
Make call suggestions more general and more accurate

Cleans up some suggestions that have to do with adding `()` to make typeck happy.

1. Drive-by rename of `expr_t` to `base_ty` since it's the type of the `base_expr`
1. Autoderef until we get to a callable type in `suggest_fn_call`.
1. Don't erroneously suggest calling constructor when a method/field does not exist on it.
1. Suggest calling a method receiver if its function output has a method (e.g. `fn.method()` => `fn().method()`)
1. Extend call suggestions to type parameters, fn pointers, trait objects where possible
1. Suggest calling in operators too (fixes #101054)
1. Use `/* {ty} */` as argument placeholder instead of just `_`, which is confusing and makes suggestions look less like `if let` syntax.
-rw-r--r--compiler/rustc_errors/src/lib.rs19
-rw-r--r--compiler/rustc_lint_defs/src/lib.rs3
-rw-r--r--compiler/rustc_middle/src/ty/structural_impls.rs6
-rw-r--r--compiler/rustc_typeck/src/check/expr.rs102
-rw-r--r--compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs269
-rw-r--r--compiler/rustc_typeck/src/check/method/suggest.rs55
-rw-r--r--compiler/rustc_typeck/src/check/mod.rs29
-rw-r--r--compiler/rustc_typeck/src/check/op.rs128
-rw-r--r--src/test/ui/associated-types/substs-ppaux.normal.stderr6
-rw-r--r--src/test/ui/associated-types/substs-ppaux.verbose.stderr6
-rw-r--r--src/test/ui/binop/issue-77910-2.stderr5
-rw-r--r--src/test/ui/fn/fn-compare-mismatch.stderr10
-rw-r--r--src/test/ui/fn/fn-trait-formatting.stderr8
-rw-r--r--src/test/ui/functions-closures/fn-help-with-err.rs28
-rw-r--r--src/test/ui/functions-closures/fn-help-with-err.stderr38
-rw-r--r--src/test/ui/impl-trait/suggest-calling-rpit-closure.stderr2
-rw-r--r--src/test/ui/issues/issue-35241.stderr4
-rw-r--r--src/test/ui/issues/issue-57362-1.stderr4
-rw-r--r--src/test/ui/issues/issue-59488.stderr18
-rw-r--r--src/test/ui/issues/issue-70724-add_type_neq_err_label-unwrap.stderr7
-rw-r--r--src/test/ui/resolve/privacy-enum-ctor.stderr12
-rw-r--r--src/test/ui/suggestions/call-boxed.rs7
-rw-r--r--src/test/ui/suggestions/call-boxed.stderr20
-rw-r--r--src/test/ui/suggestions/call-on-missing.rs39
-rw-r--r--src/test/ui/suggestions/call-on-missing.stderr75
-rw-r--r--src/test/ui/suggestions/fn-or-tuple-struct-without-args.stderr60
-rw-r--r--src/test/ui/typeck/issue-29124.stderr8
-rw-r--r--src/test/ui/typeck/issue-87181/empty-tuple-method.rs2
-rw-r--r--src/test/ui/typeck/issue-87181/empty-tuple-method.stderr6
-rw-r--r--src/test/ui/typeck/issue-87181/enum-variant.rs2
-rw-r--r--src/test/ui/typeck/issue-87181/enum-variant.stderr6
-rw-r--r--src/test/ui/typeck/issue-87181/tuple-field.stderr10
-rw-r--r--src/test/ui/typeck/issue-87181/tuple-method.stderr9
-rw-r--r--src/test/ui/typeck/issue-96738.stderr18
-rw-r--r--src/test/ui/unboxed-closures/unboxed-closures-static-call-wrong-trait.stderr4
35 files changed, 609 insertions, 416 deletions
diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs
index 7b0f4354afd..68abdd0bad1 100644
--- a/compiler/rustc_errors/src/lib.rs
+++ b/compiler/rustc_errors/src/lib.rs
@@ -1249,9 +1249,13 @@ impl HandlerInner {
     }
 
     fn treat_err_as_bug(&self) -> bool {
-        self.flags
-            .treat_err_as_bug
-            .map_or(false, |c| self.err_count() + self.lint_err_count >= c.get())
+        self.flags.treat_err_as_bug.map_or(false, |c| {
+            self.err_count()
+                + self.lint_err_count
+                + self.delayed_span_bugs.len()
+                + self.delayed_good_path_bugs.len()
+                >= c.get()
+        })
     }
 
     fn print_error_count(&mut self, registry: &Registry) {
@@ -1407,7 +1411,14 @@ impl HandlerInner {
         // This is technically `self.treat_err_as_bug()` but `delay_span_bug` is called before
         // incrementing `err_count` by one, so we need to +1 the comparing.
         // FIXME: Would be nice to increment err_count in a more coherent way.
-        if self.flags.treat_err_as_bug.map_or(false, |c| self.err_count() + 1 >= c.get()) {
+        if self.flags.treat_err_as_bug.map_or(false, |c| {
+            self.err_count()
+                + self.lint_err_count
+                + self.delayed_span_bugs.len()
+                + self.delayed_good_path_bugs.len()
+                + 1
+                >= c.get()
+        }) {
             // FIXME: don't abort here if report_delayed_bugs is off
             self.span_bug(sp, msg);
         }
diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs
index 9e7cbba9511..9c6530c8a08 100644
--- a/compiler/rustc_lint_defs/src/lib.rs
+++ b/compiler/rustc_lint_defs/src/lib.rs
@@ -41,7 +41,8 @@ macro_rules! pluralize {
 /// All suggestions are marked with an `Applicability`. Tools use the applicability of a suggestion
 /// to determine whether it should be automatically applied or if the user should be consulted
 /// before applying the suggestion.
-#[derive(Copy, Clone, Debug, PartialEq, Hash, Encodable, Decodable, Serialize, Deserialize)]
+#[derive(Copy, Clone, Debug, Hash, Encodable, Decodable, Serialize, Deserialize)]
+#[derive(PartialEq, Eq, PartialOrd, Ord)]
 pub enum Applicability {
     /// The suggestion is definitely what the user intended, or maintains the exact meaning of the code.
     /// This suggestion should be automatically applied.
diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs
index 7660a2f3af6..57555433f55 100644
--- a/compiler/rustc_middle/src/ty/structural_impls.rs
+++ b/compiler/rustc_middle/src/ty/structural_impls.rs
@@ -844,6 +844,12 @@ impl<'tcx, T: TypeVisitable<'tcx>> TypeVisitable<'tcx> for Vec<T> {
     }
 }
 
+impl<'tcx, T: TypeVisitable<'tcx>> TypeVisitable<'tcx> for &[T] {
+    fn visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> {
+        self.iter().try_for_each(|t| t.visit_with(visitor))
+    }
+}
+
 impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for Box<[T]> {
     fn try_fold_with<F: FallibleTypeFolder<'tcx>>(self, folder: &mut F) -> Result<Self, F::Error> {
         self.try_map_id(|t| t.try_fold_with(folder))
diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs
index 33d74249e7b..f6596950c2a 100644
--- a/compiler/rustc_typeck/src/check/expr.rs
+++ b/compiler/rustc_typeck/src/check/expr.rs
@@ -21,7 +21,6 @@ use crate::errors::{
 };
 use crate::type_error_struct;
 
-use super::suggest_call_constructor;
 use crate::errors::{AddressOfTemporaryTaken, ReturnStmtOutsideOfFnBody, StructExprNonExhaustive};
 use rustc_ast as ast;
 use rustc_data_structures::fx::FxHashMap;
@@ -44,7 +43,7 @@ use rustc_middle::middle::stability;
 use rustc_middle::ty::adjustment::{Adjust, Adjustment, AllowTwoPhase};
 use rustc_middle::ty::error::TypeError::FieldMisMatch;
 use rustc_middle::ty::subst::SubstsRef;
-use rustc_middle::ty::{self, AdtKind, DefIdTree, Ty, TypeVisitable};
+use rustc_middle::ty::{self, AdtKind, Ty, TypeVisitable};
 use rustc_session::parse::feature_err;
 use rustc_span::hygiene::DesugaringKind;
 use rustc_span::lev_distance::find_best_match_for_name;
@@ -2141,15 +2140,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         field: Ident,
     ) -> Ty<'tcx> {
         debug!("check_field(expr: {:?}, base: {:?}, field: {:?})", expr, base, field);
-        let expr_t = self.check_expr(base);
-        let expr_t = self.structurally_resolved_type(base.span, expr_t);
+        let base_ty = self.check_expr(base);
+        let base_ty = self.structurally_resolved_type(base.span, base_ty);
         let mut private_candidate = None;
-        let mut autoderef = self.autoderef(expr.span, expr_t);
-        while let Some((base_t, _)) = autoderef.next() {
-            debug!("base_t: {:?}", base_t);
-            match base_t.kind() {
+        let mut autoderef = self.autoderef(expr.span, base_ty);
+        while let Some((deref_base_ty, _)) = autoderef.next() {
+            debug!("deref_base_ty: {:?}", deref_base_ty);
+            match deref_base_ty.kind() {
                 ty::Adt(base_def, substs) if !base_def.is_enum() => {
-                    debug!("struct named {:?}", base_t);
+                    debug!("struct named {:?}", deref_base_ty);
                     let (ident, def_scope) =
                         self.tcx.adjust_ident_and_get_scope(field, base_def.did(), self.body_id);
                     let fields = &base_def.non_enum_variant().fields;
@@ -2197,23 +2196,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             // (#90483) apply adjustments to avoid ExprUseVisitor from
             // creating erroneous projection.
             self.apply_adjustments(base, adjustments);
-            self.ban_private_field_access(expr, expr_t, field, did);
+            self.ban_private_field_access(expr, base_ty, field, did);
             return field_ty;
         }
 
         if field.name == kw::Empty {
-        } else if self.method_exists(field, expr_t, expr.hir_id, true) {
-            self.ban_take_value_of_method(expr, expr_t, field);
-        } else if !expr_t.is_primitive_ty() {
-            self.ban_nonexisting_field(field, base, expr, expr_t);
+        } else if self.method_exists(field, base_ty, expr.hir_id, true) {
+            self.ban_take_value_of_method(expr, base_ty, field);
+        } else if !base_ty.is_primitive_ty() {
+            self.ban_nonexisting_field(field, base, expr, base_ty);
         } else {
             let field_name = field.to_string();
             let mut err = type_error_struct!(
                 self.tcx().sess,
                 field.span,
-                expr_t,
+                base_ty,
                 E0610,
-                "`{expr_t}` is a primitive type and therefore doesn't have fields",
+                "`{base_ty}` is a primitive type and therefore doesn't have fields",
             );
             let is_valid_suffix = |field: &str| {
                 if field == "f32" || field == "f64" {
@@ -2251,7 +2250,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     None
                 }
             };
-            if let ty::Infer(ty::IntVar(_)) = expr_t.kind()
+            if let ty::Infer(ty::IntVar(_)) = base_ty.kind()
                 && let ExprKind::Lit(Spanned {
                     node: ast::LitKind::Int(_, ast::LitIntType::Unsuffixed),
                     ..
@@ -2280,35 +2279,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         self.tcx().ty_error()
     }
 
-    fn check_call_constructor(
-        &self,
-        err: &mut Diagnostic,
-        base: &'tcx hir::Expr<'tcx>,
-        def_id: DefId,
-    ) {
-        if let Some(local_id) = def_id.as_local() {
-            let hir_id = self.tcx.hir().local_def_id_to_hir_id(local_id);
-            let node = self.tcx.hir().get(hir_id);
-
-            if let Some(fields) = node.tuple_fields() {
-                let kind = match self.tcx.opt_def_kind(local_id) {
-                    Some(DefKind::Ctor(of, _)) => of,
-                    _ => return,
-                };
-
-                suggest_call_constructor(base.span, kind, fields.len(), err);
-            }
-        } else {
-            // The logic here isn't smart but `associated_item_def_ids`
-            // doesn't work nicely on local.
-            if let DefKind::Ctor(of, _) = self.tcx.def_kind(def_id) {
-                let parent_def_id = self.tcx.parent(def_id);
-                let fields = self.tcx.associated_item_def_ids(parent_def_id);
-                suggest_call_constructor(base.span, of, fields.len(), err);
-            }
-        }
-    }
-
     fn suggest_await_on_field_access(
         &self,
         err: &mut Diagnostic,
@@ -2351,40 +2321,52 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
     fn ban_nonexisting_field(
         &self,
-        field: Ident,
+        ident: Ident,
         base: &'tcx hir::Expr<'tcx>,
         expr: &'tcx hir::Expr<'tcx>,
-        expr_t: Ty<'tcx>,
+        base_ty: Ty<'tcx>,
     ) {
         debug!(
-            "ban_nonexisting_field: field={:?}, base={:?}, expr={:?}, expr_ty={:?}",
-            field, base, expr, expr_t
+            "ban_nonexisting_field: field={:?}, base={:?}, expr={:?}, base_ty={:?}",
+            ident, base, expr, base_ty
         );
-        let mut err = self.no_such_field_err(field, expr_t, base.hir_id);
+        let mut err = self.no_such_field_err(ident, base_ty, base.hir_id);
 
-        match *expr_t.peel_refs().kind() {
+        match *base_ty.peel_refs().kind() {
             ty::Array(_, len) => {
-                self.maybe_suggest_array_indexing(&mut err, expr, base, field, len);
+                self.maybe_suggest_array_indexing(&mut err, expr, base, ident, len);
             }
             ty::RawPtr(..) => {
-                self.suggest_first_deref_field(&mut err, expr, base, field);
+                self.suggest_first_deref_field(&mut err, expr, base, ident);
             }
             ty::Adt(def, _) if !def.is_enum() => {
-                self.suggest_fields_on_recordish(&mut err, def, field, expr.span);
+                self.suggest_fields_on_recordish(&mut err, def, ident, expr.span);
             }
             ty::Param(param_ty) => {
                 self.point_at_param_definition(&mut err, param_ty);
             }
             ty::Opaque(_, _) => {
-                self.suggest_await_on_field_access(&mut err, field, base, expr_t.peel_refs());
-            }
-            ty::FnDef(def_id, _) => {
-                self.check_call_constructor(&mut err, base, def_id);
+                self.suggest_await_on_field_access(&mut err, ident, base, base_ty.peel_refs());
             }
             _ => {}
         }
 
-        if field.name == kw::Await {
+        self.suggest_fn_call(&mut err, base, base_ty, |output_ty| {
+            if let ty::Adt(def, _) = output_ty.kind() && !def.is_enum() {
+                def.non_enum_variant().fields.iter().any(|field| {
+                    field.ident(self.tcx) == ident
+                        && field.vis.is_accessible_from(expr.hir_id.owner.to_def_id(), self.tcx)
+                })
+            } else if let ty::Tuple(tys) = output_ty.kind()
+                && let Ok(idx) = ident.as_str().parse::<usize>()
+            {
+                idx < tys.len()
+            } else {
+                false
+            }
+        });
+
+        if ident.name == kw::Await {
             // We know by construction that `<expr>.await` is either on Rust 2015
             // or results in `ExprKind::Await`. Suggest switching the edition to 2018.
             err.note("to `.await` a `Future`, switch to Rust 2018 or later");
diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs b/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs
index 64d261285c5..939f4612d44 100644
--- a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs
+++ b/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs
@@ -2,6 +2,7 @@ use super::FnCtxt;
 use crate::astconv::AstConv;
 use crate::errors::{AddReturnTypeSuggestion, ExpectedReturnTypeLabel};
 
+use hir::def_id::DefId;
 use rustc_ast::util::parser::ExprPrecedence;
 use rustc_errors::{Applicability, Diagnostic, MultiSpan};
 use rustc_hir as hir;
@@ -61,70 +62,51 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         pointing_at_return_type
     }
 
-    /// When encountering an fn-like ctor that needs to unify with a value, check whether calling
-    /// the ctor would successfully solve the type mismatch and if so, suggest it:
+    /// When encountering an fn-like type, try accessing the output of the type
+    /// // and suggesting calling it if it satisfies a predicate (i.e. if the
+    /// output has a method or a field):
     /// ```compile_fail,E0308
     /// fn foo(x: usize) -> usize { x }
     /// let x: usize = foo;  // suggest calling the `foo` function: `foo(42)`
     /// ```
-    fn suggest_fn_call(
+    pub(crate) fn suggest_fn_call(
         &self,
         err: &mut Diagnostic,
         expr: &hir::Expr<'_>,
-        expected: Ty<'tcx>,
         found: Ty<'tcx>,
+        can_satisfy: impl FnOnce(Ty<'tcx>) -> bool,
     ) -> bool {
-        let (def_id, output, inputs) = match *found.kind() {
-            ty::FnDef(def_id, _) => {
-                let fn_sig = found.fn_sig(self.tcx);
-                (def_id, fn_sig.output(), fn_sig.inputs().skip_binder().len())
-            }
-            ty::Closure(def_id, substs) => {
-                let fn_sig = substs.as_closure().sig();
-                (def_id, fn_sig.output(), fn_sig.inputs().skip_binder().len() - 1)
-            }
-            ty::Opaque(def_id, substs) => {
-                let sig = self.tcx.bound_item_bounds(def_id).subst(self.tcx, substs).iter().find_map(|pred| {
-                    if let ty::PredicateKind::Projection(proj) = pred.kind().skip_binder()
-                    && Some(proj.projection_ty.item_def_id) == self.tcx.lang_items().fn_once_output()
-                    // args tuple will always be substs[1]
-                    && let ty::Tuple(args) = proj.projection_ty.substs.type_at(1).kind()
-                    {
-                        Some((
-                            pred.kind().rebind(proj.term.ty().unwrap()),
-                            args.len(),
-                        ))
-                    } else {
-                        None
-                    }
-                });
-                if let Some((output, inputs)) = sig {
-                    (def_id, output, inputs)
-                } else {
-                    return false;
-                }
-            }
-            _ => return false,
-        };
-
-        let output = self.replace_bound_vars_with_fresh_vars(expr.span, infer::FnCall, output);
-        let output = self.normalize_associated_types_in(expr.span, output);
-        if !output.is_ty_var() && self.can_coerce(output, expected) {
-            let (sugg_call, mut applicability) = match inputs {
+        let Some((def_id_or_name, output, inputs)) = self.extract_callable_info(expr, found)
+            else { return false; };
+        if can_satisfy(output) {
+            let (sugg_call, mut applicability) = match inputs.len() {
                 0 => ("".to_string(), Applicability::MachineApplicable),
                 1..=4 => (
-                    (0..inputs).map(|_| "_").collect::<Vec<_>>().join(", "),
-                    Applicability::MachineApplicable,
+                    inputs
+                        .iter()
+                        .map(|ty| {
+                            if ty.is_suggestable(self.tcx, false) {
+                                format!("/* {ty} */")
+                            } else {
+                                "".to_string()
+                            }
+                        })
+                        .collect::<Vec<_>>()
+                        .join(", "),
+                    Applicability::HasPlaceholders,
                 ),
-                _ => ("...".to_string(), Applicability::HasPlaceholders),
+                _ => ("/* ... */".to_string(), Applicability::HasPlaceholders),
             };
 
-            let msg = match self.tcx.def_kind(def_id) {
-                DefKind::Fn => "call this function",
-                DefKind::Closure | DefKind::OpaqueTy => "call this closure",
-                DefKind::Ctor(CtorOf::Struct, _) => "instantiate this tuple struct",
-                DefKind::Ctor(CtorOf::Variant, _) => "instantiate this tuple variant",
-                _ => "call this function",
+            let msg = match def_id_or_name {
+                DefIdOrName::DefId(def_id) => match self.tcx.def_kind(def_id) {
+                    DefKind::Ctor(CtorOf::Struct, _) => "instantiate this tuple struct".to_string(),
+                    DefKind::Ctor(CtorOf::Variant, _) => {
+                        "instantiate this tuple variant".to_string()
+                    }
+                    kind => format!("call this {}", kind.descr(def_id)),
+                },
+                DefIdOrName::Name(name) => format!("call this {name}"),
             };
 
             let sugg = match expr.kind {
@@ -161,6 +143,179 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         false
     }
 
+    fn extract_callable_info(
+        &self,
+        expr: &Expr<'_>,
+        found: Ty<'tcx>,
+    ) -> Option<(DefIdOrName, Ty<'tcx>, Vec<Ty<'tcx>>)> {
+        // Autoderef is useful here because sometimes we box callables, etc.
+        let Some((def_id_or_name, output, inputs)) = self.autoderef(expr.span, found).silence_errors().find_map(|(found, _)| {
+            match *found.kind() {
+                ty::FnPtr(fn_sig) =>
+                    Some((DefIdOrName::Name("function pointer"), fn_sig.output(), fn_sig.inputs())),
+                ty::FnDef(def_id, _) => {
+                    let fn_sig = found.fn_sig(self.tcx);
+                    Some((DefIdOrName::DefId(def_id), fn_sig.output(), fn_sig.inputs()))
+                }
+                ty::Closure(def_id, substs) => {
+                    let fn_sig = substs.as_closure().sig();
+                    Some((DefIdOrName::DefId(def_id), fn_sig.output(), fn_sig.inputs().map_bound(|inputs| &inputs[1..])))
+                }
+                ty::Opaque(def_id, substs) => {
+                    self.tcx.bound_item_bounds(def_id).subst(self.tcx, substs).iter().find_map(|pred| {
+                        if let ty::PredicateKind::Projection(proj) = pred.kind().skip_binder()
+                        && Some(proj.projection_ty.item_def_id) == self.tcx.lang_items().fn_once_output()
+                        // args tuple will always be substs[1]
+                        && let ty::Tuple(args) = proj.projection_ty.substs.type_at(1).kind()
+                        {
+                            Some((
+                                DefIdOrName::DefId(def_id),
+                                pred.kind().rebind(proj.term.ty().unwrap()),
+                                pred.kind().rebind(args.as_slice()),
+                            ))
+                        } else {
+                            None
+                        }
+                    })
+                }
+                ty::Dynamic(data, _) => {
+                    data.iter().find_map(|pred| {
+                        if let ty::ExistentialPredicate::Projection(proj) = pred.skip_binder()
+                        && Some(proj.item_def_id) == self.tcx.lang_items().fn_once_output()
+                        // for existential projection, substs are shifted over by 1
+                        && let ty::Tuple(args) = proj.substs.type_at(0).kind()
+                        {
+                            Some((
+                                DefIdOrName::Name("trait object"),
+                                pred.rebind(proj.term.ty().unwrap()),
+                                pred.rebind(args.as_slice()),
+                            ))
+                        } else {
+                            None
+                        }
+                    })
+                }
+                ty::Param(param) => {
+                    let def_id = self.tcx.generics_of(self.body_id.owner).type_param(&param, self.tcx).def_id;
+                    self.tcx.predicates_of(self.body_id.owner).predicates.iter().find_map(|(pred, _)| {
+                        if let ty::PredicateKind::Projection(proj) = pred.kind().skip_binder()
+                        && Some(proj.projection_ty.item_def_id) == self.tcx.lang_items().fn_once_output()
+                        && proj.projection_ty.self_ty() == found
+                        // args tuple will always be substs[1]
+                        && let ty::Tuple(args) = proj.projection_ty.substs.type_at(1).kind()
+                        {
+                            Some((
+                                DefIdOrName::DefId(def_id),
+                                pred.kind().rebind(proj.term.ty().unwrap()),
+                                pred.kind().rebind(args.as_slice()),
+                            ))
+                        } else {
+                            None
+                        }
+                    })
+                }
+                _ => None,
+            }
+        }) else { return None; };
+
+        let output = self.replace_bound_vars_with_fresh_vars(expr.span, infer::FnCall, output);
+        let inputs = inputs
+            .skip_binder()
+            .iter()
+            .map(|ty| {
+                self.replace_bound_vars_with_fresh_vars(
+                    expr.span,
+                    infer::FnCall,
+                    inputs.rebind(*ty),
+                )
+            })
+            .collect();
+
+        // We don't want to register any extra obligations, which should be
+        // implied by wf, but also because that would possibly result in
+        // erroneous errors later on.
+        let infer::InferOk { value: output, obligations: _ } =
+            self.normalize_associated_types_in_as_infer_ok(expr.span, output);
+
+        if output.is_ty_var() { None } else { Some((def_id_or_name, output, inputs)) }
+    }
+
+    pub fn suggest_two_fn_call(
+        &self,
+        err: &mut Diagnostic,
+        lhs_expr: &'tcx hir::Expr<'tcx>,
+        lhs_ty: Ty<'tcx>,
+        rhs_expr: &'tcx hir::Expr<'tcx>,
+        rhs_ty: Ty<'tcx>,
+        can_satisfy: impl FnOnce(Ty<'tcx>, Ty<'tcx>) -> bool,
+    ) -> bool {
+        let Some((_, lhs_output_ty, lhs_inputs)) = self.extract_callable_info(lhs_expr, lhs_ty)
+            else { return false; };
+        let Some((_, rhs_output_ty, rhs_inputs)) = self.extract_callable_info(rhs_expr, rhs_ty)
+            else { return false; };
+
+        if can_satisfy(lhs_output_ty, rhs_output_ty) {
+            let mut sugg = vec![];
+            let mut applicability = Applicability::MachineApplicable;
+
+            for (expr, inputs) in [(lhs_expr, lhs_inputs), (rhs_expr, rhs_inputs)] {
+                let (sugg_call, this_applicability) = match inputs.len() {
+                    0 => ("".to_string(), Applicability::MachineApplicable),
+                    1..=4 => (
+                        inputs
+                            .iter()
+                            .map(|ty| {
+                                if ty.is_suggestable(self.tcx, false) {
+                                    format!("/* {ty} */")
+                                } else {
+                                    "/* value */".to_string()
+                                }
+                            })
+                            .collect::<Vec<_>>()
+                            .join(", "),
+                        Applicability::HasPlaceholders,
+                    ),
+                    _ => ("/* ... */".to_string(), Applicability::HasPlaceholders),
+                };
+
+                applicability = applicability.max(this_applicability);
+
+                match expr.kind {
+                    hir::ExprKind::Call(..)
+                    | hir::ExprKind::Path(..)
+                    | hir::ExprKind::Index(..)
+                    | hir::ExprKind::Lit(..) => {
+                        sugg.extend([(expr.span.shrink_to_hi(), format!("({sugg_call})"))]);
+                    }
+                    hir::ExprKind::Closure { .. } => {
+                        // Might be `{ expr } || { bool }`
+                        applicability = Applicability::MaybeIncorrect;
+                        sugg.extend([
+                            (expr.span.shrink_to_lo(), "(".to_string()),
+                            (expr.span.shrink_to_hi(), format!(")({sugg_call})")),
+                        ]);
+                    }
+                    _ => {
+                        sugg.extend([
+                            (expr.span.shrink_to_lo(), "(".to_string()),
+                            (expr.span.shrink_to_hi(), format!(")({sugg_call})")),
+                        ]);
+                    }
+                }
+            }
+
+            err.multipart_suggestion_verbose(
+                format!("use parentheses to call these"),
+                sugg,
+                applicability,
+            );
+
+            true
+        } else {
+            false
+        }
+    }
+
     pub fn suggest_deref_ref_or_into(
         &self,
         err: &mut Diagnostic,
@@ -178,12 +333,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             } else {
                 err.span_suggestion(sp, &msg, suggestion, applicability);
             }
-        } else if let (ty::FnDef(def_id, ..), true) =
-            (&found.kind(), self.suggest_fn_call(err, expr, expected, found))
+        } else if self.suggest_fn_call(err, expr, found, |output| self.can_coerce(output, expected))
+            && let ty::FnDef(def_id, ..) = &found.kind()
+            && let Some(sp) = self.tcx.hir().span_if_local(*def_id)
         {
-            if let Some(sp) = self.tcx.hir().span_if_local(*def_id) {
-                err.span_label(sp, format!("{found} defined here"));
-            }
+            err.span_label(sp, format!("{found} defined here"));
         } else if !self.check_for_cast(err, expr, found, expected, expected_ty_expr) {
             let methods = self.get_conversion_methods(expr.span, expected, found, expr.hir_id);
             if !methods.is_empty() {
@@ -911,3 +1065,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         }
     }
 }
+
+enum DefIdOrName {
+    DefId(DefId),
+    Name(&'static str),
+}
diff --git a/compiler/rustc_typeck/src/check/method/suggest.rs b/compiler/rustc_typeck/src/check/method/suggest.rs
index e532f392157..e99782fdc65 100644
--- a/compiler/rustc_typeck/src/check/method/suggest.rs
+++ b/compiler/rustc_typeck/src/check/method/suggest.rs
@@ -31,7 +31,7 @@ use std::cmp::Ordering;
 use std::iter;
 
 use super::probe::{Mode, ProbeScope};
-use super::{super::suggest_call_constructor, CandidateSource, MethodError, NoMatchData};
+use super::{CandidateSource, MethodError, NoMatchData};
 
 impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     fn is_fn_ty(&self, ty: Ty<'tcx>, span: Span) -> bool {
@@ -363,44 +363,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     );
                 }
 
-                if self.is_fn_ty(rcvr_ty, span) {
-                    if let SelfSource::MethodCall(expr) = source {
-                        let suggest = if let ty::FnDef(def_id, _) = rcvr_ty.kind() {
-                            if let Some(local_id) = def_id.as_local() {
-                                let hir_id = tcx.hir().local_def_id_to_hir_id(local_id);
-                                let node = tcx.hir().get(hir_id);
-                                let fields = node.tuple_fields();
-                                if let Some(fields) = fields
-                                    && let Some(DefKind::Ctor(of, _)) = self.tcx.opt_def_kind(local_id) {
-                                        Some((fields.len(), of))
-                                } else {
-                                    None
-                                }
-                            } else {
-                                // The logic here isn't smart but `associated_item_def_ids`
-                                // doesn't work nicely on local.
-                                if let DefKind::Ctor(of, _) = tcx.def_kind(def_id) {
-                                    let parent_def_id = tcx.parent(*def_id);
-                                    Some((tcx.associated_item_def_ids(parent_def_id).len(), of))
-                                } else {
-                                    None
-                                }
-                            }
-                        } else {
-                            None
-                        };
-
-                        // If the function is a tuple constructor, we recommend that they call it
-                        if let Some((fields, kind)) = suggest {
-                            suggest_call_constructor(expr.span, kind, fields, &mut err);
-                        } else {
-                            // General case
-                            err.span_label(
-                                expr.span,
-                                "this is a function, perhaps you wish to call it",
-                            );
-                        }
-                    }
+                if let SelfSource::MethodCall(rcvr_expr) = source {
+                    self.suggest_fn_call(&mut err, rcvr_expr, rcvr_ty, |output_ty| {
+                        let call_expr = self
+                            .tcx
+                            .hir()
+                            .expect_expr(self.tcx.hir().get_parent_node(rcvr_expr.hir_id));
+                        let probe = self.lookup_probe(
+                            span,
+                            item_name,
+                            output_ty,
+                            call_expr,
+                            ProbeScope::AllTraits,
+                        );
+                        probe.is_ok()
+                    });
                 }
 
                 let mut custom_span_label = false;
diff --git a/compiler/rustc_typeck/src/check/mod.rs b/compiler/rustc_typeck/src/check/mod.rs
index f8d839b6483..3281dd8298b 100644
--- a/compiler/rustc_typeck/src/check/mod.rs
+++ b/compiler/rustc_typeck/src/check/mod.rs
@@ -96,7 +96,6 @@ use check::{check_abi, check_fn, check_mod_item_types};
 pub use diverges::Diverges;
 pub use expectation::Expectation;
 pub use fn_ctxt::*;
-use hir::def::CtorOf;
 pub use inherited::{Inherited, InheritedBuilder};
 
 use crate::astconv::AstConv;
@@ -960,31 +959,3 @@ fn has_expected_num_generic_args<'tcx>(
         generics.count() == expected + if generics.has_self { 1 } else { 0 }
     })
 }
-
-/// Suggests calling the constructor of a tuple struct or enum variant
-///
-/// * `snippet` - The snippet of code that references the constructor
-/// * `span` - The span of the snippet
-/// * `params` - The number of parameters the constructor accepts
-/// * `err` - A mutable diagnostic builder to add the suggestion to
-fn suggest_call_constructor(span: Span, kind: CtorOf, params: usize, err: &mut Diagnostic) {
-    // Note: tuple-structs don't have named fields, so just use placeholders
-    let args = vec!["_"; params].join(", ");
-    let applicable = if params > 0 {
-        Applicability::HasPlaceholders
-    } else {
-        // When n = 0, it's an empty-tuple struct/enum variant
-        // so we trivially know how to construct it
-        Applicability::MachineApplicable
-    };
-    let kind = match kind {
-        CtorOf::Struct => "a struct",
-        CtorOf::Variant => "an enum variant",
-    };
-    err.span_label(span, &format!("this is the constructor of {kind}"));
-    err.multipart_suggestion(
-        "call the constructor",
-        vec![(span.shrink_to_lo(), "(".to_string()), (span.shrink_to_hi(), format!(")({args})"))],
-        applicable,
-    );
-}
diff --git a/compiler/rustc_typeck/src/check/op.rs b/compiler/rustc_typeck/src/check/op.rs
index eb0c51bb2f9..952086e898f 100644
--- a/compiler/rustc_typeck/src/check/op.rs
+++ b/compiler/rustc_typeck/src/check/op.rs
@@ -410,26 +410,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         };
                         let mut err = struct_span_err!(self.tcx.sess, op.span, E0369, "{message}");
                         if !lhs_expr.span.eq(&rhs_expr.span) {
-                            self.add_type_neq_err_label(
-                                &mut err,
-                                lhs_expr.span,
-                                lhs_ty,
-                                rhs_ty,
-                                rhs_expr,
-                                op,
-                                is_assign,
-                                expected,
-                            );
-                            self.add_type_neq_err_label(
-                                &mut err,
-                                rhs_expr.span,
-                                rhs_ty,
-                                lhs_ty,
-                                lhs_expr,
-                                op,
-                                is_assign,
-                                expected,
-                            );
+                            err.span_label(lhs_expr.span, lhs_ty.to_string());
+                            err.span_label(rhs_expr.span, rhs_ty.to_string());
                         }
                         self.note_unmet_impls_on_type(&mut err, errors);
                         (err, missing_trait, use_output)
@@ -468,17 +450,50 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     }
                 };
 
+                let is_compatible = |lhs_ty, rhs_ty| {
+                    self.lookup_op_method(
+                        lhs_ty,
+                        Some(rhs_ty),
+                        Some(rhs_expr),
+                        Op::Binary(op, is_assign),
+                        expected,
+                    )
+                    .is_ok()
+                };
+
                 // We should suggest `a + b` => `*a + b` if `a` is copy, and suggest
                 // `a += b` => `*a += b` if a is a mut ref.
-                if is_assign == IsAssign::Yes
-                    && let Some(lhs_deref_ty) = self.deref_once_mutably_for_diagnostic(lhs_ty) {
-                        suggest_deref_binop(lhs_deref_ty);
+                if !op.span.can_be_used_for_suggestions() {
+                    // Suppress suggestions when lhs and rhs are not in the same span as the error
+                } else if is_assign == IsAssign::Yes
+                    && let Some(lhs_deref_ty) = self.deref_once_mutably_for_diagnostic(lhs_ty)
+                {
+                    suggest_deref_binop(lhs_deref_ty);
                 } else if is_assign == IsAssign::No
-                    && let Ref(_, lhs_deref_ty, _) = lhs_ty.kind() {
-                    if self.type_is_copy_modulo_regions(self.param_env, *lhs_deref_ty, lhs_expr.span) {
+                    && let Ref(_, lhs_deref_ty, _) = lhs_ty.kind()
+                {
+                    if self.type_is_copy_modulo_regions(
+                        self.param_env,
+                        *lhs_deref_ty,
+                        lhs_expr.span,
+                    ) {
                         suggest_deref_binop(*lhs_deref_ty);
                     }
+                } else if self.suggest_fn_call(&mut err, lhs_expr, lhs_ty, |lhs_ty| {
+                    is_compatible(lhs_ty, rhs_ty)
+                }) || self.suggest_fn_call(&mut err, rhs_expr, rhs_ty, |rhs_ty| {
+                    is_compatible(lhs_ty, rhs_ty)
+                }) || self.suggest_two_fn_call(
+                    &mut err,
+                    rhs_expr,
+                    rhs_ty,
+                    lhs_expr,
+                    lhs_ty,
+                    |lhs_ty, rhs_ty| is_compatible(lhs_ty, rhs_ty),
+                ) {
+                    // Cool
                 }
+
                 if let Some(missing_trait) = missing_trait {
                     let mut visitor = TypeParamVisitor(vec![]);
                     visitor.visit_ty(lhs_ty);
@@ -548,69 +563,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         (lhs_ty, rhs_ty, return_ty)
     }
 
-    /// If one of the types is an uncalled function and calling it would yield the other type,
-    /// suggest calling the function. Returns `true` if suggestion would apply (even if not given).
-    fn add_type_neq_err_label(
-        &self,
-        err: &mut Diagnostic,
-        span: Span,
-        ty: Ty<'tcx>,
-        other_ty: Ty<'tcx>,
-        other_expr: &'tcx hir::Expr<'tcx>,
-        op: hir::BinOp,
-        is_assign: IsAssign,
-        expected: Expectation<'tcx>,
-    ) -> bool /* did we suggest to call a function because of missing parentheses? */ {
-        err.span_label(span, ty.to_string());
-        if let FnDef(def_id, _) = *ty.kind() {
-            if !self.tcx.has_typeck_results(def_id) {
-                return false;
-            }
-            // FIXME: Instead of exiting early when encountering bound vars in
-            // the function signature, consider keeping the binder here and
-            // propagating it downwards.
-            let Some(fn_sig) = self.tcx.fn_sig(def_id).no_bound_vars() else {
-                return false;
-            };
-
-            let other_ty = if let FnDef(def_id, _) = *other_ty.kind() {
-                if !self.tcx.has_typeck_results(def_id) {
-                    return false;
-                }
-                // We're emitting a suggestion, so we can just ignore regions
-                self.tcx.fn_sig(def_id).skip_binder().output()
-            } else {
-                other_ty
-            };
-
-            if self
-                .lookup_op_method(
-                    fn_sig.output(),
-                    Some(other_ty),
-                    Some(other_expr),
-                    Op::Binary(op, is_assign),
-                    expected,
-                )
-                .is_ok()
-            {
-                let (variable_snippet, applicability) = if !fn_sig.inputs().is_empty() {
-                    ("( /* arguments */ )", Applicability::HasPlaceholders)
-                } else {
-                    ("()", Applicability::MaybeIncorrect)
-                };
-
-                err.span_suggestion_verbose(
-                    span.shrink_to_hi(),
-                    "you might have forgotten to call this function",
-                    variable_snippet,
-                    applicability,
-                );
-                return true;
-            }
-        }
-        false
-    }
-
     /// Provide actionable suggestions when trying to add two strings with incorrect types,
     /// like `&str + &str`, `String + String` and `&str + &String`.
     ///
diff --git a/src/test/ui/associated-types/substs-ppaux.normal.stderr b/src/test/ui/associated-types/substs-ppaux.normal.stderr
index 501d2cfaa26..3f180cf4f1f 100644
--- a/src/test/ui/associated-types/substs-ppaux.normal.stderr
+++ b/src/test/ui/associated-types/substs-ppaux.normal.stderr
@@ -11,7 +11,7 @@ LL |     let x: () = <i8 as Foo<'static, 'static,  u8>>::bar::<'static, char>;
    |
    = note: expected unit type `()`
                 found fn item `fn() {<i8 as Foo<'static, 'static, u8>>::bar::<'static, char>}`
-help: use parentheses to call this function
+help: use parentheses to call this associated function
    |
 LL |     let x: () = <i8 as Foo<'static, 'static,  u8>>::bar::<'static, char>();
    |                                                                         ++
@@ -29,7 +29,7 @@ LL |     let x: () = <i8 as Foo<'static, 'static,  u32>>::bar::<'static, char>;
    |
    = note: expected unit type `()`
                 found fn item `fn() {<i8 as Foo<'static, 'static>>::bar::<'static, char>}`
-help: use parentheses to call this function
+help: use parentheses to call this associated function
    |
 LL |     let x: () = <i8 as Foo<'static, 'static,  u32>>::bar::<'static, char>();
    |                                                                          ++
@@ -47,7 +47,7 @@ LL |     let x: () = <i8 as Foo<'static, 'static,  u8>>::baz;
    |
    = note: expected unit type `()`
                 found fn item `fn() {<i8 as Foo<'static, 'static, u8>>::baz}`
-help: use parentheses to call this function
+help: use parentheses to call this associated function
    |
 LL |     let x: () = <i8 as Foo<'static, 'static,  u8>>::baz();
    |                                                        ++
diff --git a/src/test/ui/associated-types/substs-ppaux.verbose.stderr b/src/test/ui/associated-types/substs-ppaux.verbose.stderr
index ae3e862dddd..16dd29de2c5 100644
--- a/src/test/ui/associated-types/substs-ppaux.verbose.stderr
+++ b/src/test/ui/associated-types/substs-ppaux.verbose.stderr
@@ -11,7 +11,7 @@ LL |     let x: () = <i8 as Foo<'static, 'static,  u8>>::bar::<'static, char>;
    |
    = note: expected unit type `()`
                 found fn item `fn() {<i8 as Foo<ReStatic, ReStatic, u8>>::bar::<ReStatic, char>}`
-help: use parentheses to call this function
+help: use parentheses to call this associated function
    |
 LL |     let x: () = <i8 as Foo<'static, 'static,  u8>>::bar::<'static, char>();
    |                                                                         ++
@@ -29,7 +29,7 @@ LL |     let x: () = <i8 as Foo<'static, 'static,  u32>>::bar::<'static, char>;
    |
    = note: expected unit type `()`
                 found fn item `fn() {<i8 as Foo<ReStatic, ReStatic>>::bar::<ReStatic, char>}`
-help: use parentheses to call this function
+help: use parentheses to call this associated function
    |
 LL |     let x: () = <i8 as Foo<'static, 'static,  u32>>::bar::<'static, char>();
    |                                                                          ++
@@ -47,7 +47,7 @@ LL |     let x: () = <i8 as Foo<'static, 'static,  u8>>::baz;
    |
    = note: expected unit type `()`
                 found fn item `fn() {<i8 as Foo<ReStatic, ReStatic, u8>>::baz}`
-help: use parentheses to call this function
+help: use parentheses to call this associated function
    |
 LL |     let x: () = <i8 as Foo<'static, 'static,  u8>>::baz();
    |                                                        ++
diff --git a/src/test/ui/binop/issue-77910-2.stderr b/src/test/ui/binop/issue-77910-2.stderr
index 5477a5762a8..a334bd85625 100644
--- a/src/test/ui/binop/issue-77910-2.stderr
+++ b/src/test/ui/binop/issue-77910-2.stderr
@@ -5,6 +5,11 @@ LL |     if foo == y {}
    |        --- ^^ - _
    |        |
    |        for<'r> fn(&'r i32) -> &'r i32 {foo}
+   |
+help: use parentheses to call this function
+   |
+LL |     if foo(/* &i32 */) == y {}
+   |           ++++++++++++
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/fn/fn-compare-mismatch.stderr b/src/test/ui/fn/fn-compare-mismatch.stderr
index 096440225b9..df838cb1181 100644
--- a/src/test/ui/fn/fn-compare-mismatch.stderr
+++ b/src/test/ui/fn/fn-compare-mismatch.stderr
@@ -6,14 +6,10 @@ LL |     let x = f == g;
    |             |
    |             fn() {f}
    |
-help: you might have forgotten to call this function
+help: use parentheses to call these
    |
-LL |     let x = f() == g;
-   |              ++
-help: you might have forgotten to call this function
-   |
-LL |     let x = f == g();
-   |                   ++
+LL |     let x = f() == g();
+   |              ++     ++
 
 error[E0308]: mismatched types
   --> $DIR/fn-compare-mismatch.rs:4:18
diff --git a/src/test/ui/fn/fn-trait-formatting.stderr b/src/test/ui/fn/fn-trait-formatting.stderr
index ea88e401bed..2a674d3c1d2 100644
--- a/src/test/ui/fn/fn-trait-formatting.stderr
+++ b/src/test/ui/fn/fn-trait-formatting.stderr
@@ -8,6 +8,10 @@ LL |     let _: () = Box::new(|_: isize| {}) as Box<dyn FnOnce(isize)>;
    |
    = note: expected unit type `()`
                  found struct `Box<dyn FnOnce(isize)>`
+help: use parentheses to call this trait object
+   |
+LL |     let _: () = (Box::new(|_: isize| {}) as Box<dyn FnOnce(isize)>)(/* isize */);
+   |                 +                                                 ++++++++++++++
 
 error[E0308]: mismatched types
   --> $DIR/fn-trait-formatting.rs:10:17
@@ -19,6 +23,10 @@ LL |     let _: () = Box::new(|_: isize, isize| {}) as Box<dyn Fn(isize, isize)>
    |
    = note: expected unit type `()`
                  found struct `Box<dyn Fn(isize, isize)>`
+help: use parentheses to call this trait object
+   |
+LL |     let _: () = (Box::new(|_: isize, isize| {}) as Box<dyn Fn(isize, isize)>)(/* isize */, /* isize */);
+   |                 +                                                           +++++++++++++++++++++++++++
 
 error[E0308]: mismatched types
   --> $DIR/fn-trait-formatting.rs:14:17
diff --git a/src/test/ui/functions-closures/fn-help-with-err.rs b/src/test/ui/functions-closures/fn-help-with-err.rs
index 3d2bcb8ad35..49a514a8b4e 100644
--- a/src/test/ui/functions-closures/fn-help-with-err.rs
+++ b/src/test/ui/functions-closures/fn-help-with-err.rs
@@ -1,16 +1,28 @@
 // This test case checks the behavior of typeck::check::method::suggest::is_fn on Ty::Error.
+
+struct Foo;
+
+trait Bar {
+    //~^ NOTE `Bar` defines an item `bar`, perhaps you need to implement it
+    //~| NOTE `Bar` defines an item `bar`, perhaps you need to implement it
+    fn bar(&self) {}
+}
+
+impl Bar for Foo {}
+
 fn main() {
     let arc = std::sync::Arc::new(oops);
     //~^ ERROR cannot find value `oops` in this scope
     //~| NOTE not found
-    // The error "note: this is a function, perhaps you wish to call it" MUST NOT appear.
-    arc.blablabla();
-    //~^ ERROR no method named `blablabla`
+    arc.bar();
+    //~^ ERROR no method named `bar`
     //~| NOTE method not found
-    let arc2 = std::sync::Arc::new(|| 1);
-    // The error "note: this is a function, perhaps you wish to call it" SHOULD appear
-    arc2.blablabla();
-    //~^ ERROR no method named `blablabla`
+    //~| HELP items from traits can only be used if the trait is implemented and in scope
+
+    let arc2 = std::sync::Arc::new(|| Foo);
+    arc2.bar();
+    //~^ ERROR no method named `bar`
     //~| NOTE method not found
-    //~| NOTE this is a function, perhaps you wish to call it
+    //~| HELP items from traits can only be used if the trait is implemented and in scope
+    //~| HELP use parentheses to call this closure
 }
diff --git a/src/test/ui/functions-closures/fn-help-with-err.stderr b/src/test/ui/functions-closures/fn-help-with-err.stderr
index 06e29daef45..2296666219e 100644
--- a/src/test/ui/functions-closures/fn-help-with-err.stderr
+++ b/src/test/ui/functions-closures/fn-help-with-err.stderr
@@ -1,22 +1,38 @@
 error[E0425]: cannot find value `oops` in this scope
-  --> $DIR/fn-help-with-err.rs:3:35
+  --> $DIR/fn-help-with-err.rs:14:35
    |
 LL |     let arc = std::sync::Arc::new(oops);
    |                                   ^^^^ not found in this scope
 
-error[E0599]: no method named `blablabla` found for struct `Arc<_>` in the current scope
-  --> $DIR/fn-help-with-err.rs:7:9
+error[E0599]: no method named `bar` found for struct `Arc<_>` in the current scope
+  --> $DIR/fn-help-with-err.rs:17:9
    |
-LL |     arc.blablabla();
-   |         ^^^^^^^^^ method not found in `Arc<_>`
+LL |     arc.bar();
+   |         ^^^ method not found in `Arc<_>`
+   |
+   = help: items from traits can only be used if the trait is implemented and in scope
+note: `Bar` defines an item `bar`, perhaps you need to implement it
+  --> $DIR/fn-help-with-err.rs:5:1
+   |
+LL | trait Bar {
+   | ^^^^^^^^^
 
-error[E0599]: no method named `blablabla` found for struct `Arc<[closure@$DIR/fn-help-with-err.rs:10:36: 10:38]>` in the current scope
-  --> $DIR/fn-help-with-err.rs:12:10
+error[E0599]: no method named `bar` found for struct `Arc<[closure@$DIR/fn-help-with-err.rs:22:36: 22:38]>` in the current scope
+  --> $DIR/fn-help-with-err.rs:23:10
+   |
+LL |     arc2.bar();
+   |          ^^^ method not found in `Arc<[closure@$DIR/fn-help-with-err.rs:22:36: 22:38]>`
+   |
+   = help: items from traits can only be used if the trait is implemented and in scope
+note: `Bar` defines an item `bar`, perhaps you need to implement it
+  --> $DIR/fn-help-with-err.rs:5:1
+   |
+LL | trait Bar {
+   | ^^^^^^^^^
+help: use parentheses to call this closure
    |
-LL |     arc2.blablabla();
-   |     ---- ^^^^^^^^^ method not found in `Arc<[closure@$DIR/fn-help-with-err.rs:10:36: 10:38]>`
-   |     |
-   |     this is a function, perhaps you wish to call it
+LL |     arc2().bar();
+   |         ++
 
 error: aborting due to 3 previous errors
 
diff --git a/src/test/ui/impl-trait/suggest-calling-rpit-closure.stderr b/src/test/ui/impl-trait/suggest-calling-rpit-closure.stderr
index 2a328a0e6f5..c10a856d83b 100644
--- a/src/test/ui/impl-trait/suggest-calling-rpit-closure.stderr
+++ b/src/test/ui/impl-trait/suggest-calling-rpit-closure.stderr
@@ -11,7 +11,7 @@ LL | fn opaque() -> impl Fn() -> i32 {
    |
    = note:     expected type `i32`
            found opaque type `impl Fn() -> i32`
-help: use parentheses to call this closure
+help: use parentheses to call this opaque type
    |
 LL |     opaque()()
    |             ++
diff --git a/src/test/ui/issues/issue-35241.stderr b/src/test/ui/issues/issue-35241.stderr
index a66289a1cf8..9ee7654a088 100644
--- a/src/test/ui/issues/issue-35241.stderr
+++ b/src/test/ui/issues/issue-35241.stderr
@@ -13,8 +13,8 @@ LL | fn test() -> Foo { Foo }
              found fn item `fn(u32) -> Foo {Foo}`
 help: use parentheses to instantiate this tuple struct
    |
-LL | fn test() -> Foo { Foo(_) }
-   |                       +++
+LL | fn test() -> Foo { Foo(/* u32 */) }
+   |                       +++++++++++
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/issues/issue-57362-1.stderr b/src/test/ui/issues/issue-57362-1.stderr
index 8e19f14009a..b10273f14bd 100644
--- a/src/test/ui/issues/issue-57362-1.stderr
+++ b/src/test/ui/issues/issue-57362-1.stderr
@@ -2,9 +2,7 @@ error[E0599]: no method named `f` found for fn pointer `fn(&u8)` in the current
   --> $DIR/issue-57362-1.rs:20:7
    |
 LL |     a.f();
-   |     - ^ method not found in `fn(&u8)`
-   |     |
-   |     this is a function, perhaps you wish to call it
+   |       ^ method not found in `fn(&u8)`
    |
    = help: items from traits can only be used if the trait is implemented and in scope
 note: `Trait` defines an item `f`, perhaps you need to implement it
diff --git a/src/test/ui/issues/issue-59488.stderr b/src/test/ui/issues/issue-59488.stderr
index bb6843a1958..e5368ddf1e5 100644
--- a/src/test/ui/issues/issue-59488.stderr
+++ b/src/test/ui/issues/issue-59488.stderr
@@ -6,7 +6,7 @@ LL |     foo > 12;
    |     |
    |     fn() -> i32 {foo}
    |
-help: you might have forgotten to call this function
+help: use parentheses to call this function
    |
 LL |     foo() > 12;
    |        ++
@@ -28,10 +28,10 @@ LL |     bar > 13;
    |     |
    |     fn(i64) -> i64 {bar}
    |
-help: you might have forgotten to call this function
+help: use parentheses to call this function
    |
-LL |     bar( /* arguments */ ) > 13;
-   |        +++++++++++++++++++
+LL |     bar(/* i64 */) > 13;
+   |        +++++++++++
 
 error[E0308]: mismatched types
   --> $DIR/issue-59488.rs:18:11
@@ -50,14 +50,10 @@ LL |     foo > foo;
    |     |
    |     fn() -> i32 {foo}
    |
-help: you might have forgotten to call this function
+help: use parentheses to call these
    |
-LL |     foo() > foo;
-   |        ++
-help: you might have forgotten to call this function
-   |
-LL |     foo > foo();
-   |              ++
+LL |     foo() > foo();
+   |        ++      ++
 
 error[E0369]: binary operation `>` cannot be applied to type `fn() -> i32 {foo}`
   --> $DIR/issue-59488.rs:25:9
diff --git a/src/test/ui/issues/issue-70724-add_type_neq_err_label-unwrap.stderr b/src/test/ui/issues/issue-70724-add_type_neq_err_label-unwrap.stderr
index c6e6ea1e096..9239385e643 100644
--- a/src/test/ui/issues/issue-70724-add_type_neq_err_label-unwrap.stderr
+++ b/src/test/ui/issues/issue-70724-add_type_neq_err_label-unwrap.stderr
@@ -8,11 +8,6 @@ LL |     assert_eq!(a, 0);
    |     {integer}
    |
    = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)
-help: you might have forgotten to call this function
-  --> $SRC_DIR/core/src/macros/mod.rs:LL:COL
-   |
-LL |                 if !(*left_val() == *right_val) {
-   |                               ++
 
 error[E0308]: mismatched types
   --> $DIR/issue-70724-add_type_neq_err_label-unwrap.rs:6:5
@@ -21,7 +16,7 @@ LL |     assert_eq!(a, 0);
    |     ^^^^^^^^^^^^^^^^ expected fn item, found integer
    |
    = note: expected fn item `fn() -> i32 {a}`
-                 found type `i32`
+                 found type `{integer}`
    = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0277]: `fn() -> i32 {a}` doesn't implement `Debug`
diff --git a/src/test/ui/resolve/privacy-enum-ctor.stderr b/src/test/ui/resolve/privacy-enum-ctor.stderr
index f885ac2151d..7cf32775a33 100644
--- a/src/test/ui/resolve/privacy-enum-ctor.stderr
+++ b/src/test/ui/resolve/privacy-enum-ctor.stderr
@@ -329,8 +329,8 @@ LL |         let _: Z = Z::Fn;
            found fn item `fn(u8) -> Z {Z::Fn}`
 help: use parentheses to instantiate this tuple variant
    |
-LL |         let _: Z = Z::Fn(_);
-   |                         +++
+LL |         let _: Z = Z::Fn(/* u8 */);
+   |                         ++++++++++
 
 error[E0618]: expected function, found enum variant `Z::Unit`
   --> $DIR/privacy-enum-ctor.rs:31:17
@@ -364,8 +364,8 @@ LL |     let _: E = m::E::Fn;
            found fn item `fn(u8) -> E {E::Fn}`
 help: use parentheses to instantiate this tuple variant
    |
-LL |     let _: E = m::E::Fn(_);
-   |                        +++
+LL |     let _: E = m::E::Fn(/* u8 */);
+   |                        ++++++++++
 
 error[E0618]: expected function, found enum variant `m::E::Unit`
   --> $DIR/privacy-enum-ctor.rs:47:16
@@ -399,8 +399,8 @@ LL |     let _: E = E::Fn;
            found fn item `fn(u8) -> E {E::Fn}`
 help: use parentheses to instantiate this tuple variant
    |
-LL |     let _: E = E::Fn(_);
-   |                     +++
+LL |     let _: E = E::Fn(/* u8 */);
+   |                     ++++++++++
 
 error[E0618]: expected function, found enum variant `E::Unit`
   --> $DIR/privacy-enum-ctor.rs:55:16
diff --git a/src/test/ui/suggestions/call-boxed.rs b/src/test/ui/suggestions/call-boxed.rs
new file mode 100644
index 00000000000..d19e4596a0c
--- /dev/null
+++ b/src/test/ui/suggestions/call-boxed.rs
@@ -0,0 +1,7 @@
+fn main() {
+    let mut x = 1i32;
+    let y = Box::new(|| 1);
+    x = y;
+    //~^ ERROR mismatched types
+    //~| HELP use parentheses to call this closure
+}
diff --git a/src/test/ui/suggestions/call-boxed.stderr b/src/test/ui/suggestions/call-boxed.stderr
new file mode 100644
index 00000000000..9b619ac9a3f
--- /dev/null
+++ b/src/test/ui/suggestions/call-boxed.stderr
@@ -0,0 +1,20 @@
+error[E0308]: mismatched types
+  --> $DIR/call-boxed.rs:4:9
+   |
+LL |     let mut x = 1i32;
+   |                 ---- expected due to this value
+LL |     let y = Box::new(|| 1);
+   |                      -- the found closure
+LL |     x = y;
+   |         ^ expected `i32`, found struct `Box`
+   |
+   = note: expected type `i32`
+            found struct `Box<[closure@$DIR/call-boxed.rs:3:22: 3:24]>`
+help: use parentheses to call this closure
+   |
+LL |     x = y();
+   |          ++
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/src/test/ui/suggestions/call-on-missing.rs b/src/test/ui/suggestions/call-on-missing.rs
new file mode 100644
index 00000000000..25ced84dd37
--- /dev/null
+++ b/src/test/ui/suggestions/call-on-missing.rs
@@ -0,0 +1,39 @@
+struct Foo { i: i32 }
+
+impl Foo {
+    fn bar(&self) {}
+}
+
+fn foo() -> Foo {
+    Foo { i: 1 }
+}
+
+fn main() {
+    foo.bar();
+    //~^ ERROR no method named `bar`
+    //~| HELP use parentheses to call this function
+
+    foo.i;
+    //~^ ERROR no field `i`
+    //~| HELP use parentheses to call this function
+
+    let callable = Box::new(|| Foo { i: 1 }) as Box<dyn Fn() -> Foo>;
+
+    callable.bar();
+    //~^ ERROR no method named `bar`
+    //~| HELP use parentheses to call this trait object
+
+    callable.i;
+    //~^ ERROR no field `i`
+    //~| HELP use parentheses to call this trait object
+}
+
+fn type_param<T: Fn() -> Foo>(t: T) {
+    t.bar();
+    //~^ ERROR no method named `bar`
+    //~| HELP use parentheses to call this type parameter
+
+    t.i;
+    //~^ ERROR no field `i`
+    //~| HELP use parentheses to call this type parameter
+}
diff --git a/src/test/ui/suggestions/call-on-missing.stderr b/src/test/ui/suggestions/call-on-missing.stderr
new file mode 100644
index 00000000000..ca9abc7e906
--- /dev/null
+++ b/src/test/ui/suggestions/call-on-missing.stderr
@@ -0,0 +1,75 @@
+error[E0599]: no method named `bar` found for fn item `fn() -> Foo {foo}` in the current scope
+  --> $DIR/call-on-missing.rs:12:9
+   |
+LL |     foo.bar();
+   |         ^^^ method not found in `fn() -> Foo {foo}`
+   |
+help: use parentheses to call this function
+   |
+LL |     foo().bar();
+   |        ++
+
+error[E0609]: no field `i` on type `fn() -> Foo {foo}`
+  --> $DIR/call-on-missing.rs:16:9
+   |
+LL |     foo.i;
+   |         ^
+   |
+help: use parentheses to call this function
+   |
+LL |     foo().i;
+   |        ++
+
+error[E0599]: no method named `bar` found for struct `Box<dyn Fn() -> Foo>` in the current scope
+  --> $DIR/call-on-missing.rs:22:14
+   |
+LL |     callable.bar();
+   |              ^^^ method not found in `Box<dyn Fn() -> Foo>`
+   |
+help: use parentheses to call this trait object
+   |
+LL |     callable().bar();
+   |             ++
+
+error[E0609]: no field `i` on type `Box<dyn Fn() -> Foo>`
+  --> $DIR/call-on-missing.rs:26:14
+   |
+LL |     callable.i;
+   |              ^ unknown field
+   |
+help: use parentheses to call this trait object
+   |
+LL |     callable().i;
+   |             ++
+
+error[E0599]: no method named `bar` found for type parameter `T` in the current scope
+  --> $DIR/call-on-missing.rs:32:7
+   |
+LL | fn type_param<T: Fn() -> Foo>(t: T) {
+   |               - method `bar` not found for this type parameter
+LL |     t.bar();
+   |       ^^^ method not found in `T`
+   |
+help: use parentheses to call this type parameter
+   |
+LL |     t().bar();
+   |      ++
+
+error[E0609]: no field `i` on type `T`
+  --> $DIR/call-on-missing.rs:36:7
+   |
+LL | fn type_param<T: Fn() -> Foo>(t: T) {
+   |               - type parameter 'T' declared here
+...
+LL |     t.i;
+   |       ^
+   |
+help: use parentheses to call this type parameter
+   |
+LL |     t().i;
+   |      ++
+
+error: aborting due to 6 previous errors
+
+Some errors have detailed explanations: E0599, E0609.
+For more information about an error, try `rustc --explain E0599`.
diff --git a/src/test/ui/suggestions/fn-or-tuple-struct-without-args.stderr b/src/test/ui/suggestions/fn-or-tuple-struct-without-args.stderr
index e75ce0da82e..3c7b895e337 100644
--- a/src/test/ui/suggestions/fn-or-tuple-struct-without-args.stderr
+++ b/src/test/ui/suggestions/fn-or-tuple-struct-without-args.stderr
@@ -33,8 +33,8 @@ LL |     let _: usize = foo;
            found fn item `fn(usize, usize) -> usize {foo}`
 help: use parentheses to call this function
    |
-LL |     let _: usize = foo(_, _);
-   |                       ++++++
+LL |     let _: usize = foo(/* usize */, /* usize */);
+   |                       ++++++++++++++++++++++++++
 
 error[E0308]: mismatched types
   --> $DIR/fn-or-tuple-struct-without-args.rs:30:16
@@ -51,8 +51,8 @@ LL |     let _: S = S;
              found fn item `fn(usize, usize) -> S {S}`
 help: use parentheses to instantiate this tuple struct
    |
-LL |     let _: S = S(_, _);
-   |                 ++++++
+LL |     let _: S = S(/* usize */, /* usize */);
+   |                 ++++++++++++++++++++++++++
 
 error[E0308]: mismatched types
   --> $DIR/fn-or-tuple-struct-without-args.rs:31:20
@@ -103,10 +103,10 @@ LL |     let _: usize = T::baz;
    |
    = note: expected type `usize`
            found fn item `fn(usize, usize) -> usize {<_ as T>::baz}`
-help: use parentheses to call this function
+help: use parentheses to call this associated function
    |
-LL |     let _: usize = T::baz(_, _);
-   |                          ++++++
+LL |     let _: usize = T::baz(/* usize */, /* usize */);
+   |                          ++++++++++++++++++++++++++
 
 error[E0308]: mismatched types
   --> $DIR/fn-or-tuple-struct-without-args.rs:34:20
@@ -121,10 +121,10 @@ LL |     let _: usize = T::bat;
    |
    = note: expected type `usize`
            found fn item `fn(usize) -> usize {<_ as T>::bat}`
-help: use parentheses to call this function
+help: use parentheses to call this associated function
    |
-LL |     let _: usize = T::bat(_);
-   |                          +++
+LL |     let _: usize = T::bat(/* usize */);
+   |                          +++++++++++++
 
 error[E0308]: mismatched types
   --> $DIR/fn-or-tuple-struct-without-args.rs:35:16
@@ -141,8 +141,8 @@ LL |     let _: E = E::A;
            found fn item `fn(usize) -> E {E::A}`
 help: use parentheses to instantiate this tuple variant
    |
-LL |     let _: E = E::A(_);
-   |                    +++
+LL |     let _: E = E::A(/* usize */);
+   |                    +++++++++++++
 
 error[E0308]: mismatched types
   --> $DIR/fn-or-tuple-struct-without-args.rs:37:20
@@ -157,10 +157,10 @@ LL |     let _: usize = X::baz;
    |
    = note: expected type `usize`
            found fn item `fn(usize, usize) -> usize {<X as T>::baz}`
-help: use parentheses to call this function
+help: use parentheses to call this associated function
    |
-LL |     let _: usize = X::baz(_, _);
-   |                          ++++++
+LL |     let _: usize = X::baz(/* usize */, /* usize */);
+   |                          ++++++++++++++++++++++++++
 
 error[E0308]: mismatched types
   --> $DIR/fn-or-tuple-struct-without-args.rs:38:20
@@ -175,10 +175,10 @@ LL |     let _: usize = X::bat;
    |
    = note: expected type `usize`
            found fn item `fn(usize) -> usize {<X as T>::bat}`
-help: use parentheses to call this function
+help: use parentheses to call this associated function
    |
-LL |     let _: usize = X::bat(_);
-   |                          +++
+LL |     let _: usize = X::bat(/* usize */);
+   |                          +++++++++++++
 
 error[E0308]: mismatched types
   --> $DIR/fn-or-tuple-struct-without-args.rs:39:20
@@ -193,10 +193,10 @@ LL |     let _: usize = X::bax;
    |
    = note: expected type `usize`
            found fn item `fn(usize) -> usize {<X as T>::bax}`
-help: use parentheses to call this function
+help: use parentheses to call this associated function
    |
-LL |     let _: usize = X::bax(_);
-   |                          +++
+LL |     let _: usize = X::bax(/* usize */);
+   |                          +++++++++++++
 
 error[E0308]: mismatched types
   --> $DIR/fn-or-tuple-struct-without-args.rs:40:20
@@ -211,10 +211,10 @@ LL |     let _: usize = X::bach;
    |
    = note: expected type `usize`
            found fn item `fn(usize) -> usize {<X as T>::bach}`
-help: use parentheses to call this function
+help: use parentheses to call this associated function
    |
-LL |     let _: usize = X::bach(_);
-   |                           +++
+LL |     let _: usize = X::bach(/* usize */);
+   |                           +++++++++++++
 
 error[E0308]: mismatched types
   --> $DIR/fn-or-tuple-struct-without-args.rs:41:20
@@ -229,10 +229,10 @@ LL |     let _: usize = X::ban;
    |
    = note: expected type `usize`
            found fn item `for<'r> fn(&'r X) -> usize {<X as T>::ban}`
-help: use parentheses to call this function
+help: use parentheses to call this associated function
    |
-LL |     let _: usize = X::ban(_);
-   |                          +++
+LL |     let _: usize = X::ban(/* &X */);
+   |                          ++++++++++
 
 error[E0308]: mismatched types
   --> $DIR/fn-or-tuple-struct-without-args.rs:42:20
@@ -247,10 +247,10 @@ LL |     let _: usize = X::bal;
    |
    = note: expected type `usize`
            found fn item `for<'r> fn(&'r X) -> usize {<X as T>::bal}`
-help: use parentheses to call this function
+help: use parentheses to call this associated function
    |
-LL |     let _: usize = X::bal(_);
-   |                          +++
+LL |     let _: usize = X::bal(/* &X */);
+   |                          ++++++++++
 
 error[E0615]: attempted to take value of method `ban` on type `X`
   --> $DIR/fn-or-tuple-struct-without-args.rs:43:22
diff --git a/src/test/ui/typeck/issue-29124.stderr b/src/test/ui/typeck/issue-29124.stderr
index c5d2ec08409..a837a7d2d62 100644
--- a/src/test/ui/typeck/issue-29124.stderr
+++ b/src/test/ui/typeck/issue-29124.stderr
@@ -2,17 +2,13 @@ error[E0599]: no method named `x` found for fn item `fn() -> Ret {Obj::func}` in
   --> $DIR/issue-29124.rs:15:15
    |
 LL |     Obj::func.x();
-   |     --------- ^ method not found in `fn() -> Ret {Obj::func}`
-   |     |
-   |     this is a function, perhaps you wish to call it
+   |               ^ method not found in `fn() -> Ret {Obj::func}`
 
 error[E0599]: no method named `x` found for fn item `fn() -> Ret {func}` in the current scope
   --> $DIR/issue-29124.rs:17:10
    |
 LL |     func.x();
-   |     ---- ^ method not found in `fn() -> Ret {func}`
-   |     |
-   |     this is a function, perhaps you wish to call it
+   |          ^ method not found in `fn() -> Ret {func}`
 
 error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/typeck/issue-87181/empty-tuple-method.rs b/src/test/ui/typeck/issue-87181/empty-tuple-method.rs
index 1875d8280cb..be68ad32ae5 100644
--- a/src/test/ui/typeck/issue-87181/empty-tuple-method.rs
+++ b/src/test/ui/typeck/issue-87181/empty-tuple-method.rs
@@ -4,7 +4,7 @@ struct Bar<T> {
 
 struct Foo();
 impl Foo {
-    fn foo() { }
+    fn foo(&self) { }
 }
 
 fn main() {
diff --git a/src/test/ui/typeck/issue-87181/empty-tuple-method.stderr b/src/test/ui/typeck/issue-87181/empty-tuple-method.stderr
index 6ed70b301e4..a18c54a29b5 100644
--- a/src/test/ui/typeck/issue-87181/empty-tuple-method.stderr
+++ b/src/test/ui/typeck/issue-87181/empty-tuple-method.stderr
@@ -2,11 +2,9 @@ error[E0599]: no method named `foo` found for fn item `fn() -> Foo {Foo}` in the
   --> $DIR/empty-tuple-method.rs:12:15
    |
 LL |     thing.bar.foo();
-   |     --------- ^^^ method not found in `fn() -> Foo {Foo}`
-   |     |
-   |     this is the constructor of a struct
+   |               ^^^ method not found in `fn() -> Foo {Foo}`
    |
-help: call the constructor
+help: use parentheses to instantiate this tuple struct
    |
 LL |     (thing.bar)().foo();
    |     +         +++
diff --git a/src/test/ui/typeck/issue-87181/enum-variant.rs b/src/test/ui/typeck/issue-87181/enum-variant.rs
index 3b926b90f10..d87f99c3c5a 100644
--- a/src/test/ui/typeck/issue-87181/enum-variant.rs
+++ b/src/test/ui/typeck/issue-87181/enum-variant.rs
@@ -6,7 +6,7 @@ enum Foo{
     Tup()
 }
 impl Foo {
-    fn foo() { }
+    fn foo(&self) { }
 }
 
 fn main() {
diff --git a/src/test/ui/typeck/issue-87181/enum-variant.stderr b/src/test/ui/typeck/issue-87181/enum-variant.stderr
index a3a818696ab..90641410d8e 100644
--- a/src/test/ui/typeck/issue-87181/enum-variant.stderr
+++ b/src/test/ui/typeck/issue-87181/enum-variant.stderr
@@ -2,11 +2,9 @@ error[E0599]: no method named `foo` found for fn item `fn() -> Foo {Foo::Tup}` i
   --> $DIR/enum-variant.rs:14:15
    |
 LL |     thing.bar.foo();
-   |     --------- ^^^ method not found in `fn() -> Foo {Foo::Tup}`
-   |     |
-   |     this is the constructor of an enum variant
+   |               ^^^ method not found in `fn() -> Foo {Foo::Tup}`
    |
-help: call the constructor
+help: use parentheses to instantiate this tuple variant
    |
 LL |     (thing.bar)().foo();
    |     +         +++
diff --git a/src/test/ui/typeck/issue-87181/tuple-field.stderr b/src/test/ui/typeck/issue-87181/tuple-field.stderr
index 4d22ada0247..c1ca26ee9af 100644
--- a/src/test/ui/typeck/issue-87181/tuple-field.stderr
+++ b/src/test/ui/typeck/issue-87181/tuple-field.stderr
@@ -2,14 +2,12 @@ error[E0609]: no field `0` on type `fn(char, u16) -> Foo {Foo}`
   --> $DIR/tuple-field.rs:12:15
    |
 LL |     thing.bar.0;
-   |     --------- ^
-   |     |
-   |     this is the constructor of a struct
+   |               ^
    |
-help: call the constructor
+help: use parentheses to instantiate this tuple struct
    |
-LL |     (thing.bar)(_, _).0;
-   |     +         +++++++
+LL |     (thing.bar)(/* char */, /* u16 */).0;
+   |     +         ++++++++++++++++++++++++
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/typeck/issue-87181/tuple-method.stderr b/src/test/ui/typeck/issue-87181/tuple-method.stderr
index 1e392e17984..e27c41858d3 100644
--- a/src/test/ui/typeck/issue-87181/tuple-method.stderr
+++ b/src/test/ui/typeck/issue-87181/tuple-method.stderr
@@ -2,14 +2,7 @@ error[E0599]: no method named `foo` found for fn item `fn(u8, i32) -> Foo {Foo}`
   --> $DIR/tuple-method.rs:12:15
    |
 LL |     thing.bar.foo();
-   |     --------- ^^^ method not found in `fn(u8, i32) -> Foo {Foo}`
-   |     |
-   |     this is the constructor of a struct
-   |
-help: call the constructor
-   |
-LL |     (thing.bar)(_, _).foo();
-   |     +         +++++++
+   |               ^^^ method not found in `fn(u8, i32) -> Foo {Foo}`
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/typeck/issue-96738.stderr b/src/test/ui/typeck/issue-96738.stderr
index 32f53849848..0d4d87ef47e 100644
--- a/src/test/ui/typeck/issue-96738.stderr
+++ b/src/test/ui/typeck/issue-96738.stderr
@@ -2,27 +2,13 @@ error[E0599]: no method named `nonexistent_method` found for fn item `fn(_) -> O
   --> $DIR/issue-96738.rs:2:10
    |
 LL |     Some.nonexistent_method();
-   |     ---- ^^^^^^^^^^^^^^^^^^ method not found in `fn(_) -> Option<_> {Option::<_>::Some}`
-   |     |
-   |     this is the constructor of an enum variant
-   |
-help: call the constructor
-   |
-LL |     (Some)(_).nonexistent_method();
-   |     +    ++++
+   |          ^^^^^^^^^^^^^^^^^^ method not found in `fn(_) -> Option<_> {Option::<_>::Some}`
 
 error[E0609]: no field `nonexistent_field` on type `fn(_) -> Option<_> {Option::<_>::Some}`
   --> $DIR/issue-96738.rs:3:10
    |
 LL |     Some.nonexistent_field;
-   |     ---- ^^^^^^^^^^^^^^^^^
-   |     |
-   |     this is the constructor of an enum variant
-   |
-help: call the constructor
-   |
-LL |     (Some)(_).nonexistent_field;
-   |     +    ++++
+   |          ^^^^^^^^^^^^^^^^^
 
 error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/unboxed-closures/unboxed-closures-static-call-wrong-trait.stderr b/src/test/ui/unboxed-closures/unboxed-closures-static-call-wrong-trait.stderr
index 4f89afa320d..e5ca0edd7a9 100644
--- a/src/test/ui/unboxed-closures/unboxed-closures-static-call-wrong-trait.stderr
+++ b/src/test/ui/unboxed-closures/unboxed-closures-static-call-wrong-trait.stderr
@@ -2,9 +2,7 @@ error[E0599]: no method named `call` found for closure `[closure@$DIR/unboxed-cl
   --> $DIR/unboxed-closures-static-call-wrong-trait.rs:7:10
    |
 LL |     mut_.call((0, ));
-   |     ---- ^^^^ method not found in `[closure@$DIR/unboxed-closures-static-call-wrong-trait.rs:6:26: 6:29]`
-   |     |
-   |     this is a function, perhaps you wish to call it
+   |          ^^^^ method not found in `[closure@$DIR/unboxed-closures-static-call-wrong-trait.rs:6:26: 6:29]`
 
 error: aborting due to previous error