about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/region_errors.rs119
-rw-r--r--compiler/rustc_hir/src/hir.rs6
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mod.rs1
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs138
-rw-r--r--compiler/rustc_typeck/src/astconv/mod.rs8
-rw-r--r--compiler/rustc_typeck/src/check/expr.rs25
-rw-r--r--compiler/rustc_typeck/src/check/method/suggest.rs536
-rw-r--r--compiler/rustc_typeck/src/check/mod.rs38
-rw-r--r--src/librustdoc/html/format.rs3
-rw-r--r--src/test/rustdoc/issue-95873.rs2
-rw-r--r--src/test/rustdoc/issue-96381.rs16
-rw-r--r--src/test/ui/functions-closures/fn-help-with-err.rs6
-rw-r--r--src/test/ui/functions-closures/fn-help-with-err.stderr6
-rw-r--r--src/test/ui/impl-trait/issues/issue-88236-2.nll.stderr6
-rw-r--r--src/test/ui/impl-trait/must_outlive_least_region_or_bound.nll.stderr36
-rw-r--r--src/test/ui/issues/issue-16922.nll.stderr5
-rw-r--r--src/test/ui/issues/issue-29124.stderr12
-rw-r--r--src/test/ui/issues/issue-57362-1.stderr5
-rw-r--r--src/test/ui/nll/mir_check_cast_unsize.stderr5
-rw-r--r--src/test/ui/object-lifetime/object-lifetime-default-from-box-error.nll.stderr5
-rw-r--r--src/test/ui/regions/region-object-lifetime-in-coercion.nll.stderr23
-rw-r--r--src/test/ui/regions/regions-close-object-into-object-2.nll.stderr9
-rw-r--r--src/test/ui/regions/regions-close-object-into-object-4.nll.stderr9
-rw-r--r--src/test/ui/regions/regions-proc-bound-capture.nll.stderr9
-rw-r--r--src/test/ui/suggestions/lifetimes/trait-object-nested-in-impl-trait.nll.stderr22
-rw-r--r--src/test/ui/typeck/issue-87181/empty-tuple-method.rs14
-rw-r--r--src/test/ui/typeck/issue-87181/empty-tuple-method.stderr16
-rw-r--r--src/test/ui/typeck/issue-87181/enum-variant.rs16
-rw-r--r--src/test/ui/typeck/issue-87181/enum-variant.stderr16
-rw-r--r--src/test/ui/typeck/issue-87181/tuple-field.rs14
-rw-r--r--src/test/ui/typeck/issue-87181/tuple-field.stderr16
-rw-r--r--src/test/ui/typeck/issue-87181/tuple-method.rs14
-rw-r--r--src/test/ui/typeck/issue-87181/tuple-method.stderr16
-rw-r--r--src/test/ui/unboxed-closures/unboxed-closures-static-call-wrong-trait.stderr6
-rw-r--r--src/test/ui/underscore-lifetime/dyn-trait-underscore.nll.stderr5
35 files changed, 756 insertions, 427 deletions
diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
index fd78b483b75..b5ee4a5edce 100644
--- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
@@ -1,17 +1,17 @@
 //! Error reporting machinery for lifetime errors.
 
-use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed};
+use rustc_errors::{Diagnostic, DiagnosticBuilder, ErrorGuaranteed};
 use rustc_infer::infer::{
-    error_reporting::nice_region_error::NiceRegionError,
-    error_reporting::unexpected_hidden_region_diagnostic, NllRegionVariableOrigin,
-    RelateParamBound,
+    error_reporting::nice_region_error::{self, find_param_with_region, NiceRegionError},
+    error_reporting::unexpected_hidden_region_diagnostic,
+    NllRegionVariableOrigin, RelateParamBound,
 };
 use rustc_middle::hir::place::PlaceBase;
 use rustc_middle::mir::{ConstraintCategory, ReturnConstraint};
-use rustc_middle::ty::subst::{InternalSubsts, Subst};
+use rustc_middle::ty::subst::InternalSubsts;
 use rustc_middle::ty::{self, RegionVid, Ty};
-use rustc_span::symbol::{kw, sym};
-use rustc_span::{BytePos, Span};
+use rustc_span::symbol::sym;
+use rustc_span::Span;
 
 use crate::borrowck_errors;
 
@@ -651,82 +651,47 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
         fr_name: RegionName,
         outlived_fr: RegionVid,
     ) {
-        if let (Some(f), Some(ty::ReStatic)) =
-            (self.to_error_region(fr), self.to_error_region(outlived_fr).as_deref())
+        if let (Some(f), Some(outlived_f)) =
+            (self.to_error_region(fr), self.to_error_region(outlived_fr))
         {
-            if let Some(&ty::Opaque(did, substs)) = self
+            if *outlived_f != ty::ReStatic {
+                return;
+            }
+
+            let fn_returns = self
                 .infcx
                 .tcx
                 .is_suitable_region(f)
-                .map(|r| r.def_id)
-                .and_then(|id| self.infcx.tcx.return_type_impl_trait(id))
-                .map(|(ty, _)| ty.kind())
-            {
-                // Check whether or not the impl trait return type is intended to capture
-                // data with the static lifetime.
-                //
-                // eg. check for `impl Trait + 'static` instead of `impl Trait`.
-                let has_static_predicate = {
-                    let bounds = self.infcx.tcx.explicit_item_bounds(did);
-
-                    let mut found = false;
-                    for (bound, _) in bounds {
-                        if let ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(_, r)) =
-                            bound.kind().skip_binder()
-                        {
-                            let r = r.subst(self.infcx.tcx, substs);
-                            if r.is_static() {
-                                found = true;
-                                break;
-                            } else {
-                                // If there's already a lifetime bound, don't
-                                // suggest anything.
-                                return;
-                            }
-                        }
-                    }
-
-                    found
-                };
+                .map(|r| self.infcx.tcx.return_type_impl_or_dyn_traits(r.def_id))
+                .unwrap_or_default();
 
-                debug!(
-                    "add_static_impl_trait_suggestion: has_static_predicate={:?}",
-                    has_static_predicate
-                );
-                let static_str = kw::StaticLifetime;
-                // If there is a static predicate, then the only sensible suggestion is to replace
-                // fr with `'static`.
-                if has_static_predicate {
-                    diag.help(&format!("consider replacing `{fr_name}` with `{static_str}`"));
-                } else {
-                    // Otherwise, we should suggest adding a constraint on the return type.
-                    let span = self.infcx.tcx.def_span(did);
-                    if let Ok(snippet) = self.infcx.tcx.sess.source_map().span_to_snippet(span) {
-                        let suggestable_fr_name = if fr_name.was_named() {
-                            fr_name.to_string()
-                        } else {
-                            "'_".to_string()
-                        };
-                        let span = if snippet.ends_with(';') {
-                            // `type X = impl Trait;`
-                            span.with_hi(span.hi() - BytePos(1))
-                        } else {
-                            span
-                        };
-                        let suggestion = format!(" + {suggestable_fr_name}");
-                        let span = span.shrink_to_hi();
-                        diag.span_suggestion(
-                            span,
-                            &format!(
-                                "to allow this `impl Trait` to capture borrowed data with lifetime \
-                                 `{fr_name}`, add `{suggestable_fr_name}` as a bound",
-                            ),
-                            suggestion,
-                            Applicability::MachineApplicable,
-                        );
-                    }
-                }
+            if fn_returns.is_empty() {
+                return;
             }
+
+            let param = if let Some(param) = find_param_with_region(self.infcx.tcx, f, outlived_f) {
+                param
+            } else {
+                return;
+            };
+
+            let lifetime = if f.has_name() { fr_name.to_string() } else { "'_".to_string() };
+
+            let arg = match param.param.pat.simple_ident() {
+                Some(simple_ident) => format!("argument `{}`", simple_ident),
+                None => "the argument".to_string(),
+            };
+            let captures = format!("captures data from {}", arg);
+
+            return nice_region_error::suggest_new_region_bound(
+                self.infcx.tcx,
+                diag,
+                fn_returns,
+                lifetime,
+                Some(arg),
+                captures,
+                Some((param.param_ty_span, param.param_ty.to_string())),
+            );
         }
     }
 }
diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs
index ef56093bed8..2d01673b61d 100644
--- a/compiler/rustc_hir/src/hir.rs
+++ b/compiler/rustc_hir/src/hir.rs
@@ -3314,6 +3314,12 @@ impl<'hir> Node<'hir> {
             _ => None,
         }
     }
+
+    /// Get the fields for the tuple-constructor,
+    /// if this node is a tuple constructor, otherwise None
+    pub fn tuple_fields(&self) -> Option<&'hir [FieldDef<'hir>]> {
+        if let Node::Ctor(&VariantData::Tuple(fields, _)) = self { Some(fields) } else { None }
+    }
 }
 
 // Some nodes are used a lot. Make sure they don't unintentionally get bigger.
diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mod.rs
index df81aea6ef9..79f852d8a95 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mod.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mod.rs
@@ -15,6 +15,7 @@ mod trait_impl_difference;
 mod util;
 
 pub use static_impl_trait::suggest_new_region_bound;
+pub use util::find_param_with_region;
 
 impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
     pub fn try_report_nice_region_error(&self, error: &RegionResolutionError<'tcx>) -> bool {
diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs
index 719f6b37a43..7d3ed2ed38a 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs
@@ -2,6 +2,7 @@
 //! anonymous regions.
 
 use crate::infer::error_reporting::nice_region_error::NiceRegionError;
+use crate::infer::TyCtxt;
 use rustc_hir as hir;
 use rustc_hir::def_id::LocalDefId;
 use rustc_middle::ty::{self, Binder, DefIdTree, Region, Ty, TypeFoldable};
@@ -9,7 +10,7 @@ use rustc_span::Span;
 
 /// Information about the anonymous region we are searching for.
 #[derive(Debug)]
-pub(super) struct AnonymousParamInfo<'tcx> {
+pub struct AnonymousParamInfo<'tcx> {
     /// The parameter corresponding to the anonymous region.
     pub param: &'tcx hir::Param<'tcx>,
     /// The type corresponding to the anonymous region parameter.
@@ -22,76 +23,83 @@ pub(super) struct AnonymousParamInfo<'tcx> {
     pub is_first: bool,
 }
 
+// This method walks the Type of the function body parameters using
+// `fold_regions()` function and returns the
+// &hir::Param of the function parameter corresponding to the anonymous
+// region and the Ty corresponding to the named region.
+// Currently only the case where the function declaration consists of
+// one named region and one anonymous region is handled.
+// Consider the example `fn foo<'a>(x: &'a i32, y: &i32) -> &'a i32`
+// Here, we would return the hir::Param for y, we return the type &'a
+// i32, which is the type of y but with the anonymous region replaced
+// with 'a, the corresponding bound region and is_first which is true if
+// the hir::Param is the first parameter in the function declaration.
+pub fn find_param_with_region<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    anon_region: Region<'tcx>,
+    replace_region: Region<'tcx>,
+) -> Option<AnonymousParamInfo<'tcx>> {
+    let (id, bound_region) = match *anon_region {
+        ty::ReFree(ref free_region) => (free_region.scope, free_region.bound_region),
+        ty::ReEarlyBound(ebr) => {
+            (tcx.parent(ebr.def_id).unwrap(), ty::BoundRegionKind::BrNamed(ebr.def_id, ebr.name))
+        }
+        _ => return None, // not a free region
+    };
+
+    let hir = &tcx.hir();
+    let hir_id = hir.local_def_id_to_hir_id(id.as_local()?);
+    let body_id = hir.maybe_body_owned_by(hir_id)?;
+    let body = hir.body(body_id);
+    let owner_id = hir.body_owner(body_id);
+    let fn_decl = hir.fn_decl_by_hir_id(owner_id).unwrap();
+    let poly_fn_sig = tcx.fn_sig(id);
+    let fn_sig = tcx.liberate_late_bound_regions(id, poly_fn_sig);
+    body.params
+        .iter()
+        .take(if fn_sig.c_variadic {
+            fn_sig.inputs().len()
+        } else {
+            assert_eq!(fn_sig.inputs().len(), body.params.len());
+            body.params.len()
+        })
+        .enumerate()
+        .find_map(|(index, param)| {
+            // May return None; sometimes the tables are not yet populated.
+            let ty = fn_sig.inputs()[index];
+            let mut found_anon_region = false;
+            let new_param_ty = tcx.fold_regions(ty, &mut false, |r, _| {
+                if r == anon_region {
+                    found_anon_region = true;
+                    replace_region
+                } else {
+                    r
+                }
+            });
+            if found_anon_region {
+                let ty_hir_id = fn_decl.inputs[index].hir_id;
+                let param_ty_span = hir.span(ty_hir_id);
+                let is_first = index == 0;
+                Some(AnonymousParamInfo {
+                    param,
+                    param_ty: new_param_ty,
+                    param_ty_span,
+                    bound_region,
+                    is_first,
+                })
+            } else {
+                None
+            }
+        })
+}
+
 impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
-    // This method walks the Type of the function body parameters using
-    // `fold_regions()` function and returns the
-    // &hir::Param of the function parameter corresponding to the anonymous
-    // region and the Ty corresponding to the named region.
-    // Currently only the case where the function declaration consists of
-    // one named region and one anonymous region is handled.
-    // Consider the example `fn foo<'a>(x: &'a i32, y: &i32) -> &'a i32`
-    // Here, we would return the hir::Param for y, we return the type &'a
-    // i32, which is the type of y but with the anonymous region replaced
-    // with 'a, the corresponding bound region and is_first which is true if
-    // the hir::Param is the first parameter in the function declaration.
     pub(super) fn find_param_with_region(
         &self,
         anon_region: Region<'tcx>,
         replace_region: Region<'tcx>,
     ) -> Option<AnonymousParamInfo<'_>> {
-        let (id, bound_region) = match *anon_region {
-            ty::ReFree(ref free_region) => (free_region.scope, free_region.bound_region),
-            ty::ReEarlyBound(ebr) => (
-                self.tcx().parent(ebr.def_id).unwrap(),
-                ty::BoundRegionKind::BrNamed(ebr.def_id, ebr.name),
-            ),
-            _ => return None, // not a free region
-        };
-
-        let hir = &self.tcx().hir();
-        let hir_id = hir.local_def_id_to_hir_id(id.as_local()?);
-        let body_id = hir.maybe_body_owned_by(hir_id)?;
-        let body = hir.body(body_id);
-        let owner_id = hir.body_owner(body_id);
-        let fn_decl = hir.fn_decl_by_hir_id(owner_id).unwrap();
-        let poly_fn_sig = self.tcx().fn_sig(id);
-        let fn_sig = self.tcx().liberate_late_bound_regions(id, poly_fn_sig);
-        body.params
-            .iter()
-            .take(if fn_sig.c_variadic {
-                fn_sig.inputs().len()
-            } else {
-                assert_eq!(fn_sig.inputs().len(), body.params.len());
-                body.params.len()
-            })
-            .enumerate()
-            .find_map(|(index, param)| {
-                // May return None; sometimes the tables are not yet populated.
-                let ty = fn_sig.inputs()[index];
-                let mut found_anon_region = false;
-                let new_param_ty = self.tcx().fold_regions(ty, &mut false, |r, _| {
-                    if r == anon_region {
-                        found_anon_region = true;
-                        replace_region
-                    } else {
-                        r
-                    }
-                });
-                if found_anon_region {
-                    let ty_hir_id = fn_decl.inputs[index].hir_id;
-                    let param_ty_span = hir.span(ty_hir_id);
-                    let is_first = index == 0;
-                    Some(AnonymousParamInfo {
-                        param,
-                        param_ty: new_param_ty,
-                        param_ty_span,
-                        bound_region,
-                        is_first,
-                    })
-                } else {
-                    None
-                }
-            })
+        find_param_with_region(self.tcx(), anon_region, replace_region)
     }
 
     // Here, we check for the case where the anonymous region
diff --git a/compiler/rustc_typeck/src/astconv/mod.rs b/compiler/rustc_typeck/src/astconv/mod.rs
index 1cd0ace8adb..1caf93e5fe0 100644
--- a/compiler/rustc_typeck/src/astconv/mod.rs
+++ b/compiler/rustc_typeck/src/astconv/mod.rs
@@ -2681,21 +2681,21 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
         let trait_ref =
             self.instantiate_mono_trait_ref(i.of_trait.as_ref()?, self.ast_ty_to_ty(i.self_ty));
 
-        let x: &ty::AssocItem = tcx.associated_items(trait_ref.def_id).find_by_name_and_kind(
+        let assoc = tcx.associated_items(trait_ref.def_id).find_by_name_and_kind(
             tcx,
             *ident,
             ty::AssocKind::Fn,
             trait_ref.def_id,
         )?;
 
-        let fn_sig = tcx.fn_sig(x.def_id).subst(
+        let fn_sig = tcx.fn_sig(assoc.def_id).subst(
             tcx,
-            trait_ref.substs.extend_to(tcx, x.def_id, |param, _| tcx.mk_param_from_def(param)),
+            trait_ref.substs.extend_to(tcx, assoc.def_id, |param, _| tcx.mk_param_from_def(param)),
         );
 
         let ty = if let Some(arg_idx) = arg_idx { fn_sig.input(arg_idx) } else { fn_sig.output() };
 
-        Some(tcx.erase_late_bound_regions(ty))
+        Some(tcx.liberate_late_bound_regions(fn_hir_id.expect_owner().to_def_id(), ty))
     }
 
     fn validate_late_bound_regions(
diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs
index 480a5512249..47cb1ea48cb 100644
--- a/compiler/rustc_typeck/src/check/expr.rs
+++ b/compiler/rustc_typeck/src/check/expr.rs
@@ -21,11 +21,13 @@ 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;
 use rustc_data_structures::stack::ensure_sufficient_stack;
 use rustc_errors::Diagnostic;
+use rustc_errors::EmissionGuarantee;
 use rustc_errors::ErrorGuaranteed;
 use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder, DiagnosticId};
 use rustc_hir as hir;
@@ -1986,6 +1988,26 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         self.tcx().ty_error()
     }
 
