diff --git a/src/librustc_hir/hir.rs b/src/librustc_hir/hir.rs
index 3ed0ad16eeb..86252203f1d 100644
--- a/src/librustc_hir/hir.rs
+++ b/src/librustc_hir/hir.rs
@@ -2260,10 +2260,10 @@ impl TraitRef<'_> {
 
 #[derive(RustcEncodable, RustcDecodable, Debug, HashStable_Generic)]
 pub struct PolyTraitRef<'hir> {
-    /// The `'a` in `<'a> Foo<&'a T>`.
+    /// The `'a` in `for<'a> Foo<&'a T>`.
     pub bound_generic_params: &'hir [GenericParam<'hir>],
 
-    /// The `Foo<&'a T>` in `<'a> Foo<&'a T>`.
+    /// The `Foo<&'a T>` in `for <'a> Foo<&'a T>`.
     pub trait_ref: TraitRef<'hir>,
 
     pub span: Span,
diff --git a/src/librustc_resolve/diagnostics.rs b/src/librustc_resolve/diagnostics.rs
index a612ad9e783..fafceb1f97c 100644
--- a/src/librustc_resolve/diagnostics.rs
+++ b/src/librustc_resolve/diagnostics.rs
@@ -8,7 +8,6 @@ use rustc_ast_pretty::pprust;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder};
 use rustc_feature::BUILTIN_ATTRIBUTES;
-use rustc_hir as hir;
 use rustc_hir::def::Namespace::{self, *};
 use rustc_hir::def::{self, CtorKind, CtorOf, DefKind, NonMacroAttrKind};
 use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX, LOCAL_CRATE};
@@ -20,6 +19,7 @@ use syntax::ast::{self, Ident, Path};
 use syntax::util::lev_distance::find_best_match_for_name;
 
 use crate::imports::{ImportDirective, ImportDirectiveSubclass, ImportResolver};
+use crate::lifetimes::{HRLTSpanType, MissingLifetimeSpot};
 use crate::path_names_to_string;
 use crate::{AmbiguityError, AmbiguityErrorMisc, AmbiguityKind};
 use crate::{BindingError, CrateLint, HasGenericParams, LegacyScope, Module, ModuleOrUniformRoot};
