about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2020-01-21 06:28:33 +0000
committerbors <bors@rust-lang.org>2020-01-21 06:28:33 +0000
commitce361fb24f0896bf7d983549117cbe1f70f32dcf (patch)
tree35a67664aa7a5bb3fdeb8fa0ed0f173ebec6d733
parent8c73fa70f390efa88e6b0adc58d2bd72fcc51915 (diff)
parent03d7fed165a350c0b9acfbbaf76feae7014c97d1 (diff)
downloadrust-ce361fb24f0896bf7d983549117cbe1f70f32dcf.tar.gz
rust-ce361fb24f0896bf7d983549117cbe1f70f32dcf.zip
Auto merge of #68267 - estebank:lt-sugg, r=petrochenkov
Tweak lifetime definition errors

Taking inspiration from the narrative in @fasterthanlime's https://fasterthanli.me/blog/2019/declarative-memory-management/, add suggestions to some lifetime definition errors.
-rw-r--r--src/librustc_resolve/diagnostics.rs74
-rw-r--r--src/librustc_resolve/lib.rs1
-rw-r--r--src/librustc_resolve/lifetimes.rs79
-rw-r--r--src/test/ui/error-codes/E0106.rs2
-rw-r--r--src/test/ui/error-codes/E0106.stderr32
-rw-r--r--src/test/ui/error-codes/E0261.stderr6
-rw-r--r--src/test/ui/feature-gates/feature-gate-in_band_lifetimes.stderr118
-rw-r--r--src/test/ui/generic-associated-types/generic_associated_type_undeclared_lifetimes.stderr18
-rw-r--r--src/test/ui/impl-header-lifetime-elision/assoc-type.stderr14
-rw-r--r--src/test/ui/in-band-lifetimes/issue-61124-anon-lifetime-in-struct-declaration.stderr7
-rw-r--r--src/test/ui/in-band-lifetimes/no_in_band_in_struct.stderr4
-rw-r--r--src/test/ui/in-band-lifetimes/no_introducing_in_band_in_locals.stderr2
-rw-r--r--src/test/ui/issues/issue-19707.stderr12
-rw-r--r--src/test/ui/issues/issue-26638.stderr6
-rw-r--r--src/test/ui/issues/issue-30255.stderr18
-rw-r--r--src/test/ui/lifetimes/lifetime-elision-return-type-requires-explicit-lifetime.stderr12
-rw-r--r--src/test/ui/lifetimes/lifetime-errors/ex1b-return-no-names-if-else.stderr6
-rw-r--r--src/test/ui/methods/method-call-lifetime-args-unresolved.stderr2
-rw-r--r--src/test/ui/parser/trait-object-trait-parens.stderr3
-rw-r--r--src/test/ui/proc-macro/item-error.stderr8
-rw-r--r--src/test/ui/regions/regions-in-enums-anon.stderr8
-rw-r--r--src/test/ui/regions/regions-in-enums.stderr4
-rw-r--r--src/test/ui/regions/regions-in-structs-anon.stderr8
-rw-r--r--src/test/ui/regions/regions-in-structs.stderr5
-rw-r--r--src/test/ui/regions/regions-name-undeclared.stderr46
-rw-r--r--src/test/ui/regions/regions-undeclared.stderr11
-rw-r--r--src/test/ui/rfc1623.stderr4
-rw-r--r--src/test/ui/unboxed-closures/unboxed-closure-sugar-lifetime-elision.stderr11
-rw-r--r--src/test/ui/underscore-lifetime/dyn-trait-underscore-in-struct.stderr8
-rw-r--r--src/test/ui/underscore-lifetime/in-fn-return-illegal.stderr6
-rw-r--r--src/test/ui/underscore-lifetime/in-struct.stderr16
-rw-r--r--src/test/ui/underscore-lifetime/underscore-lifetime-binders.stderr8
-rw-r--r--src/test/ui/where-clauses/where-lifetime-resolution.stderr6
33 files changed, 485 insertions, 80 deletions
diff --git a/src/librustc_resolve/diagnostics.rs b/src/librustc_resolve/diagnostics.rs
index 3d4384cabe2..77dfe3d9f1d 100644
--- a/src/librustc_resolve/diagnostics.rs
+++ b/src/librustc_resolve/diagnostics.rs
@@ -5,8 +5,9 @@ use rustc::bug;
 use rustc::session::Session;
 use rustc::ty::{self, DefIdTree};
 use rustc_data_structures::fx::FxHashSet;
-use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder};
+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};
@@ -1445,3 +1446,74 @@ crate fn show_candidates(
         }
     }
 }
+
+crate fn report_missing_lifetime_specifiers(
+    sess: &Session,
+    span: Span,
+    count: usize,
+) -> DiagnosticBuilder<'_> {
+    struct_span_err!(sess, span, E0106, "missing lifetime specifier{}", pluralize!(count))
+}
+
+crate fn add_missing_lifetime_specifiers_label(
+    err: &mut DiagnosticBuilder<'_>,
+    span: Span,
+    count: usize,
+    lifetime_names: &FxHashSet<ast::Ident>,
+    snippet: Option<&str>,
+    missing_named_lifetime_spots: &[&hir::Generics<'_>],
+) {
+    if count > 1 {
+        err.span_label(span, format!("expected {} lifetime parameters", count));
+    } else {
+        let suggest_existing = |err: &mut DiagnosticBuilder<'_>, sugg| {
+            err.span_suggestion(
+                span,
+                "consider using the named lifetime",
+                sugg,
+                Applicability::MaybeIncorrect,
+            );
+        };
+        let suggest_new = |err: &mut DiagnosticBuilder<'_>, sugg| {
+            err.span_label(span, "expected named lifetime parameter");
+
+            if let Some(generics) = missing_named_lifetime_spots.iter().last() {
+                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()),
+                });
+                introduce_suggestion.push((span, sugg));
+                err.multipart_suggestion(
+                    "consider introducing a named lifetime parameter",
+                    introduce_suggestion,
+                    Applicability::MaybeIncorrect,
+                );
+            }
+        };
+
+        match (lifetime_names.len(), lifetime_names.iter().next(), snippet) {
+            (1, Some(name), Some("&")) => {
+                suggest_existing(err, format!("&{} ", name));
+            }
+            (1, Some(name), Some("'_")) => {
+                suggest_existing(err, name.to_string());
+            }
+            (1, Some(name), Some(snippet)) if !snippet.ends_with(">") => {
+                suggest_existing(err, format!("{}<{}>", snippet, name));
+            }
+            (0, _, Some("&")) => {
+                suggest_new(err, "&'lifetime ".to_string());
+            }
+            (0, _, Some("'_")) => {
+                suggest_new(err, "'lifetime".to_string());
+            }
+            (0, _, Some(snippet)) if !snippet.ends_with(">") => {
+                suggest_new(err, format!("{}<'lifetime>", snippet));
+            }
+            _ => {
+                err.span_label(span, "expected lifetime parameter");
+            }
+        }
+    }
+}
diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs
index 5d132191f92..0e6f40fa846 100644
--- a/src/librustc_resolve/lib.rs
+++ b/src/librustc_resolve/lib.rs
@@ -11,6 +11,7 @@
 #![feature(crate_visibility_modifier)]
 #![feature(label_break_value)]
 #![feature(nll)]
+#![cfg_attr(bootstrap, feature(slice_patterns))]
 #![recursion_limit = "256"]
 
 pub use rustc_hir::def::{Namespace, PerNS};
