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};
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,
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;
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;
}
}
-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: &str) -> 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
}
(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 {
// Items from this module
self.r.add_module_candidates(module, &mut names, &filter_fn);
- if let ModuleKind::Block(..) = module.kind {
+ if let ModuleKind::Block = module.kind {
// We can see through blocks
} else {
// Items from the prelude
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;
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) {
.emit();
}
}
-}
-/// Report lifetime/lifetime shadowing as an error.
-pub fn signal_lifetime_shadowing(sess: &Session, orig: Ident, shadower: Ident) {
- let mut err = struct_span_err!(
- sess,
- shadower.span,
- E0496,
- "lifetime name `{}` shadows a lifetime name that is already in scope",
- orig.name,
- );
- err.span_label(orig.span, "first declared here");
- err.span_label(shadower.span, format!("lifetime `{}` already in scope", orig.name));
- err.emit();
-}
-
-/// Shadowing involving a label is only a warning for historical reasons.
-//FIXME: make this a proper lint.
-pub fn signal_label_shadowing(sess: &Session, orig: Span, shadower: Ident) {
- let name = shadower.name;
- let shadower = shadower.span;
- let mut err = sess.struct_span_warn(
- shadower,
- &format!("label name `{}` shadows a label name that is already in scope", name),
- );
- err.span_label(orig, "first declared here");
- 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,
+ &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!(count)
- )
+ pluralize!(num_lifetimes)
+ );
+ self.add_missing_lifetime_specifiers_label(
+ &mut err,
+ lifetime_refs,
+ function_param_lifetimes,
+ );
+ err.emit()
}
- /// 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();
+ 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 < 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 elided_params: Vec<_> =
- params.iter().cloned().filter(|info| info.lifetime_count > 0).collect();
+ 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);
- let elided_len = elided_params.len();
+ debug!(?function_param_lifetimes);
+ if let Some((param_lifetimes, params)) = &function_param_lifetimes {
+ let elided_len = param_lifetimes.len();
+ let num_params = params.len();
- for (i, info) in elided_params.into_iter().enumerate() {
- let ElisionFailureInfo { parent, index, lifetime_count: n, have_bound_regions, span } =
- info;
+ let mut m = String::new();
- 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)
- };
+ for (i, info) in params.iter().enumerate() {
+ let ElisionFnParameter { ident, index, lifetime_count, span } = *info;
+ debug_assert_ne!(lifetime_count, 0);
- m.push_str(
- &(if n == 1 {
- help_name
+ 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!(
- "one of {}'s {} {}lifetimes",
- help_name,
- n,
- if have_bound_regions { "free " } else { "" }
- )
- })[..],
- );
+ format!("argument {}", index + 1)
+ };
- 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 lifetime_count == 1 {
+ m.push_str(&help_name[..])
+ } else {
+ m.push_str(&format!("one of {}'s {} lifetimes", help_name, lifetime_count)[..])
+ }
}
- }
- if len == 0 {
- diag.help(
- "this function's return type contains a borrowed value, \
+ 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",
- );
- true
- } else if elided_len == 0 {
- diag.help(
- "this function's return type contains a borrowed value with \
+ );
+ 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",
- );
- true
- } else if elided_len == 1 {
- diag.help(&format!(
- "this function's return type contains a borrowed value, \
+ );
+ 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
- ));
- false
- } else {
- diag.help(&format!(
- "this function's return type contains a borrowed value, \
+ 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
- ));
- false
+ m
+ ));
+ }
}
- }
- 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;
- }
+ let existing_name = match &in_scope_lifetimes[..] {
+ [] => Symbol::intern("'a"),
+ [(existing, _)] => existing.name,
+ _ => Symbol::intern("'lifetime"),
};
- 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 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())
}
- }
- 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();
+ 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 < in &lifetime_refs {
+ spans_suggs.push(build_sugg(lt));
}
-
- 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: &str,
- formatters: Vec<Option<Box<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 let Some(stripped) = snippet.strip_prefix("&'_ ") {
- introduce_suggestion
- .push((param.span, format!("&{} {}", lt_name, stripped)));
- }
- }
+ 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))
}
- 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(<_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()));
- }
+ self.suggest_introducing_lifetime(
+ err,
+ None,
+ |err, higher_ranked, span, message, intro_sugg| {
err.multipart_suggestion_verbose(
- "consider using the `'static` lifetime",
- spans_suggs,
+ message,
+ std::iter::once((span, intro_sugg))
+ .chain(spans_suggs.clone())
+ .collect(),
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,
+ higher_ranked
},
- ) {
- 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()));
- }
+ );
+ }
+ 1 => {
err.multipart_suggestion_verbose(
- &msg,
- introduce_suggestion,
+ &format!("consider using the `{}` lifetime", existing_name),
+ spans_suggs,
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(&str) -> 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).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,
- });
+ // Record as using the suggested resolution.
+ let (_, (_, res)) = in_scope_lifetimes[0];
+ for < in &lifetime_refs {
+ self.r.lifetimes_res_map.insert(lt.id, res);
}
- suggest_existing(err, name.as_str(), 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 => {
+ _ => {
+ 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");
- 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.
);
}
}
- _ => unreachable!(),
}
}
}
+
+/// Report lifetime/lifetime shadowing as an error.
+pub fn signal_lifetime_shadowing(sess: &Session, orig: Ident, shadower: Ident) {
+ let mut err = struct_span_err!(
+ sess,
+ shadower.span,
+ E0496,
+ "lifetime name `{}` shadows a lifetime name that is already in scope",
+ orig.name,
+ );
+ err.span_label(orig.span, "first declared here");
+ err.span_label(shadower.span, format!("lifetime `{}` already in scope", orig.name));
+ err.emit();
+}
+
+/// Shadowing involving a label is only a warning for historical reasons.
+//FIXME: make this a proper lint.
+pub fn signal_label_shadowing(sess: &Session, orig: Span, shadower: Ident) {
+ let name = shadower.name;
+ let shadower = shadower.span;
+ let mut err = sess.struct_span_warn(
+ shadower,
+ &format!("label name `{}` shadows a label name that is already in scope", name),
+ );
+ err.span_label(orig, "first declared here");
+ err.span_label(shadower, format!("label `{}` already in scope", name));
+ err.emit();
+}