about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEsteban Küber <esteban@kuber.com.ar>2020-04-16 21:33:42 -0700
committerEsteban Küber <esteban@kuber.com.ar>2020-04-22 12:12:33 -0700
commit13c1daeb2f626b3923f7574226a089d95c8c7772 (patch)
treeb6bf9fcb96ecab3b859bd8c1f85cfd34775becf4
parent82e90d64266b8a4b53935d629786e69610b33f25 (diff)
downloadrust-13c1daeb2f626b3923f7574226a089d95c8c7772.tar.gz
rust-13c1daeb2f626b3923f7574226a089d95c8c7772.zip
Tweak `'static` suggestion code
Fix #71196.
-rw-r--r--src/librustc_ast_lowering/path.rs5
-rw-r--r--src/librustc_resolve/late/diagnostics.rs185
-rw-r--r--src/librustc_resolve/late/lifetimes.rs52
-rw-r--r--src/librustc_typeck/astconv.rs1
-rw-r--r--src/test/ui/associated-types/bound-lifetime-in-binding-only.elision.stderr6
-rw-r--r--src/test/ui/associated-types/bound-lifetime-in-return-only.elision.stderr6
-rw-r--r--src/test/ui/async-await/issues/issue-63388-2.stderr6
-rw-r--r--src/test/ui/c-variadic/variadic-ffi-6.stderr6
-rw-r--r--src/test/ui/foreign-fn-return-lifetime.stderr6
-rw-r--r--src/test/ui/generics/issue-65285-incorrect-explicit-lifetime-name-needed.stderr13
-rw-r--r--src/test/ui/issues/issue-13497.stderr6
-rw-r--r--src/test/ui/issues/issue-26638.stderr12
-rw-r--r--src/test/ui/lifetimes/lifetime-elision-return-type-requires-explicit-lifetime.stderr24
-rw-r--r--src/test/ui/suggestions/missing-lifetime-specifier.rs65
-rw-r--r--src/test/ui/suggestions/missing-lifetime-specifier.stderr256
-rw-r--r--src/test/ui/suggestions/return-without-lifetime.stderr19
-rw-r--r--src/test/ui/underscore-lifetime/underscore-lifetime-binders.stderr13
17 files changed, 532 insertions, 149 deletions
diff --git a/src/librustc_ast_lowering/path.rs b/src/librustc_ast_lowering/path.rs
index dde73475651..cf6dde81309 100644
--- a/src/librustc_ast_lowering/path.rs
+++ b/src/librustc_ast_lowering/path.rs
@@ -273,7 +273,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
             .next();
         if !generic_args.parenthesized && !has_lifetimes {
             generic_args.args = self
-                .elided_path_lifetimes(path_span, expected_lifetimes)
+                .elided_path_lifetimes(
+                    first_generic_span.map(|s| s.shrink_to_lo()).unwrap_or(segment.ident.span),
+                    expected_lifetimes,
+                )
                 .map(GenericArg::Lifetime)
                 .chain(generic_args.args.into_iter())
                 .collect();
diff --git a/src/librustc_resolve/late/diagnostics.rs b/src/librustc_resolve/late/diagnostics.rs
index e7fa88bff97..5e03d4e1140 100644
--- a/src/librustc_resolve/late/diagnostics.rs
+++ b/src/librustc_resolve/late/diagnostics.rs
@@ -1034,101 +1034,110 @@ impl<'tcx> LifetimeContext<'_, 'tcx> {
         lifetime_names: &FxHashSet<ast::Ident>,
         params: &[ElisionFailureInfo],
     ) {
-        if count > 1 {
-            err.span_label(span, format!("expected {} lifetime parameters", count));
-        } else {
-            let snippet = self.tcx.sess.source_map().span_to_snippet(span).ok();
-            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: &str| {
-                err.span_label(span, "expected named lifetime parameter");
+        err.span_label(
+            span,
+            &format!(
+                "expected {} lifetime parameter{}",
+                if count == 1 { "named".to_string() } else { count.to_string() },
+                pluralize!(count)
+            ),
+        );
 
-                for missing in self.missing_named_lifetime_spots.iter().rev() {
-                    let mut introduce_suggestion = vec![];
-                    let msg;
-                    let should_break;
-                    introduce_suggestion.push(match missing {
-                        MissingLifetimeSpot::Generics(generics) => {
-                            msg = "consider introducing a named lifetime parameter".to_string();
-                            should_break = true;
-                            if let Some(param) = generics.params.iter().find(|p| match p.kind {
-                                hir::GenericParamKind::Type {
-                                    synthetic: Some(hir::SyntheticTyParamKind::ImplTrait),
-                                    ..
-                                } => false,
-                                _ => true,
-                            }) {
-                                (param.span.shrink_to_lo(), "'a, ".to_string())
-                            } else {
-                                (generics.span, "<'a>".to_string())
-                            }
-                        }
-                        MissingLifetimeSpot::HigherRanked { span, span_type } => {
-                            msg = format!(
-                                "consider making the {} lifetime-generic with a new `'a` lifetime",
-                                span_type.descr(),
-                            );
-                            should_break = false;
-                            err.note(
-                                "for more information on higher-ranked polymorphism, visit \
-                             https://doc.rust-lang.org/nomicon/hrtb.html",
-                            );
-                            (*span, span_type.suggestion("'a"))
-                        }
-                    });
-                    for param in params {
-                        if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(param.span)
-                        {
-                            if snippet.starts_with('&') && !snippet.starts_with("&'") {
-                                introduce_suggestion
-                                    .push((param.span, format!("&'a {}", &snippet[1..])));
-                            } else if snippet.starts_with("&'_ ") {
-                                introduce_suggestion
-                                    .push((param.span, format!("&'a {}", &snippet[4..])));
-                            }
+        let snippet = self.tcx.sess.source_map().span_to_snippet(span).ok();
+        let suggest_existing = |err: &mut DiagnosticBuilder<'_>, sugg| {
+            err.span_suggestion_verbose(
+                span,
+                "consider using the named lifetime",
+                sugg,
+                Applicability::MaybeIncorrect,
+            );
+        };
+        let suggest_new = |err: &mut DiagnosticBuilder<'_>, sugg: &str| {
+            for missing in self.missing_named_lifetime_spots.iter().rev() {
+                let mut introduce_suggestion = vec![];
+                let msg;
+                let should_break;
+                introduce_suggestion.push(match missing {
+                    MissingLifetimeSpot::Generics(generics) => {
+                        msg = "consider introducing a named lifetime parameter".to_string();
+                        should_break = true;
+                        if let Some(param) = generics.params.iter().find(|p| match p.kind {
+                            hir::GenericParamKind::Type {
+                                synthetic: Some(hir::SyntheticTyParamKind::ImplTrait),
+                                ..
+                            } => false,
+                            _ => true,
+                        }) {
+                            (param.span.shrink_to_lo(), "'a, ".to_string())
+                        } else {
+                            (generics.span, "<'a>".to_string())
                         }
                     }
-                    introduce_suggestion.push((span, sugg.to_string()));
-                    err.multipart_suggestion(
-                        &msg,
-                        introduce_suggestion,
-                        Applicability::MaybeIncorrect,
-                    );
-                    if should_break {
-                        break;
+                    MissingLifetimeSpot::HigherRanked { span, span_type } => {
+                        msg = format!(
+                            "consider making the {} lifetime-generic with a new `'a` lifetime",
+                            span_type.descr(),
+                        );
+                        should_break = false;
+                        err.note(
+                            "for more information on higher-ranked polymorphism, visit \
+                            https://doc.rust-lang.org/nomicon/hrtb.html",
+                        );
+                        (*span, span_type.suggestion("'a"))
+                    }
+                });
+                for param in params {
+                    if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(param.span) {
+                        if snippet.starts_with('&') && !snippet.starts_with("&'") {
+                            introduce_suggestion
+                                .push((param.span, format!("&'a {}", &snippet[1..])));
+                        } else if snippet.starts_with("&'_ ") {
+                            introduce_suggestion
+                                .push((param.span, format!("&'a {}", &snippet[4..])));
+                        }
                     }
                 }
-            };
-
-            match (lifetime_names.len(), lifetime_names.iter().next(), snippet.as_deref()) {
-                (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, "&'a ");
-                }
-                (0, _, Some("'_")) => {
-                    suggest_new(err, "'a");
-                }
-                (0, _, Some(snippet)) if !snippet.ends_with('>') => {
-                    suggest_new(err, &format!("{}<'a>", snippet));
-                }
-                _ => {
-                    err.span_label(span, "expected lifetime parameter");
+                introduce_suggestion.push((span, sugg.to_string()));
+                err.multipart_suggestion(&msg, introduce_suggestion, Applicability::MaybeIncorrect);
+                if should_break {
+                    break;
                 }
             }
+        };
+
+        match (lifetime_names.len(), lifetime_names.iter().next(), snippet.as_deref()) {
+            (1, Some(name), Some("&")) => {
+                suggest_existing(err, format!("&{} ", name));
+            }
+            (1, Some(name), Some("'_")) => {
+                suggest_existing(err, name.to_string());
+            }
+            (1, Some(name), Some("")) => {
+                suggest_existing(err, format!("{}, ", name).repeat(count));
+            }
+            (1, Some(name), Some(snippet)) if !snippet.ends_with('>') => {
+                suggest_existing(
+                    err,
+                    format!(
+                        "{}<{}>",
+                        snippet,
+                        std::iter::repeat(name.to_string())
+                            .take(count)
+                            .collect::<Vec<_>>()
+                            .join(", ")
+                    ),
+                );
+            }
+            (0, _, Some("&")) if count == 1 => {
+                suggest_new(err, "&'a ");
+            }
+            (0, _, Some("'_")) if count == 1 => {
+                suggest_new(err, "'a");
+            }
+            (0, _, Some(snippet)) if !snippet.ends_with('>') && count == 1 => {
+                suggest_new(err, &format!("{}<'a>", snippet));
+            }
+            _ => {}
         }
     }
 }
diff --git a/src/librustc_resolve/late/lifetimes.rs b/src/librustc_resolve/late/lifetimes.rs
index defb2c06b32..41c798f9229 100644
--- a/src/librustc_resolve/late/lifetimes.rs
+++ b/src/librustc_resolve/late/lifetimes.rs
@@ -2388,51 +2388,26 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
         };
 
         let mut err = self.report_missing_lifetime_specifiers(span, lifetime_refs.len());
-        let mut add_label = true;
 
         if let Some(params) = error {
-            if lifetime_refs.len() == 1 {
-                add_label = add_label && self.report_elision_failure(&mut err, params, span);
+            if self.report_elision_failure(&mut err, params) && lifetime_names.is_empty() {
+                lifetime_names.insert(ast::Ident::from_str("'static"));
             }
         }
-        if add_label {
-            self.add_missing_lifetime_specifiers_label(
-                &mut err,
-                span,
-                lifetime_refs.len(),
-                &lifetime_names,
-                error.map(|p| &p[..]).unwrap_or(&[]),
-            );
-        }
-
+        self.add_missing_lifetime_specifiers_label(
+            &mut err,
+            span,
+            lifetime_refs.len(),
+            &lifetime_names,
+            error.map(|p| &p[..]).unwrap_or(&[]),
+        );
         err.emit();
     }
 
-    fn suggest_lifetime(&self, db: &mut DiagnosticBuilder<'_>, span: Span, msg: &str) -> bool {
-        match self.tcx.sess.source_map().span_to_snippet(span) {
-            Ok(ref snippet) => {
-                let (sugg, applicability) = if snippet == "&" {
-                    ("&'static ".to_owned(), Applicability::MachineApplicable)
-                } else if snippet == "'_" {
-                    ("'static".to_owned(), Applicability::MachineApplicable)
-                } else {
-                    (format!("{} + 'static", snippet), Applicability::MaybeIncorrect)
-                };
-                db.span_suggestion(span, msg, sugg, applicability);
-                false
-            }
-            Err(_) => {
-                db.help(msg);
-                true
-            }
-        }
-    }
-
     fn report_elision_failure(
         &mut self,
         db: &mut DiagnosticBuilder<'_>,
         params: &[ElisionFailureInfo],
-        span: Span,
     ) -> bool {
         let mut m = String::new();
         let len = params.len();
@@ -2482,29 +2457,28 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
                 "this function's return type contains a borrowed value, \
                  but there is no value for it to be borrowed from",
             );
-            self.suggest_lifetime(db, span, "consider giving it a 'static lifetime")
+            true
         } else if elided_len == 0 {
             db.help(
                 "this function's return type contains a borrowed value with \
                  an elided lifetime, but the lifetime cannot be derived from \
                  the arguments",
             );
-            let msg = "consider giving it an explicit bounded or 'static lifetime";
-            self.suggest_lifetime(db, span, msg)
+            true
         } else if elided_len == 1 {
             db.help(&format!(
                 "this function's return type contains a borrowed value, \
                  but the signature does not say which {} it is borrowed from",
                 m
             ));
-            true
+            false
         } else {
             db.help(&format!(
                 "this function's return type contains a borrowed value, \
                  but the signature does not say whether it is borrowed from {}",
                 m
             ));
-            true
+            false
         }
     }
 
diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs
index 19c8f540c57..51e5a0113c6 100644
--- a/src/librustc_typeck/astconv.rs
+++ b/src/librustc_typeck/astconv.rs
@@ -1729,6 +1729,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                     self.ast_region_to_region(lifetime, None)
                 } else {
                     self.re_infer(None, span).unwrap_or_else(|| {
+                        // FIXME: these can be redundant with E0106, but not always.
                         struct_span_err!(
                             tcx.sess,
                             span,
diff --git a/src/test/ui/associated-types/bound-lifetime-in-binding-only.elision.stderr b/src/test/ui/associated-types/bound-lifetime-in-binding-only.elision.stderr
index 2745e44ac0c..7608c4a68f1 100644
--- a/src/test/ui/associated-types/bound-lifetime-in-binding-only.elision.stderr
+++ b/src/test/ui/associated-types/bound-lifetime-in-binding-only.elision.stderr
@@ -2,9 +2,13 @@ error[E0106]: missing lifetime specifier
   --> $DIR/bound-lifetime-in-binding-only.rs:52:23
    |
 LL | fn elision<T: Fn() -> &i32>() {
-   |                       ^ help: consider giving it a 'static lifetime: `&'static`
+   |                       ^ expected named lifetime parameter
    |
    = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
+help: consider using the named lifetime
+   |
+LL | fn elision<T: Fn() -> &'static i32>() {
+   |                       ^^^^^^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/associated-types/bound-lifetime-in-return-only.elision.stderr b/src/test/ui/associated-types/bound-lifetime-in-return-only.elision.stderr
index 96f0cb85c8c..2e31a4f3abb 100644
--- a/src/test/ui/associated-types/bound-lifetime-in-return-only.elision.stderr
+++ b/src/test/ui/associated-types/bound-lifetime-in-return-only.elision.stderr
@@ -2,9 +2,13 @@ error[E0106]: missing lifetime specifier
   --> $DIR/bound-lifetime-in-return-only.rs:34:23
    |
 LL | fn elision(_: fn() -> &i32) {
-   |                       ^ help: consider giving it a 'static lifetime: `&'static`
+   |                       ^ expected named lifetime parameter
    |
    = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
+help: consider using the named lifetime
+   |
+LL | fn elision(_: fn() -> &'static i32) {
+   |                       ^^^^^^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/async-await/issues/issue-63388-2.stderr b/src/test/ui/async-await/issues/issue-63388-2.stderr
index 6edb9e63d48..74f760d6c92 100644
--- a/src/test/ui/async-await/issues/issue-63388-2.stderr
+++ b/src/test/ui/async-await/issues/issue-63388-2.stderr
@@ -4,9 +4,13 @@ error[E0106]: missing lifetime specifier
 LL |         foo: &dyn Foo, bar: &'a dyn Foo
    |              --------       -----------
 LL |     ) -> &dyn Foo
-   |          ^ help: consider using the named lifetime: `&'a`
+   |          ^ 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 `foo` or `bar`
+help: consider using the named lifetime
+   |
+LL |     ) -> &'a dyn Foo
+   |          ^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/c-variadic/variadic-ffi-6.stderr b/src/test/ui/c-variadic/variadic-ffi-6.stderr
index 882e7f89f2a..0d491faa9d1 100644
--- a/src/test/ui/c-variadic/variadic-ffi-6.stderr
+++ b/src/test/ui/c-variadic/variadic-ffi-6.stderr
@@ -2,9 +2,13 @@ error[E0106]: missing lifetime specifier
   --> $DIR/variadic-ffi-6.rs:7:6
    |
 LL | ) -> &usize {
-   |      ^ help: consider giving it an explicit bounded or 'static lifetime: `&'static`
+   |      ^ expected named lifetime parameter
    |
    = help: this function's return type contains a borrowed value with an elided lifetime, but the lifetime cannot be derived from the arguments
+help: consider using the named lifetime
+   |
+LL | ) -> &'static usize {
+   |      ^^^^^^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/foreign-fn-return-lifetime.stderr b/src/test/ui/foreign-fn-return-lifetime.stderr
index 575da18f240..14153f302da 100644
--- a/src/test/ui/foreign-fn-return-lifetime.stderr
+++ b/src/test/ui/foreign-fn-return-lifetime.stderr
@@ -2,9 +2,13 @@ error[E0106]: missing lifetime specifier
   --> $DIR/foreign-fn-return-lifetime.rs:5:19
    |
 LL |     pub fn f() -> &u8;
-   |                   ^ help: consider giving it a 'static lifetime: `&'static`
+   |                   ^ expected named lifetime parameter
    |
    = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
+help: consider using the named lifetime
+   |
+LL |     pub fn f() -> &'static u8;
+   |                   ^^^^^^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/generics/issue-65285-incorrect-explicit-lifetime-name-needed.stderr b/src/test/ui/generics/issue-65285-incorrect-explicit-lifetime-name-needed.stderr
index 9f410c0dbbb..3c5bdccd966 100644
--- a/src/test/ui/generics/issue-65285-incorrect-explicit-lifetime-name-needed.stderr
+++ b/src/test/ui/generics/issue-65285-incorrect-explicit-lifetime-name-needed.stderr
@@ -5,16 +5,21 @@ LL | fn should_error<T>() where T : Into<&u32> {}
    |                                     ^ explicit lifetime name needed here
 
 error[E0106]: missing lifetime specifier
-  --> $DIR/issue-65285-incorrect-explicit-lifetime-name-needed.rs:9:19
+  --> $DIR/issue-65285-incorrect-explicit-lifetime-name-needed.rs:9:21
    |
 LL |     fn foo<'b, L: X<&'b Nested<K>>>();
-   |                   ^^^^^^^^^^^^^^^^ expected lifetime parameter
+   |                     ^ expected named lifetime parameter
 
 error[E0106]: missing lifetime specifier
-  --> $DIR/issue-65285-incorrect-explicit-lifetime-name-needed.rs:13:15
+  --> $DIR/issue-65285-incorrect-explicit-lifetime-name-needed.rs:13:17
    |
 LL | fn bar<'b, L: X<&'b Nested<i32>>>(){}
-   |               ^^^^^^^^^^^^^^^^^^ expected lifetime parameter
+   |                 ^ expected named lifetime parameter
+   |
+help: consider using the named lifetime
+   |
+LL | fn bar<'b, L: X<'b, &'b Nested<i32>>>(){}
+   |                 ^^^
 
 error: aborting due to 3 previous errors
 
diff --git a/src/test/ui/issues/issue-13497.stderr b/src/test/ui/issues/issue-13497.stderr
index b72f0277052..8454c6be61d 100644
--- a/src/test/ui/issues/issue-13497.stderr
+++ b/src/test/ui/issues/issue-13497.stderr
@@ -2,9 +2,13 @@ error[E0106]: missing lifetime specifier
   --> $DIR/issue-13497.rs:2:5
    |
 LL |     &str
-   |     ^ help: consider giving it a 'static lifetime: `&'static`
+   |     ^ expected named lifetime parameter
    |
    = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
+help: consider using the named lifetime
+   |
+LL |     &'static str
+   |     ^^^^^^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/issues/issue-26638.stderr b/src/test/ui/issues/issue-26638.stderr
index 1d8fbdc63c5..3049ea772f2 100644
--- a/src/test/ui/issues/issue-26638.stderr
+++ b/src/test/ui/issues/issue-26638.stderr
@@ -14,17 +14,25 @@ error[E0106]: missing lifetime specifier
   --> $DIR/issue-26638.rs:4:40
    |
 LL | fn parse_type_2(iter: fn(&u8)->&u8) -> &str { iter() }
-   |                                        ^ help: consider giving it an explicit bounded or 'static lifetime: `&'static`
+   |                                        ^ expected named lifetime parameter
    |
    = help: this function's return type contains a borrowed value with an elided lifetime, but the lifetime cannot be derived from the arguments
+help: consider using the named lifetime
+   |
+LL | fn parse_type_2(iter: fn(&u8)->&u8) -> &'static str { iter() }
+   |                                        ^^^^^^^^
 
 error[E0106]: missing lifetime specifier
   --> $DIR/issue-26638.rs:7:22
    |
 LL | fn parse_type_3() -> &str { unimplemented!() }
-   |                      ^ help: consider giving it a 'static lifetime: `&'static`
+   |                      ^ expected named lifetime parameter
    |
    = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
+help: consider using the named lifetime
+   |
+LL | fn parse_type_3() -> &'static str { unimplemented!() }
+   |                      ^^^^^^^^
 
 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 461c1832e9a..dae1e390189 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
@@ -2,9 +2,13 @@ error[E0106]: missing lifetime specifier
   --> $DIR/lifetime-elision-return-type-requires-explicit-lifetime.rs:2:11
    |
 LL | fn f() -> &isize {
-   |           ^ help: consider giving it a 'static lifetime: `&'static`
+   |           ^ expected named lifetime parameter
    |
    = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
+help: consider using the named lifetime
+   |
+LL | fn f() -> &'static isize {
+   |           ^^^^^^^^
 
 error[E0106]: missing lifetime specifier
   --> $DIR/lifetime-elision-return-type-requires-explicit-lifetime.rs:7:33
@@ -34,25 +38,37 @@ error[E0106]: missing lifetime specifier
   --> $DIR/lifetime-elision-return-type-requires-explicit-lifetime.rs:21:20
    |
 LL | fn i(_x: isize) -> &isize {
-   |                    ^ help: consider giving it an explicit bounded or 'static lifetime: `&'static`
+   |                    ^ expected named lifetime parameter
    |
    = help: this function's return type contains a borrowed value with an elided lifetime, but the lifetime cannot be derived from the arguments
+help: consider using the named lifetime
+   |
+LL | fn i(_x: isize) -> &'static isize {
+   |                    ^^^^^^^^
 
 error[E0106]: missing lifetime specifier
   --> $DIR/lifetime-elision-return-type-requires-explicit-lifetime.rs:34:24
    |
 LL | fn j(_x: StaticStr) -> &isize {
-   |                        ^ help: consider giving it an explicit bounded or 'static lifetime: `&'static`
+   |                        ^ expected named lifetime parameter
    |
    = help: this function's return type contains a borrowed value with an elided lifetime, but the lifetime cannot be derived from the arguments
+help: consider using the named lifetime
+   |
+LL | fn j(_x: StaticStr) -> &'static isize {
+   |                        ^^^^^^^^
 
 error[E0106]: missing lifetime specifier
   --> $DIR/lifetime-elision-return-type-requires-explicit-lifetime.rs:40:49
    |
 LL | fn k<'a, T: WithLifetime<'a>>(_x: T::Output) -> &isize {
-   |                                                 ^ help: consider giving it an explicit bounded or 'static lifetime: `&'static`
+   |                                                 ^ expected named lifetime parameter
    |
    = help: this function's return type contains a borrowed value with an elided lifetime, but the lifetime cannot be derived from the arguments
+help: consider using the named lifetime
+   |
+LL | fn k<'a, T: WithLifetime<'a>>(_x: T::Output) -> &'a isize {
+   |                                                 ^^^
 
 error: aborting due to 6 previous errors
 
diff --git a/src/test/ui/suggestions/missing-lifetime-specifier.rs b/src/test/ui/suggestions/missing-lifetime-specifier.rs
new file mode 100644
index 00000000000..b09c1879d70
--- /dev/null
+++ b/src/test/ui/suggestions/missing-lifetime-specifier.rs
@@ -0,0 +1,65 @@
+#![allow(bare_trait_objects)]
+use std::collections::HashMap;
+use std::cell::RefCell;
+
+pub union Foo<'t, 'k> {
+    i: &'t i64,
+    f: &'k f64,
+}
+trait Bar<'t, 'k> {}
+
+pub union Qux<'t, 'k, I> {
+    i: &'t I,
+    f: &'k I,
+}
+trait Tar<'t, 'k, I> {}
+
+thread_local! {
+    static a: RefCell<HashMap<i32, Vec<Vec<Foo>>>> = RefCell::new(HashMap::new());
+    //~^ ERROR missing lifetime specifier
+    //~| ERROR missing lifetime specifier
+}
+thread_local! {
+    static b: RefCell<HashMap<i32, Vec<Vec<&Bar>>>> = RefCell::new(HashMap::new());
+    //~^ ERROR missing lifetime specifier
+    //~| ERROR missing lifetime specifier
+    //~| ERROR missing lifetime specifier
+    //~| ERROR missing lifetime specifier
+    //~| ERROR the lifetime bound for this object type cannot be deduced from context
+    //~| ERROR the lifetime bound for this object type cannot be deduced from context
+}
+thread_local! {
+    static c: RefCell<HashMap<i32, Vec<Vec<Qux<i32>>>>> = RefCell::new(HashMap::new());
+    //~^ ERROR missing lifetime specifier
+    //~| ERROR missing lifetime specifier
+}
+thread_local! {
+    static d: RefCell<HashMap<i32, Vec<Vec<&Tar<i32>>>>> = RefCell::new(HashMap::new());
+    //~^ ERROR missing lifetime specifier
+    //~| ERROR missing lifetime specifier
+    //~| ERROR missing lifetime specifier
+    //~| ERROR missing lifetime specifier
+    //~| ERROR the lifetime bound for this object type cannot be deduced from context
+    //~| ERROR the lifetime bound for this object type cannot be deduced from context
+}
+
+thread_local! {
+    static e: RefCell<HashMap<i32, Vec<Vec<Qux<'static, i32>>>>> = RefCell::new(HashMap::new());
+    //~^ ERROR wrong number of lifetime arguments: expected 2, found 1
+    //~| ERROR wrong number of lifetime arguments: expected 2, found 1
+    //~| ERROR wrong number of lifetime arguments: expected 2, found 1
+    //~| ERROR wrong number of lifetime arguments: expected 2, found 1
+}
+thread_local! {
+    static f: RefCell<HashMap<i32, Vec<Vec<&Tar<'static, i32>>>>> = RefCell::new(HashMap::new());
+    //~^ ERROR the lifetime bound for this object type cannot be deduced from context
+    //~| ERROR the lifetime bound for this object type cannot be deduced from context
+    //~| ERROR wrong number of lifetime arguments: expected 2, found 1
+    //~| ERROR wrong number of lifetime arguments: expected 2, found 1
+    //~| ERROR wrong number of lifetime arguments: expected 2, found 1
+    //~| ERROR wrong number of lifetime arguments: expected 2, found 1
+    //~| ERROR missing lifetime specifier
+    //~| ERROR missing lifetime specifier
+}
+
+fn main() {}
diff --git a/src/test/ui/suggestions/missing-lifetime-specifier.stderr b/src/test/ui/suggestions/missing-lifetime-specifier.stderr
new file mode 100644
index 00000000000..aeb1229d174
--- /dev/null
+++ b/src/test/ui/suggestions/missing-lifetime-specifier.stderr
@@ -0,0 +1,256 @@
+error[E0106]: missing lifetime specifiers
+  --> $DIR/missing-lifetime-specifier.rs:18:44
+   |
+LL |     static a: RefCell<HashMap<i32, Vec<Vec<Foo>>>> = RefCell::new(HashMap::new());
+   |                                            ^^^ expected 2 lifetime parameters
+   |
+   = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
+help: consider using the named lifetime
+   |
+LL |     static a: RefCell<HashMap<i32, Vec<Vec<Foo<'static, 'static>>>>> = RefCell::new(HashMap::new());
+   |                                            ^^^^^^^^^^^^^^^^^^^^^
+
+error[E0106]: missing lifetime specifiers
+  --> $DIR/missing-lifetime-specifier.rs:18:44
+   |
+LL |     static a: RefCell<HashMap<i32, Vec<Vec<Foo>>>> = RefCell::new(HashMap::new());
+   |                                            ^^^ expected 2 lifetime parameters
+   |
+   = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
+help: consider using the named lifetime
+   |
+LL |     static a: RefCell<HashMap<i32, Vec<Vec<Foo<'static, 'static>>>>> = RefCell::new(HashMap::new());
+   |                                            ^^^^^^^^^^^^^^^^^^^^^
+
+error[E0106]: missing lifetime specifier
+  --> $DIR/missing-lifetime-specifier.rs:23:44
+   |
+LL |     static b: RefCell<HashMap<i32, Vec<Vec<&Bar>>>> = RefCell::new(HashMap::new());
+   |                                            ^ expected named lifetime parameter
+   |
+   = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
+help: consider using the named lifetime
+   |
+LL |     static b: RefCell<HashMap<i32, Vec<Vec<&'static Bar>>>> = RefCell::new(HashMap::new());
+   |                                            ^^^^^^^^
+
+error[E0106]: missing lifetime specifiers
+  --> $DIR/missing-lifetime-specifier.rs:23:45
+   |
+LL |     static b: RefCell<HashMap<i32, Vec<Vec<&Bar>>>> = RefCell::new(HashMap::new());
+   |                                             ^^^ expected 2 lifetime parameters
+   |
+   = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
+help: consider using the named lifetime
+   |
+LL |     static b: RefCell<HashMap<i32, Vec<Vec<&Bar<'static, 'static>>>>> = RefCell::new(HashMap::new());
+   |                                             ^^^^^^^^^^^^^^^^^^^^^
+
+error[E0106]: missing lifetime specifier
+  --> $DIR/missing-lifetime-specifier.rs:23:44
+   |
+LL |     static b: RefCell<HashMap<i32, Vec<Vec<&Bar>>>> = RefCell::new(HashMap::new());
+   |                                            ^ expected named lifetime parameter
+   |
+   = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
+help: consider using the named lifetime
+   |
+LL |     static b: RefCell<HashMap<i32, Vec<Vec<&'static Bar>>>> = RefCell::new(HashMap::new());
+   |                                            ^^^^^^^^
+
+error[E0106]: missing lifetime specifiers
+  --> $DIR/missing-lifetime-specifier.rs:23:45
+   |
+LL |     static b: RefCell<HashMap<i32, Vec<Vec<&Bar>>>> = RefCell::new(HashMap::new());
+   |                                             ^^^ expected 2 lifetime parameters
+   |
+   = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
+help: consider using the named lifetime
+   |
+LL |     static b: RefCell<HashMap<i32, Vec<Vec<&Bar<'static, 'static>>>>> = RefCell::new(HashMap::new());
+   |                                             ^^^^^^^^^^^^^^^^^^^^^
+
+error[E0106]: missing lifetime specifiers
+  --> $DIR/missing-lifetime-specifier.rs:32:48
+   |
+LL |     static c: RefCell<HashMap<i32, Vec<Vec<Qux<i32>>>>> = RefCell::new(HashMap::new());
+   |                                                ^ expected 2 lifetime parameters
+   |
+   = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
+help: consider using the named lifetime
+   |
+LL |     static c: RefCell<HashMap<i32, Vec<Vec<Qux<'static, 'static, i32>>>>> = RefCell::new(HashMap::new());
+   |                                                ^^^^^^^^^^^^^^^^^
+
+error[E0106]: missing lifetime specifiers
+  --> $DIR/missing-lifetime-specifier.rs:32:48
+   |
+LL |     static c: RefCell<HashMap<i32, Vec<Vec<Qux<i32>>>>> = RefCell::new(HashMap::new());
+   |                                                ^ expected 2 lifetime parameters
+   |
+   = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
+help: consider using the named lifetime
+   |
+LL |     static c: RefCell<HashMap<i32, Vec<Vec<Qux<'static, 'static, i32>>>>> = RefCell::new(HashMap::new());
+   |                                                ^^^^^^^^^^^^^^^^^
+
+error[E0106]: missing lifetime specifier
+  --> $DIR/missing-lifetime-specifier.rs:37:44
+   |
+LL |     static d: RefCell<HashMap<i32, Vec<Vec<&Tar<i32>>>>> = RefCell::new(HashMap::new());
+   |                                            ^ expected named lifetime parameter
+   |
+   = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
+help: consider using the named lifetime
+   |
+LL |     static d: RefCell<HashMap<i32, Vec<Vec<&'static Tar<i32>>>>> = RefCell::new(HashMap::new());
+   |                                            ^^^^^^^^
+
+error[E0106]: missing lifetime specifiers
+  --> $DIR/missing-lifetime-specifier.rs:37:49
+   |
+LL |     static d: RefCell<HashMap<i32, Vec<Vec<&Tar<i32>>>>> = RefCell::new(HashMap::new());
+   |                                                 ^ expected 2 lifetime parameters
+   |
+   = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
+help: consider using the named lifetime
+   |
+LL |     static d: RefCell<HashMap<i32, Vec<Vec<&Tar<'static, 'static, i32>>>>> = RefCell::new(HashMap::new());
+   |                                                 ^^^^^^^^^^^^^^^^^
+
+error[E0106]: missing lifetime specifier
+  --> $DIR/missing-lifetime-specifier.rs:37:44
+   |
+LL |     static d: RefCell<HashMap<i32, Vec<Vec<&Tar<i32>>>>> = RefCell::new(HashMap::new());
+   |                                            ^ expected named lifetime parameter
+   |
+   = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
+help: consider using the named lifetime
+   |
+LL |     static d: RefCell<HashMap<i32, Vec<Vec<&'static Tar<i32>>>>> = RefCell::new(HashMap::new());
+   |                                            ^^^^^^^^
+
+error[E0106]: missing lifetime specifiers
+  --> $DIR/missing-lifetime-specifier.rs:37:49
+   |
+LL |     static d: RefCell<HashMap<i32, Vec<Vec<&Tar<i32>>>>> = RefCell::new(HashMap::new());
+   |                                                 ^ expected 2 lifetime parameters
+   |
+   = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
+help: consider using the named lifetime
+   |
+LL |     static d: RefCell<HashMap<i32, Vec<Vec<&Tar<'static, 'static, i32>>>>> = RefCell::new(HashMap::new());
+   |                                                 ^^^^^^^^^^^^^^^^^
+
+error[E0106]: missing lifetime specifier
+  --> $DIR/missing-lifetime-specifier.rs:54:44
+   |
+LL |     static f: RefCell<HashMap<i32, Vec<Vec<&Tar<'static, i32>>>>> = RefCell::new(HashMap::new());
+   |                                            ^ expected named lifetime parameter
+   |
+   = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
+help: consider using the named lifetime
+   |
+LL |     static f: RefCell<HashMap<i32, Vec<Vec<&'static Tar<'static, i32>>>>> = RefCell::new(HashMap::new());
+   |                                            ^^^^^^^^
+
+error[E0106]: missing lifetime specifier
+  --> $DIR/missing-lifetime-specifier.rs:54:44
+   |
+LL |     static f: RefCell<HashMap<i32, Vec<Vec<&Tar<'static, i32>>>>> = RefCell::new(HashMap::new());
+   |                                            ^ expected named lifetime parameter
+   |
+   = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
+help: consider using the named lifetime
+   |
+LL |     static f: RefCell<HashMap<i32, Vec<Vec<&'static Tar<'static, i32>>>>> = RefCell::new(HashMap::new());
+   |                                            ^^^^^^^^
+
+error[E0228]: the lifetime bound for this object type cannot be deduced from context; please supply an explicit bound
+  --> $DIR/missing-lifetime-specifier.rs:23:45
+   |
+LL |     static b: RefCell<HashMap<i32, Vec<Vec<&Bar>>>> = RefCell::new(HashMap::new());
+   |                                             ^^^
+
+error[E0228]: the lifetime bound for this object type cannot be deduced from context; please supply an explicit bound
+  --> $DIR/missing-lifetime-specifier.rs:23:45
+   |
+LL |     static b: RefCell<HashMap<i32, Vec<Vec<&Bar>>>> = RefCell::new(HashMap::new());
+   |                                             ^^^
+
+error[E0228]: the lifetime bound for this object type cannot be deduced from context; please supply an explicit bound
+  --> $DIR/missing-lifetime-specifier.rs:37:45
+   |
+LL |     static d: RefCell<HashMap<i32, Vec<Vec<&Tar<i32>>>>> = RefCell::new(HashMap::new());
+   |                                             ^^^^^^^^
+
+error[E0228]: the lifetime bound for this object type cannot be deduced from context; please supply an explicit bound
+  --> $DIR/missing-lifetime-specifier.rs:37:45
+   |
+LL |     static d: RefCell<HashMap<i32, Vec<Vec<&Tar<i32>>>>> = RefCell::new(HashMap::new());
+   |                                             ^^^^^^^^
+
+error[E0107]: wrong number of lifetime arguments: expected 2, found 1
+  --> $DIR/missing-lifetime-specifier.rs:47:44
+   |
+LL |     static e: RefCell<HashMap<i32, Vec<Vec<Qux<'static, i32>>>>> = RefCell::new(HashMap::new());
+   |                                            ^^^^^^^^^^^^^^^^^ expected 2 lifetime arguments
+
+error[E0107]: wrong number of lifetime arguments: expected 2, found 1
+  --> $DIR/missing-lifetime-specifier.rs:47:44
+   |
+LL |     static e: RefCell<HashMap<i32, Vec<Vec<Qux<'static, i32>>>>> = RefCell::new(HashMap::new());
+   |                                            ^^^^^^^^^^^^^^^^^ expected 2 lifetime arguments
+
+error[E0107]: wrong number of lifetime arguments: expected 2, found 1
+  --> $DIR/missing-lifetime-specifier.rs:47:44
+   |
+LL |     static e: RefCell<HashMap<i32, Vec<Vec<Qux<'static, i32>>>>> = RefCell::new(HashMap::new());
+   |                                            ^^^^^^^^^^^^^^^^^ expected 2 lifetime arguments
+
+error[E0107]: wrong number of lifetime arguments: expected 2, found 1
+  --> $DIR/missing-lifetime-specifier.rs:47:44
+   |
+LL |     static e: RefCell<HashMap<i32, Vec<Vec<Qux<'static, i32>>>>> = RefCell::new(HashMap::new());
+   |                                            ^^^^^^^^^^^^^^^^^ expected 2 lifetime arguments
+
+error[E0107]: wrong number of lifetime arguments: expected 2, found 1
+  --> $DIR/missing-lifetime-specifier.rs:54:45
+   |
+LL |     static f: RefCell<HashMap<i32, Vec<Vec<&Tar<'static, i32>>>>> = RefCell::new(HashMap::new());
+   |                                             ^^^ expected 2 lifetime arguments
+
+error[E0107]: wrong number of lifetime arguments: expected 2, found 1
+  --> $DIR/missing-lifetime-specifier.rs:54:45
+   |
+LL |     static f: RefCell<HashMap<i32, Vec<Vec<&Tar<'static, i32>>>>> = RefCell::new(HashMap::new());
+   |                                             ^^^ expected 2 lifetime arguments
+
+error[E0228]: the lifetime bound for this object type cannot be deduced from context; please supply an explicit bound
+  --> $DIR/missing-lifetime-specifier.rs:54:45
+   |
+LL |     static f: RefCell<HashMap<i32, Vec<Vec<&Tar<'static, i32>>>>> = RefCell::new(HashMap::new());
+   |                                             ^^^^^^^^^^^^^^^^^
+
+error[E0107]: wrong number of lifetime arguments: expected 2, found 1
+  --> $DIR/missing-lifetime-specifier.rs:54:45
+   |
+LL |     static f: RefCell<HashMap<i32, Vec<Vec<&Tar<'static, i32>>>>> = RefCell::new(HashMap::new());
+   |                                             ^^^ expected 2 lifetime arguments
+
+error[E0228]: the lifetime bound for this object type cannot be deduced from context; please supply an explicit bound
+  --> $DIR/missing-lifetime-specifier.rs:54:45
+   |
+LL |     static f: RefCell<HashMap<i32, Vec<Vec<&Tar<'static, i32>>>>> = RefCell::new(HashMap::new());
+   |                                             ^^^^^^^^^^^^^^^^^
+
+error[E0107]: wrong number of lifetime arguments: expected 2, found 1
+  --> $DIR/missing-lifetime-specifier.rs:54:45
+   |
+LL |     static f: RefCell<HashMap<i32, Vec<Vec<&Tar<'static, i32>>>>> = RefCell::new(HashMap::new());
+   |                                             ^^^ expected 2 lifetime arguments
+
+error: aborting due to 28 previous errors
+
+Some errors have detailed explanations: E0106, E0107.
+For more information about an error, try `rustc --explain E0106`.
diff --git a/src/test/ui/suggestions/return-without-lifetime.stderr b/src/test/ui/suggestions/return-without-lifetime.stderr
index ce3b1748da4..a93830edf3e 100644
--- a/src/test/ui/suggestions/return-without-lifetime.stderr
+++ b/src/test/ui/suggestions/return-without-lifetime.stderr
@@ -2,23 +2,36 @@ error[E0106]: missing lifetime specifier
   --> $DIR/return-without-lifetime.rs:2:16
    |
 LL | struct Foo<'a>(&usize);
-   |                ^ help: consider using the named lifetime: `&'a`
+   |                ^ expected named lifetime parameter
+   |
+help: consider using the named lifetime
+   |
+LL | struct Foo<'a>(&'a usize);
+   |                ^^^
 
 error[E0106]: missing lifetime specifier
   --> $DIR/return-without-lifetime.rs:5:34
    |
 LL | fn func1<'a>(_arg: &'a Thing) -> &() { unimplemented!() }
-   |                    ---------     ^ help: consider using the named lifetime: `&'a`
+   |                    ---------     ^ expected named lifetime parameter
    |
    = help: this function's return type contains a borrowed value, but the signature does not say which one of `_arg`'s 2 lifetimes it is borrowed from
+help: consider using the named lifetime
+   |
+LL | fn func1<'a>(_arg: &'a Thing) -> &'a () { unimplemented!() }
+   |                                  ^^^
 
 error[E0106]: missing lifetime specifier
   --> $DIR/return-without-lifetime.rs:7:35
    |
 LL | fn func2<'a>(_arg: &Thing<'a>) -> &() { unimplemented!() }
-   |                    ----------     ^ help: consider using the named lifetime: `&'a`
+   |                    ----------     ^ expected named lifetime parameter
    |
    = help: this function's return type contains a borrowed value, but the signature does not say which one of `_arg`'s 2 lifetimes it is borrowed from
+help: consider using the named lifetime
+   |
+LL | fn func2<'a>(_arg: &Thing<'a>) -> &'a () { unimplemented!() }
+   |                                   ^^^
 
 error: aborting due to 3 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 ada4551baef..ffccd100cb0 100644
--- a/src/test/ui/underscore-lifetime/underscore-lifetime-binders.stderr
+++ b/src/test/ui/underscore-lifetime/underscore-lifetime-binders.stderr
@@ -14,15 +14,24 @@ error[E0106]: missing lifetime specifier
   --> $DIR/underscore-lifetime-binders.rs:2:17
    |
 LL | struct Baz<'a>(&'_ &'a u8);
-   |                 ^^ help: consider using the named lifetime: `'a`
+   |                 ^^ expected named lifetime parameter
+   |
+help: consider using the named lifetime
+   |
+LL | struct Baz<'a>(&'a &'a u8);
+   |                 ^^
 
 error[E0106]: missing lifetime specifier
   --> $DIR/underscore-lifetime-binders.rs:10:33
    |
 LL | fn meh() -> Box<dyn for<'_> Meh<'_>>
-   |                                 ^^ help: consider giving it a 'static lifetime: `'static`
+   |                                 ^^ expected named lifetime parameter
    |
    = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
+help: consider using the named lifetime
+   |
+LL | fn meh() -> Box<dyn for<'_> Meh<'static>>
+   |                                 ^^^^^^^
 
 error[E0106]: missing lifetime specifier
   --> $DIR/underscore-lifetime-binders.rs:16:35