diff --git a/src/librustc_resolve/lifetimes.rs b/src/librustc_resolve/lifetimes.rs
index a2ca80cf5f3..6e9ed5fdc17 100644
--- a/src/librustc_resolve/lifetimes.rs
+++ b/src/librustc_resolve/lifetimes.rs
@@ -5,14 +5,16 @@
 //! used between functions, and they operate in a purely top-down
 //! way. Therefore, we break lifetime name resolution into a separate pass.
 
+use crate::diagnostics::{
+    add_missing_lifetime_specifiers_label, report_missing_lifetime_specifiers,
+};
 use rustc::hir::map::Map;
 use rustc::lint;
 use rustc::middle::resolve_lifetime::*;
-use rustc::session::Session;
 use rustc::ty::{self, DefIdTree, GenericParamDefKind, TyCtxt};
 use rustc::{bug, span_bug};
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
-use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder};
+use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder};
 use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId, LOCAL_CRATE};
@@ -181,6 +183,10 @@ struct LifetimeContext<'a, 'tcx> {
     xcrate_object_lifetime_defaults: DefIdMap<Vec<ObjectLifetimeDefault>>,
 
     lifetime_uses: &'a mut DefIdMap<LifetimeUseSet<'tcx>>,
+
+    /// When encountering an undefined named lifetime, we will suggest introducing it in these
+    /// places.
+    missing_named_lifetime_spots: Vec<&'tcx hir::Generics<'tcx>>,
 }
 
 #[derive(Debug)]
@@ -340,6 +346,7 @@ fn krate(tcx: TyCtxt<'_>) -> NamedRegionMap {
             labels_in_fn: vec![],
             xcrate_object_lifetime_defaults: Default::default(),
             lifetime_uses: &mut Default::default(),
+            missing_named_lifetime_spots: vec![],
         };
         for (_, item) in &krate.items {
             visitor.visit_item(item);
@@ -382,9 +389,11 @@ 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.visit_early_late(None, &sig.decl, generics, |this| {
                     intravisit::walk_item(this, item);
                 });
+                self.missing_named_lifetime_spots.pop();
             }
 
             hir::ItemKind::ExternCrate(_)
@@ -415,6 +424,8 @@ 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);
+
                 // Impls permit `'_` to be used and it is equivalent to "some fresh lifetime name".
                 // This is not true for other kinds of items.x
                 let track_lifetime_uses = match item.kind {
@@ -452,6 +463,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
                     this.check_lifetime_params(old_scope, &generics.params);
                     intravisit::walk_item(this, item);
                 });
+                self.missing_named_lifetime_spots.pop();
             }
         }
     }
@@ -684,6 +696,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);
         match trait_item.kind {
             Method(ref sig, _) => {
                 let tcx = self.tcx;
@@ -735,10 +748,12 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
                 intravisit::walk_trait_item(self, trait_item);
             }
         }
+        self.missing_named_lifetime_spots.pop();
     }
 
     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);
         match impl_item.kind {
             Method(ref sig, _) => {
                 let tcx = self.tcx;
@@ -822,6 +837,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
                 intravisit::walk_impl_item(self, impl_item);
             }
         }
+        self.missing_named_lifetime_spots.pop();
     }
 
     fn visit_lifetime(&mut self, lifetime_ref: &'tcx hir::Lifetime) {
@@ -1307,6 +1323,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
         let LifetimeContext { tcx, map, lifetime_uses, .. } = self;
         let labels_in_fn = take(&mut self.labels_in_fn);
         let xcrate_object_lifetime_defaults = take(&mut self.xcrate_object_lifetime_defaults);
+        let missing_named_lifetime_spots = take(&mut self.missing_named_lifetime_spots);
         let mut this = LifetimeContext {
             tcx: *tcx,
             map: map,
@@ -1315,7 +1332,8 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
             is_in_fn_syntax: self.is_in_fn_syntax,
             labels_in_fn,
             xcrate_object_lifetime_defaults,
-            lifetime_uses: lifetime_uses,
+            lifetime_uses,
+            missing_named_lifetime_spots,
         };
         debug!("entering scope {:?}", this.scope);
         f(self.scope, &mut this);
@@ -1323,6 +1341,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
         debug!("exiting scope {:?}", this.scope);
         self.labels_in_fn = this.labels_in_fn;
         self.xcrate_object_lifetime_defaults = this.xcrate_object_lifetime_defaults;
+        self.missing_named_lifetime_spots = this.missing_named_lifetime_spots;
     }
 
     /// helper method to determine the span to remove when suggesting the
@@ -1805,15 +1824,29 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
 
             self.insert_lifetime(lifetime_ref, def);
         } else {
-            struct_span_err!(
+            let mut err = struct_span_err!(
                 self.tcx.sess,
                 lifetime_ref.span,
                 E0261,
                 "use of undeclared lifetime name `{}`",
                 lifetime_ref
-            )
-            .span_label(lifetime_ref.span, "undeclared lifetime")
-            .emit();
+            );
+            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,
+                    );
+                }
+            }
+            err.emit();
         }
     }
 
@@ -2367,6 +2400,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
                 lifetime_refs.len(),
                 &lifetime_names,
                 self.tcx.sess.source_map().span_to_snippet(span).ok().as_ref().map(|s| s.as_str()),
+                &self.missing_named_lifetime_spots,
             );
         }
 
@@ -2862,34 +2896,3 @@ fn insert_late_bound_lifetimes(
         }
     }
 }
