1
Fork 0

Report elision failures on the AST.

This commit is contained in:
Camille GILLOT 2022-06-05 18:33:09 +02:00
parent bfd0435fd7
commit 3c5048d2ec
57 changed files with 1641 additions and 1749 deletions

View file

@ -1,7 +1,6 @@
use crate::diagnostics::{ImportSuggestion, LabelSuggestion, TypoSuggestion};
use crate::late::lifetimes::{ElisionFailureInfo, LifetimeContext};
use crate::late::{AliasPossibility, LateResolutionVisitor, RibKind};
use crate::late::{LifetimeBinderKind, LifetimeRibKind, LifetimeUseSet};
use crate::late::{LifetimeBinderKind, LifetimeRes, LifetimeRibKind, LifetimeUseSet};
use crate::path_names_to_string;
use crate::{Module, ModuleKind, ModuleOrUniformRoot};
use crate::{PathResult, PathSource, Segment};
@ -9,10 +8,10 @@ use crate::{PathResult, PathSource, Segment};
use rustc_ast::visit::{FnCtxt, FnKind, LifetimeCtxt};
use rustc_ast::{
self as ast, AssocItemKind, Expr, ExprKind, GenericParam, GenericParamKind, Item, ItemKind,
NodeId, Path, Ty, TyKind,
NodeId, Path, Ty, TyKind, DUMMY_NODE_ID,
};
use rustc_ast_pretty::pprust::path_segment_to_string;
use rustc_data_structures::fx::{FxHashSet, FxIndexSet};
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::{
pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed,
MultiSpan,
@ -20,7 +19,7 @@ use rustc_errors::{
use rustc_hir as hir;
use rustc_hir::def::Namespace::{self, *};
use rustc_hir::def::{self, CtorKind, CtorOf, DefKind};
use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID, LOCAL_CRATE};
use rustc_hir::def_id::{DefId, CRATE_DEF_ID, LOCAL_CRATE};
use rustc_hir::PrimTy;
use rustc_session::lint;
use rustc_session::parse::feature_err;
@ -29,7 +28,7 @@ use rustc_span::edition::Edition;
use rustc_span::hygiene::MacroKind;
use rustc_span::lev_distance::find_best_match_for_name;
use rustc_span::symbol::{kw, sym, Ident, Symbol};
use rustc_span::{BytePos, Span, DUMMY_SP};
use rustc_span::{BytePos, Span};
use std::iter;
use std::ops::Deref;
@ -59,45 +58,6 @@ impl AssocSuggestion {
}
}
pub(crate) enum MissingLifetimeSpot<'tcx> {
Generics(&'tcx hir::Generics<'tcx>),
HigherRanked { span: Span, span_type: ForLifetimeSpanType },
Static,
}
pub(crate) enum ForLifetimeSpanType {
BoundEmpty,
BoundTail,
TypeEmpty,
TypeTail,
ClosureEmpty,
ClosureTail,
}
impl ForLifetimeSpanType {
pub(crate) fn descr(&self) -> &'static str {
match self {
Self::BoundEmpty | Self::BoundTail => "bound",
Self::TypeEmpty | Self::TypeTail => "type",
Self::ClosureEmpty | Self::ClosureTail => "closure",
}
}
pub(crate) fn suggestion(&self, sugg: impl std::fmt::Display) -> String {
match self {
Self::BoundEmpty | Self::TypeEmpty => format!("for<{}> ", sugg),
Self::ClosureEmpty => format!("for<{}>", sugg),
Self::BoundTail | Self::TypeTail | Self::ClosureTail => format!(", {}", sugg),
}
}
}
impl<'tcx> Into<MissingLifetimeSpot<'tcx>> for &&'tcx hir::Generics<'tcx> {
fn into(self) -> MissingLifetimeSpot<'tcx> {
MissingLifetimeSpot::Generics(self)
}
}
fn is_self_type(path: &[Segment], namespace: Namespace) -> bool {
namespace == TypeNS && path.len() == 1 && path[0].ident.name == kw::SelfUpper
}
@ -122,6 +82,56 @@ fn import_candidate_to_enum_paths(suggestion: &ImportSuggestion) -> (String, Str
(variant_path_string, enum_path_string)
}
/// Description of an elided lifetime.
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub(super) struct MissingLifetime {
/// Used to overwrite the resolution with the suggestion, to avoid cascasing errors.
pub id: NodeId,
/// Where to suggest adding the lifetime.
pub span: Span,
/// How the lifetime was introduced, to have the correct space and comma.
pub kind: MissingLifetimeKind,
/// Number of elided lifetimes, used for elision in path.
pub count: usize,
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub(super) enum MissingLifetimeKind {
/// An explicit `'_`.
Underscore,
/// An elided lifetime `&' ty`.
Ampersand,
/// An elided lifetime in brackets with written brackets.
Comma,
/// An elided lifetime with elided brackets.
Brackets,
}
/// Description of the lifetimes appearing in a function parameter.
/// This is used to provide a literal explanation to the elision failure.
#[derive(Clone, Debug)]
pub(super) struct ElisionFnParameter {
/// The index of the argument in the original definition.
pub index: usize,
/// The name of the argument if it's a simple ident.
pub ident: Option<Ident>,
/// The number of lifetimes in the parameter.
pub lifetime_count: usize,
/// The span of the parameter.
pub span: Span,
}
/// Description of lifetimes that appear as candidates for elision.
/// This is used to suggest introducing an explicit lifetime.
#[derive(Debug)]
pub(super) enum LifetimeElisionCandidate {
/// This is not a real lifetime.
Ignore,
/// There is a named lifetime, we won't suggest anything.
Named,
Missing(MissingLifetime),
}
impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
fn def_span(&self, def_id: DefId) -> Option<Span> {
match def_id.krate {
@ -2003,18 +2013,35 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
err.span_label(lifetime_ref.ident.span, "undeclared lifetime");
err
};
let mut suggest_note = true;
self.suggest_introducing_lifetime(
&mut err,
Some(lifetime_ref.ident.name.as_str()),
|err, _, span, message, suggestion| {
err.span_suggestion(span, message, suggestion, Applicability::MaybeIncorrect);
true
},
);
err.emit();
}
fn suggest_introducing_lifetime(
&self,
err: &mut DiagnosticBuilder<'_, ErrorGuaranteed>,
name: Option<&str>,
suggest: impl Fn(&mut DiagnosticBuilder<'_, ErrorGuaranteed>, bool, Span, &str, String) -> bool,
) {
let mut suggest_note = true;
for rib in self.lifetime_ribs.iter().rev() {
let mut should_continue = true;
match rib.kind {
LifetimeRibKind::Generics { binder: _, span, kind } => {
if !span.can_be_used_for_suggestions() && suggest_note {
if !span.can_be_used_for_suggestions() && suggest_note && let Some(name) = name {
suggest_note = false; // Avoid displaying the same help multiple times.
err.span_label(
span,
&format!(
"lifetime `{}` is missing in item created through this procedural macro",
lifetime_ref.ident,
name,
),
);
continue;
@ -2030,46 +2057,42 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
let sugg = format!(
"{}<{}>{}",
if higher_ranked { "for" } else { "" },
lifetime_ref.ident,
name.unwrap_or("'a"),
if higher_ranked { " " } else { "" },
);
(span, sugg)
} else {
let span =
self.r.session.source_map().span_through_char(span, '<').shrink_to_hi();
let sugg = format!("{}, ", lifetime_ref.ident);
let sugg = format!("{}, ", name.unwrap_or("'a"));
(span, sugg)
};
if higher_ranked {
err.span_suggestion(
span,
&format!(
"consider making the {} lifetime-generic with a new `{}` lifetime",
kind.descr(),
lifetime_ref
),
sugg,
Applicability::MaybeIncorrect,
let message = format!(
"consider making the {} lifetime-generic with a new `{}` lifetime",
kind.descr(),
name.unwrap_or("'a"),
);
should_continue = suggest(err, true, span, &message, sugg);
err.note_once(
"for more information on higher-ranked polymorphism, visit \
https://doc.rust-lang.org/nomicon/hrtb.html",
);
} else if let Some(name) = name {
let message = format!("consider introducing lifetime `{}` here", name);
should_continue = suggest(err, false, span, &message, sugg);
} else {
err.span_suggestion(
span,
&format!("consider introducing lifetime `{}` here", lifetime_ref.ident),
sugg,
Applicability::MaybeIncorrect,
);
let message = format!("consider introducing a named lifetime parameter");
should_continue = suggest(err, false, span, &message, sugg);
}
}
LifetimeRibKind::Item => break,
_ => {}
}
if !should_continue {
break;
}
}
err.emit();
}
pub(crate) fn emit_non_static_lt_in_const_generic_error(&self, lifetime_ref: &ast::Lifetime) {
@ -2105,6 +2128,221 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
.emit();
}
}
pub(crate) fn report_missing_lifetime_specifiers(
&mut self,
lifetime_refs: Vec<MissingLifetime>,
function_param_lifetimes: Option<(Vec<MissingLifetime>, Vec<ElisionFnParameter>)>,
) -> ErrorGuaranteed {
let num_lifetimes: usize = lifetime_refs.iter().map(|lt| lt.count).sum();
let spans: Vec<_> = lifetime_refs.iter().map(|lt| lt.span).collect();
let mut err = struct_span_err!(
self.r.session,
spans,
E0106,
"missing lifetime specifier{}",
pluralize!(num_lifetimes)
);
self.add_missing_lifetime_specifiers_label(
&mut err,
lifetime_refs,
function_param_lifetimes,
);
err.emit()
}
pub(crate) fn add_missing_lifetime_specifiers_label(
&mut self,
err: &mut DiagnosticBuilder<'_, ErrorGuaranteed>,
lifetime_refs: Vec<MissingLifetime>,
function_param_lifetimes: Option<(Vec<MissingLifetime>, Vec<ElisionFnParameter>)>,
) {
for &lt in &lifetime_refs {
err.span_label(
lt.span,
format!(
"expected {} lifetime parameter{}",
if lt.count == 1 { "named".to_string() } else { lt.count.to_string() },
pluralize!(lt.count),
),
);
}
let mut in_scope_lifetimes: Vec<_> = self
.lifetime_ribs
.iter()
.rev()
.take_while(|rib| !matches!(rib.kind, LifetimeRibKind::Item))
.flat_map(|rib| rib.bindings.iter())
.map(|(&ident, &res)| (ident, res))
.filter(|(ident, _)| ident.name != kw::UnderscoreLifetime)
.collect();
debug!(?in_scope_lifetimes);
debug!(?function_param_lifetimes);
if let Some((param_lifetimes, params)) = &function_param_lifetimes {
let elided_len = param_lifetimes.len();
let num_params = params.len();
let mut m = String::new();
for (i, info) in params.iter().enumerate() {
let ElisionFnParameter { ident, index, lifetime_count, span } = *info;
debug_assert_ne!(lifetime_count, 0);
err.span_label(span, "");
if i != 0 {
if i + 1 < num_params {
m.push_str(", ");
} else if num_params == 2 {
m.push_str(" or ");
} else {
m.push_str(", or ");
}
}
let help_name = if let Some(ident) = ident {
format!("`{}`", ident)
} else {
format!("argument {}", index + 1)
};
if lifetime_count == 1 {
m.push_str(&help_name[..])
} else {
m.push_str(&format!("one of {}'s {} lifetimes", help_name, lifetime_count)[..])
}
}
if num_params == 0 {
err.help(
"this function's return type contains a borrowed value, \
but there is no value for it to be borrowed from",
);
if in_scope_lifetimes.is_empty() {
in_scope_lifetimes = vec![(
Ident::with_dummy_span(kw::StaticLifetime),
(DUMMY_NODE_ID, LifetimeRes::Static),
)];
}
} else if elided_len == 0 {
err.help(
"this function's return type contains a borrowed value with \
an elided lifetime, but the lifetime cannot be derived from \
the arguments",
);
if in_scope_lifetimes.is_empty() {
in_scope_lifetimes = vec![(
Ident::with_dummy_span(kw::StaticLifetime),
(DUMMY_NODE_ID, LifetimeRes::Static),
)];
}
} else if num_params == 1 {
err.help(&format!(
"this function's return type contains a borrowed value, \
but the signature does not say which {} it is borrowed from",
m
));
} else {
err.help(&format!(
"this function's return type contains a borrowed value, \
but the signature does not say whether it is borrowed from {}",
m
));
}
}
let existing_name = match &in_scope_lifetimes[..] {
[] => Symbol::intern("'a"),
[(existing, _)] => existing.name,
_ => Symbol::intern("'lifetime"),
};
let mut spans_suggs: Vec<_> = Vec::new();
let build_sugg = |lt: MissingLifetime| match lt.kind {
MissingLifetimeKind::Underscore => {
debug_assert_eq!(lt.count, 1);
(lt.span, existing_name.to_string())
}
MissingLifetimeKind::Ampersand => {
debug_assert_eq!(lt.count, 1);
(lt.span.shrink_to_hi(), format!("{} ", existing_name))
}
MissingLifetimeKind::Comma => {
let sugg: String = std::iter::repeat([existing_name.as_str(), ", "])
.take(lt.count)
.flatten()
.collect();
(lt.span.shrink_to_hi(), sugg)
}
MissingLifetimeKind::Brackets => {
let sugg: String = std::iter::once("<")
.chain(
std::iter::repeat(existing_name.as_str()).take(lt.count).intersperse(", "),
)
.chain([">"])
.collect();
(lt.span.shrink_to_hi(), sugg)
}
};
for &lt in &lifetime_refs {
spans_suggs.push(build_sugg(lt));
}
debug!(?spans_suggs);
match in_scope_lifetimes.len() {
0 => {
if let Some((param_lifetimes, _)) = function_param_lifetimes {
for lt in param_lifetimes {
spans_suggs.push(build_sugg(lt))
}
}
self.suggest_introducing_lifetime(
err,
None,
|err, higher_ranked, span, message, intro_sugg| {
err.multipart_suggestion_verbose(
message,
std::iter::once((span, intro_sugg))
.chain(spans_suggs.clone())
.collect(),
Applicability::MaybeIncorrect,
);
higher_ranked
},
);
}
1 => {
err.multipart_suggestion_verbose(
&format!("consider using the `{}` lifetime", existing_name),
spans_suggs,
Applicability::MaybeIncorrect,
);
// Record as using the suggested resolution.
let (_, (_, res)) = in_scope_lifetimes[0];
for &lt in &lifetime_refs {
self.r.lifetimes_res_map.insert(lt.id, res);
}
}
_ => {
let lifetime_spans: Vec<_> =
in_scope_lifetimes.iter().map(|(ident, _)| ident.span).collect();
err.span_note(lifetime_spans, "these named lifetimes are available to use");
if spans_suggs.len() > 0 {
// 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.
err.multipart_suggestion_verbose(
"consider using one of the available lifetimes here",
spans_suggs,
Applicability::HasPlaceholders,
);
}
}
}
}
}
/// Report lifetime/lifetime shadowing as an error.
@ -2134,534 +2372,3 @@ pub fn signal_label_shadowing(sess: &Session, orig: Span, shadower: Ident) {
err.span_label(shadower, format!("label `{}` already in scope", name));
err.emit();
}
impl<'tcx> LifetimeContext<'_, 'tcx> {
pub(crate) fn report_missing_lifetime_specifiers(
&self,
spans: Vec<Span>,
count: usize,
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
struct_span_err!(
self.tcx.sess,
spans,
E0106,
"missing lifetime specifier{}",
pluralize!(count)
)
}
/// Returns whether to add `'static` lifetime to the suggested lifetime list.
pub(crate) fn report_elision_failure(
&self,
diag: &mut Diagnostic,
params: &[ElisionFailureInfo],
) -> bool {
let mut m = String::new();
let len = params.len();
let elided_params: Vec<_> =
params.iter().cloned().filter(|info| info.lifetime_count > 0).collect();
let elided_len = elided_params.len();
for (i, info) in elided_params.into_iter().enumerate() {
let ElisionFailureInfo { parent, index, lifetime_count: n, have_bound_regions, span } =
info;
diag.span_label(span, "");
let help_name = if let Some(ident) =
parent.and_then(|body| self.tcx.hir().body(body).params[index].pat.simple_ident())
{
format!("`{}`", ident)
} else {
format!("argument {}", index + 1)
};
m.push_str(
&(if n == 1 {
help_name
} else {
format!(
"one of {}'s {} {}lifetimes",
help_name,
n,
if have_bound_regions { "free " } else { "" }
)
})[..],
);
if elided_len == 2 && i == 0 {
m.push_str(" or ");
} else if i + 2 == elided_len {
m.push_str(", or ");
} else if i != elided_len - 1 {
m.push_str(", ");
}
}
if len == 0 {
diag.help(
"this function's return type contains a borrowed value, \
but there is no value for it to be borrowed from",
);
true
} else if elided_len == 0 {
diag.help(
"this function's return type contains a borrowed value with \
an elided lifetime, but the lifetime cannot be derived from \
the arguments",
);
true
} else if elided_len == 1 {
diag.help(&format!(
"this function's return type contains a borrowed value, \
but the signature does not say which {} it is borrowed from",
m
));
false
} else {
diag.help(&format!(
"this function's return type contains a borrowed value, \
but the signature does not say whether it is borrowed from {}",
m
));
false
}
}
pub(crate) fn is_trait_ref_fn_scope(
&mut self,
trait_ref: &'tcx hir::PolyTraitRef<'tcx>,
) -> bool {
if let def::Res::Def(_, did) = trait_ref.trait_ref.path.res {
if [
self.tcx.lang_items().fn_once_trait(),
self.tcx.lang_items().fn_trait(),
self.tcx.lang_items().fn_mut_trait(),
]
.contains(&Some(did))
{
let (span, span_type) = if let Some(bound) =
trait_ref.bound_generic_params.iter().rfind(|param| {
matches!(
param.kind,
hir::GenericParamKind::Lifetime {
kind: hir::LifetimeParamKind::Explicit
}
)
}) {
(bound.span.shrink_to_hi(), ForLifetimeSpanType::BoundTail)
} else {
(trait_ref.span.shrink_to_lo(), ForLifetimeSpanType::BoundEmpty)
};
self.missing_named_lifetime_spots
.push(MissingLifetimeSpot::HigherRanked { span, span_type });
return true;
}
};
false
}
pub(crate) fn add_missing_lifetime_specifiers_label(
&self,
err: &mut Diagnostic,
mut spans_with_counts: Vec<(Span, usize)>,
in_scope_lifetimes: FxIndexSet<LocalDefId>,
params: Option<&[ElisionFailureInfo]>,
) {
let (mut lifetime_names, lifetime_spans): (FxHashSet<_>, Vec<_>) = in_scope_lifetimes
.iter()
.filter_map(|def_id| {
let name = self.tcx.item_name(def_id.to_def_id());
let span = self.tcx.def_ident_span(def_id.to_def_id())?;
Some((name, span))
})
.filter(|&(n, _)| n != kw::UnderscoreLifetime)
.unzip();
if let Some(params) = params {
// If there's no lifetime available, suggest `'static`.
if self.report_elision_failure(err, params) && lifetime_names.is_empty() {
lifetime_names.insert(kw::StaticLifetime);
}
}
let params = params.unwrap_or(&[]);
let snippets: Vec<Option<String>> = spans_with_counts
.iter()
.map(|(span, _)| self.tcx.sess.source_map().span_to_snippet(*span).ok())
.collect();
// Empty generics are marked with a span of "<", but since from now on
// that information is in the snippets it can be removed from the spans.
for ((span, _), snippet) in spans_with_counts.iter_mut().zip(&snippets) {
if snippet.as_deref() == Some("<") {
*span = span.shrink_to_hi();
}
}
for &(span, count) in &spans_with_counts {
err.span_label(
span,
format!(
"expected {} lifetime parameter{}",
if count == 1 { "named".to_string() } else { count.to_string() },
pluralize!(count),
),
);
}
let suggest_existing =
|err: &mut Diagnostic,
name: Symbol,
formatters: Vec<Option<Box<dyn Fn(Symbol) -> 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)
.map(|lt| Symbol::intern(&lt))
.find(|lt| !lifetime_names.contains(lt))
.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(&lt_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 let Some(stripped) = snippet.strip_prefix("&'_ ") {
introduce_suggestion
.push((param.span, format!("&{} {}", lt_name, stripped)));
}
}
}
introduce_suggestion.push((*for_span, for_sugg));
for ((span, _), formatter) in spans_with_counts.iter().zip(formatters.iter()) {
if let Some(formatter) = formatter {
introduce_suggestion.push((*span, formatter(lt_name)));
}
}
err.multipart_suggestion_verbose(
&msg,
introduce_suggestion,
Applicability::MaybeIncorrect,
);
}
let spans_suggs: Vec<_> = formatters
.into_iter()
.zip(spans_with_counts.iter())
.filter_map(|(formatter, (span, _))| {
if let Some(formatter) = formatter {
Some((*span, formatter(name)))
} else {
None
}
})
.collect();
if spans_suggs.is_empty() {
// If all the spans come from macros, we cannot extract snippets and then
// `formatters` only contains None and `spans_suggs` is empty.
return;
}
err.multipart_suggestion_verbose(
&format!(
"consider using the `{}` lifetime",
lifetime_names.iter().next().unwrap()
),
spans_suggs,
Applicability::MaybeIncorrect,
);
};
let suggest_new = |err: &mut Diagnostic, suggs: Vec<Option<String>>| {
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) => {
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
// nonsensical suggestions. Guard against that by skipping these.
// (#74264)
continue;
}
msg = "consider introducing a named lifetime parameter".to_string();
should_break = true;
if let Some(param) = generics.params.iter().find(|p| {
!matches!(
p.kind,
hir::GenericParamKind::Type { synthetic: true, .. }
| hir::GenericParamKind::Lifetime {
kind: hir::LifetimeParamKind::Elided
}
)
}) {
(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"))
}
MissingLifetimeSpot::Static => {
let mut spans_suggs = Vec::new();
for ((span, count), snippet) in
spans_with_counts.iter().copied().zip(snippets.iter())
{
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 if snippet == "<" || snippet == "(" {
(
span.shrink_to_hi(),
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,
};
spans_suggs.push((span, sugg.to_string()));
}
err.multipart_suggestion_verbose(
"consider using the `'static` lifetime",
spans_suggs,
Applicability::MaybeIncorrect,
);
continue;
}
});
struct Lifetime(Span, String);
impl Lifetime {
fn is_unnamed(&self) -> bool {
self.1.starts_with('&') && !self.1.starts_with("&'")
}
fn is_underscore(&self) -> bool {
self.1.starts_with("&'_ ")
}
fn is_named(&self) -> bool {
self.1.starts_with("&'")
}
fn suggestion(&self, sugg: String) -> Option<(Span, String)> {
Some(
match (
self.is_unnamed(),
self.is_underscore(),
self.is_named(),
sugg.starts_with('&'),
) {
(true, _, _, false) => (self.span_unnamed_borrow(), sugg),
(true, _, _, true) => {
(self.span_unnamed_borrow(), sugg[1..].to_string())
}
(_, true, _, false) => {
(self.span_underscore_borrow(), sugg.trim().to_string())
}
(_, true, _, true) => {
(self.span_underscore_borrow(), sugg[1..].trim().to_string())
}
(_, _, true, false) => {
(self.span_named_borrow(), sugg.trim().to_string())
}
(_, _, true, true) => {
(self.span_named_borrow(), sugg[1..].trim().to_string())
}
_ => return None,
},
)
}
fn span_unnamed_borrow(&self) -> Span {
let lo = self.0.lo() + BytePos(1);
self.0.with_lo(lo).with_hi(lo)
}
fn span_named_borrow(&self) -> Span {
let lo = self.0.lo() + BytePos(1);
self.0.with_lo(lo)
}
fn span_underscore_borrow(&self) -> Span {
let lo = self.0.lo() + BytePos(1);
let hi = lo + BytePos(2);
self.0.with_lo(lo).with_hi(hi)
}
}
for param in params {
if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(param.span) {
if let Some((span, sugg)) =
Lifetime(param.span, snippet).suggestion("'a ".to_string())
{
introduce_suggestion.push((span, sugg));
}
}
}
for (span, sugg) in spans_with_counts.iter().copied().zip(suggs.iter()).filter_map(
|((span, _), sugg)| match &sugg {
Some(sugg) => Some((span, sugg.to_string())),
_ => None,
},
) {
let (span, sugg) = self
.tcx
.sess
.source_map()
.span_to_snippet(span)
.ok()
.and_then(|snippet| Lifetime(span, snippet).suggestion(sugg.clone()))
.unwrap_or((span, sugg));
introduce_suggestion.push((span, sugg.to_string()));
}
err.multipart_suggestion_verbose(
&msg,
introduce_suggestion,
Applicability::MaybeIncorrect,
);
if should_break {
break;
}
}
};
let lifetime_names: Vec<_> = lifetime_names.iter().collect();
match &lifetime_names[..] {
[name] => {
let mut suggs: Vec<Option<Box<dyn Fn(Symbol) -> String>>> = Vec::new();
for (snippet, (_, count)) in snippets.iter().zip(spans_with_counts.iter().copied())
{
suggs.push(match snippet.as_deref() {
Some("&") => Some(Box::new(|name| format!("&{} ", name))),
Some("'_") => Some(Box::new(|n| n.to_string())),
Some("") => Some(Box::new(move |n| format!("{}, ", n).repeat(count))),
Some("<") => Some(Box::new(move |n| {
std::iter::repeat(n)
.take(count)
.map(|n| n.to_string())
.collect::<Vec<_>>()
.join(", ")
})),
Some(snippet) if !snippet.ends_with('>') => Some(Box::new(move |name| {
format!(
"{}<{}>",
snippet,
std::iter::repeat(name.to_string())
.take(count)
.collect::<Vec<_>>()
.join(", ")
)
})),
_ => None,
});
}
suggest_existing(err, **name, suggs);
}
[] => {
let mut suggs = Vec::new();
for (snippet, (_, count)) in
snippets.iter().cloned().zip(spans_with_counts.iter().copied())
{
suggs.push(match snippet.as_deref() {
Some("&") => Some("&'a ".to_string()),
Some("'_") => Some("'a".to_string()),
Some("") => {
Some(std::iter::repeat("'a, ").take(count).collect::<Vec<_>>().join(""))
}
Some("<") => {
Some(std::iter::repeat("'a").take(count).collect::<Vec<_>>().join(", "))
}
Some(snippet) => Some(format!(
"{}<{}>",
snippet,
std::iter::repeat("'a").take(count).collect::<Vec<_>>().join(", "),
)),
None => None,
});
}
suggest_new(err, suggs);
}
lts if lts.len() > 1 => {
err.span_note(lifetime_spans, "these named lifetimes are available to use");
let mut spans_suggs: Vec<_> = Vec::new();
for ((span, _), snippet) in spans_with_counts.iter().copied().zip(snippets.iter()) {
match snippet.as_deref() {
Some("") => spans_suggs.push((span, "'lifetime, ".to_string())),
Some("&") => spans_suggs
.push((span.with_lo(span.lo() + BytePos(1)), "'lifetime ".to_string())),
_ => {}
}
}
if spans_suggs.len() > 0 {
// 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.
err.multipart_suggestion_verbose(
"consider using one of the available lifetimes here",
spans_suggs,
Applicability::HasPlaceholders,
);
}
}
_ => unreachable!(),
}
}
}

