From 6a3deb0ae04c4cb6400b30fecd1cbbee0506348b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Tue, 11 Aug 2020 13:02:14 -0700 Subject: [PATCH] Suggest using `'static` in assoc consts and suggest when multiple lts are needed --- src/librustc_resolve/late/diagnostics.rs | 54 ++++++++++++++- src/librustc_resolve/late/lifetimes.rs | 12 +++- src/test/ui/error-codes/E0106.stderr | 9 +++ .../missing-lifetime-in-assoc-const-type.rs | 13 +++- ...issing-lifetime-in-assoc-const-type.stderr | 68 +++++++++++++++++-- 5 files changed, 144 insertions(+), 12 deletions(-) diff --git a/src/librustc_resolve/late/diagnostics.rs b/src/librustc_resolve/late/diagnostics.rs index 9dbde5e785290..6e7a004f853e5 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::>() + .join(", "), + ) + } else { + ( + span.shrink_to_hi(), + format!( + "<{}>", + std::iter::repeat("'static") + .take(count) + .collect::>() + .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` 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::>().join(""), + ); } else { - suggest_new(err, &format!("{}<'a>", snippet)); + suggest_new( + err, + &format!( + "{}<{}>", + snippet, + std::iter::repeat("'a").take(count).collect::>().join(", ") + ), + ); } } (lts, ..) if lts.len() > 1 => { diff --git a/src/librustc_resolve/late/lifetimes.rs b/src/librustc_resolve/late/lifetimes.rs index e5accc7fde9d5..2046419d984d0 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 a23bcbfd71a56..ac70e887626ab 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 b9e0108fe714a..38332627f4c87 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 ee9d1a15d2a7b..b20778ce20817 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`.