-
-fn report_missing_lifetime_specifiers(
-    sess: &Session,
-    span: Span,
-    count: usize,
-) -> DiagnosticBuilder<'_> {
-    struct_span_err!(sess, span, E0106, "missing lifetime specifier{}", pluralize!(count))
-}
-
-fn add_missing_lifetime_specifiers_label(
-    err: &mut DiagnosticBuilder<'_>,
-    span: Span,
-    count: usize,
-    lifetime_names: &FxHashSet<ast::Ident>,
-    snippet: Option<&str>,
-) {
-    if count > 1 {
-        err.span_label(span, format!("expected {} lifetime parameters", count));
-    } else if let (1, Some(name), Some("&")) =
-        (lifetime_names.len(), lifetime_names.iter().next(), snippet)
-    {
-        err.span_suggestion(
-            span,
-            "consider using the named lifetime",
-            format!("&{} ", name),
-            Applicability::MaybeIncorrect,
-        );
-    } else {
-        err.span_label(span, "expected lifetime parameter");
-    }
-}
diff --git a/src/test/ui/error-codes/E0106.rs b/src/test/ui/error-codes/E0106.rs
index d6537d12363..cc3438727a8 100644
--- a/src/test/ui/error-codes/E0106.rs
+++ b/src/test/ui/error-codes/E0106.rs
@@ -16,7 +16,7 @@ struct Buzz<'a, 'b>(&'a str, &'b str);
 struct Quux {
     baz: Baz,
     //~^ ERROR E0106
-    //~| expected lifetime parameter
+    //~| expected named lifetime parameter
     buzz: Buzz,
     //~^ ERROR E0106
     //~| expected 2 lifetime parameters
diff --git a/src/test/ui/error-codes/E0106.stderr b/src/test/ui/error-codes/E0106.stderr
index cea9581e701..e01e0a6f54b 100644
--- a/src/test/ui/error-codes/E0106.stderr
+++ b/src/test/ui/error-codes/E0106.stderr
@@ -2,25 +2,49 @@ error[E0106]: missing lifetime specifier
   --> $DIR/E0106.rs:2:8
    |
 LL |     x: &bool,
-   |        ^ expected lifetime parameter
+   |        ^ expected named lifetime parameter
+   |
+help: consider introducing a named lifetime parameter
+   |
+LL | struct Foo<'lifetime> {
+LL |     x: &'lifetime bool,
+   |
 
 error[E0106]: missing lifetime specifier
   --> $DIR/E0106.rs:7:7
    |
 LL |     B(&bool),
-   |       ^ expected lifetime parameter
+   |       ^ expected named lifetime parameter
+   |
+help: consider introducing a named lifetime parameter
+   |
+LL | enum Bar<'lifetime> {
+LL |     A(u8),
+LL |     B(&'lifetime bool),
+   |
 
 error[E0106]: missing lifetime specifier
   --> $DIR/E0106.rs:10:14
    |
 LL | type MyStr = &str;
-   |              ^ expected lifetime parameter
+   |              ^ expected named lifetime parameter
+   |
+help: consider introducing a named lifetime parameter
+   |
+LL | type MyStr<'lifetime> = &'lifetime str;
+   |           ^^^^^^^^^^^   ^^^^^^^^^^
 
 error[E0106]: missing lifetime specifier
   --> $DIR/E0106.rs:17:10
    |
 LL |     baz: Baz,
-   |          ^^^ expected lifetime parameter
+   |          ^^^ expected named lifetime parameter
+   |
+help: consider introducing a named lifetime parameter
+   |
+LL | struct Quux<'lifetime> {
+LL |     baz: Baz<'lifetime>,
+   |
 
 error[E0106]: missing lifetime specifiers
   --> $DIR/E0106.rs:20:11
diff --git a/src/test/ui/error-codes/E0261.stderr b/src/test/ui/error-codes/E0261.stderr
index 3bf5e9d8154..0eab2dc0ee0 100644
--- a/src/test/ui/error-codes/E0261.stderr
+++ b/src/test/ui/error-codes/E0261.stderr
@@ -2,11 +2,15 @@ error[E0261]: use of undeclared lifetime name `'a`
   --> $DIR/E0261.rs:1:12
    |
 LL | fn foo(x: &'a str) { }
-   |            ^^ undeclared lifetime
+   |       -    ^^ undeclared lifetime
+   |       |
+   |       help: consider introducing lifetime `'a` here: `<'a>`
 
 error[E0261]: use of undeclared lifetime name `'a`
   --> $DIR/E0261.rs:5:9
    |
+LL | struct Foo {
+   |           - help: consider introducing lifetime `'a` here: `<'a>`
 LL |     x: &'a str,
    |         ^^ undeclared lifetime
 
diff --git a/src/test/ui/feature-gates/feature-gate-in_band_lifetimes.stderr b/src/test/ui/feature-gates/feature-gate-in_band_lifetimes.stderr
index 5c64bf6539c..bbf3ea8a89f 100644
--- a/src/test/ui/feature-gates/feature-gate-in_band_lifetimes.stderr
+++ b/src/test/ui/feature-gates/feature-gate-in_band_lifetimes.stderr
@@ -2,103 +2,207 @@ error[E0261]: use of undeclared lifetime name `'x`
   --> $DIR/feature-gate-in_band_lifetimes.rs:3:12
    |
 LL | fn foo(x: &'x u8) -> &'x u8 { x }
-   |            ^^ undeclared lifetime
+   |       -    ^^ undeclared lifetime
+   |       |
+   |       help: consider introducing lifetime `'x` here: `<'x>`
 
 error[E0261]: use of undeclared lifetime name `'x`
   --> $DIR/feature-gate-in_band_lifetimes.rs:3:23
    |
 LL | fn foo(x: &'x u8) -> &'x u8 { x }
-   |                       ^^ undeclared lifetime
+   |       -               ^^ undeclared lifetime
+   |       |
+   |       help: consider introducing lifetime `'x` here: `<'x>`
 
 error[E0261]: use of undeclared lifetime name `'b`
   --> $DIR/feature-gate-in_band_lifetimes.rs:15:12
    |
 LL | impl<'a> X<'b> {
-   |            ^^ undeclared lifetime
+   |      -     ^^ undeclared lifetime
+   |      |
+   |      help: consider introducing lifetime `'b` here: `'b,`
 
 error[E0261]: use of undeclared lifetime name `'b`
   --> $DIR/feature-gate-in_band_lifetimes.rs:17:27
    |
 LL |     fn inner_2(&self) -> &'b u8 {
    |                           ^^ undeclared lifetime
+   |
+help: consider introducing lifetime `'b` here
+   |
+LL | impl<'b, 'a> X<'b> {
+   |      ^^^
+help: consider introducing lifetime `'b` here
+   |
+LL |     fn inner_2<'b>(&self) -> &'b u8 {
+   |               ^^^^
 
 error[E0261]: use of undeclared lifetime name `'b`
   --> $DIR/feature-gate-in_band_lifetimes.rs:23:8
    |
 LL | impl X<'b> {
-   |        ^^ undeclared lifetime
+   |     -  ^^ undeclared lifetime
+   |     |
+   |     help: consider introducing lifetime `'b` here: `<'b>`
 
 error[E0261]: use of undeclared lifetime name `'b`
   --> $DIR/feature-gate-in_band_lifetimes.rs:25:27
    |
 LL |     fn inner_3(&self) -> &'b u8 {
    |                           ^^ undeclared lifetime
+   |
+help: consider introducing lifetime `'b` here
+   |
+LL | impl<'b> X<'b> {
+   |     ^^^^
+help: consider introducing lifetime `'b` here
+   |
+LL |     fn inner_3<'b>(&self) -> &'b u8 {
+   |               ^^^^
 
 error[E0261]: use of undeclared lifetime name `'a`
   --> $DIR/feature-gate-in_band_lifetimes.rs:33:9
    |
 LL | impl Y<&'a u8> {
-   |         ^^ undeclared lifetime
+   |     -   ^^ undeclared lifetime
+   |     |
+   |     help: consider introducing lifetime `'a` here: `<'a>`
 
 error[E0261]: use of undeclared lifetime name `'a`
   --> $DIR/feature-gate-in_band_lifetimes.rs:35:25
    |
 LL |     fn inner(&self) -> &'a u8 {
    |                         ^^ undeclared lifetime
+   |
+help: consider introducing lifetime `'a` here
+   |
+LL | impl<'a> Y<&'a u8> {
+   |     ^^^^
+help: consider introducing lifetime `'a` here
+   |
+LL |     fn inner<'a>(&self) -> &'a u8 {
+   |             ^^^^
 
 error[E0261]: use of undeclared lifetime name `'b`
   --> $DIR/feature-gate-in_band_lifetimes.rs:43:27
    |
 LL |     fn any_lifetime() -> &'b u8;
    |                           ^^ undeclared lifetime
+   |
+help: consider introducing lifetime `'b` here
+   |
+LL | trait MyTrait<'b, 'a> {
+   |               ^^^
+help: consider introducing lifetime `'b` here
+   |
+LL |     fn any_lifetime<'b>() -> &'b u8;
+   |                    ^^^^
 
 error[E0261]: use of undeclared lifetime name `'b`
   --> $DIR/feature-gate-in_band_lifetimes.rs:45:27
    |
 LL |     fn borrowed_lifetime(&'b self) -> &'b u8;
    |                           ^^ undeclared lifetime
+   |
+help: consider introducing lifetime `'b` here
+   |
+LL | trait MyTrait<'b, 'a> {
+   |               ^^^
+help: consider introducing lifetime `'b` here
+   |
+LL |     fn borrowed_lifetime<'b>(&'b self) -> &'b u8;
+   |                         ^^^^
 
 error[E0261]: use of undeclared lifetime name `'b`
   --> $DIR/feature-gate-in_band_lifetimes.rs:45:40
    |
 LL |     fn borrowed_lifetime(&'b self) -> &'b u8;
    |                                        ^^ undeclared lifetime
+   |
+help: consider introducing lifetime `'b` here
+   |
+LL | trait MyTrait<'b, 'a> {
+   |               ^^^
+help: consider introducing lifetime `'b` here
+   |
+LL |     fn borrowed_lifetime<'b>(&'b self) -> &'b u8;
+   |                         ^^^^
 
 error[E0261]: use of undeclared lifetime name `'a`
   --> $DIR/feature-gate-in_band_lifetimes.rs:50:14
    |
 LL | impl MyTrait<'a> for Y<&'a u8> {
-   |              ^^ undeclared lifetime
+   |     -        ^^ undeclared lifetime
+   |     |
+   |     help: consider introducing lifetime `'a` here: `<'a>`
 
 error[E0261]: use of undeclared lifetime name `'a`
   --> $DIR/feature-gate-in_band_lifetimes.rs:50:25
    |
 LL | impl MyTrait<'a> for Y<&'a u8> {
-   |                         ^^ undeclared lifetime
+   |     -                   ^^ undeclared lifetime
+   |     |
+   |     help: consider introducing lifetime `'a` here: `<'a>`
 
 error[E0261]: use of undeclared lifetime name `'a`
   --> $DIR/feature-gate-in_band_lifetimes.rs:53:31
    |
 LL |     fn my_lifetime(&self) -> &'a u8 { self.0 }
    |                               ^^ undeclared lifetime
+   |
+help: consider introducing lifetime `'a` here
+   |
+LL | impl<'a> MyTrait<'a> for Y<&'a u8> {
+   |     ^^^^
+help: consider introducing lifetime `'a` here
+   |
+LL |     fn my_lifetime<'a>(&self) -> &'a u8 { self.0 }
+   |                   ^^^^
 
 error[E0261]: use of undeclared lifetime name `'b`
   --> $DIR/feature-gate-in_band_lifetimes.rs:55:27
    |
 LL |     fn any_lifetime() -> &'b u8 { &0 }
    |                           ^^ undeclared lifetime
+   |
+help: consider introducing lifetime `'b` here
+   |
+LL | impl<'b> MyTrait<'a> for Y<&'a u8> {
+   |     ^^^^
+help: consider introducing lifetime `'b` here
+   |
+LL |     fn any_lifetime<'b>() -> &'b u8 { &0 }
+   |                    ^^^^
 
 error[E0261]: use of undeclared lifetime name `'b`
   --> $DIR/feature-gate-in_band_lifetimes.rs:57:27
    |
 LL |     fn borrowed_lifetime(&'b self) -> &'b u8 { &*self.0 }
    |                           ^^ undeclared lifetime
+   |
+help: consider introducing lifetime `'b` here
+   |
+LL | impl<'b> MyTrait<'a> for Y<&'a u8> {
+   |     ^^^^
+help: consider introducing lifetime `'b` here
+   |
+LL |     fn borrowed_lifetime<'b>(&'b self) -> &'b u8 { &*self.0 }
+   |                         ^^^^
 
 error[E0261]: use of undeclared lifetime name `'b`
   --> $DIR/feature-gate-in_band_lifetimes.rs:57:40
    |
 LL |     fn borrowed_lifetime(&'b self) -> &'b u8 { &*self.0 }
    |                                        ^^ undeclared lifetime
+   |
+help: consider introducing lifetime `'b` here
+   |
+LL | impl<'b> MyTrait<'a> for Y<&'a u8> {
+   |     ^^^^
+help: consider introducing lifetime `'b` here
+   |
+LL |     fn borrowed_lifetime<'b>(&'b self) -> &'b u8 { &*self.0 }
+   |                         ^^^^
 
 error: aborting due to 17 previous errors
 
diff --git a/src/test/ui/generic-associated-types/generic_associated_type_undeclared_lifetimes.stderr b/src/test/ui/generic-associated-types/generic_associated_type_undeclared_lifetimes.stderr
index 81137e81dc4..fc2ce1cb866 100644
--- a/src/test/ui/generic-associated-types/generic_associated_type_undeclared_lifetimes.stderr
+++ b/src/test/ui/generic-associated-types/generic_associated_type_undeclared_lifetimes.stderr
@@ -3,12 +3,30 @@ error[E0261]: use of undeclared lifetime name `'b`
    |
 LL |         + Deref<Target = Self::Item<'b>>;
    |                                     ^^ undeclared lifetime
+   |
+help: consider introducing lifetime `'b` here
+   |
+LL | trait Iterable<'b> {
+   |               ^^^^
+help: consider introducing lifetime `'b` here
+   |
+LL |     type Iter<'b, 'a>: Iterator<Item = Self::Item<'a>>
+   |               ^^^
 
 error[E0261]: use of undeclared lifetime name `'undeclared`
   --> $DIR/generic_associated_type_undeclared_lifetimes.rs:12:41
    |
 LL |     fn iter<'a>(&'a self) -> Self::Iter<'undeclared>;
    |                                         ^^^^^^^^^^^ undeclared lifetime
+   |
+help: consider introducing lifetime `'undeclared` here
+   |
+LL | trait Iterable<'undeclared> {
+   |               ^^^^^^^^^^^^^
+help: consider introducing lifetime `'undeclared` here
+   |
+LL |     fn iter<'undeclared, 'a>(&'a self) -> Self::Iter<'undeclared>;
+   |             ^^^^^^^^^^^^
 
 error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/impl-header-lifetime-elision/assoc-type.stderr b/src/test/ui/impl-header-lifetime-elision/assoc-type.stderr
index 492ca872187..14c53f90665 100644
--- a/src/test/ui/impl-header-lifetime-elision/assoc-type.stderr
+++ b/src/test/ui/impl-header-lifetime-elision/assoc-type.stderr
@@ -2,13 +2,23 @@ error[E0106]: missing lifetime specifier
   --> $DIR/assoc-type.rs:11:19
    |
 LL |     type Output = &i32;
-   |                   ^ expected lifetime parameter
+   |                   ^ expected named lifetime parameter
+   |
+help: consider introducing a named lifetime parameter
+   |
+LL |     type Output<'lifetime> = &'lifetime i32;
+   |                ^^^^^^^^^^^   ^^^^^^^^^^
 
 error[E0106]: missing lifetime specifier
   --> $DIR/assoc-type.rs:16:20
    |
 LL |     type Output = &'_ i32;
-   |                    ^^ expected lifetime parameter
+   |                    ^^ expected named lifetime parameter
+   |
+help: consider introducing a named lifetime parameter
+   |
+LL |     type Output<'lifetime> = &'lifetime i32;
+   |                ^^^^^^^^^^^    ^^^^^^^^^
 
 error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/in-band-lifetimes/issue-61124-anon-lifetime-in-struct-declaration.stderr b/src/test/ui/in-band-lifetimes/issue-61124-anon-lifetime-in-struct-declaration.stderr
index 9579abb76b3..5f101a24c1d 100644
--- a/src/test/ui/in-band-lifetimes/issue-61124-anon-lifetime-in-struct-declaration.stderr
+++ b/src/test/ui/in-band-lifetimes/issue-61124-anon-lifetime-in-struct-declaration.stderr
@@ -2,7 +2,12 @@ error[E0106]: missing lifetime specifier
   --> $DIR/issue-61124-anon-lifetime-in-struct-declaration.rs:8:19
    |
 LL | struct Heartbreak(Betrayal);
-   |                   ^^^^^^^^ expected lifetime parameter
+   |                   ^^^^^^^^ expected named lifetime parameter
+   |
+help: consider introducing a named lifetime parameter
+   |
+LL | struct Heartbreak<'lifetime>(Betrayal<'lifetime>);
+   |                  ^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/in-band-lifetimes/no_in_band_in_struct.stderr b/src/test/ui/in-band-lifetimes/no_in_band_in_struct.stderr
index a270dd03926..fe656f7af7e 100644
--- a/src/test/ui/in-band-lifetimes/no_in_band_in_struct.stderr
+++ b/src/test/ui/in-band-lifetimes/no_in_band_in_struct.stderr
@@ -1,12 +1,16 @@
 error[E0261]: use of undeclared lifetime name `'test`
   --> $DIR/no_in_band_in_struct.rs:5:9
    |
+LL | struct Foo {
+   |           - help: consider introducing lifetime `'test` here: `<'test>`
 LL |     x: &'test u32,
    |         ^^^^^ undeclared lifetime
 
 error[E0261]: use of undeclared lifetime name `'test`
   --> $DIR/no_in_band_in_struct.rs:9:10
    |
+LL | enum Bar {
+   |         - help: consider introducing lifetime `'test` here: `<'test>`
 LL |     Baz(&'test u32),
    |          ^^^^^ undeclared lifetime
 
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 c307066be6b..bfb20ade035 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
@@ -1,6 +1,8 @@
 error[E0261]: use of undeclared lifetime name `'test`
   --> $DIR/no_introducing_in_band_in_locals.rs:5:13
    |
+LL | fn foo(x: &u32) {
+   |       - help: consider introducing lifetime `'test` here: `<'test>`
 LL |     let y: &'test u32 = x;
    |             ^^^^^ undeclared lifetime
 
diff --git a/src/test/ui/issues/issue-19707.stderr b/src/test/ui/issues/issue-19707.stderr
index c85ce0eb3a7..8a627bc0bd4 100644
--- a/src/test/ui/issues/issue-19707.stderr
+++ b/src/test/ui/issues/issue-19707.stderr
@@ -2,17 +2,25 @@ error[E0106]: missing lifetime specifier
   --> $DIR/issue-19707.rs:3:28
    |
 LL | type Foo = fn(&u8, &u8) -> &u8;
-   |                            ^ expected lifetime parameter
+   |                            ^ 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
+help: consider introducing a named lifetime parameter
+   |
+LL | type Foo<'lifetime> = fn(&u8, &u8) -> &'lifetime u8;
+   |         ^^^^^^^^^^^                   ^^^^^^^^^^
 
 error[E0106]: missing lifetime specifier
   --> $DIR/issue-19707.rs:5:27
    |
 LL | fn bar<F: Fn(&u8, &u8) -> &u8>(f: &F) {}
-   |                           ^ expected lifetime parameter
+   |                           ^ 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
+help: consider introducing a named lifetime parameter
+   |
+LL | fn bar<'lifetime, F: Fn(&u8, &u8) -> &'lifetime u8>(f: &F) {}
+   |        ^^^^^^^^^^                    ^^^^^^^^^^
 
 error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/issues/issue-26638.stderr b/src/test/ui/issues/issue-26638.stderr
index 6d7c1b0c43f..85d5d9cc42e 100644
--- a/src/test/ui/issues/issue-26638.stderr
+++ b/src/test/ui/issues/issue-26638.stderr
@@ -2,9 +2,13 @@ error[E0106]: missing lifetime specifier
   --> $DIR/issue-26638.rs:1:62
    |
 LL | fn parse_type(iter: Box<dyn Iterator<Item=&str>+'static>) -> &str { iter.next() }
-   |                                                              ^ expected lifetime parameter
+   |                                                              ^ expected named lifetime parameter
    |
    = help: this function's return type contains a borrowed value, but the signature does not say which one of `iter`'s 2 lifetimes it is borrowed from
+help: consider introducing a named lifetime parameter
+   |
+LL | fn parse_type<'lifetime>(iter: Box<dyn Iterator<Item=&str>+'static>) -> &'lifetime str { iter.next() }
+   |              ^^^^^^^^^^^                                                ^^^^^^^^^^
 
 error[E0106]: missing lifetime specifier
   --> $DIR/issue-26638.rs:4:40
diff --git a/src/test/ui/issues/issue-30255.stderr b/src/test/ui/issues/issue-30255.stderr
index c53129b7f29..c9402277640 100644
--- a/src/test/ui/issues/issue-30255.stderr
+++ b/src/test/ui/issues/issue-30255.stderr
@@ -2,25 +2,37 @@ error[E0106]: missing lifetime specifier
   --> $DIR/issue-30255.rs:9:24
    |
 LL | fn f(a: &S, b: i32) -> &i32 {
-   |                        ^ expected lifetime parameter
+   |                        ^ expected named lifetime parameter
    |
    = help: this function's return type contains a borrowed value, but the signature does not say which one of `a`'s 2 lifetimes it is borrowed from
+help: consider introducing a named lifetime parameter
+   |
+LL | fn f<'lifetime>(a: &S, b: i32) -> &'lifetime i32 {
+   |     ^^^^^^^^^^^                   ^^^^^^^^^^
 
 error[E0106]: missing lifetime specifier
   --> $DIR/issue-30255.rs:14:34
    |
 LL | fn g(a: &S, b: bool, c: &i32) -> &i32 {
-   |                                  ^ expected lifetime parameter
+   |                                  ^ 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 one of `a`'s 2 lifetimes or `c`
+help: consider introducing a named lifetime parameter
+   |
+LL | fn g<'lifetime>(a: &S, b: bool, c: &i32) -> &'lifetime i32 {
+   |     ^^^^^^^^^^^                             ^^^^^^^^^^
 
 error[E0106]: missing lifetime specifier
   --> $DIR/issue-30255.rs:19:44
    |
 LL | fn h(a: &bool, b: bool, c: &S, d: &i32) -> &i32 {
-   |                                            ^ expected lifetime parameter
+   |                                            ^ 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 `a`, one of `c`'s 2 lifetimes, or `d`
+help: consider introducing a named lifetime parameter
+   |
+LL | fn h<'lifetime>(a: &bool, b: bool, c: &S, d: &i32) -> &'lifetime i32 {
+   |     ^^^^^^^^^^^                                       ^^^^^^^^^^
 
 error: aborting due to 3 previous errors
 
diff --git a/src/test/ui/lifetimes/lifetime-elision-return-type-requires-explicit-lifetime.stderr b/src/test/ui/lifetimes/lifetime-elision-return-type-requires-explicit-lifetime.stderr
index 3f7c3934a0b..1d5eeac23f9 100644
--- a/src/test/ui/lifetimes/lifetime-elision-return-type-requires-explicit-lifetime.stderr
+++ b/src/test/ui/lifetimes/lifetime-elision-return-type-requires-explicit-lifetime.stderr
@@ -10,17 +10,25 @@ error[E0106]: missing lifetime specifier
   --> $DIR/lifetime-elision-return-type-requires-explicit-lifetime.rs:7:33
    |
 LL | fn g(_x: &isize, _y: &isize) -> &isize {
-   |                                 ^ expected lifetime parameter
+   |                                 ^ 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 `_x` or `_y`
+help: consider introducing a named lifetime parameter
+   |
+LL | fn g<'lifetime>(_x: &isize, _y: &isize) -> &'lifetime isize {
+   |     ^^^^^^^^^^^                            ^^^^^^^^^^
 
 error[E0106]: missing lifetime specifier
   --> $DIR/lifetime-elision-return-type-requires-explicit-lifetime.rs:17:19
    |
 LL | fn h(_x: &Foo) -> &isize {
-   |                   ^ expected lifetime parameter
+   |                   ^ expected named lifetime parameter
    |
    = help: this function's return type contains a borrowed value, but the signature does not say which one of `_x`'s 2 lifetimes it is borrowed from
+help: consider introducing a named lifetime parameter
+   |
+LL | fn h<'lifetime>(_x: &Foo) -> &'lifetime isize {
+   |     ^^^^^^^^^^^              ^^^^^^^^^^
 
 error[E0106]: missing lifetime specifier
   --> $DIR/lifetime-elision-return-type-requires-explicit-lifetime.rs:21:20
diff --git a/src/test/ui/lifetimes/lifetime-errors/ex1b-return-no-names-if-else.stderr b/src/test/ui/lifetimes/lifetime-errors/ex1b-return-no-names-if-else.stderr
index a4e0d71a3fa..2990ab86824 100644
--- a/src/test/ui/lifetimes/lifetime-errors/ex1b-return-no-names-if-else.stderr
+++ b/src/test/ui/lifetimes/lifetime-errors/ex1b-return-no-names-if-else.stderr
@@ -2,9 +2,13 @@ error[E0106]: missing lifetime specifier
   --> $DIR/ex1b-return-no-names-if-else.rs:1:29
    |
 LL | fn foo(x: &i32, y: &i32) -> &i32 {
-   |                             ^ expected lifetime parameter
+   |                             ^ 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 `x` or `y`
+help: consider introducing a named lifetime parameter
+   |
+LL | fn foo<'lifetime>(x: &i32, y: &i32) -> &'lifetime i32 {
+   |       ^^^^^^^^^^^                      ^^^^^^^^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/methods/method-call-lifetime-args-unresolved.stderr b/src/test/ui/methods/method-call-lifetime-args-unresolved.stderr
index 67fd8d7a13e..c9f235c4f7d 100644
--- a/src/test/ui/methods/method-call-lifetime-args-unresolved.stderr
+++ b/src/test/ui/methods/method-call-lifetime-args-unresolved.stderr
@@ -1,6 +1,8 @@
 error[E0261]: use of undeclared lifetime name `'a`
   --> $DIR/method-call-lifetime-args-unresolved.rs:2:15
    |
+LL | fn main() {
+   |        - help: consider introducing lifetime `'a` here: `<'a>`
 LL |     0.clone::<'a>();
    |               ^^ undeclared lifetime
 
diff --git a/src/test/ui/parser/trait-object-trait-parens.stderr b/src/test/ui/parser/trait-object-trait-parens.stderr
index 03fb764ee03..4b9f49423cb 100644
--- a/src/test/ui/parser/trait-object-trait-parens.stderr
+++ b/src/test/ui/parser/trait-object-trait-parens.stderr
@@ -33,6 +33,9 @@ LL |     let _: Box<(for<'a> Trait<'a>) + (Copy) + (?Sized)>;
 error[E0261]: use of undeclared lifetime name `'a`
   --> $DIR/trait-object-trait-parens.rs:11:31
    |
+LL | fn main() {
+   |        - help: consider introducing lifetime `'a` here: `<'a>`
+...
 LL |     let _: Box<(for<'a> Trait<'a>) + (Copy) + (?Sized)>;
    |                               ^^ undeclared lifetime
 
diff --git a/src/test/ui/proc-macro/item-error.stderr b/src/test/ui/proc-macro/item-error.stderr
index e801c26c43b..01eadbe252e 100644
--- a/src/test/ui/proc-macro/item-error.stderr
+++ b/src/test/ui/proc-macro/item-error.stderr
@@ -2,7 +2,13 @@ error[E0106]: missing lifetime specifier
   --> $DIR/item-error.rs:10:8
    |
 LL |     a: &u64
-   |        ^ expected lifetime parameter
+   |        ^ expected named lifetime parameter
+   |
+help: consider introducing a named lifetime parameter
+   |
+LL | struct A<'lifetime> {
+LL |     a: &'lifetime u64
+   |
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/regions/regions-in-enums-anon.stderr b/src/test/ui/regions/regions-in-enums-anon.stderr
index ae06e7653db..41655a210b3 100644
--- a/src/test/ui/regions/regions-in-enums-anon.stderr
+++ b/src/test/ui/regions/regions-in-enums-anon.stderr
@@ -2,7 +2,13 @@ error[E0106]: missing lifetime specifier
   --> $DIR/regions-in-enums-anon.rs:4:9
    |
 LL |     Bar(&isize)
-   |         ^ expected lifetime parameter
+   |         ^ expected named lifetime parameter
+   |
+help: consider introducing a named lifetime parameter
+   |
+LL | enum Foo<'lifetime> {
+LL |     Bar(&'lifetime isize)
+   |
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/regions/regions-in-enums.stderr b/src/test/ui/regions/regions-in-enums.stderr
index cfed9feba4b..66537653291 100644
--- a/src/test/ui/regions/regions-in-enums.stderr
+++ b/src/test/ui/regions/regions-in-enums.stderr
@@ -1,12 +1,16 @@
 error[E0261]: use of undeclared lifetime name `'foo`
   --> $DIR/regions-in-enums.rs:13:9
    |
+LL | enum No0 {
+   |         - help: consider introducing lifetime `'foo` here: `<'foo>`
 LL |     X5(&'foo usize)
    |         ^^^^ undeclared lifetime
 
 error[E0261]: use of undeclared lifetime name `'a`
   --> $DIR/regions-in-enums.rs:17:9
    |
+LL | enum No1 {
+   |         - help: consider introducing lifetime `'a` here: `<'a>`
 LL |     X6(&'a usize)
    |         ^^ undeclared lifetime
 
diff --git a/src/test/ui/regions/regions-in-structs-anon.stderr b/src/test/ui/regions/regions-in-structs-anon.stderr
index a1d4ebb597b..fbe8036880f 100644
--- a/src/test/ui/regions/regions-in-structs-anon.stderr
+++ b/src/test/ui/regions/regions-in-structs-anon.stderr
@@ -2,7 +2,13 @@ error[E0106]: missing lifetime specifier
   --> $DIR/regions-in-structs-anon.rs:4:8
    |
 LL |     x: &isize
-   |        ^ expected lifetime parameter
+   |        ^ expected named lifetime parameter
+   |
+help: consider introducing a named lifetime parameter
+   |
+LL | struct Foo<'lifetime> {
+LL |     x: &'lifetime isize
+   |
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/regions/regions-in-structs.stderr b/src/test/ui/regions/regions-in-structs.stderr
index 8314942759d..5dfdc2ee93b 100644
--- a/src/test/ui/regions/regions-in-structs.stderr
+++ b/src/test/ui/regions/regions-in-structs.stderr
@@ -1,12 +1,17 @@
 error[E0261]: use of undeclared lifetime name `'a`
   --> $DIR/regions-in-structs.rs:10:9
    |
+LL | struct StructDecl {
+   |                  - help: consider introducing lifetime `'a` here: `<'a>`
 LL |     a: &'a isize,
    |         ^^ undeclared lifetime
 
 error[E0261]: use of undeclared lifetime name `'a`
   --> $DIR/regions-in-structs.rs:11:9
    |
+LL | struct StructDecl {
+   |                  - help: consider introducing lifetime `'a` here: `<'a>`
+LL |     a: &'a isize,
 LL |     b: &'a isize,
    |         ^^ undeclared lifetime
 
diff --git a/src/test/ui/regions/regions-name-undeclared.stderr b/src/test/ui/regions/regions-name-undeclared.stderr
index 5f6a48a35f3..79ebef41dcc 100644
--- a/src/test/ui/regions/regions-name-undeclared.stderr
+++ b/src/test/ui/regions/regions-name-undeclared.stderr
@@ -3,34 +3,67 @@ error[E0261]: use of undeclared lifetime name `'b`
    |
 LL |     fn m4(&self, arg: &'b isize) { }
    |                        ^^ undeclared lifetime
+   |
+help: consider introducing lifetime `'b` here
+   |
+LL | impl<'b, 'a> Foo<'a> {
+   |      ^^^
+help: consider introducing lifetime `'b` here
+   |
+LL |     fn m4<'b>(&self, arg: &'b isize) { }
+   |          ^^^^
 
 error[E0261]: use of undeclared lifetime name `'b`
   --> $DIR/regions-name-undeclared.rs:16:12
    |
 LL |     fn m5(&'b self) { }
    |            ^^ undeclared lifetime
+   |
+help: consider introducing lifetime `'b` here
+   |
+LL | impl<'b, 'a> Foo<'a> {
+   |      ^^^
+help: consider introducing lifetime `'b` here
+   |
+LL |     fn m5<'b>(&'b self) { }
+   |          ^^^^
 
 error[E0261]: use of undeclared lifetime name `'b`
   --> $DIR/regions-name-undeclared.rs:17:27
    |
 LL |     fn m6(&self, arg: Foo<'b>) { }
    |                           ^^ undeclared lifetime
+   |
+help: consider introducing lifetime `'b` here
+   |
+LL | impl<'b, 'a> Foo<'a> {
+   |      ^^^
+help: consider introducing lifetime `'b` here
+   |
+LL |     fn m6<'b>(&self, arg: Foo<'b>) { }
+   |          ^^^^
 
 error[E0261]: use of undeclared lifetime name `'a`
   --> $DIR/regions-name-undeclared.rs:25:22
    |
 LL |     type X = Option<&'a isize>;
-   |                      ^^ undeclared lifetime
+   |           -          ^^ undeclared lifetime
+   |           |
+   |           help: consider introducing lifetime `'a` here: `<'a>`
 
 error[E0261]: use of undeclared lifetime name `'a`
   --> $DIR/regions-name-undeclared.rs:27:13
    |
+LL |     enum E {
+   |           - help: consider introducing lifetime `'a` here: `<'a>`
 LL |         E1(&'a isize)
    |             ^^ undeclared lifetime
 
 error[E0261]: use of undeclared lifetime name `'a`
   --> $DIR/regions-name-undeclared.rs:30:13
    |
+LL |     struct S {
+   |             - help: consider introducing lifetime `'a` here: `<'a>`
 LL |         f: &'a isize
    |             ^^ undeclared lifetime
 
@@ -38,13 +71,17 @@ error[E0261]: use of undeclared lifetime name `'a`
   --> $DIR/regions-name-undeclared.rs:32:14
    |
 LL |     fn f(a: &'a isize) { }
-   |              ^^ undeclared lifetime
+   |         -    ^^ undeclared lifetime
+   |         |
+   |         help: consider introducing lifetime `'a` here: `<'a>`
 
 error[E0261]: use of undeclared lifetime name `'a`
   --> $DIR/regions-name-undeclared.rs:40:17
    |
 LL | fn fn_types(a: &'a isize,
-   |                 ^^ undeclared lifetime
+   |            -    ^^ undeclared lifetime
+   |            |
+   |            help: consider introducing lifetime `'a` here: `<'a>`
 
 error[E0261]: use of undeclared lifetime name `'b`
   --> $DIR/regions-name-undeclared.rs:42:36
@@ -61,6 +98,9 @@ LL | ...                   &'b isize)>,
 error[E0261]: use of undeclared lifetime name `'a`
   --> $DIR/regions-name-undeclared.rs:46:17
    |
+LL | fn fn_types(a: &'a isize,
+   |            - help: consider introducing lifetime `'a` here: `<'a>`
+...
 LL |             c: &'a isize)
    |                 ^^ undeclared lifetime
 
diff --git a/src/test/ui/regions/regions-undeclared.stderr b/src/test/ui/regions/regions-undeclared.stderr
index 495aec3fde5..6bfde5524ac 100644
--- a/src/test/ui/regions/regions-undeclared.stderr
+++ b/src/test/ui/regions/regions-undeclared.stderr
@@ -7,12 +7,17 @@ LL | static c_x: &'blk isize = &22;
 error[E0261]: use of undeclared lifetime name `'a`
   --> $DIR/regions-undeclared.rs:4:10
    |
+LL | enum EnumDecl {
+   |              - help: consider introducing lifetime `'a` here: `<'a>`
 LL |     Foo(&'a isize),
    |          ^^ undeclared lifetime
 
 error[E0261]: use of undeclared lifetime name `'a`
   --> $DIR/regions-undeclared.rs:5:10
    |
+LL | enum EnumDecl {
+   |              - help: consider introducing lifetime `'a` here: `<'a>`
+LL |     Foo(&'a isize),
 LL |     Bar(&'a isize),
    |          ^^ undeclared lifetime
 
@@ -20,11 +25,15 @@ error[E0261]: use of undeclared lifetime name `'a`
   --> $DIR/regions-undeclared.rs:8:15
    |
 LL | fn fnDecl(x: &'a isize,
-   |               ^^ undeclared lifetime
+   |          -    ^^ undeclared lifetime
+   |          |
+   |          help: consider introducing lifetime `'a` here: `<'a>`
 
 error[E0261]: use of undeclared lifetime name `'a`
   --> $DIR/regions-undeclared.rs:9:15
    |
+LL | fn fnDecl(x: &'a isize,
+   |          - help: consider introducing lifetime `'a` here: `<'a>`
 LL |           y: &'a isize)
    |               ^^ undeclared lifetime
 
diff --git a/src/test/ui/rfc1623.stderr b/src/test/ui/rfc1623.stderr
index 171c00ba7b8..5b665e18141 100644
--- a/src/test/ui/rfc1623.stderr
+++ b/src/test/ui/rfc1623.stderr
@@ -2,7 +2,7 @@ error[E0106]: missing lifetime specifier
   --> $DIR/rfc1623.rs:8:42
    |
 LL | static NON_ELIDABLE_FN: &fn(&u8, &u8) -> &u8 =
-   |                                          ^ expected lifetime parameter
+   |                                          ^ 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
 
@@ -10,7 +10,7 @@ error[E0106]: missing lifetime specifier
   --> $DIR/rfc1623.rs:10:39
    |
 LL |     &(non_elidable as fn(&u8, &u8) -> &u8);
-   |                                       ^ expected lifetime parameter
+   |                                       ^ 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
 
diff --git a/src/test/ui/unboxed-closures/unboxed-closure-sugar-lifetime-elision.stderr b/src/test/ui/unboxed-closures/unboxed-closure-sugar-lifetime-elision.stderr
index 9fb9a07166f..0a028e44919 100644
--- a/src/test/ui/unboxed-closures/unboxed-closure-sugar-lifetime-elision.stderr
+++ b/src/test/ui/unboxed-closures/unboxed-closure-sugar-lifetime-elision.stderr
@@ -2,9 +2,18 @@ error[E0106]: missing lifetime specifier
   --> $DIR/unboxed-closure-sugar-lifetime-elision.rs:26:39
    |
 LL |     let _: dyn Foo(&isize, &usize) -> &usize;
-   |                                       ^ expected lifetime parameter
+   |                                       ^ 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
+help: consider introducing a named lifetime parameter
+   |
+LL | fn main<'lifetime>() {
+LL |     eq::< dyn for<'a> Foo<(&'a isize,), Output=&'a isize>,
+LL |           dyn Foo(&isize) -> &isize                                   >();
+LL |     eq::< dyn for<'a> Foo<(&'a isize,), Output=(&'a isize, &'a isize)>,
+LL |           dyn Foo(&isize) -> (&isize, &isize)                           >();
+LL | 
+ ...
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/underscore-lifetime/dyn-trait-underscore-in-struct.stderr b/src/test/ui/underscore-lifetime/dyn-trait-underscore-in-struct.stderr
index b20c23ade2b..04df2e45703 100644
--- a/src/test/ui/underscore-lifetime/dyn-trait-underscore-in-struct.stderr
+++ b/src/test/ui/underscore-lifetime/dyn-trait-underscore-in-struct.stderr
@@ -2,7 +2,13 @@ error[E0106]: missing lifetime specifier
   --> $DIR/dyn-trait-underscore-in-struct.rs:9:24
    |
 LL |     x: Box<dyn Debug + '_>,
-   |                        ^^ expected lifetime parameter
+   |                        ^^ expected named lifetime parameter
+   |
+help: consider introducing a named lifetime parameter
+   |
+LL | struct Foo<'lifetime> {
+LL |     x: Box<dyn Debug + 'lifetime>,
+   |
 
 error[E0228]: the lifetime bound for this object type cannot be deduced from context; please supply an explicit bound
   --> $DIR/dyn-trait-underscore-in-struct.rs:9:12
diff --git a/src/test/ui/underscore-lifetime/in-fn-return-illegal.stderr b/src/test/ui/underscore-lifetime/in-fn-return-illegal.stderr
index ed61bdfddda..cf820249c80 100644
--- a/src/test/ui/underscore-lifetime/in-fn-return-illegal.stderr
+++ b/src/test/ui/underscore-lifetime/in-fn-return-illegal.stderr
@@ -2,9 +2,13 @@ error[E0106]: missing lifetime specifier
   --> $DIR/in-fn-return-illegal.rs:5:30
    |
 LL | fn foo(x: &u32, y: &u32) -> &'_ u32 { loop { } }
-   |                              ^^ expected lifetime parameter
+   |                              ^^ 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 `x` or `y`
+help: consider introducing a named lifetime parameter
+   |
+LL | fn foo<'lifetime>(x: &u32, y: &u32) -> &'lifetime u32 { loop { } }
+   |       ^^^^^^^^^^^                       ^^^^^^^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/underscore-lifetime/in-struct.stderr b/src/test/ui/underscore-lifetime/in-struct.stderr
index 6bbdc71195a..e01b39a4b64 100644
--- a/src/test/ui/underscore-lifetime/in-struct.stderr
+++ b/src/test/ui/underscore-lifetime/in-struct.stderr
@@ -2,13 +2,25 @@ error[E0106]: missing lifetime specifier
   --> $DIR/in-struct.rs:6:9
    |
 LL |     x: &'_ u32,
-   |         ^^ expected lifetime parameter
+   |         ^^ expected named lifetime parameter
+   |
+help: consider introducing a named lifetime parameter
+   |
+LL | struct Foo<'lifetime> {
+LL |     x: &'lifetime u32,
+   |
 
 error[E0106]: missing lifetime specifier
   --> $DIR/in-struct.rs:10:14
    |
 LL |     Variant(&'_ u32),
-   |              ^^ expected lifetime parameter
+   |              ^^ expected named lifetime parameter
+   |
+help: consider introducing a named lifetime parameter
+   |
+LL | enum Bar<'lifetime> {
+LL |     Variant(&'lifetime u32),
+   |
 
 error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/underscore-lifetime/underscore-lifetime-binders.stderr b/src/test/ui/underscore-lifetime/underscore-lifetime-binders.stderr
index ef9e7e39df0..517904ee628 100644
--- a/src/test/ui/underscore-lifetime/underscore-lifetime-binders.stderr
+++ b/src/test/ui/underscore-lifetime/underscore-lifetime-binders.stderr
@@ -14,7 +14,7 @@ error[E0106]: missing lifetime specifier
   --> $DIR/underscore-lifetime-binders.rs:2:17
    |
 LL | struct Baz<'a>(&'_ &'a u8);
-   |                 ^^ expected lifetime parameter
+   |                 ^^ help: consider using the named lifetime: `'a`
 
 error[E0106]: missing lifetime specifier
   --> $DIR/underscore-lifetime-binders.rs:10:33
@@ -28,9 +28,13 @@ error[E0106]: missing lifetime specifier
   --> $DIR/underscore-lifetime-binders.rs:16:35
    |
 LL | fn foo2(_: &'_ u8, y: &'_ u8) -> &'_ u8 { y }
-   |                                   ^^ expected lifetime parameter
+   |                                   ^^ 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 `y`
+help: consider introducing a named lifetime parameter
+   |
+LL | fn foo2<'lifetime>(_: &'_ u8, y: &'_ u8) -> &'lifetime u8 { y }
+   |        ^^^^^^^^^^^                           ^^^^^^^^^
 
 error: aborting due to 5 previous errors
 
diff --git a/src/test/ui/where-clauses/where-lifetime-resolution.stderr b/src/test/ui/where-clauses/where-lifetime-resolution.stderr
index 0081ae07163..49799a93017 100644
--- a/src/test/ui/where-clauses/where-lifetime-resolution.stderr
+++ b/src/test/ui/where-clauses/where-lifetime-resolution.stderr
@@ -1,6 +1,9 @@
 error[E0261]: use of undeclared lifetime name `'a`
   --> $DIR/where-lifetime-resolution.rs:6:38
    |
+LL | fn f() where
+   |     - help: consider introducing lifetime `'a` here: `<'a>`
+LL |     for<'a> dyn Trait1<'a>: Trait1<'a>, // OK
 LL |     (dyn for<'a> Trait1<'a>): Trait1<'a>,
    |                                      ^^ undeclared lifetime
 
@@ -13,6 +16,9 @@ LL |     for<'a> dyn for<'b> Trait2<'a, 'b>: Trait2<'a, 'b>,
 error[E0261]: use of undeclared lifetime name `'b`
   --> $DIR/where-lifetime-resolution.rs:8:52
    |
+LL | fn f() where
+   |     - help: consider introducing lifetime `'b` here: `<'b>`
+...
 LL |     for<'a> dyn for<'b> Trait2<'a, 'b>: Trait2<'a, 'b>,
    |                                                    ^^ undeclared lifetime