@@ -1471,7 +1471,7 @@ crate fn add_missing_lifetime_specifiers_label(
     count: usize,
     lifetime_names: &FxHashSet<ast::Ident>,
     snippet: Option<&str>,
-    missing_named_lifetime_spots: &[&hir::Generics<'_>],
+    missing_named_lifetime_spots: &[MissingLifetimeSpot<'_>],
 ) {
     if count > 1 {
         err.span_label(span, format!("expected {} lifetime parameters", count));
@@ -1484,21 +1484,41 @@ crate fn add_missing_lifetime_specifiers_label(
                 Applicability::MaybeIncorrect,
             );
         };
-        let suggest_new = |err: &mut DiagnosticBuilder<'_>, sugg| {
+        let suggest_new = |err: &mut DiagnosticBuilder<'_>, sugg: &str| {
             err.span_label(span, "expected named lifetime parameter");
 
-            if let Some(generics) = missing_named_lifetime_spots.iter().last() {
+            for missing in missing_named_lifetime_spots.iter().rev() {
                 let mut introduce_suggestion = vec![];
-                introduce_suggestion.push(match &generics.params {
-                    [] => (generics.span, "<'lifetime>".to_string()),
-                    [param, ..] => (param.span.shrink_to_lo(), "'lifetime, ".to_string()),
+                let msg;
+                let should_break;
+                introduce_suggestion.push(match missing {
+                    MissingLifetimeSpot::Generics(generics) => {
+                        msg = "consider introducing a named lifetime parameter";
+                        should_break = true;
+                        match &generics.params {
+                            [] => (generics.span, "<'lifetime>".to_string()),
+                            [param, ..] => (param.span.shrink_to_lo(), "'lifetime, ".to_string()),
+                        }
+                    }
+                    MissingLifetimeSpot::HRLT { span, span_type } => {
+                        msg = "consider introducing a Higher-Ranked lifetime";
+                        should_break = false;
+                        err.note(
+                            "for more information on Higher-Ranked lifetimes, visit \
+                             https://doc.rust-lang.org/nomicon/hrtb.html",
+                        );
+                        let suggestion = match span_type {
+                            HRLTSpanType::Empty => "for<'lifetime> ",
+                            HRLTSpanType::Tail => ", 'lifetime",
+                        };
+                        (*span, suggestion.to_string())
+                    }
                 });
-                introduce_suggestion.push((span, sugg));
-                err.multipart_suggestion(
-                    "consider introducing a named lifetime parameter",
-                    introduce_suggestion,
-                    Applicability::MaybeIncorrect,
-                );
+                introduce_suggestion.push((span, sugg.to_string()));
+                err.multipart_suggestion(msg, introduce_suggestion, Applicability::MaybeIncorrect);
+                if should_break {
+                    break;
+                }
             }
         };
 
@@ -1513,13 +1533,13 @@ crate fn add_missing_lifetime_specifiers_label(
                 suggest_existing(err, format!("{}<{}>", snippet, name));
             }
             (0, _, Some("&")) => {
-                suggest_new(err, "&'lifetime ".to_string());
+                suggest_new(err, "&'lifetime ");
             }
             (0, _, Some("'_")) => {
-                suggest_new(err, "'lifetime".to_string());
+                suggest_new(err, "'lifetime");
             }
             (0, _, Some(snippet)) if !snippet.ends_with(">") => {
-                suggest_new(err, format!("{}<'lifetime>", snippet));
+                suggest_new(err, &format!("{}<'lifetime>", snippet));
             }
             _ => {
                 err.span_label(span, "expected lifetime parameter");
diff --git a/src/librustc_resolve/lifetimes.rs b/src/librustc_resolve/lifetimes.rs
index 6e9ed5fdc17..a8d6afa0e55 100644
--- a/src/librustc_resolve/lifetimes.rs
+++ b/src/librustc_resolve/lifetimes.rs
@@ -153,6 +153,22 @@ struct NamedRegionMap {
     object_lifetime_defaults: HirIdMap<Vec<ObjectLifetimeDefault>>,
 }
 
+crate enum MissingLifetimeSpot<'tcx> {
+    Generics(&'tcx hir::Generics<'tcx>),
+    HRLT { span: Span, span_type: HRLTSpanType },
+}
+
+crate enum HRLTSpanType {
+    Empty,
+    Tail,
+}
+
+impl<'tcx> Into<MissingLifetimeSpot<'tcx>> for &'tcx hir::Generics<'tcx> {
+    fn into(self) -> MissingLifetimeSpot<'tcx> {
+        MissingLifetimeSpot::Generics(self)
+    }
+}
+
 struct LifetimeContext<'a, 'tcx> {
     tcx: TyCtxt<'tcx>,
     map: &'a mut NamedRegionMap,
@@ -186,7 +202,7 @@ struct LifetimeContext<'a, 'tcx> {
 
     /// When encountering an undefined named lifetime, we will suggest introducing it in these
     /// places.
-    missing_named_lifetime_spots: Vec<&'tcx hir::Generics<'tcx>>,
+    missing_named_lifetime_spots: Vec<MissingLifetimeSpot<'tcx>>,
 }
 
 #[derive(Debug)]
@@ -389,7 +405,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
     fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) {
         match item.kind {
             hir::ItemKind::Fn(ref sig, ref generics, _) => {
-                self.missing_named_lifetime_spots.push(generics);
+                self.missing_named_lifetime_spots.push(generics.into());
                 self.visit_early_late(None, &sig.decl, generics, |this| {
                     intravisit::walk_item(this, item);
                 });
@@ -424,7 +440,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
             | hir::ItemKind::Trait(_, _, ref generics, ..)
             | hir::ItemKind::TraitAlias(ref generics, ..)
             | hir::ItemKind::Impl { ref generics, .. } => {
-                self.missing_named_lifetime_spots.push(generics);
+                self.missing_named_lifetime_spots.push(generics.into());
 
                 // Impls permit `'_` to be used and it is equivalent to "some fresh lifetime name".
                 // This is not true for other kinds of items.x
@@ -696,7 +712,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
 
     fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem<'tcx>) {
         use self::hir::TraitItemKind::*;
-        self.missing_named_lifetime_spots.push(&trait_item.generics);
+        self.missing_named_lifetime_spots.push((&trait_item.generics).into());
         match trait_item.kind {
             Method(ref sig, _) => {
                 let tcx = self.tcx;
@@ -753,7 +769,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
 
     fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem<'tcx>) {
         use self::hir::ImplItemKind::*;
-        self.missing_named_lifetime_spots.push(&impl_item.generics);
+        self.missing_named_lifetime_spots.push((&impl_item.generics).into());
         match impl_item.kind {
             Method(ref sig, _) => {
                 let tcx = self.tcx;
@@ -953,6 +969,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
     ) {
         debug!("visit_poly_trait_ref(trait_ref={:?})", trait_ref);
 
+        let should_pop_missing_lt = self.is_trait_ref_fn_scope(trait_ref);
         if !self.trait_ref_hack
             || trait_ref.bound_generic_params.iter().any(|param| match param.kind {
                 GenericParamKind::Lifetime { .. } => true,
@@ -988,10 +1005,13 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
             self.with(scope, |old_scope, this| {
                 this.check_lifetime_params(old_scope, &trait_ref.bound_generic_params);
                 walk_list!(this, visit_generic_param, trait_ref.bound_generic_params);
-                this.visit_trait_ref(&trait_ref.trait_ref)
+                this.visit_trait_ref(&trait_ref.trait_ref);
             })
         } else {
-            self.visit_trait_ref(&trait_ref.trait_ref)
+            self.visit_trait_ref(&trait_ref.trait_ref);
+        }
+        if should_pop_missing_lt {
+            self.missing_named_lifetime_spots.pop();
         }
     }
 }
@@ -1832,18 +1852,41 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
                 lifetime_ref
             );
             err.span_label(lifetime_ref.span, "undeclared lifetime");
-            if !self.is_in_fn_syntax {
-                for generics in &self.missing_named_lifetime_spots {
-                    let (span, sugg) = match &generics.params {
-                        [] => (generics.span, format!("<{}>", lifetime_ref)),
-                        [param, ..] => (param.span.shrink_to_lo(), format!("{}, ", lifetime_ref)),
-                    };
-                    err.span_suggestion(
-                        span,
-                        &format!("consider introducing lifetime `{}` here", lifetime_ref),
-                        sugg,
-                        Applicability::MaybeIncorrect,
-                    );
+            for missing in &self.missing_named_lifetime_spots {
+                match missing {
+                    MissingLifetimeSpot::Generics(generics) => {
+                        let (span, sugg) = match &generics.params {
+                            [] => (generics.span, format!("<{}>", lifetime_ref)),
+                            [param, ..] => {
+                                (param.span.shrink_to_lo(), format!("{}, ", lifetime_ref))
+                            }
+                        };
+                        err.span_suggestion(
+                            span,
+                            &format!("consider introducing lifetime `{}` here", lifetime_ref),
+                            sugg,
+                            Applicability::MaybeIncorrect,
+                        );
+                    }
+                    MissingLifetimeSpot::HRLT { span, span_type } => {
+                        err.span_suggestion(
+                            *span,
+                            &format!(
+                                "consider introducing a Higher-Ranked lifetime `{}` here",
+                                lifetime_ref
+                            ),
+                            match span_type {
+                                HRLTSpanType::Empty => format!("for<{}> ", lifetime_ref),
+                                HRLTSpanType::Tail => format!(", {}", lifetime_ref),
+                            }
+                            .to_string(),
+                            Applicability::MaybeIncorrect,
+                        );
+                        err.note(
+                            "for more information on Higher-Ranked lifetimes, visit \
+                             https://doc.rust-lang.org/nomicon/hrtb.html",
+                        );
+                    }
                 }
             }
             err.emit();
@@ -2441,6 +2484,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
 
         let elided_len = elided_params.len();
 
+        // FIXME: collect spans of the input params when appropriate to use in the diagnostic.
         for (i, info) in elided_params.into_iter().enumerate() {
             let ElisionFailureInfo { parent, index, lifetime_count: n, have_bound_regions } = info;
 
@@ -2747,6 +2791,27 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
         let old_value = self.map.defs.remove(&lifetime_ref.hir_id);
         assert_eq!(old_value, Some(bad_def));
     }
+
+    fn is_trait_ref_fn_scope(&mut self, trait_ref: &'tcx hir::PolyTraitRef<'tcx>) -> bool {
+        if let Res::Def(_, did) = trait_ref.trait_ref.path.res {
+            if [
+                self.tcx.lang_items().fn_once_trait(),
+                self.tcx.lang_items().fn_trait(),
+                self.tcx.lang_items().fn_mut_trait(),
+            ]
+            .contains(&Some(did))
+            {
+                let (span, span_type) = match &trait_ref.bound_generic_params {
+                    [] => (trait_ref.span.shrink_to_lo(), HRLTSpanType::Empty),
+                    [.., bound] => (bound.span.shrink_to_hi(), HRLTSpanType::Tail),
+                };
+                self.missing_named_lifetime_spots
+                    .push(MissingLifetimeSpot::HRLT { span, span_type });
+                return true;
+            }
+        };
+        false
+    }
 }
 
 /// Detects late-bound lifetimes and inserts them into
diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs
index 231aed48fb6..6bd120d818d 100644
--- a/src/librustc_typeck/astconv.rs
+++ b/src/librustc_typeck/astconv.rs
@@ -1307,12 +1307,15 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                             );
                         }
                     };
+                    // FIXME: point at the type params that don't have appropriate lifetimes:
+                    // struct S1<F: for<'a> Fn(&i32, &i32) -> &'a i32>(F);
+                    //                         ----  ----     ^^^^^^^
                     struct_span_err!(
                         tcx.sess,
                         binding.span,
                         E0582,
                         "binding for associated type `{}` references lifetime `{}`, \
-                                     which does not appear in the trait input types",
+                         which does not appear in the trait input types",
                         binding.item_name,
                         br_name
                     )
diff --git a/src/test/ui/in-band-lifetimes/no_introducing_in_band_in_locals.stderr b/src/test/ui/in-band-lifetimes/no_introducing_in_band_in_locals.stderr
index bfb20ade035..7d71230e162 100644
--- a/src/test/ui/in-band-lifetimes/no_introducing_in_band_in_locals.stderr
+++ b/src/test/ui/in-band-lifetimes/no_introducing_in_band_in_locals.stderr
@@ -9,6 +9,8 @@ LL |     let y: &'test u32 = x;
 error[E0261]: use of undeclared lifetime name `'test`
   --> $DIR/no_introducing_in_band_in_locals.rs:10:16
    |
+LL | fn bar() {
+   |       - help: consider introducing lifetime `'test` here: `<'test>`
 LL |     let y: fn(&'test u32) = foo2;
    |                ^^^^^ undeclared lifetime
 
diff --git a/src/test/ui/issues/issue-19707.stderr b/src/test/ui/issues/issue-19707.stderr
index 8a627bc0bd4..c5129152aa5 100644
--- a/src/test/ui/issues/issue-19707.stderr
+++ b/src/test/ui/issues/issue-19707.stderr
@@ -17,6 +17,11 @@ LL | fn bar<F: Fn(&u8, &u8) -> &u8>(f: &F) {}
    |                           ^ expected named lifetime parameter
    |
    = help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from argument 1 or argument 2
+   = note: for more information on Higher-Ranked lifetimes, visit https://doc.rust-lang.org/nomicon/hrtb.html
+help: consider introducing a Higher-Ranked lifetime
+   |
+LL | fn bar<F: for<'lifetime> Fn(&u8, &u8) -> &'lifetime u8>(f: &F) {}
+   |           ^^^^^^^^^^^^^^                 ^^^^^^^^^^
 help: consider introducing a named lifetime parameter
    |
 LL | fn bar<'lifetime, F: Fn(&u8, &u8) -> &'lifetime u8>(f: &F) {}
diff --git a/src/test/ui/regions/regions-name-undeclared.stderr b/src/test/ui/regions/regions-name-undeclared.stderr
index 79ebef41dcc..cb72d1ec9bc 100644
--- a/src/test/ui/regions/regions-name-undeclared.stderr
+++ b/src/test/ui/regions/regions-name-undeclared.stderr
@@ -88,12 +88,32 @@ error[E0261]: use of undeclared lifetime name `'b`
    |
 LL | ...                   &'b isize,
    |                        ^^ undeclared lifetime
+   |
+   = note: for more information on Higher-Ranked lifetimes, visit https://doc.rust-lang.org/nomicon/hrtb.html
+help: consider introducing lifetime `'b` here
+   |
+LL | fn fn_types<'b>(a: &'a isize,
+   |            ^^^^
+help: consider introducing a Higher-Ranked lifetime `'b` here
+   |
+LL |             b: Box<dyn for<'a, 'b> FnOnce(&'a isize,
+   |                              ^^^^
 
 error[E0261]: use of undeclared lifetime name `'b`
   --> $DIR/regions-name-undeclared.rs:45:36
    |
 LL | ...                   &'b isize)>,
    |                        ^^ undeclared lifetime
+   |
+   = note: for more information on Higher-Ranked lifetimes, visit https://doc.rust-lang.org/nomicon/hrtb.html
+help: consider introducing lifetime `'b` here
+   |
+LL | fn fn_types<'b>(a: &'a isize,
+   |            ^^^^
+help: consider introducing a Higher-Ranked lifetime `'b` here
+   |
+LL |             b: Box<dyn for<'a, 'b> FnOnce(&'a isize,
+   |                              ^^^^
 
 error[E0261]: use of undeclared lifetime name `'a`
   --> $DIR/regions-name-undeclared.rs:46:17