View file

@ -6,7 +6,6 @@
//! used between functions, and they operate in a purely top-down
//! way. Therefore, we break lifetime name resolution into a separate pass.
use crate::late::diagnostics::{ForLifetimeSpanType, MissingLifetimeSpot};
use rustc_ast::walk_list;
use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
use rustc_errors::struct_span_err;
@ -14,13 +13,12 @@ use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::{DefIdMap, LocalDefId};
use rustc_hir::intravisit::{self, Visitor};
use rustc_hir::{GenericArg, GenericParam, LifetimeName, Node};
use rustc_hir::{GenericParamKind, HirIdMap, LifetimeParamKind};
use rustc_hir::{GenericArg, GenericParam, GenericParamKind, HirIdMap, LifetimeName, Node};
use rustc_middle::bug;
use rustc_middle::hir::map::Map;
use rustc_middle::hir::nested_filter;
use rustc_middle::middle::resolve_lifetime::*;
use rustc_middle::ty::{self, GenericParamDefKind, TyCtxt};
use rustc_middle::{bug, span_bug};
use rustc_span::def_id::DefId;
use rustc_span::symbol::{kw, sym, Ident};
use rustc_span::Span;
@ -152,10 +150,6 @@ pub(crate) struct LifetimeContext<'a, 'tcx> {
/// Cache for cross-crate per-definition object lifetime defaults.
xcrate_object_lifetime_defaults: DefIdMap<Vec<ObjectLifetimeDefault>>,
/// When encountering an undefined named lifetime, we will suggest introducing it in these
/// places.
pub(crate) missing_named_lifetime_spots: Vec<MissingLifetimeSpot<'tcx>>,
}
#[derive(Debug)]
@ -323,23 +317,12 @@ enum Elide {
/// Always use this one lifetime.
Exact(Region),
/// Less or more than one lifetime were found, error on unspecified.
Error(Vec<ElisionFailureInfo>),
Error,
/// Forbid lifetime elision inside of a larger scope where it would be
/// permitted. For example, in let position impl trait.
Forbid,
}
#[derive(Clone, Debug)]
pub(crate) struct ElisionFailureInfo {
/// Where we can find the argument pattern.
pub(crate) parent: Option<hir::BodyId>,
/// The index of the argument in the original definition.
pub(crate) index: usize,
pub(crate) lifetime_count: usize,
pub(crate) have_bound_regions: bool,
pub(crate) span: Span,
}
type ScopeRef<'a> = &'a Scope<'a>;
const ROOT_SCOPE: ScopeRef<'static> = &Scope::Root;
@ -421,7 +404,6 @@ fn do_resolve(
scope: ROOT_SCOPE,
trait_definition_only,
xcrate_object_lifetime_defaults: Default::default(),
missing_named_lifetime_spots: vec![],
};
visitor.visit_item(item);
@ -644,40 +626,11 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
where_bound_origin: None,
};
if let &hir::ClosureBinder::For { span, .. } = binder {
let last_lt = bound_generic_params
.iter()
.filter(|p| {
matches!(
p,
GenericParam {
kind: GenericParamKind::Lifetime {
kind: LifetimeParamKind::Explicit
},
..
}
)
})
.last();
let (span, span_type) = match last_lt {
Some(GenericParam { span: last_sp, .. }) => {
(last_sp.shrink_to_hi(), ForLifetimeSpanType::ClosureTail)
}
None => (span, ForLifetimeSpanType::ClosureEmpty),
};
self.missing_named_lifetime_spots
.push(MissingLifetimeSpot::HigherRanked { span, span_type });
}
self.with(scope, |this| {
// a closure has no bounds, so everything
// contained within is scoped within its binder.
intravisit::walk_expr(this, e)
});
if let hir::ClosureBinder::For { .. } = binder {
self.missing_named_lifetime_spots.pop();
}
} else {
intravisit::walk_expr(self, e)
}
@ -694,11 +647,9 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
}
match item.kind {
hir::ItemKind::Fn(_, ref generics, _) => {
self.missing_named_lifetime_spots.push(generics.into());
self.visit_early_late(None, item.hir_id(), generics, |this| {
intravisit::walk_item(this, item);
});
self.missing_named_lifetime_spots.pop();
}
hir::ItemKind::ExternCrate(_)
@ -761,8 +712,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
| hir::ItemKind::Trait(_, _, ref generics, ..)
| hir::ItemKind::TraitAlias(ref generics, ..)
| hir::ItemKind::Impl(hir::Impl { ref generics, .. }) => {
self.missing_named_lifetime_spots.push(generics.into());
// These kinds of items have only early-bound lifetime parameters.
let mut index = if sub_items_have_self_param(&item.kind) {
1 // Self comes before lifetimes
@ -800,7 +749,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
intravisit::walk_item(this, item);
});
});
self.missing_named_lifetime_spots.pop();
}
}
}
@ -826,20 +774,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
match ty.kind {
hir::TyKind::BareFn(ref c) => {
let next_early_index = self.next_early_index();
let lifetime_span: Option<Span> =
c.generic_params.iter().rev().find_map(|param| match param.kind {
GenericParamKind::Lifetime { kind: hir::LifetimeParamKind::Explicit } => {
Some(param.span)
}
_ => None,
});
let (span, span_type) = if let Some(span) = lifetime_span {
(span.shrink_to_hi(), ForLifetimeSpanType::TypeTail)
} else {
(ty.span.shrink_to_lo(), ForLifetimeSpanType::TypeEmpty)
};
self.missing_named_lifetime_spots
.push(MissingLifetimeSpot::HigherRanked { span, span_type });
let (lifetimes, binders): (FxIndexMap<LocalDefId, Region>, Vec<_>) = c
.generic_params
.iter()
@ -867,7 +801,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
// contained within is scoped within its binder.
intravisit::walk_ty(this, ty);
});
self.missing_named_lifetime_spots.pop();
}
hir::TyKind::TraitObject(bounds, ref lifetime, _) => {
debug!(?bounds, ?lifetime, "TraitObject");
@ -878,11 +811,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
}
});
match lifetime.name {
LifetimeName::Implicit => {
// For types like `dyn Foo`, we should
// generate a special form of elided.
span_bug!(ty.span, "object-lifetime-default expected, not implicit",);
}
LifetimeName::ImplicitObjectLifetimeDefault => {
// If the user does not write *anything*, we
// use the object lifetime defaulting
@ -890,7 +818,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
// `Box<dyn Debug + 'static>`.
self.resolve_object_lifetime_default(lifetime)
}
LifetimeName::Underscore => {
LifetimeName::Implicit | LifetimeName::Underscore => {
// If the user writes `'_`, we use the *ordinary* elision
// rules. So the `'_` in e.g., `Box<dyn Debug + '_>` will be
// resolved the same as the `'_` in `&'_ Foo`.
@ -1010,10 +938,12 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
let mut elision = None;
let mut lifetimes = FxIndexMap::default();
let mut non_lifetime_count = 0;
debug!(?generics.params);
for param in generics.params {
match param.kind {
GenericParamKind::Lifetime { .. } => {
let (def_id, reg) = Region::early(self.tcx.hir(), &mut index, &param);
lifetimes.insert(def_id, reg);
if let hir::ParamName::Plain(Ident {
name: kw::UnderscoreLifetime,
..
@ -1022,8 +952,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
// Pick the elided lifetime "definition" if one exists
// and use it to make an elision scope.
elision = Some(reg);
} else {
lifetimes.insert(def_id, reg);
}
}
GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => {
@ -1088,7 +1016,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
use self::hir::TraitItemKind::*;
match trait_item.kind {
Fn(_, _) => {
self.missing_named_lifetime_spots.push((&trait_item.generics).into());
let tcx = self.tcx;
self.visit_early_late(
Some(tcx.hir().get_parent_item(trait_item.hir_id())),
@ -1096,10 +1023,8 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
&trait_item.generics,
|this| intravisit::walk_trait_item(this, trait_item),
);
self.missing_named_lifetime_spots.pop();
}
Type(bounds, ref ty) => {
self.missing_named_lifetime_spots.push((&trait_item.generics).into());
let generics = &trait_item.generics;
let mut index = self.next_early_index();
debug!("visit_ty: index = {}", index);
@ -1140,14 +1065,11 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
}
})
});
self.missing_named_lifetime_spots.pop();
}
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();
}
}
}
@ -1156,7 +1078,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
use self::hir::ImplItemKind::*;
match impl_item.kind {
Fn(..) => {
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())),
@ -1164,11 +1085,9 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
&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);
@ -1203,14 +1122,11 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
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();
}
}
}
@ -1393,8 +1309,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
) {
debug!("visit_poly_trait_ref(trait_ref={:?})", trait_ref);
let should_pop_missing_lt = self.is_trait_ref_fn_scope(trait_ref);
let next_early_index = self.next_early_index();
let (mut binders, scope_type) = self.poly_trait_ref_binder_info();
@ -1435,10 +1349,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
walk_list!(this, visit_generic_param, trait_ref.bound_generic_params);
this.visit_trait_ref(&trait_ref.trait_ref);
});
if should_pop_missing_lt {
self.missing_named_lifetime_spots.pop();
}
}
}
@ -1584,14 +1494,12 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
{
let LifetimeContext { tcx, map, .. } = self;
let xcrate_object_lifetime_defaults = take(&mut self.xcrate_object_lifetime_defaults);
let missing_named_lifetime_spots = take(&mut self.missing_named_lifetime_spots);
let mut this = LifetimeContext {
tcx: *tcx,
map,
scope: &wrap_scope,
trait_definition_only: self.trait_definition_only,
xcrate_object_lifetime_defaults,
missing_named_lifetime_spots,
};
let span = tracing::debug_span!("scope", scope = ?TruncatedScopeDebug(&this.scope));
{
@ -1599,7 +1507,6 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
f(&mut this);
}
self.xcrate_object_lifetime_defaults = this.xcrate_object_lifetime_defaults;
self.missing_named_lifetime_spots = this.missing_named_lifetime_spots;
}
/// Visits self by adding a scope and handling recursive walk over the contents with `walk`.
@ -2202,24 +2109,20 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
let mut assoc_item_kind = None;
let mut impl_self = None;
let parent = self.tcx.hir().get_parent_node(output.hir_id);
let body = match self.tcx.hir().get(parent) {
match self.tcx.hir().get(parent) {
// `fn` definitions and methods.
Node::Item(&hir::Item { kind: hir::ItemKind::Fn(.., body), .. }) => Some(body),
Node::Item(&hir::Item { kind: hir::ItemKind::Fn(..), .. }) => {}
Node::TraitItem(&hir::TraitItem { kind: hir::TraitItemKind::Fn(_, ref m), .. }) => {
Node::TraitItem(&hir::TraitItem { kind: hir::TraitItemKind::Fn(..), .. }) => {
if let hir::ItemKind::Trait(.., ref trait_items) =
self.tcx.hir().expect_item(self.tcx.hir().get_parent_item(parent)).kind
{
assoc_item_kind =
trait_items.iter().find(|ti| ti.id.hir_id() == parent).map(|ti| ti.kind);
}
match *m {
hir::TraitFn::Required(_) => None,
hir::TraitFn::Provided(body) => Some(body),
}
}
Node::ImplItem(&hir::ImplItem { kind: hir::ImplItemKind::Fn(_, body), .. }) => {
Node::ImplItem(&hir::ImplItem { kind: hir::ImplItemKind::Fn(_, _), .. }) => {
if let hir::ItemKind::Impl(hir::Impl { ref self_ty, ref items, .. }) =
self.tcx.hir().expect_item(self.tcx.hir().get_parent_item(parent)).kind
{
@ -2227,13 +2130,12 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
assoc_item_kind =
items.iter().find(|ii| ii.id.hir_id() == parent).map(|ii| ii.kind);
}
Some(body)
}
// Foreign functions, `fn(...) -> R` and `Trait(...) -> R` (both types and bounds).
Node::ForeignItem(_) | Node::Ty(_) | Node::TraitRef(_) => None,
Node::ForeignItem(_) | Node::Ty(_) | Node::TraitRef(_) => {},
Node::TypeBinding(_) if let Node::TraitRef(_) = self.tcx.hir().get(self.tcx.hir().get_parent_node(parent)) => None,
Node::TypeBinding(_) if let Node::TraitRef(_) = self.tcx.hir().get(self.tcx.hir().get_parent_node(parent)) => {},
// Everything else (only closures?) doesn't
// actually enjoy elision in return types.
@ -2320,42 +2222,29 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
// have that lifetime.
let mut possible_implied_output_region = None;
let mut lifetime_count = 0;
let arg_lifetimes = inputs
.iter()
.enumerate()
.skip(has_self as usize)
.map(|(i, input)| {
let mut gather = GatherLifetimes {
map: self.map,
outer_index: ty::INNERMOST,
have_bound_regions: false,
lifetimes: Default::default(),
};
gather.visit_ty(input);
for input in inputs.iter().skip(has_self as usize) {
let mut gather = GatherLifetimes {
map: self.map,
outer_index: ty::INNERMOST,
have_bound_regions: false,
lifetimes: Default::default(),
};
gather.visit_ty(input);
lifetime_count += gather.lifetimes.len();
lifetime_count += gather.lifetimes.len();
if lifetime_count == 1 && gather.lifetimes.len() == 1 {
// there's a chance that the unique lifetime of this
// iteration will be the appropriate lifetime for output
// parameters, so lets store it.
possible_implied_output_region = gather.lifetimes.iter().cloned().next();
}
ElisionFailureInfo {
parent: body,
index: i,
lifetime_count: gather.lifetimes.len(),
have_bound_regions: gather.have_bound_regions,
span: input.span,
}
})
.collect();
if lifetime_count == 1 && gather.lifetimes.len() == 1 {
// there's a chance that the unique lifetime of this
// iteration will be the appropriate lifetime for output
// parameters, so lets store it.
possible_implied_output_region = gather.lifetimes.iter().cloned().next();
}
}
let elide = if lifetime_count == 1 {
Elide::Exact(possible_implied_output_region.unwrap())
} else {
Elide::Error(arg_lifetimes)
Elide::Error
};
debug!(?elide);
@ -2487,17 +2376,14 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
let mut late_depth = 0;
let mut scope = self.scope;
let mut in_scope_lifetimes = FxIndexSet::default();
let error = loop {
loop {
match *scope {
// Do not assign any resolution, it will be inferred.
Scope::Body { .. } => return,
Scope::Root => break None,
Scope::Root => break,
Scope::Binder { s, ref lifetimes, scope_type, .. } => {
// collect named lifetimes for suggestions
in_scope_lifetimes.extend(lifetimes.keys().copied());
Scope::Binder { s, scope_type, .. } => {
match scope_type {
BinderScopeType::Normal => late_depth += 1,
BinderScopeType::Concatenating => {}
@ -2526,27 +2412,8 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
return;
}
Scope::Elision { elide: Elide::Error(ref e), ref s, .. } => {
let mut scope = s;
loop {
match scope {
Scope::Binder { ref lifetimes, s, .. } => {
// Collect named lifetimes for suggestions.
in_scope_lifetimes.extend(lifetimes.keys().copied());
scope = s;
}
Scope::ObjectLifetimeDefault { ref s, .. }
| Scope::Elision { ref s, .. }
| Scope::TraitRefBoundary { ref s, .. } => {
scope = s;
}
_ => break,
}
}
break Some(&e[..]);
}
Scope::Elision { elide: Elide::Forbid, .. } => break None,
Scope::Elision { elide: Elide::Error, .. }
| Scope::Elision { elide: Elide::Forbid, .. } => break,
Scope::ObjectLifetimeDefault { s, .. }
| Scope::Supertrait { s, .. }
@ -2554,26 +2421,11 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
scope = s;
}
}
};
}
let mut spans: Vec<_> = lifetime_refs.iter().map(|lt| lt.span).collect();
spans.sort();
let mut spans_dedup = spans.clone();
spans_dedup.dedup();
let spans_with_counts: Vec<_> = spans_dedup
.into_iter()
.map(|sp| (sp, spans.iter().filter(|nsp| *nsp == &sp).count()))
.collect();
let mut err = self.report_missing_lifetime_specifiers(spans.clone(), lifetime_refs.len());
self.add_missing_lifetime_specifiers_label(
&mut err,
spans_with_counts,
in_scope_lifetimes,
error,
);
err.emit();
for lt in lifetime_refs {
self.tcx.sess.delay_span_bug(lt.span, "Missing lifetime specifier");
}
}
fn resolve_object_lifetime_default(&mut self, lifetime_ref: &'tcx hir::Lifetime) {