+    fn check_call_constructor<G: EmissionGuarantee>(
+        &self,
+        err: &mut DiagnosticBuilder<'_, G>,
+        base: &'tcx hir::Expr<'tcx>,
+        def_id: DefId,
+    ) {
+        let local_id = def_id.expect_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);
+        }
+    }
+
     fn suggest_await_on_field_access(
         &self,
         err: &mut Diagnostic,
@@ -2055,6 +2077,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             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);
+            }
             _ => {}
         }
 
diff --git a/compiler/rustc_typeck/src/check/method/suggest.rs b/compiler/rustc_typeck/src/check/method/suggest.rs
index 88e0a4bada8..80c31355fe7 100644
--- a/compiler/rustc_typeck/src/check/method/suggest.rs
+++ b/compiler/rustc_typeck/src/check/method/suggest.rs
@@ -8,6 +8,7 @@ use rustc_errors::{
     MultiSpan,
 };
 use rustc_hir as hir;
+use rustc_hir::def::DefKind;
 use rustc_hir::def_id::DefId;
 use rustc_hir::lang_items::LangItem;
 use rustc_hir::{ExprKind, Node, QPath};
@@ -29,7 +30,7 @@ use std::cmp::Ordering;
 use std::iter;
 
 use super::probe::{Mode, ProbeScope};
-use super::{CandidateSource, MethodError, NoMatchData};
+use super::{super::suggest_call_constructor, CandidateSource, MethodError, NoMatchData};
 
 impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     fn is_fn_ty(&self, ty: Ty<'tcx>, span: Span) -> bool {
@@ -271,205 +272,82 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         (None, true) => "variant",
                     }
                 };
