about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEsteban Küber <esteban@kuber.com.ar>2020-08-11 13:02:14 -0700
committerEsteban Küber <esteban@kuber.com.ar>2020-08-11 13:02:14 -0700
commit6a3deb0ae04c4cb6400b30fecd1cbbee0506348b (patch)
treef8fbe36545beda6580ad94cc5f0742cfb7d95965
parentbecd479482935a63633861595e276bae6533b25b (diff)
downloadrust-6a3deb0ae04c4cb6400b30fecd1cbbee0506348b.tar.gz
rust-6a3deb0ae04c4cb6400b30fecd1cbbee0506348b.zip
Suggest using `'static` in assoc consts and suggest when multiple lts are needed
-rw-r--r--src/librustc_resolve/late/diagnostics.rs54
-rw-r--r--src/librustc_resolve/late/lifetimes.rs12
-rw-r--r--src/test/ui/error-codes/E0106.stderr9
-rw-r--r--src/test/ui/suggestions/missing-lifetime-in-assoc-const-type.rs13
-rw-r--r--src/test/ui/suggestions/missing-lifetime-in-assoc-const-type.stderr68
5 files changed, 144 insertions, 12 deletions
diff --git a/src/librustc_resolve/late/diagnostics.rs b/src/librustc_resolve/late/diagnostics.rs
index 9dbde5e7852..6e7a004f853 100644
--- a/src/librustc_resolve/late/diagnostics.rs
+++ b/src/librustc_resolve/late/diagnostics.rs
@@ -33,6 +33,7 @@ enum AssocSuggestion {
 crate enum MissingLifetimeSpot<'tcx> {
     Generics(&'tcx hir::Generics<'tcx>),
     HigherRanked { span: Span, span_type: ForLifetimeSpanType },
+    Static,
 }
 
 crate enum ForLifetimeSpanType {
@@ -1186,6 +1187,7 @@ impl<'tcx> LifetimeContext<'_, 'tcx> {
                             https://doc.rust-lang.org/nomicon/hrtb.html",
                     );
                 }
+                _ => {}
             }
         }
         if nightly_options::is_nightly_build()
@@ -1358,6 +1360,42 @@ impl<'tcx> LifetimeContext<'_, 'tcx> {
                         );
                         (*span, span_type.suggestion("'a"))
                     }
