]> git.lizzy.rs Git - rust.git/blobdiff - compiler/rustc_resolve/src/late/diagnostics.rs
Rollup merge of #99729 - cjgillot:rm-unused-tuple, r=michaelwoerister
[rust.git] / compiler / rustc_resolve / src / late / diagnostics.rs
index e428bae479bc2f4d70258cfd172203db8502acde..6b49c6b1ac63eeaeb66086678a5c6e06d302791e 100644 (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 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: 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 {
@@ -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,552 +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<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 &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 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 &lt 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: 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)));
-                            }
-                        }
+        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(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()));
-                        }
+                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(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,
-                    });
+                // 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);
                 }
-                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 => {
+            _ => {
+                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.
@@ -2661,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();
+}