Suggest using 'static in assoc consts and suggest when multiple lts are needed

This commit is contained in:
Esteban Küber 2020-08-11 13:02:14 -07:00
parent becd479482
commit 6a3deb0ae0
5 changed files with 144 additions and 12 deletions

View File

@ -33,6 +33,7 @@ enum AssocSuggestion {
crate enum MissingLifetimeSpot<'tcx> { crate enum MissingLifetimeSpot<'tcx> {
Generics(&'tcx hir::Generics<'tcx>), Generics(&'tcx hir::Generics<'tcx>),
HigherRanked { span: Span, span_type: ForLifetimeSpanType }, HigherRanked { span: Span, span_type: ForLifetimeSpanType },
Static,
} }
crate enum ForLifetimeSpanType { crate enum ForLifetimeSpanType {
@ -1186,6 +1187,7 @@ impl<'tcx> LifetimeContext<'_, 'tcx> {
https://doc.rust-lang.org/nomicon/hrtb.html", https://doc.rust-lang.org/nomicon/hrtb.html",
); );
} }
_ => {}
} }
} }
if nightly_options::is_nightly_build() if nightly_options::is_nightly_build()
@ -1358,6 +1360,42 @@ impl<'tcx> LifetimeContext<'_, 'tcx> {
); );
(*span, span_type.suggestion("'a")) (*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 { for param in params {
if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(param.span) { 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 => { ([], Some("'_")) if count == 1 => {
suggest_new(err, "'a"); suggest_new(err, "'a");
} }
([], Some(snippet)) if !snippet.ends_with('>') && count == 1 => { ([], Some(snippet)) if !snippet.ends_with('>') => {
if snippet == "" { if snippet == "" {
// This happens when we have `type Bar<'a> = Foo<T>` where we point at the space // 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>`. // 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 { } 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 => { (lts, ..) if lts.len() > 1 => {

View File

@ -764,26 +764,30 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
Const(_, _) => { Const(_, _) => {
// Only methods and types support generics. // Only methods and types support generics.
assert!(trait_item.generics.params.is_empty()); assert!(trait_item.generics.params.is_empty());
self.missing_named_lifetime_spots.push(MissingLifetimeSpot::Static);
intravisit::walk_trait_item(self, trait_item); 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>) { fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem<'tcx>) {
use self::hir::ImplItemKind::*; use self::hir::ImplItemKind::*;
self.missing_named_lifetime_spots.push((&impl_item.generics).into());
match impl_item.kind { match impl_item.kind {
Fn(ref sig, _) => { Fn(ref sig, _) => {
self.missing_named_lifetime_spots.push((&impl_item.generics).into());
let tcx = self.tcx; let tcx = self.tcx;
self.visit_early_late( self.visit_early_late(
Some(tcx.hir().get_parent_item(impl_item.hir_id)), Some(tcx.hir().get_parent_item(impl_item.hir_id)),
&sig.decl, &sig.decl,
&impl_item.generics, &impl_item.generics,
|this| intravisit::walk_impl_item(this, impl_item), |this| intravisit::walk_impl_item(this, impl_item),
) );
self.missing_named_lifetime_spots.pop();
} }
TyAlias(ref ty) => { TyAlias(ref ty) => {
let generics = &impl_item.generics; let generics = &impl_item.generics;
self.missing_named_lifetime_spots.push(generics.into());
let mut index = self.next_early_index(); let mut index = self.next_early_index();
let mut non_lifetime_count = 0; let mut non_lifetime_count = 0;
debug!("visit_ty: index = {}", index); debug!("visit_ty: index = {}", index);
@ -812,15 +816,17 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
this.visit_generics(generics); this.visit_generics(generics);
this.visit_ty(ty); this.visit_ty(ty);
}); });
self.missing_named_lifetime_spots.pop();
} }
Const(_, _) => { Const(_, _) => {
// Only methods and types support generics. // Only methods and types support generics.
assert!(impl_item.generics.params.is_empty()); assert!(impl_item.generics.params.is_empty());
self.missing_named_lifetime_spots.push(MissingLifetimeSpot::Static);
intravisit::walk_impl_item(self, impl_item); 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) { fn visit_lifetime(&mut self, lifetime_ref: &'tcx hir::Lifetime) {
debug!("visit_lifetime(lifetime_ref={:?})", lifetime_ref); debug!("visit_lifetime(lifetime_ref={:?})", lifetime_ref);

View File

@ -51,6 +51,15 @@ error[E0106]: missing lifetime specifiers
| |
LL | buzz: Buzz, LL | buzz: Buzz,
| ^^^^ expected 2 lifetime parameters | ^^^^ 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 error: aborting due to 5 previous errors

View File

@ -1,5 +1,16 @@
trait ZstAssert: Sized { 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() {} fn main() {}

View File

@ -1,15 +1,73 @@
error[E0106]: missing lifetime specifier 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 = ""; LL | const A: &str = "";
| ^ expected named lifetime parameter | ^ expected named lifetime parameter
| |
help: consider using the `'static` lifetime
|
LL | const A: &'static str = "";
| ^^^^^^^
help: consider introducing a named lifetime parameter help: consider introducing a named lifetime parameter
| |
LL | trait ZstAssert<'a>: Sized { LL | trait ZstAssert<'a>: Sized {
LL | const TYPE_NAME: &'a str = ""; LL | const A: &'a str = "";
| |
error: aborting due to previous error 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 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 4 previous errors
For more information about this error, try `rustc --explain E0106`. For more information about this error, try `rustc --explain E0106`.