+                    MissingLifetimeSpot::Static => {
+                        let (span, sugg) = match snippet.as_deref() {
+                            Some("&") => (span.shrink_to_hi(), "'static ".to_owned()),
+                            Some("'_") => (span, "'static".to_owned()),
+                            Some(snippet) if !snippet.ends_with('>') => {
+                                if snippet == "" {
+                                    (
+                                        span,
+                                        std::iter::repeat("'static")
+                                            .take(count)
+                                            .collect::<Vec<_>>()
+                                            .join(", "),
+                                    )
+                                } else {
+                                    (
+                                        span.shrink_to_hi(),
+                                        format!(
+                                            "<{}>",
+                                            std::iter::repeat("'static")
+                                                .take(count)
+                                                .collect::<Vec<_>>()
+                                                .join(", ")
+                                        ),
+                                    )
+                                }
+                            }
+                            _ => continue,
+                        };
+                        err.span_suggestion_verbose(
+                            span,
+                            "consider using the `'static` lifetime",
+                            sugg.to_string(),
+                            Applicability::MaybeIncorrect,
+                        );
+                        continue;
+                    }
                 });
                 for param in params {
                     if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(param.span) {
@@ -1408,13 +1446,23 @@ impl<'tcx> LifetimeContext<'_, 'tcx> {
             ([], Some("'_")) if count == 1 => {
                 suggest_new(err, "'a");
             }
-            ([], Some(snippet)) if !snippet.ends_with('>') && count == 1 => {
+            ([], Some(snippet)) if !snippet.ends_with('>') => {
                 if snippet == "" {
                     // This happens when we have `type Bar<'a> = Foo<T>` where we point at the space
                     // before `T`. We will suggest `type Bar<'a> = Foo<'a, T>`.
-                    suggest_new(err, "'a, ");
+                    suggest_new(
+                        err,
+                        &std::iter::repeat("'a, ").take(count).collect::<Vec<_>>().join(""),
+                    );
                 } else {
-                    suggest_new(err, &format!("{}<'a>", snippet));
+                    suggest_new(
+                        err,
+                        &format!(
+                            "{}<{}>",
+                            snippet,
+                            std::iter::repeat("'a").take(count).collect::<Vec<_>>().join(", ")
+                        ),
+                    );
                 }
             }
             (lts, ..) if lts.len() > 1 => {
diff --git a/src/librustc_resolve/late/lifetimes.rs b/src/librustc_resolve/late/lifetimes.rs
index e5accc7fde9..2046419d984 100644
--- a/src/librustc_resolve/late/lifetimes.rs
+++ b/src/librustc_resolve/late/lifetimes.rs
@@ -764,26 +764,30 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
             Const(_, _) => {
                 // Only methods and types support generics.
                 assert!(trait_item.generics.params.is_empty());
+                self.missing_named_lifetime_spots.push(MissingLifetimeSpot::Static);
                 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).into());
         match impl_item.kind {
             Fn(ref sig, _) => {
+                self.missing_named_lifetime_spots.push((&impl_item.generics).into());
                 let tcx = self.tcx;
                 self.visit_early_late(
                     Some(tcx.hir().get_parent_item(impl_item.hir_id)),
                     &sig.decl,
                     &impl_item.generics,
                     |this| intravisit::walk_impl_item(this, impl_item),
-                )
+                );
+                self.missing_named_lifetime_spots.pop();
             }
             TyAlias(ref ty) => {
                 let generics = &impl_item.generics;
+                self.missing_named_lifetime_spots.push(generics.into());
                 let mut index = self.next_early_index();
                 let mut non_lifetime_count = 0;
                 debug!("visit_ty: index = {}", index);
@@ -812,14 +816,16 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
                     this.visit_generics(generics);
                     this.visit_ty(ty);
                 });
+                self.missing_named_lifetime_spots.pop();
             }
             Const(_, _) => {
                 // Only methods and types support generics.
                 assert!(impl_item.generics.params.is_empty());
+                self.missing_named_lifetime_spots.push(MissingLifetimeSpot::Static);
                 intravisit::walk_impl_item(self, impl_item);
+                self.missing_named_lifetime_spots.pop();
             }
         }
-        self.missing_named_lifetime_spots.pop();
     }
 
     fn visit_lifetime(&mut self, lifetime_ref: &'tcx hir::Lifetime) {
diff --git a/src/test/ui/error-codes/E0106.stderr b/src/test/ui/error-codes/E0106.stderr
index a23bcbfd71a..ac70e887626 100644
--- a/src/test/ui/error-codes/E0106.stderr
+++ b/src/test/ui/error-codes/E0106.stderr
@@ -51,6 +51,15 @@ error[E0106]: missing lifetime specifiers
    |
 LL |     buzz: Buzz,
    |           ^^^^ expected 2 lifetime parameters
+   |
+help: consider introducing a named lifetime parameter
+   |
+LL | struct Quux<'a> {
+LL |     baz: Baz,
+LL |
+LL |
+LL |     buzz: Buzz<'a, 'a>,
+   |
 
 error: aborting due to 5 previous errors
 
diff --git a/src/test/ui/suggestions/missing-lifetime-in-assoc-const-type.rs b/src/test/ui/suggestions/missing-lifetime-in-assoc-const-type.rs
index b9e0108fe71..38332627f4c 100644
--- a/src/test/ui/suggestions/missing-lifetime-in-assoc-const-type.rs
+++ b/src/test/ui/suggestions/missing-lifetime-in-assoc-const-type.rs
@@ -1,5 +1,16 @@
 trait ZstAssert: Sized {
-    const TYPE_NAME: &str = ""; //~ ERROR missing lifetime specifier
+    const A: &str = ""; //~ ERROR missing lifetime specifier
+    const B: S = S { s: &() }; //~ ERROR missing lifetime specifier
+    const C: &'_ str = ""; //~ ERROR missing lifetime specifier
+    const D: T = T { a: &(), b: &() }; //~ ERROR missing lifetime specifier
+}
+
+struct S<'a> {
+    s: &'a (),
+}
+struct T<'a, 'b> {
+    a: &'a (),
+    b: &'b (),
 }
 
 fn main() {}