-                // FIXME(eddyb) this indentation is probably unnecessary.
-                let mut err = {
-                    // Suggest clamping down the type if the method that is being attempted to
-                    // be used exists at all, and the type is an ambiguous numeric type
-                    // ({integer}/{float}).
-                    let mut candidates = all_traits(self.tcx)
-                        .into_iter()
-                        .filter_map(|info| self.associated_value(info.def_id, item_name));
-                    // There are methods that are defined on the primitive types and won't be
-                    // found when exploring `all_traits`, but we also need them to be accurate on
-                    // our suggestions (#47759).
-                    let found_assoc = |ty: Ty<'tcx>| {
-                        simplify_type(tcx, ty, TreatParams::AsPlaceholders)
-                            .and_then(|simp| {
-                                tcx.incoherent_impls(simp)
-                                    .iter()
-                                    .find_map(|&id| self.associated_value(id, item_name))
-                            })
-                            .is_some()
-                    };
-                    let found_candidate = candidates.next().is_some()
-                        || found_assoc(tcx.types.i8)
-                        || found_assoc(tcx.types.i16)
-                        || found_assoc(tcx.types.i32)
-                        || found_assoc(tcx.types.i64)
-                        || found_assoc(tcx.types.i128)
-                        || found_assoc(tcx.types.u8)
-                        || found_assoc(tcx.types.u16)
-                        || found_assoc(tcx.types.u32)
-                        || found_assoc(tcx.types.u64)
-                        || found_assoc(tcx.types.u128)
-                        || found_assoc(tcx.types.f32)
-                        || found_assoc(tcx.types.f32);
-                    if let (true, false, SelfSource::MethodCall(expr), true) = (
-                        actual.is_numeric(),
-                        actual.has_concrete_skeleton(),
-                        source,
-                        found_candidate,
-                    ) {
-                        let mut err = struct_span_err!(
-                            tcx.sess,
-                            span,
-                            E0689,
-                            "can't call {} `{}` on ambiguous numeric type `{}`",
-                            item_kind,
-                            item_name,
-                            ty_str
-                        );
-                        let concrete_type = if actual.is_integral() { "i32" } else { "f32" };
-                        match expr.kind {
-                            ExprKind::Lit(ref lit) => {
-                                // numeric literal
-                                let snippet = tcx
-                                    .sess
-                                    .source_map()
-                                    .span_to_snippet(lit.span)
-                                    .unwrap_or_else(|_| "<numeric literal>".to_owned());
-
-                                // If this is a floating point literal that ends with '.',
-                                // get rid of it to stop this from becoming a member access.
-                                let snippet = snippet.strip_suffix('.').unwrap_or(&snippet);
 
-                                err.span_suggestion(
-                                    lit.span,
-                                    &format!(
-                                        "you must specify a concrete type for this numeric value, \
-                                         like `{}`",
-                                        concrete_type
-                                    ),
-                                    format!("{snippet}_{concrete_type}"),
-                                    Applicability::MaybeIncorrect,
-                                );
-                            }
-                            ExprKind::Path(QPath::Resolved(_, path)) => {
-                                // local binding
-                                if let hir::def::Res::Local(hir_id) = path.res {
-                                    let span = tcx.hir().span(hir_id);
-                                    let snippet = tcx.sess.source_map().span_to_snippet(span);
-                                    let filename = tcx.sess.source_map().span_to_filename(span);
-
-                                    let parent_node =
-                                        self.tcx.hir().get(self.tcx.hir().get_parent_node(hir_id));
-                                    let msg = format!(
-                                        "you must specify a type for this binding, like `{}`",
-                                        concrete_type,
-                                    );
-
-                                    match (filename, parent_node, snippet) {
-                                        (
-                                            FileName::Real(_),
-                                            Node::Local(hir::Local {
-                                                source: hir::LocalSource::Normal,
-                                                ty,
-                                                ..
-                                            }),
-                                            Ok(ref snippet),
-                                        ) => {
-                                            err.span_suggestion(
-                                                // account for `let x: _ = 42;`
-                                                //                  ^^^^
-                                                span.to(ty
-                                                    .as_ref()
-                                                    .map(|ty| ty.span)
-                                                    .unwrap_or(span)),
-                                                &msg,
-                                                format!("{}: {}", snippet, concrete_type),
-                                                Applicability::MaybeIncorrect,
-                                            );
-                                        }
-                                        _ => {
-                                            err.span_label(span, msg);
-                                        }
-                                    }
-                                }
+                if self.suggest_constraining_numerical_ty(
+                    tcx, actual, source, span, item_kind, item_name, &ty_str,
+                ) {
+                    return None;
+                }
+
+                span = item_name.span;
+
+                // Don't show generic arguments when the method can't be found in any implementation (#81576).
+                let mut ty_str_reported = ty_str.clone();
+                if let ty::Adt(_, generics) = actual.kind() {
+                    if generics.len() > 0 {
+                        let mut autoderef = self.autoderef(span, actual);
+                        let candidate_found = autoderef.any(|(ty, _)| {
+                            if let ty::Adt(adt_deref, _) = ty.kind() {
+                                self.tcx
+                                    .inherent_impls(adt_deref.did())
+                                    .iter()
+                                    .filter_map(|def_id| self.associated_value(*def_id, item_name))
+                                    .count()
+                                    >= 1
+                            } else {
+                                false
                             }
-                            _ => {}
-                        }
-                        err.emit();
-                        return None;
-                    } else {
-                        span = item_name.span;
-
-                        // Don't show generic arguments when the method can't be found in any implementation (#81576).
-                        let mut ty_str_reported = ty_str.clone();
-                        if let ty::Adt(_, generics) = actual.kind() {
-                            if generics.len() > 0 {
-                                let mut autoderef = self.autoderef(span, actual);
-                                let candidate_found = autoderef.any(|(ty, _)| {
-                                    if let ty::Adt(adt_deref, _) = ty.kind() {
-                                        self.tcx
-                                            .inherent_impls(adt_deref.did())
-                                            .iter()
-                                            .filter_map(|def_id| {
-                                                self.associated_value(*def_id, item_name)
-                                            })
-                                            .count()
-                                            >= 1
-                                    } else {
-                                        false
-                                    }
-                                });
-                                let has_deref = autoderef.step_count() > 0;
-                                if !candidate_found
-                                    && !has_deref
-                                    && unsatisfied_predicates.is_empty()
-                                {
-                                    if let Some((path_string, _)) = ty_str.split_once('<') {
-                                        ty_str_reported = path_string.to_string();
-                                    }
-                                }
+                        });
+                        let has_deref = autoderef.step_count() > 0;
+                        if !candidate_found && !has_deref && unsatisfied_predicates.is_empty() {
+                            if let Some((path_string, _)) = ty_str.split_once('<') {
+                                ty_str_reported = path_string.to_string();
                             }
                         }
+                    }
+                }
 
-                        let mut err = struct_span_err!(
-                            tcx.sess,
-                            span,
-                            E0599,
-                            "no {} named `{}` found for {} `{}` in the current scope",
-                            item_kind,
-                            item_name,
-                            actual.prefix_string(self.tcx),
-                            ty_str_reported,
-                        );
-                        if let Mode::MethodCall = mode && let SelfSource::MethodCall(cal) = source {
-                            self.suggest_await_before_method(
-                                &mut err, item_name, actual, cal, span,
-                            );
-                        }
-                        if let Some(span) =
-                            tcx.resolutions(()).confused_type_with_std_module.get(&span)
-                        {
-                            if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(*span) {
-                                err.span_suggestion(
-                                    *span,
-                                    "you are looking for the module in `std`, \
+                let mut err = struct_span_err!(
+                    tcx.sess,
+                    span,
+                    E0599,
+                    "no {} named `{}` found for {} `{}` in the current scope",
+                    item_kind,
+                    item_name,
+                    actual.prefix_string(self.tcx),
+                    ty_str_reported,
+                );
+                if actual.references_error() {
+                    err.downgrade_to_delayed_bug();
+                }
+
+                if let Mode::MethodCall = mode && let SelfSource::MethodCall(cal) = source {
+                    self.suggest_await_before_method(
+                        &mut err, item_name, actual, cal, span,
+                    );
+                }
+                if let Some(span) = tcx.resolutions(()).confused_type_with_std_module.get(&span) {
+                    if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(*span) {
+                        err.span_suggestion(
+                            *span,
+                            "you are looking for the module in `std`, \
                                      not the primitive type",
-                                    format!("std::{}", snippet),
-                                    Applicability::MachineApplicable,
-                                );
-                            }
-                        }
-                        if let ty::RawPtr(_) = &actual.kind() {
-                            err.note(
-                                "try using `<*const T>::as_ref()` to get a reference to the \
+                            format!("std::{}", snippet),
+                            Applicability::MachineApplicable,
+                        );
+                    }
+                }
+                if let ty::RawPtr(_) = &actual.kind() {
+                    err.note(
+                        "try using `<*const T>::as_ref()` to get a reference to the \
                                       type behind the pointer: https://doc.rust-lang.org/std/\
                                       primitive.pointer.html#method.as_ref",
-                            );
-                            err.note(
-                                "using `<*const T>::as_ref()` on a pointer \
+                    );
+                    err.note(
+                        "using `<*const T>::as_ref()` on a pointer \
                                       which is unaligned or points to invalid \
                                       or uninitialized memory is undefined behavior",
-                            );
-                        }
-                        err
-                    }
-                };
-
-                if actual.references_error() {
-                    err.downgrade_to_delayed_bug();
+                    );
                 }
 
                 if let Some(def) = actual.ty_adt_def() {
@@ -488,19 +366,32 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 }
 
                 if self.is_fn_ty(rcvr_ty, span) {
-                    fn report_function<T: std::fmt::Display>(err: &mut Diagnostic, name: T) {
-                        err.note(
-                            &format!("`{}` is a function, perhaps you wish to call it", name,),
-                        );
-                    }
-
                     if let SelfSource::MethodCall(expr) = source {
-                        if let Ok(expr_string) = tcx.sess.source_map().span_to_snippet(expr.span) {
-                            report_function(&mut err, expr_string);
-                        } else if let ExprKind::Path(QPath::Resolved(_, path)) = expr.kind {
-                            if let Some(segment) = path.segments.last() {
-                                report_function(&mut err, segment.ident);
+                        let suggest = if let ty::FnDef(def_id, _) = rcvr_ty.kind() {
+                            let local_id = def_id.expect_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, 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.len(), &mut err);
+                        } else {
+                            // General case
+                            err.span_label(
+                                expr.span,
+                                "this is a function, perhaps you wish to call it",
+                            );
                         }
                     }
                 }
@@ -985,7 +876,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     }
                 }
 
-                let mut label_span_not_found = || {
+                let label_span_not_found = |err: &mut DiagnosticBuilder<'_, _>| {
                     if unsatisfied_predicates.is_empty() {
                         err.span_label(span, format!("{item_kind} not found in `{ty_str}`"));
                         let is_string_or_ref_str = match actual.kind() {
@@ -1071,62 +962,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 // If the method name is the name of a field with a function or closure type,
                 // give a helping note that it has to be called as `(x.f)(...)`.
                 if let SelfSource::MethodCall(expr) = source {
-                    let field_receiver =
-                        self.autoderef(span, rcvr_ty).find_map(|(ty, _)| match ty.kind() {
-                            ty::Adt(def, substs) if !def.is_enum() => {
-                                let variant = &def.non_enum_variant();
-                                self.tcx.find_field_index(item_name, variant).map(|index| {
-                                    let field = &variant.fields[index];
-                                    let field_ty = field.ty(tcx, substs);
-                                    (field, field_ty)
-                                })
-                            }
-                            _ => None,
-                        });
-
-                    if let Some((field, field_ty)) = field_receiver {
-                        let scope = self.tcx.parent_module(self.body_id).to_def_id();
-                        let is_accessible = field.vis.is_accessible_from(scope, self.tcx);
-
-                        if is_accessible {
-                            if self.is_fn_ty(field_ty, span) {
-                                let expr_span = expr.span.to(item_name.span);
-                                err.multipart_suggestion(
-                                    &format!(
-                                        "to call the function stored in `{}`, \
-                                         surround the field access with parentheses",
-                                        item_name,
-                                    ),
-                                    vec![
-                                        (expr_span.shrink_to_lo(), '('.to_string()),
-                                        (expr_span.shrink_to_hi(), ')'.to_string()),
-                                    ],
-                                    Applicability::MachineApplicable,
-                                );
-                            } else {
-                                let call_expr = self
-                                    .tcx
-                                    .hir()
-                                    .expect_expr(self.tcx.hir().get_parent_node(expr.hir_id));
-
-                                if let Some(span) = call_expr.span.trim_start(item_name.span) {
-                                    err.span_suggestion(
-                                        span,
-                                        "remove the arguments",
-                                        String::new(),
-                                        Applicability::MaybeIncorrect,
-                                    );
-                                }
-                            }
-                        }
-
-                        let field_kind = if is_accessible { "field" } else { "private field" };
-                        err.span_label(item_name.span, format!("{}, not a method", field_kind));
-                    } else if lev_candidate.is_none() && !custom_span_label {
-                        label_span_not_found();
+                    if !self.suggest_field_call(span, rcvr_ty, expr, item_name, &mut err)
+                        && lev_candidate.is_none()
+                        && !custom_span_label
+                    {
+                        label_span_not_found(&mut err);
                     }
                 } else if !custom_span_label {
-                    label_span_not_found();
+                    label_span_not_found(&mut err);
                 }
 
                 if let SelfSource::MethodCall(expr) = source
@@ -1313,6 +1156,187 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         None
     }
 
+    fn suggest_field_call(
+        &self,
+        span: Span,
+        rcvr_ty: Ty<'tcx>,
+        expr: &hir::Expr<'_>,
+        item_name: Ident,
+        err: &mut DiagnosticBuilder<'tcx, ErrorGuaranteed>,
+    ) -> bool {
+        let tcx = self.tcx;
+        let field_receiver = self.autoderef(span, rcvr_ty).find_map(|(ty, _)| match ty.kind() {
+            ty::Adt(def, substs) if !def.is_enum() => {
+                let variant = &def.non_enum_variant();
+                tcx.find_field_index(item_name, variant).map(|index| {
+                    let field = &variant.fields[index];
+                    let field_ty = field.ty(tcx, substs);
+                    (field, field_ty)
+                })
+            }
+            _ => None,
+        });
+        if let Some((field, field_ty)) = field_receiver {
+            let scope = tcx.parent_module(self.body_id).to_def_id();
+            let is_accessible = field.vis.is_accessible_from(scope, tcx);
+
+            if is_accessible {
+                if self.is_fn_ty(field_ty, span) {
+                    let expr_span = expr.span.to(item_name.span);
+                    err.multipart_suggestion(
+                        &format!(
+                            "to call the function stored in `{}`, \
+                                         surround the field access with parentheses",
+                            item_name,
+                        ),
+                        vec![
+                            (expr_span.shrink_to_lo(), '('.to_string()),
+                            (expr_span.shrink_to_hi(), ')'.to_string()),
+                        ],
+                        Applicability::MachineApplicable,
+                    );
+                } else {
+                    let call_expr = tcx.hir().expect_expr(tcx.hir().get_parent_node(expr.hir_id));
+
+                    if let Some(span) = call_expr.span.trim_start(item_name.span) {
+                        err.span_suggestion(
+                            span,
+                            "remove the arguments",
+                            String::new(),
+                            Applicability::MaybeIncorrect,
+                        );
+                    }
+                }
+            }
+
+            let field_kind = if is_accessible { "field" } else { "private field" };
+            err.span_label(item_name.span, format!("{}, not a method", field_kind));
+            return true;
+        }
+        false
+    }
+
+    fn suggest_constraining_numerical_ty(
+        &self,
+        tcx: TyCtxt<'tcx>,
+        actual: Ty<'tcx>,
+        source: SelfSource<'_>,
+        span: Span,
+        item_kind: &str,
+        item_name: Ident,
+        ty_str: &str,
+    ) -> bool {
+        let found_candidate = all_traits(self.tcx)
+            .into_iter()
+            .any(|info| self.associated_value(info.def_id, item_name).is_some());
+        let found_assoc = |ty: Ty<'tcx>| {
+            simplify_type(tcx, ty, TreatParams::AsPlaceholders)
+                .and_then(|simp| {
+                    tcx.incoherent_impls(simp)
+                        .iter()
+                        .find_map(|&id| self.associated_value(id, item_name))
+                })
+                .is_some()
+        };
+        let found_candidate = found_candidate
+            || found_assoc(tcx.types.i8)
+            || found_assoc(tcx.types.i16)
+            || found_assoc(tcx.types.i32)
+            || found_assoc(tcx.types.i64)
+            || found_assoc(tcx.types.i128)
+            || found_assoc(tcx.types.u8)
+            || found_assoc(tcx.types.u16)
+            || found_assoc(tcx.types.u32)
+            || found_assoc(tcx.types.u64)
+            || found_assoc(tcx.types.u128)
+            || found_assoc(tcx.types.f32)
+            || found_assoc(tcx.types.f32);
+        if found_candidate
+            && actual.is_numeric()
+            && !actual.has_concrete_skeleton()
+            && let SelfSource::MethodCall(expr) = source
+        {
+            let mut err = struct_span_err!(
+                tcx.sess,
+                span,
+                E0689,
+                "can't call {} `{}` on ambiguous numeric type `{}`",
+                item_kind,
+                item_name,
+                ty_str
+            );
+            let concrete_type = if actual.is_integral() { "i32" } else { "f32" };
+            match expr.kind {
+                ExprKind::Lit(ref lit) => {
+                    // numeric literal
+                    let snippet = tcx
+                        .sess
+                        .source_map()
+                        .span_to_snippet(lit.span)
+                        .unwrap_or_else(|_| "<numeric literal>".to_owned());
+
+                    // If this is a floating point literal that ends with '.',
+                    // get rid of it to stop this from becoming a member access.
+                    let snippet = snippet.strip_suffix('.').unwrap_or(&snippet);
+
+                    err.span_suggestion(
+                        lit.span,
+                        &format!(
+                            "you must specify a concrete type for this numeric value, \
+                                         like `{}`",
+                            concrete_type
+                        ),
+                        format!("{snippet}_{concrete_type}"),
+                        Applicability::MaybeIncorrect,
+                    );
+                }
+                ExprKind::Path(QPath::Resolved(_, path)) => {
+                    // local binding
+                    if let hir::def::Res::Local(hir_id) = path.res {
+                        let span = tcx.hir().span(hir_id);
+                        let snippet = tcx.sess.source_map().span_to_snippet(span);
+                        let filename = tcx.sess.source_map().span_to_filename(span);
+
+                        let parent_node =
+                            self.tcx.hir().get(self.tcx.hir().get_parent_node(hir_id));
+                        let msg = format!(
+                            "you must specify a type for this binding, like `{}`",
+                            concrete_type,
+                        );
+
+                        match (filename, parent_node, snippet) {
+                            (
+                                FileName::Real(_),
+                                Node::Local(hir::Local {
+                                    source: hir::LocalSource::Normal,
+                                    ty,
+                                    ..
+                                }),
+                                Ok(ref snippet),
+                            ) => {
+                                err.span_suggestion(
+                                    // account for `let x: _ = 42;`
+                                    //                  ^^^^
+                                    span.to(ty.as_ref().map(|ty| ty.span).unwrap_or(span)),
+                                    &msg,
+                                    format!("{}: {}", snippet, concrete_type),
+                                    Applicability::MaybeIncorrect,
+                                );
+                            }
+                            _ => {
+                                err.span_label(span, msg);
+                            }
+                        }
+                    }
+                }
+                _ => {}
+            }
+            err.emit();
+            return true;
+        }
+        false
+    }
+
     crate fn note_unmet_impls_on_type(
         &self,
         err: &mut Diagnostic,
diff --git a/compiler/rustc_typeck/src/check/mod.rs b/compiler/rustc_typeck/src/check/mod.rs
index 0d5e7b28a4e..f7bb30cd13e 100644
--- a/compiler/rustc_typeck/src/check/mod.rs
+++ b/compiler/rustc_typeck/src/check/mod.rs
@@ -98,12 +98,15 @@ pub use check::{check_item_type, check_wf_new};
 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;
 use crate::check::gather_locals::GatherLocalsVisitor;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
-use rustc_errors::{pluralize, struct_span_err, Applicability, MultiSpan};
+use rustc_errors::{
+    pluralize, struct_span_err, Applicability, DiagnosticBuilder, EmissionGuarantee, MultiSpan,
+};
 use rustc_hir as hir;
 use rustc_hir::def::Res;
 use rustc_hir::def_id::{DefId, LocalDefId};
@@ -988,3 +991,36 @@ 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<G: EmissionGuarantee>(
+    span: Span,
+    kind: CtorOf,
+    params: usize,
+    err: &mut DiagnosticBuilder<'_, G>,
+) {
+    // 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/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs
index d3545236e3d..118807a8286 100644
--- a/src/librustdoc/html/format.rs
+++ b/src/librustdoc/html/format.rs
@@ -18,6 +18,7 @@ use rustc_hir::def_id::DefId;
 use rustc_middle::ty;
 use rustc_middle::ty::DefIdTree;
 use rustc_middle::ty::TyCtxt;
+use rustc_span::symbol::kw;
 use rustc_span::{sym, Symbol};
 use rustc_target::spec::abi::Abi;
 
@@ -679,7 +680,7 @@ fn resolved_path<'cx>(
 
     if print_all {
         for seg in &path.segments[..path.segments.len() - 1] {
-            write!(w, "{}::", seg.name)?;
+            write!(w, "{}::", if seg.name == kw::PathRoot { "" } else { seg.name.as_str() })?;
         }
     }
     if w.alternate() {
diff --git a/src/test/rustdoc/issue-95873.rs b/src/test/rustdoc/issue-95873.rs
new file mode 100644
index 00000000000..ff33fb63a0b
--- /dev/null
+++ b/src/test/rustdoc/issue-95873.rs
@@ -0,0 +1,2 @@
+// @has issue_95873/index.html "//*[@class='item-left import-item']" "pub use ::std as x;"
+pub use ::std as x;
diff --git a/src/test/rustdoc/issue-96381.rs b/src/test/rustdoc/issue-96381.rs
new file mode 100644
index 00000000000..f0f123f85a0
--- /dev/null
+++ b/src/test/rustdoc/issue-96381.rs
@@ -0,0 +1,16 @@
+// should-fail
+
+#![allow(unused)]
+
+trait Foo<T>: Sized {
+    fn bar(i: i32, t: T, s: &Self) -> (T, i32);
+}
+
+impl Foo<usize> for () {
+    fn bar(i: _, t: _, s: _) -> _ {
+        //~^ ERROR the placeholder `_` is not allowed within types on item signatures for functions
+        (1, 2)
+    }
+}
+
+fn main() {}
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 f8a81af786f..3d2bcb8ad35 100644
--- a/src/test/ui/functions-closures/fn-help-with-err.rs
+++ b/src/test/ui/functions-closures/fn-help-with-err.rs
@@ -3,14 +3,14 @@ fn main() {
     let arc = std::sync::Arc::new(oops);
     //~^ ERROR cannot find value `oops` in this scope
     //~| NOTE not found
-    // The error "note: `arc` is a function, perhaps you wish to call it" MUST NOT appear.
+    // The error "note: this is a function, perhaps you wish to call it" MUST NOT appear.
     arc.blablabla();
     //~^ ERROR no method named `blablabla`
     //~| NOTE method not found
     let arc2 = std::sync::Arc::new(|| 1);
-    // The error "note: `arc2` is a function, perhaps you wish to call it" SHOULD appear
+    // The error "note: this is a function, perhaps you wish to call it" SHOULD appear
     arc2.blablabla();
     //~^ ERROR no method named `blablabla`
     //~| NOTE method not found
-    //~| NOTE `arc2` is a function, perhaps you wish to call it
+    //~| NOTE this is a function, perhaps you wish to call it
 }
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 4d6b3282ad9..3e42cb1fb6e 100644
--- a/src/test/ui/functions-closures/fn-help-with-err.stderr
+++ b/src/test/ui/functions-closures/fn-help-with-err.stderr
@@ -14,9 +14,9 @@ error[E0599]: no method named `blablabla` found for struct `Arc<[closure@$DIR/fn
   --> $DIR/fn-help-with-err.rs:12:10
    |
 LL |     arc2.blablabla();
-   |          ^^^^^^^^^ method not found in `Arc<[closure@$DIR/fn-help-with-err.rs:10:36: 10:40]>`
-   |
-   = note: `arc2` is a function, perhaps you wish to call it
+   |     ---- ^^^^^^^^^ method not found in `Arc<[closure@$DIR/fn-help-with-err.rs:10:36: 10:40]>`
+   |     |
+   |     this is a function, perhaps you wish to call it
 
 error: aborting due to 3 previous errors
 
diff --git a/src/test/ui/impl-trait/issues/issue-88236-2.nll.stderr b/src/test/ui/impl-trait/issues/issue-88236-2.nll.stderr
index 9cf8ff76c87..66cffa9e36c 100644
--- a/src/test/ui/impl-trait/issues/issue-88236-2.nll.stderr
+++ b/src/test/ui/impl-trait/issues/issue-88236-2.nll.stderr
@@ -24,10 +24,14 @@ LL | fn make_bad_impl<'b>(x: &'b ()) -> impl for<'a> Hrtb<'a, Assoc = impl Send
 LL |     x
    |     ^ returning this value requires that `'b` must outlive `'static`
    |
-help: to allow this `impl Trait` to capture borrowed data with lifetime `'b`, add `'b` as a bound
+help: to declare that the `impl Trait` captures data from argument `x`, you can add an explicit `'b` lifetime bound
    |
 LL | fn make_bad_impl<'b>(x: &'b ()) -> impl for<'a> Hrtb<'a, Assoc = impl Send + 'a> + 'b {
    |                                                                                  ++++
+help: to declare that the `impl Trait` captures data from argument `x`, you can add an explicit `'b` lifetime bound
+   |
+LL | fn make_bad_impl<'b>(x: &'b ()) -> impl for<'a> Hrtb<'a, Assoc = impl Send + 'a + 'b> {
+   |                                                                                 ++++
 
 error: implementation of `Hrtb` is not general enough
   --> $DIR/issue-88236-2.rs:20:5
diff --git a/src/test/ui/impl-trait/must_outlive_least_region_or_bound.nll.stderr b/src/test/ui/impl-trait/must_outlive_least_region_or_bound.nll.stderr
index 0059f729bae..5a190649b63 100644
--- a/src/test/ui/impl-trait/must_outlive_least_region_or_bound.nll.stderr
+++ b/src/test/ui/impl-trait/must_outlive_least_region_or_bound.nll.stderr
@@ -32,7 +32,14 @@ LL | fn elided2(x: &i32) -> impl Copy + 'static { x }
    |               |
    |               let's call the lifetime of this reference `'1`
    |
-   = help: consider replacing `'1` with `'static`
+help: consider changing the `impl Trait`'s explicit `'static` bound to the lifetime of argument `x`
+   |
+LL | fn elided2(x: &i32) -> impl Copy + '_ { x }
+   |                                    ~~
+help: alternatively, add an explicit `'static` bound to this reference
+   |
+LL | fn elided2(x: &'static i32) -> impl Copy + 'static { x }
+   |               ~~~~~~~~~~~~
 
 error: lifetime may not live long enough
   --> $DIR/must_outlive_least_region_or_bound.rs:11:55
@@ -40,7 +47,14 @@ error: lifetime may not live long enough
 LL | fn explicit2<'a>(x: &'a i32) -> impl Copy + 'static { x }
    |              -- lifetime `'a` defined here            ^ returning this value requires that `'a` must outlive `'static`
    |
-   = help: consider replacing `'a` with `'static`
+help: consider changing the `impl Trait`'s explicit `'static` bound to the lifetime of argument `x`
+   |
+LL | fn explicit2<'a>(x: &'a i32) -> impl Copy + 'a { x }
+   |                                             ~~
+help: alternatively, add an explicit `'static` bound to this reference
+   |
+LL | fn explicit2<'a>(x: &'static i32) -> impl Copy + 'static { x }
+   |                     ~~~~~~~~~~~~
 
 error[E0621]: explicit lifetime required in the type of `x`
   --> $DIR/must_outlive_least_region_or_bound.rs:13:41
@@ -57,6 +71,15 @@ LL | fn elided5(x: &i32) -> (Box<dyn Debug>, impl Debug) { (Box::new(x), x) }
    |               -                                       ^^^^^^^^^^^^^^^^ returning this value requires that `'1` must outlive `'static`
    |               |
    |               let's call the lifetime of this reference `'1`
+   |
+help: to declare that the trait object captures data from argument `x`, you can add an explicit `'_` lifetime bound
+   |
+LL | fn elided5(x: &i32) -> (Box<dyn Debug + '_>, impl Debug) { (Box::new(x), x) }
+   |                                       ++++
+help: to declare that the `impl Trait` captures data from argument `x`, you can add an explicit `'_` lifetime bound
+   |
+LL | fn elided5(x: &i32) -> (Box<dyn Debug>, impl Debug + '_) { (Box::new(x), x) }
+   |                                                    ++++
 
 error: lifetime may not live long enough
   --> $DIR/must_outlive_least_region_or_bound.rs:29:69
@@ -64,7 +87,14 @@ error: lifetime may not live long enough
 LL | fn with_bound<'a>(x: &'a i32) -> impl LifetimeTrait<'a> + 'static { x }
    |               -- lifetime `'a` defined here                         ^ returning this value requires that `'a` must outlive `'static`
    |
-   = help: consider replacing `'a` with `'static`
+help: consider changing the `impl Trait`'s explicit `'static` bound to the lifetime of argument `x`
+   |
+LL | fn with_bound<'a>(x: &'a i32) -> impl LifetimeTrait<'a> + 'a { x }
+   |                                                           ~~
+help: alternatively, add an explicit `'static` bound to this reference
+   |
+LL | fn with_bound<'a>(x: &'static i32) -> impl LifetimeTrait<'a> + 'static { x }
+   |                      ~~~~~~~~~~~~
 
 error[E0700]: hidden type for `impl Trait` captures lifetime that does not appear in bounds
   --> $DIR/must_outlive_least_region_or_bound.rs:34:5
diff --git a/src/test/ui/issues/issue-16922.nll.stderr b/src/test/ui/issues/issue-16922.nll.stderr
index 7f4f5b22eb3..9d9f32a97c0 100644
--- a/src/test/ui/issues/issue-16922.nll.stderr
+++ b/src/test/ui/issues/issue-16922.nll.stderr
@@ -5,6 +5,11 @@ LL | fn foo<T: Any>(value: &T) -> Box<dyn Any> {
    |                       - let's call the lifetime of this reference `'1`
 LL |     Box::new(value) as Box<dyn Any>
    |     ^^^^^^^^^^^^^^^ cast requires that `'1` must outlive `'static`
+   |
+help: to declare that the trait object captures data from argument `value`, you can add an explicit `'_` lifetime bound
+   |
+LL | fn foo<T: Any>(value: &T) -> Box<dyn Any + '_> {
+   |                                          ++++
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/issues/issue-29124.stderr b/src/test/ui/issues/issue-29124.stderr
index 42d89cd01a4..c5d2ec08409 100644
--- a/src/test/ui/issues/issue-29124.stderr
+++ b/src/test/ui/issues/issue-29124.stderr
@@ -2,17 +2,17 @@ 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}`
-   |
-   = note: `Obj::func` is a function, perhaps you wish to call it
+   |     --------- ^ method not found in `fn() -> Ret {Obj::func}`
+   |     |
+   |     this is a function, perhaps you wish to call it
 
 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}`
-   |
-   = note: `func` is a function, perhaps you wish to call it
+   |     ---- ^ method not found in `fn() -> Ret {func}`
+   |     |
+   |     this is a function, perhaps you wish to call it
 
 error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/issues/issue-57362-1.stderr b/src/test/ui/issues/issue-57362-1.stderr
index 5c611cd43d3..8e19f14009a 100644
--- a/src/test/ui/issues/issue-57362-1.stderr
+++ b/src/test/ui/issues/issue-57362-1.stderr
@@ -2,9 +2,10 @@ 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)`
+   |     - ^ method not found in `fn(&u8)`
+   |     |
+   |     this is a function, perhaps you wish to call it
    |
-   = note: `a` 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
 note: `Trait` defines an item `f`, perhaps you need to implement it
   --> $DIR/issue-57362-1.rs:8:1
diff --git a/src/test/ui/nll/mir_check_cast_unsize.stderr b/src/test/ui/nll/mir_check_cast_unsize.stderr
index 364d6c17ea7..8d02ef71d1b 100644
--- a/src/test/ui/nll/mir_check_cast_unsize.stderr
+++ b/src/test/ui/nll/mir_check_cast_unsize.stderr
@@ -5,6 +5,11 @@ LL | fn bar<'a>(x: &'a u32) -> &'static dyn Debug {
    |        -- lifetime `'a` defined here
 LL |     x
    |     ^ returning this value requires that `'a` must outlive `'static`
+   |
+help: to declare that the trait object captures data from argument `x`, you can add an explicit `'a` lifetime bound
+   |
+LL | fn bar<'a>(x: &'a u32) -> &'static dyn Debug + 'a {
+   |                                              ++++
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/object-lifetime/object-lifetime-default-from-box-error.nll.stderr b/src/test/ui/object-lifetime/object-lifetime-default-from-box-error.nll.stderr
index ae02c58d080..43695a7511d 100644
--- a/src/test/ui/object-lifetime/object-lifetime-default-from-box-error.nll.stderr
+++ b/src/test/ui/object-lifetime/object-lifetime-default-from-box-error.nll.stderr
@@ -6,6 +6,11 @@ LL | fn load(ss: &mut SomeStruct) -> Box<dyn SomeTrait> {
 ...
 LL |     ss.r
    |     ^^^^ returning this value requires that `'1` must outlive `'static`
+   |
+help: to declare that the trait object captures data from argument `ss`, you can add an explicit `'_` lifetime bound
+   |
+LL | fn load(ss: &mut SomeStruct) -> Box<dyn SomeTrait + '_> {
+   |                                                   ++++
 
 error[E0507]: cannot move out of `ss.r` which is behind a mutable reference
   --> $DIR/object-lifetime-default-from-box-error.rs:18:5
diff --git a/src/test/ui/regions/region-object-lifetime-in-coercion.nll.stderr b/src/test/ui/regions/region-object-lifetime-in-coercion.nll.stderr
index 4d72724586e..724b06ce8b1 100644
--- a/src/test/ui/regions/region-object-lifetime-in-coercion.nll.stderr
+++ b/src/test/ui/regions/region-object-lifetime-in-coercion.nll.stderr
@@ -5,6 +5,15 @@ LL | fn a(v: &[u8]) -> Box<dyn Foo + 'static> {
    |         - let's call the lifetime of this reference `'1`
 LL |     let x: Box<dyn Foo + 'static> = Box::new(v);
    |            ^^^^^^^^^^^^^^^^^^^^^^ type annotation requires that `'1` must outlive `'static`
+   |
+help: consider changing the trait object's explicit `'static` bound to the lifetime of argument `v`
+   |
+LL | fn a(v: &[u8]) -> Box<dyn Foo + '_> {
+   |                                 ~~
+help: alternatively, add an explicit `'static` bound to this reference
+   |
+LL | fn a(v: &'static [u8]) -> Box<dyn Foo + 'static> {
+   |         ~~~~~~~~~~~~~
 
 error: lifetime may not live long enough
   --> $DIR/region-object-lifetime-in-coercion.rs:19:5
@@ -13,6 +22,15 @@ LL | fn b(v: &[u8]) -> Box<dyn Foo + 'static> {
    |         - let's call the lifetime of this reference `'1`
 LL |     Box::new(v)
    |     ^^^^^^^^^^^ returning this value requires that `'1` must outlive `'static`
+   |
+help: consider changing the trait object's explicit `'static` bound to the lifetime of argument `v`
+   |
+LL | fn b(v: &[u8]) -> Box<dyn Foo + '_> {
+   |                                 ~~
+help: alternatively, add an explicit `'static` bound to this reference
+   |
+LL | fn b(v: &'static [u8]) -> Box<dyn Foo + 'static> {
+   |         ~~~~~~~~~~~~~
 
 error: lifetime may not live long enough
   --> $DIR/region-object-lifetime-in-coercion.rs:27:5
@@ -22,6 +40,11 @@ LL | fn c(v: &[u8]) -> Box<dyn Foo> {
 ...
 LL |     Box::new(v)
    |     ^^^^^^^^^^^ returning this value requires that `'1` must outlive `'static`
+   |
+help: to declare that the trait object captures data from argument `v`, you can add an explicit `'_` lifetime bound
+   |
+LL | fn c(v: &[u8]) -> Box<dyn Foo + '_> {
+   |                               ++++
 
 error: lifetime may not live long enough
   --> $DIR/region-object-lifetime-in-coercion.rs:33:5
diff --git a/src/test/ui/regions/regions-close-object-into-object-2.nll.stderr b/src/test/ui/regions/regions-close-object-into-object-2.nll.stderr
index 1a79da2776b..473c99b672f 100644
--- a/src/test/ui/regions/regions-close-object-into-object-2.nll.stderr
+++ b/src/test/ui/regions/regions-close-object-into-object-2.nll.stderr
@@ -5,6 +5,15 @@ LL | fn g<'a, T: 'static>(v: Box<dyn A<T> + 'a>) -> Box<dyn X + 'static> {
    |      -- lifetime `'a` defined here
 LL |     Box::new(B(&*v)) as Box<dyn X>
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ returning this value requires that `'a` must outlive `'static`
+   |
+help: consider changing the trait object's explicit `'static` bound to the lifetime of argument `v`
+   |
+LL | fn g<'a, T: 'static>(v: Box<dyn A<T> + 'a>) -> Box<dyn X + 'a> {
+   |                                                            ~~
+help: alternatively, add an explicit `'static` bound to this reference
+   |
+LL | fn g<'a, T: 'static>(v: Box<(dyn A<T> + 'static)>) -> Box<dyn X + 'static> {
+   |                         ~~~~~~~~~~~~~~~~~~~~~~~~~
 
 error[E0515]: cannot return value referencing local data `*v`
   --> $DIR/regions-close-object-into-object-2.rs:13:5
diff --git a/src/test/ui/regions/regions-close-object-into-object-4.nll.stderr b/src/test/ui/regions/regions-close-object-into-object-4.nll.stderr
index 993b13ddbf8..05ddc09b2d0 100644
--- a/src/test/ui/regions/regions-close-object-into-object-4.nll.stderr
+++ b/src/test/ui/regions/regions-close-object-into-object-4.nll.stderr
@@ -29,6 +29,15 @@ LL | fn i<'a, T, U>(v: Box<dyn A<U>+'a>) -> Box<dyn X + 'static> {
    |      -- lifetime `'a` defined here
 LL |     Box::new(B(&*v)) as Box<dyn X>
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ returning this value requires that `'a` must outlive `'static`
+   |
+help: consider changing the trait object's explicit `'static` bound to the lifetime of argument `v`
+   |
+LL | fn i<'a, T, U>(v: Box<dyn A<U>+'a>) -> Box<dyn X + 'a> {
+   |                                                    ~~
+help: alternatively, add an explicit `'static` bound to this reference
+   |
+LL | fn i<'a, T, U>(v: Box<(dyn A<U> + 'static)>) -> Box<dyn X + 'static> {
+   |                   ~~~~~~~~~~~~~~~~~~~~~~~~~
 
 error[E0515]: cannot return value referencing local data `*v`
   --> $DIR/regions-close-object-into-object-4.rs:13:5
diff --git a/src/test/ui/regions/regions-proc-bound-capture.nll.stderr b/src/test/ui/regions/regions-proc-bound-capture.nll.stderr
index 6120a53eb09..ce4d2d4d111 100644
--- a/src/test/ui/regions/regions-proc-bound-capture.nll.stderr
+++ b/src/test/ui/regions/regions-proc-bound-capture.nll.stderr
@@ -6,6 +6,15 @@ LL | fn static_proc(x: &isize) -> Box<dyn FnMut() -> (isize) + 'static> {
 LL |     // This is illegal, because the region bound on `proc` is 'static.
 LL |     Box::new(move || { *x })
    |     ^^^^^^^^^^^^^^^^^^^^^^^^ returning this value requires that `'1` must outlive `'static`
+   |
+help: consider changing the trait object's explicit `'static` bound to the lifetime of argument `x`
+   |
+LL | fn static_proc(x: &isize) -> Box<dyn FnMut() -> (isize) + '_> {
+   |                                                           ~~
+help: alternatively, add an explicit `'static` bound to this reference
+   |
+LL | fn static_proc(x: &'static isize) -> Box<dyn FnMut() -> (isize) + 'static> {
+   |                   ~~~~~~~~~~~~~~
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/suggestions/lifetimes/trait-object-nested-in-impl-trait.nll.stderr b/src/test/ui/suggestions/lifetimes/trait-object-nested-in-impl-trait.nll.stderr
index 3ed3827b97d..6c65e4f0175 100644
--- a/src/test/ui/suggestions/lifetimes/trait-object-nested-in-impl-trait.nll.stderr
+++ b/src/test/ui/suggestions/lifetimes/trait-object-nested-in-impl-trait.nll.stderr
@@ -9,10 +9,14 @@ LL | |             remaining: self.0.iter(),
 LL | |         }
    | |_________^ returning this value requires that `'1` must outlive `'static`
    |
-help: to allow this `impl Trait` to capture borrowed data with lifetime `'1`, add `'_` as a bound
+help: to declare that the `impl Trait` captures data from argument `self`, you can add an explicit `'_` lifetime bound
    |
 LL |     fn iter(&self) -> impl Iterator<Item = Box<dyn Foo>> + '_ {
    |                                                          ++++
+help: to declare that the trait object captures data from argument `self`, you can add an explicit `'_` lifetime bound
+   |
+LL |     fn iter(&self) -> impl Iterator<Item = Box<dyn Foo + '_>> {
+   |                                                        ++++
 
 error: lifetime may not live long enough
   --> $DIR/trait-object-nested-in-impl-trait.rs:39:9
@@ -24,6 +28,11 @@ LL | |             current: None,
 LL | |             remaining: self.0.iter(),
 LL | |         }
    | |_________^ returning this value requires that `'1` must outlive `'static`
+   |
+help: to declare that the trait object captures data from argument `self`, you can add an explicit `'_` lifetime bound
+   |
+LL |     fn iter(&self) -> impl Iterator<Item = Box<dyn Foo + '_>> + '_ {
+   |                                                        ++++
 
 error: lifetime may not live long enough
   --> $DIR/trait-object-nested-in-impl-trait.rs:50:9
@@ -35,6 +44,11 @@ LL | |             current: None,
 LL | |             remaining: self.0.iter(),
 LL | |         }
    | |_________^ returning this value requires that `'a` must outlive `'static`
+   |
+help: to declare that the trait object captures data from argument `self`, you can add an explicit `'a` lifetime bound
+   |
+LL |     fn iter<'a>(&'a self) -> impl Iterator<Item = Box<dyn Foo + 'a>> + 'a {
+   |                                                               ++++
 
 error: lifetime may not live long enough
   --> $DIR/trait-object-nested-in-impl-trait.rs:61:9
@@ -47,10 +61,14 @@ LL | |             remaining: self.0.iter(),
 LL | |         }
    | |_________^ returning this value requires that `'a` must outlive `'static`
    |
-help: to allow this `impl Trait` to capture borrowed data with lifetime `'a`, add `'a` as a bound
+help: to declare that the `impl Trait` captures data from argument `self`, you can add an explicit `'a` lifetime bound
    |
 LL |     fn iter<'a>(&'a self) -> impl Iterator<Item = Box<dyn Foo>> + 'a {
    |                                                                 ++++
+help: to declare that the trait object captures data from argument `self`, you can add an explicit `'a` lifetime bound
+   |
+LL |     fn iter<'a>(&'a self) -> impl Iterator<Item = Box<dyn Foo + 'a>> {
+   |                                                               ++++
 
 error: aborting due to 4 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
new file mode 100644
index 00000000000..1875d8280cb
--- /dev/null
+++ b/src/test/ui/typeck/issue-87181/empty-tuple-method.rs
@@ -0,0 +1,14 @@
+struct Bar<T> {
+    bar: T
+}
+
+struct Foo();
+impl Foo {
+    fn foo() { }
+}
+
+fn main() {
+    let thing = Bar { bar: Foo };
+    thing.bar.foo();
+    //~^ ERROR no method named `foo` found for fn item `fn() -> Foo {Foo}` in the current scope [E0599]
+}
diff --git a/src/test/ui/typeck/issue-87181/empty-tuple-method.stderr b/src/test/ui/typeck/issue-87181/empty-tuple-method.stderr
new file mode 100644
index 00000000000..6ed70b301e4
--- /dev/null
+++ b/src/test/ui/typeck/issue-87181/empty-tuple-method.stderr
@@ -0,0 +1,16 @@
+error[E0599]: no method named `foo` found for fn item `fn() -> Foo {Foo}` in the current scope
+  --> $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
+   |
+help: call the constructor
+   |
+LL |     (thing.bar)().foo();
+   |     +         +++
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0599`.
diff --git a/src/test/ui/typeck/issue-87181/enum-variant.rs b/src/test/ui/typeck/issue-87181/enum-variant.rs
new file mode 100644
index 00000000000..3b926b90f10
--- /dev/null
+++ b/src/test/ui/typeck/issue-87181/enum-variant.rs
@@ -0,0 +1,16 @@
+struct Bar<T> {
+    bar: T
+}
+
+enum Foo{
+    Tup()
+}
+impl Foo {
+    fn foo() { }
+}
+
+fn main() {
+    let thing = Bar { bar: Foo::Tup };
+    thing.bar.foo();
+    //~^ ERROR no method named `foo` found for fn item `fn() -> Foo {Foo::Tup}` in the current scope [E0599]
+}
diff --git a/src/test/ui/typeck/issue-87181/enum-variant.stderr b/src/test/ui/typeck/issue-87181/enum-variant.stderr
new file mode 100644
index 00000000000..a3a818696ab
--- /dev/null
+++ b/src/test/ui/typeck/issue-87181/enum-variant.stderr
@@ -0,0 +1,16 @@
+error[E0599]: no method named `foo` found for fn item `fn() -> Foo {Foo::Tup}` in the current scope
+  --> $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
+   |
+help: call the constructor
+   |
+LL |     (thing.bar)().foo();
+   |     +         +++
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0599`.
diff --git a/src/test/ui/typeck/issue-87181/tuple-field.rs b/src/test/ui/typeck/issue-87181/tuple-field.rs
new file mode 100644
index 00000000000..00e3b460ecf
--- /dev/null
+++ b/src/test/ui/typeck/issue-87181/tuple-field.rs
@@ -0,0 +1,14 @@
+struct Bar<T> {
+    bar: T
+}
+
+struct Foo(char, u16);
+impl Foo {
+    fn foo() { }
+}
+
+fn main() {
+    let thing = Bar { bar: Foo };
+    thing.bar.0;
+    //~^ ERROR no field `0` on type `fn(char, u16) -> Foo {Foo}` [E0609]
+}
diff --git a/src/test/ui/typeck/issue-87181/tuple-field.stderr b/src/test/ui/typeck/issue-87181/tuple-field.stderr
new file mode 100644
index 00000000000..4d22ada0247
--- /dev/null
+++ b/src/test/ui/typeck/issue-87181/tuple-field.stderr
@@ -0,0 +1,16 @@
+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
+   |
+LL |     (thing.bar)(_, _).0;
+   |     +         +++++++
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0609`.
diff --git a/src/test/ui/typeck/issue-87181/tuple-method.rs b/src/test/ui/typeck/issue-87181/tuple-method.rs
new file mode 100644
index 00000000000..e88f642b070
--- /dev/null
+++ b/src/test/ui/typeck/issue-87181/tuple-method.rs
@@ -0,0 +1,14 @@
+struct Bar<T> {
+    bar: T
+}
+
+struct Foo(u8, i32);
+impl Foo {
+    fn foo() { }
+}
+
+fn main() {
+    let thing = Bar { bar: Foo };
+    thing.bar.foo();
+    //~^ ERROR no method named `foo` found for fn item `fn(u8, i32) -> Foo {Foo}` in the current scope [E0599]
+}
diff --git a/src/test/ui/typeck/issue-87181/tuple-method.stderr b/src/test/ui/typeck/issue-87181/tuple-method.stderr
new file mode 100644
index 00000000000..1e392e17984
--- /dev/null
+++ b/src/test/ui/typeck/issue-87181/tuple-method.stderr
@@ -0,0 +1,16 @@
+error[E0599]: no method named `foo` found for fn item `fn(u8, i32) -> Foo {Foo}` in the current scope
+  --> $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();
+   |     +         +++++++
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0599`.
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 0b6d94e71f0..e9883903674 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,9 @@ 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:31]`
-   |
-   = note: `mut_` 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:31]`
+   |     |
+   |     this is a function, perhaps you wish to call it
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/underscore-lifetime/dyn-trait-underscore.nll.stderr b/src/test/ui/underscore-lifetime/dyn-trait-underscore.nll.stderr
index 53d45f6a8f2..0ffb77cf021 100644
--- a/src/test/ui/underscore-lifetime/dyn-trait-underscore.nll.stderr
+++ b/src/test/ui/underscore-lifetime/dyn-trait-underscore.nll.stderr
@@ -6,6 +6,11 @@ LL | fn a<T>(items: &[T]) -> Box<dyn Iterator<Item=&T>> {
 LL |     //                      ^^^^^^^^^^^^^^^^^^^^^ bound *here* defaults to `'static`
 LL |     Box::new(items.iter())
    |     ^^^^^^^^^^^^^^^^^^^^^^ returning this value requires that `'1` must outlive `'static`
+   |
+help: to declare that the trait object captures data from argument `items`, you can add an explicit `'_` lifetime bound
+   |
+LL | fn a<T>(items: &[T]) -> Box<dyn Iterator<Item=&T> + '_> {
+   |                                                   ++++
 
 error: aborting due to previous error