Rollup merge of #75372 - estebank:lt-sugg-in-type, r=lcnr
Fix suggestion to use lifetime in type and in assoc const _Do not merge until #75363 has landed, as it has the test case for this._ * Account for associated types * Associated `const`s can't have generics (fix #74264) * Do not suggest duplicate lifetimes and suggest `for<'a>` more (fix #72404)
This commit is contained in:
commit
d90a4b8ae9
10 changed files with 360 additions and 38 deletions
|
@ -16,8 +16,8 @@ use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX, LOCAL_CRATE};
|
||||||
use rustc_hir::PrimTy;
|
use rustc_hir::PrimTy;
|
||||||
use rustc_session::config::nightly_options;
|
use rustc_session::config::nightly_options;
|
||||||
use rustc_span::hygiene::MacroKind;
|
use rustc_span::hygiene::MacroKind;
|
||||||
use rustc_span::symbol::{kw, sym, Ident};
|
use rustc_span::symbol::{kw, sym, Ident, Symbol};
|
||||||
use rustc_span::{BytePos, Span};
|
use rustc_span::{BytePos, Span, DUMMY_SP};
|
||||||
|
|
||||||
use log::debug;
|
use log::debug;
|
||||||
|
|
||||||
|
@ -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 {
|
||||||
|
@ -1195,6 +1196,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()
|
||||||
|
@ -1253,7 +1255,8 @@ impl<'tcx> LifetimeContext<'_, 'tcx> {
|
||||||
err: &mut DiagnosticBuilder<'_>,
|
err: &mut DiagnosticBuilder<'_>,
|
||||||
span: Span,
|
span: Span,
|
||||||
count: usize,
|
count: usize,
|
||||||
lifetime_names: &FxHashSet<Ident>,
|
lifetime_names: &FxHashSet<Symbol>,
|
||||||
|
lifetime_spans: Vec<Span>,
|
||||||
params: &[ElisionFailureInfo],
|
params: &[ElisionFailureInfo],
|
||||||
) {
|
) {
|
||||||
let snippet = self.tcx.sess.source_map().span_to_snippet(span).ok();
|
let snippet = self.tcx.sess.source_map().span_to_snippet(span).ok();
|
||||||
|
@ -1267,11 +1270,60 @@ impl<'tcx> LifetimeContext<'_, 'tcx> {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
let suggest_existing = |err: &mut DiagnosticBuilder<'_>, sugg| {
|
let suggest_existing = |err: &mut DiagnosticBuilder<'_>,
|
||||||
|
name: &str,
|
||||||
|
formatter: &dyn Fn(&str) -> String| {
|
||||||
|
if let Some(MissingLifetimeSpot::HigherRanked { span: for_span, span_type }) =
|
||||||
|
self.missing_named_lifetime_spots.iter().rev().next()
|
||||||
|
{
|
||||||
|
// When we have `struct S<'a>(&'a dyn Fn(&X) -> &X);` we want to not only suggest
|
||||||
|
// using `'a`, but also introduce the concept of HRLTs by suggesting
|
||||||
|
// `struct S<'a>(&'a dyn for<'b> Fn(&X) -> &'b X);`. (#72404)
|
||||||
|
let mut introduce_suggestion = vec![];
|
||||||
|
|
||||||
|
let a_to_z_repeat_n = |n| {
|
||||||
|
(b'a'..=b'z').map(move |c| {
|
||||||
|
let mut s = '\''.to_string();
|
||||||
|
s.extend(std::iter::repeat(char::from(c)).take(n));
|
||||||
|
s
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
// If all single char lifetime names are present, we wrap around and double the chars.
|
||||||
|
let lt_name = (1..)
|
||||||
|
.flat_map(a_to_z_repeat_n)
|
||||||
|
.find(|lt| !lifetime_names.contains(&Symbol::intern(<)))
|
||||||
|
.unwrap();
|
||||||
|
let msg = format!(
|
||||||
|
"consider making the {} lifetime-generic with a new `{}` lifetime",
|
||||||
|
span_type.descr(),
|
||||||
|
lt_name,
|
||||||
|
);
|
||||||
|
err.note(
|
||||||
|
"for more information on higher-ranked polymorphism, visit \
|
||||||
|
https://doc.rust-lang.org/nomicon/hrtb.html",
|
||||||
|
);
|
||||||
|
let for_sugg = span_type.suggestion(<_name);
|
||||||
|
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!("&{} {}", lt_name, &snippet[1..])));
|
||||||
|
} else if snippet.starts_with("&'_ ") {
|
||||||
|
introduce_suggestion
|
||||||
|
.push((param.span, format!("&{} {}", lt_name, &snippet[4..])));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
introduce_suggestion.push((*for_span, for_sugg.to_string()));
|
||||||
|
introduce_suggestion.push((span, formatter(<_name)));
|
||||||
|
err.multipart_suggestion(&msg, introduce_suggestion, Applicability::MaybeIncorrect);
|
||||||
|
}
|
||||||
|
|
||||||
err.span_suggestion_verbose(
|
err.span_suggestion_verbose(
|
||||||
span,
|
span,
|
||||||
&format!("consider using the `{}` lifetime", lifetime_names.iter().next().unwrap()),
|
&format!("consider using the `{}` lifetime", lifetime_names.iter().next().unwrap()),
|
||||||
sugg,
|
formatter(name),
|
||||||
Applicability::MaybeIncorrect,
|
Applicability::MaybeIncorrect,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1282,6 +1334,15 @@ impl<'tcx> LifetimeContext<'_, 'tcx> {
|
||||||
let should_break;
|
let should_break;
|
||||||
introduce_suggestion.push(match missing {
|
introduce_suggestion.push(match missing {
|
||||||
MissingLifetimeSpot::Generics(generics) => {
|
MissingLifetimeSpot::Generics(generics) => {
|
||||||
|
if generics.span == DUMMY_SP {
|
||||||
|
// Account for malformed generics in the HIR. This shouldn't happen,
|
||||||
|
// but if we make a mistake elsewhere, mainly by keeping something in
|
||||||
|
// `missing_named_lifetime_spots` that we shouldn't, like associated
|
||||||
|
// `const`s or making a mistake in the AST lowering we would provide
|
||||||
|
// non-sensical suggestions. Guard against that by skipping these.
|
||||||
|
// (#74264)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
msg = "consider introducing a named lifetime parameter".to_string();
|
msg = "consider introducing a named lifetime parameter".to_string();
|
||||||
should_break = true;
|
should_break = true;
|
||||||
if let Some(param) = generics.params.iter().find(|p| match p.kind {
|
if let Some(param) = generics.params.iter().find(|p| match p.kind {
|
||||||
|
@ -1308,6 +1369,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) {
|
||||||
|
@ -1328,19 +1425,19 @@ impl<'tcx> LifetimeContext<'_, 'tcx> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
match (lifetime_names.len(), lifetime_names.iter().next(), snippet.as_deref()) {
|
let lifetime_names: Vec<_> = lifetime_names.into_iter().collect();
|
||||||
(1, Some(name), Some("&")) => {
|
match (&lifetime_names[..], snippet.as_deref()) {
|
||||||
suggest_existing(err, format!("&{} ", name));
|
([name], Some("&")) => {
|
||||||
|
suggest_existing(err, &name.as_str()[..], &|name| format!("&{} ", name));
|
||||||
}
|
}
|
||||||
(1, Some(name), Some("'_")) => {
|
([name], Some("'_")) => {
|
||||||
suggest_existing(err, name.to_string());
|
suggest_existing(err, &name.as_str()[..], &|n| n.to_string());
|
||||||
}
|
}
|
||||||
(1, Some(name), Some("")) => {
|
([name], Some("")) => {
|
||||||
suggest_existing(err, format!("{}, ", name).repeat(count));
|
suggest_existing(err, &name.as_str()[..], &|n| format!("{}, ", n).repeat(count));
|
||||||
}
|
}
|
||||||
(1, Some(name), Some(snippet)) if !snippet.ends_with('>') => {
|
([name], Some(snippet)) if !snippet.ends_with('>') => {
|
||||||
suggest_existing(
|
let f = |name: &str| {
|
||||||
err,
|
|
||||||
format!(
|
format!(
|
||||||
"{}<{}>",
|
"{}<{}>",
|
||||||
snippet,
|
snippet,
|
||||||
|
@ -1348,21 +1445,37 @@ impl<'tcx> LifetimeContext<'_, 'tcx> {
|
||||||
.take(count)
|
.take(count)
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.join(", ")
|
.join(", ")
|
||||||
),
|
)
|
||||||
);
|
};
|
||||||
|
suggest_existing(err, &name.as_str()[..], &f);
|
||||||
}
|
}
|
||||||
(0, _, Some("&")) if count == 1 => {
|
([], Some("&")) if count == 1 => {
|
||||||
suggest_new(err, "&'a ");
|
suggest_new(err, "&'a ");
|
||||||
}
|
}
|
||||||
(0, _, Some("'_")) if count == 1 => {
|
([], Some("'_")) if count == 1 => {
|
||||||
suggest_new(err, "'a");
|
suggest_new(err, "'a");
|
||||||
}
|
}
|
||||||
(0, _, Some(snippet)) if !snippet.ends_with('>') && count == 1 => {
|
([], Some(snippet)) if !snippet.ends_with('>') => {
|
||||||
suggest_new(err, &format!("{}<'a>", snippet));
|
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,
|
||||||
|
&std::iter::repeat("'a, ").take(count).collect::<Vec<_>>().join(""),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
suggest_new(
|
||||||
|
err,
|
||||||
|
&format!(
|
||||||
|
"{}<{}>",
|
||||||
|
snippet,
|
||||||
|
std::iter::repeat("'a").take(count).collect::<Vec<_>>().join(", ")
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
(n, ..) if n > 1 => {
|
(lts, ..) if lts.len() > 1 => {
|
||||||
let spans: Vec<Span> = lifetime_names.iter().map(|lt| lt.span).collect();
|
err.span_note(lifetime_spans, "these named lifetimes are available to use");
|
||||||
err.span_note(spans, "these named lifetimes are available to use");
|
|
||||||
if Some("") == snippet.as_deref() {
|
if Some("") == snippet.as_deref() {
|
||||||
// This happens when we have `Foo<T>` where we point at the space before `T`,
|
// This happens when we have `Foo<T>` where we point at the space before `T`,
|
||||||
// but this can be confusing so we give a suggestion with placeholders.
|
// but this can be confusing so we give a suggestion with placeholders.
|
||||||
|
|
|
@ -711,9 +711,9 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
|
||||||
|
|
||||||
fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem<'tcx>) {
|
fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem<'tcx>) {
|
||||||
use self::hir::TraitItemKind::*;
|
use self::hir::TraitItemKind::*;
|
||||||
self.missing_named_lifetime_spots.push((&trait_item.generics).into());
|
|
||||||
match trait_item.kind {
|
match trait_item.kind {
|
||||||
Fn(ref sig, _) => {
|
Fn(ref sig, _) => {
|
||||||
|
self.missing_named_lifetime_spots.push((&trait_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(trait_item.hir_id)),
|
Some(tcx.hir().get_parent_item(trait_item.hir_id)),
|
||||||
|
@ -721,8 +721,10 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
|
||||||
&trait_item.generics,
|
&trait_item.generics,
|
||||||
|this| intravisit::walk_trait_item(this, trait_item),
|
|this| intravisit::walk_trait_item(this, trait_item),
|
||||||
);
|
);
|
||||||
|
self.missing_named_lifetime_spots.pop();
|
||||||
}
|
}
|
||||||
Type(bounds, ref ty) => {
|
Type(bounds, ref ty) => {
|
||||||
|
self.missing_named_lifetime_spots.push((&trait_item.generics).into());
|
||||||
let generics = &trait_item.generics;
|
let generics = &trait_item.generics;
|
||||||
let mut index = self.next_early_index();
|
let mut index = self.next_early_index();
|
||||||
debug!("visit_ty: index = {}", index);
|
debug!("visit_ty: index = {}", index);
|
||||||
|
@ -757,31 +759,35 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
|
||||||
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!(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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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);
|
||||||
|
@ -810,14 +816,16 @@ 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) {
|
||||||
|
@ -2315,6 +2323,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
|
||||||
let mut late_depth = 0;
|
let mut late_depth = 0;
|
||||||
let mut scope = self.scope;
|
let mut scope = self.scope;
|
||||||
let mut lifetime_names = FxHashSet::default();
|
let mut lifetime_names = FxHashSet::default();
|
||||||
|
let mut lifetime_spans = vec![];
|
||||||
let error = loop {
|
let error = loop {
|
||||||
match *scope {
|
match *scope {
|
||||||
// Do not assign any resolution, it will be inferred.
|
// Do not assign any resolution, it will be inferred.
|
||||||
|
@ -2326,7 +2335,8 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
|
||||||
// collect named lifetimes for suggestions
|
// collect named lifetimes for suggestions
|
||||||
for name in lifetimes.keys() {
|
for name in lifetimes.keys() {
|
||||||
if let hir::ParamName::Plain(name) = name {
|
if let hir::ParamName::Plain(name) = name {
|
||||||
lifetime_names.insert(*name);
|
lifetime_names.insert(name.name);
|
||||||
|
lifetime_spans.push(name.span);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
late_depth += 1;
|
late_depth += 1;
|
||||||
|
@ -2344,12 +2354,24 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
Elide::Exact(l) => l.shifted(late_depth),
|
Elide::Exact(l) => l.shifted(late_depth),
|
||||||
Elide::Error(ref e) => {
|
Elide::Error(ref e) => {
|
||||||
if let Scope::Binder { ref lifetimes, .. } = s {
|
let mut scope = s;
|
||||||
// collect named lifetimes for suggestions
|
loop {
|
||||||
for name in lifetimes.keys() {
|
match scope {
|
||||||
if let hir::ParamName::Plain(name) = name {
|
Scope::Binder { ref lifetimes, s, .. } => {
|
||||||
lifetime_names.insert(*name);
|
// Collect named lifetimes for suggestions.
|
||||||
|
for name in lifetimes.keys() {
|
||||||
|
if let hir::ParamName::Plain(name) = name {
|
||||||
|
lifetime_names.insert(name.name);
|
||||||
|
lifetime_spans.push(name.span);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
scope = s;
|
||||||
}
|
}
|
||||||
|
Scope::ObjectLifetimeDefault { ref s, .. }
|
||||||
|
| Scope::Elision { ref s, .. } => {
|
||||||
|
scope = s;
|
||||||
|
}
|
||||||
|
_ => break,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break Some(e);
|
break Some(e);
|
||||||
|
@ -2373,7 +2395,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
|
||||||
if let Some(params) = error {
|
if let Some(params) = error {
|
||||||
// If there's no lifetime available, suggest `'static`.
|
// If there's no lifetime available, suggest `'static`.
|
||||||
if self.report_elision_failure(&mut err, params) && lifetime_names.is_empty() {
|
if self.report_elision_failure(&mut err, params) && lifetime_names.is_empty() {
|
||||||
lifetime_names.insert(Ident::with_dummy_span(kw::StaticLifetime));
|
lifetime_names.insert(kw::StaticLifetime);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.add_missing_lifetime_specifiers_label(
|
self.add_missing_lifetime_specifiers_label(
|
||||||
|
@ -2381,6 +2403,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
|
||||||
span,
|
span,
|
||||||
lifetime_refs.len(),
|
lifetime_refs.len(),
|
||||||
&lifetime_names,
|
&lifetime_names,
|
||||||
|
lifetime_spans,
|
||||||
error.map(|p| &p[..]).unwrap_or(&[]),
|
error.map(|p| &p[..]).unwrap_or(&[]),
|
||||||
);
|
);
|
||||||
err.emit();
|
err.emit();
|
||||||
|
|
|
@ -5,6 +5,11 @@ LL | fn elision<T: Fn() -> &i32>() {
|
||||||
| ^ expected named lifetime parameter
|
| ^ 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: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
|
||||||
|
= note: for more information on higher-ranked polymorphism, visit https://doc.rust-lang.org/nomicon/hrtb.html
|
||||||
|
help: consider making the bound lifetime-generic with a new `'a` lifetime
|
||||||
|
|
|
||||||
|
LL | fn elision<T: for<'a> Fn() -> &'a i32>() {
|
||||||
|
| ^^^^^^^ ^^^
|
||||||
help: consider using the `'static` lifetime
|
help: consider using the `'static` lifetime
|
||||||
|
|
|
|
||||||
LL | fn elision<T: Fn() -> &'static i32>() {
|
LL | fn elision<T: Fn() -> &'static i32>() {
|
||||||
|
|
|
@ -5,6 +5,11 @@ LL | fn elision(_: fn() -> &i32) {
|
||||||
| ^ expected named lifetime parameter
|
| ^ 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: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
|
||||||
|
= note: for more information on higher-ranked polymorphism, visit https://doc.rust-lang.org/nomicon/hrtb.html
|
||||||
|
help: consider making the type lifetime-generic with a new `'a` lifetime
|
||||||
|
|
|
||||||
|
LL | fn elision(_: for<'a> fn() -> &'a i32) {
|
||||||
|
| ^^^^^^^ ^^^
|
||||||
help: consider using the `'static` lifetime
|
help: consider using the `'static` lifetime
|
||||||
|
|
|
|
||||||
LL | fn elision(_: fn() -> &'static i32) {
|
LL | fn elision(_: fn() -> &'static i32) {
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -6,8 +6,8 @@ LL | type Item = IteratorChunk<T, S>;
|
||||||
|
|
|
|
||||||
help: consider introducing a named lifetime parameter
|
help: consider introducing a named lifetime parameter
|
||||||
|
|
|
|
||||||
LL | type Item<'a> = IteratorChunk<<'a>T, S>;
|
LL | type Item<'a> = IteratorChunk<'a, T, S>;
|
||||||
| ^^^^ ^^^^
|
| ^^^^ ^^^
|
||||||
|
|
||||||
error: `impl` item signature doesn't match `trait` item signature
|
error: `impl` item signature doesn't match `trait` item signature
|
||||||
--> $DIR/issue-74918-missing-lifetime.rs:11:5
|
--> $DIR/issue-74918-missing-lifetime.rs:11:5
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
trait ZstAssert: Sized {
|
||||||
|
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() {}
|
|
@ -0,0 +1,73 @@
|
||||||
|
error[E0106]: missing lifetime specifier
|
||||||
|
--> $DIR/missing-lifetime-in-assoc-const-type.rs:2:14
|
||||||
|
|
|
||||||
|
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 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`.
|
15
src/test/ui/suggestions/missing-lt-for-hrtb.rs
Normal file
15
src/test/ui/suggestions/missing-lt-for-hrtb.rs
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
struct X<'a>(&'a ());
|
||||||
|
struct S<'a>(&'a dyn Fn(&X) -> &X);
|
||||||
|
//~^ ERROR missing lifetime specifier
|
||||||
|
//~| ERROR missing lifetime specifier
|
||||||
|
struct V<'a>(&'a dyn for<'b> Fn(&X) -> &X);
|
||||||
|
//~^ ERROR missing lifetime specifier
|
||||||
|
//~| ERROR missing lifetime specifier
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let x = S(&|x| {
|
||||||
|
println!("hi");
|
||||||
|
x
|
||||||
|
});
|
||||||
|
x.0(&X(&()));
|
||||||
|
}
|
63
src/test/ui/suggestions/missing-lt-for-hrtb.stderr
Normal file
63
src/test/ui/suggestions/missing-lt-for-hrtb.stderr
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
error[E0106]: missing lifetime specifier
|
||||||
|
--> $DIR/missing-lt-for-hrtb.rs:2:32
|
||||||
|
|
|
||||||
|
LL | struct S<'a>(&'a dyn Fn(&X) -> &X);
|
||||||
|
| -- ^ expected named lifetime parameter
|
||||||
|
|
|
||||||
|
= help: this function's return type contains a borrowed value, but the signature does not say which one of argument 1's 2 lifetimes it is borrowed from
|
||||||
|
= note: for more information on higher-ranked polymorphism, visit https://doc.rust-lang.org/nomicon/hrtb.html
|
||||||
|
help: consider making the bound lifetime-generic with a new `'b` lifetime
|
||||||
|
|
|
||||||
|
LL | struct S<'a>(&'a dyn for<'b> Fn(&'b X) -> &'b X);
|
||||||
|
| ^^^^^^^ ^^^^^ ^^^
|
||||||
|
help: consider using the `'a` lifetime
|
||||||
|
|
|
||||||
|
LL | struct S<'a>(&'a dyn Fn(&X) -> &'a X);
|
||||||
|
| ^^^
|
||||||
|
|
||||||
|
error[E0106]: missing lifetime specifier
|
||||||
|
--> $DIR/missing-lt-for-hrtb.rs:2:33
|
||||||
|
|
|
||||||
|
LL | struct S<'a>(&'a dyn Fn(&X) -> &X);
|
||||||
|
| -- ^ expected named lifetime parameter
|
||||||
|
|
|
||||||
|
= help: this function's return type contains a borrowed value, but the signature does not say which one of argument 1's 2 lifetimes it is borrowed from
|
||||||
|
= note: for more information on higher-ranked polymorphism, visit https://doc.rust-lang.org/nomicon/hrtb.html
|
||||||
|
help: consider making the bound lifetime-generic with a new `'b` lifetime
|
||||||
|
|
|
||||||
|
LL | struct S<'a>(&'a dyn for<'b> Fn(&'b X) -> &X<'b>);
|
||||||
|
| ^^^^^^^ ^^^^^ ^^^^^
|
||||||
|
help: consider using the `'a` lifetime
|
||||||
|
|
|
||||||
|
LL | struct S<'a>(&'a dyn Fn(&X) -> &X<'a>);
|
||||||
|
| ^^^^^
|
||||||
|
|
||||||
|
error[E0106]: missing lifetime specifier
|
||||||
|
--> $DIR/missing-lt-for-hrtb.rs:5:40
|
||||||
|
|
|
||||||
|
LL | struct V<'a>(&'a dyn for<'b> Fn(&X) -> &X);
|
||||||
|
| -- ^ expected named lifetime parameter
|
||||||
|
|
|
||||||
|
= help: this function's return type contains a borrowed value, but the signature does not say which one of argument 1's 2 lifetimes it is borrowed from
|
||||||
|
note: these named lifetimes are available to use
|
||||||
|
--> $DIR/missing-lt-for-hrtb.rs:5:10
|
||||||
|
|
|
||||||
|
LL | struct V<'a>(&'a dyn for<'b> Fn(&X) -> &X);
|
||||||
|
| ^^ ^^
|
||||||
|
|
||||||
|
error[E0106]: missing lifetime specifier
|
||||||
|
--> $DIR/missing-lt-for-hrtb.rs:5:41
|
||||||
|
|
|
||||||
|
LL | struct V<'a>(&'a dyn for<'b> Fn(&X) -> &X);
|
||||||
|
| -- ^ expected named lifetime parameter
|
||||||
|
|
|
||||||
|
= help: this function's return type contains a borrowed value, but the signature does not say which one of argument 1's 2 lifetimes it is borrowed from
|
||||||
|
note: these named lifetimes are available to use
|
||||||
|
--> $DIR/missing-lt-for-hrtb.rs:5:10
|
||||||
|
|
|
||||||
|
LL | struct V<'a>(&'a dyn for<'b> Fn(&X) -> &X);
|
||||||
|
| ^^ ^^
|
||||||
|
|
||||||
|
error: aborting due to 4 previous errors
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0106`.
|
Loading…
Add table
Add a link
Reference in a new issue