X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=compiler%2Frustc_resolve%2Fsrc%2Flate%2Fdiagnostics.rs;h=6b49c6b1ac63eeaeb66086678a5c6e06d302791e;hb=ddb6a463167aac560279ed746f276ef29f86250e;hp=1e53c73620a08a2f7a9b820ffe874b55726c2297;hpb=99f3132cd7a36b665aba4704fae457711eea929e;p=rust.git diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 1e53c73620a..6b49c6b1ac6 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -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 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_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::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 @@ fn action(&self) -> &'static str { } } -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> 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, + /// 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 { match def_id.krate { @@ -1434,7 +1444,7 @@ fn lookup_typo_candidate( // 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 @@ -2003,18 +2013,35 @@ pub(crate) fn emit_undeclared_lifetime_error( 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 @@ pub(crate) fn emit_undeclared_lifetime_error( 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,547 +2128,209 @@ pub(crate) fn maybe_emit_forbidden_non_static_lifetime_error( .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, - count: usize, - ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { - struct_span_err!( - self.tcx.sess, + &mut self, + lifetime_refs: Vec, + function_param_lifetimes: Option<(Vec, Vec)>, + ) -> 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, + function_param_lifetimes: Option<(Vec, Vec)>, + ) { + 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, - 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> = 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 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>| { - 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::>() - .join(", "), - ) - } else if snippet == "<" || snippet == "(" { - ( - span.shrink_to_hi(), - std::iter::repeat("'static") - .take(count) - .collect::>() - .join(", "), - ) - } else { - ( - span.shrink_to_hi(), - format!( - "<{}>", - std::iter::repeat("'static") - .take(count) - .collect::>() - .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 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::>().join(", ") - })), - Some(snippet) if !snippet.ends_with('>') => Some(Box::new(move |name| { - format!( - "{}<{}>", - snippet, - std::iter::repeat(name.to_string()) - .take(count) - .collect::>() - .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::>().join("")) - } - Some("<") => { - Some(std::iter::repeat("'a").take(count).collect::>().join(", ")) - } - Some(snippet) => Some(format!( - "{}<{}>", - snippet, - std::iter::repeat("'a").take(count).collect::>().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` where we point at the space before `T`, // but this can be confusing so we give a suggestion with placeholders. @@ -2656,7 +2341,34 @@ fn span_underscore_borrow(&self) -> Span { ); } } - _ => 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(); +}