diff --git a/src/test/ui/suggestions/missing-lifetime-in-assoc-const-type.stderr b/src/test/ui/suggestions/missing-lifetime-in-assoc-const-type.stderr
index ee9d1a15d2a..b20778ce208 100644
--- a/src/test/ui/suggestions/missing-lifetime-in-assoc-const-type.stderr
+++ b/src/test/ui/suggestions/missing-lifetime-in-assoc-const-type.stderr
@@ -1,15 +1,73 @@
 error[E0106]: missing lifetime specifier
-  --> $DIR/missing-lifetime-in-assoc-const-type.rs:2:22
+  --> $DIR/missing-lifetime-in-assoc-const-type.rs:2:14
    |
-LL |     const TYPE_NAME: &str = "";
-   |                      ^ expected named lifetime parameter
+LL |     const A: &str = "";
+   |              ^ expected named lifetime parameter
    |
+help: consider using the `'static` lifetime
+   |
+LL |     const A: &'static str = "";
+   |               ^^^^^^^
+help: consider introducing a named lifetime parameter
+   |
+LL | trait ZstAssert<'a>: Sized {
+LL |     const A: &'a str = "";
+   |
+
+error[E0106]: missing lifetime specifier
+  --> $DIR/missing-lifetime-in-assoc-const-type.rs:3:14
+   |
+LL |     const B: S = S { s: &() };
+   |              ^ expected named lifetime parameter
+   |
+help: consider using the `'static` lifetime
+   |
+LL |     const B: S<'static> = S { s: &() };
+   |               ^^^^^^^^^
+help: consider introducing a named lifetime parameter
+   |
+LL | trait ZstAssert<'a>: Sized {
+LL |     const A: &str = "";
+LL |     const B: S<'a> = S { s: &() };
+   |
+
+error[E0106]: missing lifetime specifier
+  --> $DIR/missing-lifetime-in-assoc-const-type.rs:4:15
+   |
+LL |     const C: &'_ str = "";
+   |               ^^ expected named lifetime parameter
+   |
+help: consider using the `'static` lifetime
+   |
+LL |     const C: &'static str = "";
+   |               ^^^^^^^
+help: consider introducing a named lifetime parameter
+   |
+LL | trait ZstAssert<'a>: Sized {
+LL |     const A: &str = "";
+LL |     const B: S = S { s: &() };
+LL |     const C: &'a str = "";
+   |
+
+error[E0106]: missing lifetime specifiers
+  --> $DIR/missing-lifetime-in-assoc-const-type.rs:5:14
+   |
+LL |     const D: T = T { a: &(), b: &() };
+   |              ^ expected 2 lifetime parameters
+   |
+help: consider using the `'static` lifetime
+   |
+LL |     const D: T<'static, 'static> = T { a: &(), b: &() };
+   |               ^^^^^^^^^^^^^^^^^^
 help: consider introducing a named lifetime parameter
    |
 LL | trait ZstAssert<'a>: Sized {
-LL |     const TYPE_NAME: &'a str = "";
+LL |     const A: &str = "";
+LL |     const B: S = S { s: &() };
+LL |     const C: &'_ str = "";
+LL |     const D: T<'a, 'a> = T { a: &(), b: &() };
    |
 
-error: aborting due to previous error
+error: aborting due to 4 previous errors
 
 For more information about this error, try `rustc --explain E0106`.