about summary refs log tree commit diff
path: root/compiler
diff options
context:
space:
mode:
authorUrgau <urgau@numericable.fr>2023-12-30 16:13:23 +0100
committerUrgau <urgau@numericable.fr>2023-12-30 16:17:14 +0100
commit429287243bb2bae9f4b1abda963b6de27140307c (patch)
tree9749ef3d5a09ef21e359d32a7b4112acdba39568 /compiler
parentd59f06fc64844ec2073e5e888f7470989ef25ff9 (diff)
downloadrust-429287243bb2bae9f4b1abda963b6de27140307c.tar.gz
rust-429287243bb2bae9f4b1abda963b6de27140307c.zip
Move around the code responsible for decorating builtin diagnostics
Diffstat (limited to 'compiler')
-rw-r--r--compiler/rustc_lint/src/context.rs450
-rw-r--r--compiler/rustc_lint/src/context/diagnostics.rs532
2 files changed, 538 insertions, 444 deletions
diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs
index bd86a0be676..88a02917f36 100644
--- a/compiler/rustc_lint/src/context.rs
+++ b/compiler/rustc_lint/src/context.rs
@@ -18,34 +18,32 @@ use self::TargetLint::*;
 
 use crate::levels::LintLevelsBuilder;
 use crate::passes::{EarlyLintPassObject, LateLintPassObject};
-use rustc_ast::util::unicode::TEXT_FLOW_CONTROL_CHARS;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::sync;
-use rustc_errors::{add_elided_lifetime_in_path_suggestion, DiagnosticBuilder, DiagnosticMessage};
-use rustc_errors::{Applicability, DecorateLint, MultiSpan, SuggestionStyle};
+use rustc_errors::{DecorateLint, DiagnosticBuilder, DiagnosticMessage, MultiSpan};
 use rustc_feature::Features;
 use rustc_hir as hir;
 use rustc_hir::def::Res;
 use rustc_hir::def_id::{CrateNum, DefId};
 use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData};
 use rustc_middle::middle::privacy::EffectiveVisibilities;
-use rustc_middle::middle::stability;
 use rustc_middle::ty::layout::{LayoutError, LayoutOfHelpers, TyAndLayout};
 use rustc_middle::ty::print::{with_no_trimmed_paths, PrintError};
 use rustc_middle::ty::{self, print::Printer, GenericArg, RegisteredTools, Ty, TyCtxt};
-use rustc_session::config::ExpectedValues;
 use rustc_session::lint::{BuiltinLintDiagnostics, LintExpectationId};
 use rustc_session::lint::{FutureIncompatibleInfo, Level, Lint, LintBuffer, LintId};
 use rustc_session::{LintStoreMarker, Session};
 use rustc_span::edit_distance::find_best_match_for_name;
 use rustc_span::symbol::{sym, Ident, Symbol};
-use rustc_span::{BytePos, Span};
+use rustc_span::Span;
 use rustc_target::abi;
 
 use std::cell::Cell;
 use std::iter;
 use std::slice;
 
+mod diagnostics;
+
 type EarlyLintPassFactory = dyn Fn() -> EarlyLintPassObject + sync::DynSend + sync::DynSync;
 type LateLintPassFactory =
     dyn for<'tcx> Fn(TyCtxt<'tcx>) -> LateLintPassObject<'tcx> + sync::DynSend + sync::DynSync;
@@ -531,445 +529,9 @@ pub trait LintContext {
         diagnostic: BuiltinLintDiagnostics,
     ) {
         // We first generate a blank diagnostic.
-        self.lookup(lint, span, msg,|db| {
+        self.lookup(lint, span, msg, |db| {
             // Now, set up surrounding context.
-            let sess = self.sess();
-            match diagnostic {
-                BuiltinLintDiagnostics::UnicodeTextFlow(span, content) => {
-                    let spans: Vec<_> = content
-                        .char_indices()
-                        .filter_map(|(i, c)| {
-                            TEXT_FLOW_CONTROL_CHARS.contains(&c).then(|| {
-                                let lo = span.lo() + BytePos(2 + i as u32);
-                                (c, span.with_lo(lo).with_hi(lo + BytePos(c.len_utf8() as u32)))
-                            })
-                        })
-                        .collect();
-                    let (an, s) = match spans.len() {
-                        1 => ("an ", ""),
-                        _ => ("", "s"),
-                    };
-                    db.span_label(span, format!(
-                        "this comment contains {an}invisible unicode text flow control codepoint{s}",
-                    ));
-                    for (c, span) in &spans {
-                        db.span_label(*span, format!("{c:?}"));
-                    }
-                    db.note(
-                        "these kind of unicode codepoints change the way text flows on \
-                         applications that support them, but can cause confusion because they \
-                         change the order of characters on the screen",
-                    );
-                    if !spans.is_empty() {
-                        db.multipart_suggestion_with_style(
-                            "if their presence wasn't intentional, you can remove them",
-                            spans.into_iter().map(|(_, span)| (span, "".to_string())).collect(),
-                            Applicability::MachineApplicable,
-                            SuggestionStyle::HideCodeAlways,
-                        );
-                    }
-                },
-                BuiltinLintDiagnostics::Normal => (),
-                BuiltinLintDiagnostics::AbsPathWithModule(span) => {
-                    let (sugg, app) = match sess.source_map().span_to_snippet(span) {
-                        Ok(ref s) => {
-                            // FIXME(Manishearth) ideally the emitting code
-                            // can tell us whether or not this is global
-                            let opt_colon =
-                                if s.trim_start().starts_with("::") { "" } else { "::" };
-
-                            (format!("crate{opt_colon}{s}"), Applicability::MachineApplicable)
-                        }
-                        Err(_) => ("crate::<path>".to_string(), Applicability::HasPlaceholders),
-                    };
-                    db.span_suggestion(span, "use `crate`", sugg, app);
-                }
-                BuiltinLintDiagnostics::ProcMacroDeriveResolutionFallback(span) => {
-                    db.span_label(
-                        span,
-                        "names from parent modules are not accessible without an explicit import",
-                    );
-                }
-                BuiltinLintDiagnostics::MacroExpandedMacroExportsAccessedByAbsolutePaths(
-                    span_def,
-                ) => {
-                    db.span_note(span_def, "the macro is defined here");
-                }
-                BuiltinLintDiagnostics::ElidedLifetimesInPaths(
-                    n,
-                    path_span,
-                    incl_angl_brckt,
-                    insertion_span,
-                ) => {
-                    add_elided_lifetime_in_path_suggestion(
-                        sess.source_map(),
-                        db,
-                        n,
-                        path_span,
-                        incl_angl_brckt,
-                        insertion_span,
-                    );
-                }
-                BuiltinLintDiagnostics::UnknownCrateTypes(span, note, sugg) => {
-                    db.span_suggestion(span, note, sugg, Applicability::MaybeIncorrect);
-                }
-                BuiltinLintDiagnostics::UnusedImports(message, replaces, in_test_module) => {
-                    if !replaces.is_empty() {
-                        db.tool_only_multipart_suggestion(
-                            message,
-                            replaces,
-                            Applicability::MachineApplicable,
-                        );
-                    }
-
-                    if let Some(span) = in_test_module {
-                        db.span_help(
-                            self.sess().source_map().guess_head_span(span),
-                            "consider adding a `#[cfg(test)]` to the containing module",
-                        );
-                    }
-                }
-                BuiltinLintDiagnostics::RedundantImport(spans, ident) => {
-                    for (span, is_imported) in spans {
-                        let introduced = if is_imported { "imported" } else { "defined" };
-                        db.span_label(
-                            span,
-                            format!("the item `{ident}` is already {introduced} here"),
-                        );
-                    }
-                }
-                BuiltinLintDiagnostics::DeprecatedMacro(suggestion, span) => {
-                    stability::deprecation_suggestion(db, "macro", suggestion, span)
-                }
-                BuiltinLintDiagnostics::UnusedDocComment(span) => {
-                    db.span_label(span, "rustdoc does not generate documentation for macro invocations");
-                    db.help("to document an item produced by a macro, \
-                                  the macro must produce the documentation as part of its expansion");
-                }
-                BuiltinLintDiagnostics::PatternsInFnsWithoutBody(span, ident) => {
-                    db.span_suggestion(span, "remove `mut` from the parameter", ident, Applicability::MachineApplicable);
-                }
-                BuiltinLintDiagnostics::MissingAbi(span, default_abi) => {
-                    db.span_label(span, "ABI should be specified here");
-                    db.help(format!("the default ABI is {}", default_abi.name()));
-                }
-                BuiltinLintDiagnostics::LegacyDeriveHelpers(span) => {
-                    db.span_label(span, "the attribute is introduced here");
-                }
-                BuiltinLintDiagnostics::ProcMacroBackCompat(note) => {
-                    db.note(note);
-                }
-                BuiltinLintDiagnostics::OrPatternsBackCompat(span,suggestion) => {
-                    db.span_suggestion(span, "use pat_param to preserve semantics", suggestion, Applicability::MachineApplicable);
-                }
-                BuiltinLintDiagnostics::ReservedPrefix(span) => {
-                    db.span_label(span, "unknown prefix");
-                    db.span_suggestion_verbose(
-                        span.shrink_to_hi(),
-                        "insert whitespace here to avoid this being parsed as a prefix in Rust 2021",
-                        " ",
-                        Applicability::MachineApplicable,
-                    );
-                }
-                BuiltinLintDiagnostics::UnusedBuiltinAttribute {
-                    attr_name,
-                    macro_name,
-                    invoc_span
-                } => {
-                    db.span_note(
-                        invoc_span,
-                        format!("the built-in attribute `{attr_name}` will be ignored, since it's applied to the macro invocation `{macro_name}`")
-                    );
-                }
-                BuiltinLintDiagnostics::TrailingMacro(is_trailing, name) => {
-                    if is_trailing {
-                        db.note("macro invocations at the end of a block are treated as expressions");
-                        db.note(format!("to ignore the value produced by the macro, add a semicolon after the invocation of `{name}`"));
-                    }
-                }
-                BuiltinLintDiagnostics::BreakWithLabelAndLoop(span) => {
-                    db.multipart_suggestion(
-                        "wrap this expression in parentheses",
-                        vec![(span.shrink_to_lo(), "(".to_string()),
-                             (span.shrink_to_hi(), ")".to_string())],
-                        Applicability::MachineApplicable
-                    );
-                }
-                BuiltinLintDiagnostics::NamedAsmLabel(help) => {
-                    db.help(help);
-                    db.note("see the asm section of Rust By Example <https://doc.rust-lang.org/nightly/rust-by-example/unsafe/asm.html#labels> for more information");
-                },
-                BuiltinLintDiagnostics::UnexpectedCfgName((name, name_span), value) => {
-                    let possibilities: Vec<Symbol> = sess.parse_sess.check_config.expecteds.keys().copied().collect();
-                    let is_from_cargo = std::env::var_os("CARGO").is_some();
-                    let mut is_feature_cfg = name == sym::feature;
-
-                    if is_feature_cfg && is_from_cargo {
-                        db.help("consider defining some features in `Cargo.toml`");
-                    // Suggest the most probable if we found one
-                    } else if let Some(best_match) = find_best_match_for_name(&possibilities, name, None) {
-                        if let Some(ExpectedValues::Some(best_match_values)) =
-                            sess.parse_sess.check_config.expecteds.get(&best_match) {
-                            let mut possibilities = best_match_values.iter()
-                                .flatten()
-                                .map(Symbol::as_str)
-                                .collect::<Vec<_>>();
-                            possibilities.sort();
-
-                            let mut should_print_possibilities = true;
-                            if let Some((value, value_span)) = value {
-                                if best_match_values.contains(&Some(value)) {
-                                    db.span_suggestion(name_span, "there is a config with a similar name and value", best_match, Applicability::MaybeIncorrect);
-                                    should_print_possibilities = false;
-                                } else if best_match_values.contains(&None) {
-                                    db.span_suggestion(name_span.to(value_span), "there is a config with a similar name and no value", best_match, Applicability::MaybeIncorrect);
-                                    should_print_possibilities = false;
-                                } else if let Some(first_value) = possibilities.first() {
-                                    db.span_suggestion(name_span.to(value_span), "there is a config with a similar name and different values", format!("{best_match} = \"{first_value}\""), Applicability::MaybeIncorrect);
-                                } else {
-                                    db.span_suggestion(name_span.to(value_span), "there is a config with a similar name and different values", best_match, Applicability::MaybeIncorrect);
-                                };
-                            } else {
-                                db.span_suggestion(name_span, "there is a config with a similar name", best_match, Applicability::MaybeIncorrect);
-                            }
-
-                            if !possibilities.is_empty() && should_print_possibilities {
-                                let possibilities = possibilities.join("`, `");
-                                db.help(format!("expected values for `{best_match}` are: `{possibilities}`"));
-                            }
-                        } else {
-                            db.span_suggestion(name_span, "there is a config with a similar name", best_match, Applicability::MaybeIncorrect);
-                        }
-
-                        is_feature_cfg |= best_match == sym::feature;
-                    } else if !possibilities.is_empty() {
-                        let mut possibilities = possibilities.iter()
-                            .map(Symbol::as_str)
-                            .collect::<Vec<_>>();
-                        possibilities.sort();
-                        let possibilities = possibilities.join("`, `");
-
-                        // The list of expected names can be long (even by default) and
-                        // so the diagnostic produced can take a lot of space. To avoid
-                        // cloging the user output we only want to print that diagnostic
-                        // once.
-                        db.help_once(format!("expected names are: `{possibilities}`"));
-                    }
-
-                    let inst = if let Some((value, _value_span)) = value {
-                        let pre = if is_from_cargo { "\\" } else { "" };
-                        format!("cfg({name}, values({pre}\"{value}{pre}\"))")
-                    } else {
-                        format!("cfg({name})")
-                    };
-
-                    if is_from_cargo {
-                        if !is_feature_cfg {
-                            db.help(format!("consider using a Cargo feature instead or adding `println!(\"cargo:rustc-check-cfg={inst}\");` to the top of a `build.rs`"));
-                        }
-                        db.note("see <https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#check-cfg> for more information about checking conditional configuration");
-                    } else {
-                        db.help(format!("to expect this configuration use `--check-cfg={inst}`"));
-                        db.note("see <https://doc.rust-lang.org/nightly/unstable-book/compiler-flags/check-cfg.html> for more information about checking conditional configuration");
-                    }
-                },
-                BuiltinLintDiagnostics::UnexpectedCfgValue((name, name_span), value) => {
-                    let Some(ExpectedValues::Some(values)) = &sess.parse_sess.check_config.expecteds.get(&name) else {
-                        bug!("it shouldn't be possible to have a diagnostic on a value whose name is not in values");
-                    };
-                    let mut have_none_possibility = false;
-                    let possibilities: Vec<Symbol> = values.iter()
-                        .inspect(|a| have_none_possibility |= a.is_none())
-                        .copied()
-                        .flatten()
-                        .collect();
-                    let is_from_cargo = std::env::var_os("CARGO").is_some();
-
-                    // Show the full list if all possible values for a given name, but don't do it
-                    // for names as the possibilities could be very long
-                    if !possibilities.is_empty() {
-                        {
-                            let mut possibilities = possibilities.iter().map(Symbol::as_str).collect::<Vec<_>>();
-                            possibilities.sort();
-
-                            let possibilities = possibilities.join("`, `");
-                            let none = if have_none_possibility { "(none), " } else { "" };
-
-                            db.note(format!("expected values for `{name}` are: {none}`{possibilities}`"));
-                        }
-
-                        if let Some((value, value_span)) = value {
-                            // Suggest the most probable if we found one
-                            if let Some(best_match) = find_best_match_for_name(&possibilities, value, None) {
-                                db.span_suggestion(value_span, "there is a expected value with a similar name", format!("\"{best_match}\""), Applicability::MaybeIncorrect);
-
-                            }
-                        } else if let &[first_possibility] = &possibilities[..] {
-                            db.span_suggestion(name_span.shrink_to_hi(), "specify a config value", format!(" = \"{first_possibility}\""), Applicability::MaybeIncorrect);
-                        }
-                    } else if have_none_possibility {
-                        db.note(format!("no expected value for `{name}`"));
-                        if let Some((_value, value_span)) = value {
-                            db.span_suggestion(name_span.shrink_to_hi().to(value_span), "remove the value", "", Applicability::MaybeIncorrect);
-                        }
-                    }
-
-                    let inst = if let Some((value, _value_span)) = value {
-                        let pre = if is_from_cargo { "\\" } else { "" };
-                        format!("cfg({name}, values({pre}\"{value}{pre}\"))")
-                    } else {
-                        format!("cfg({name})")
-                    };
-
-                    if is_from_cargo {
-                        if name == sym::feature {
-                            if let Some((value, _value_span)) = value {
-                                db.help(format!("consider adding `{value}` as a feature in `Cargo.toml`"));
-                            }
-                        } else {
-                            db.help(format!("consider using a Cargo feature instead or adding `println!(\"cargo:rustc-check-cfg={inst}\");` to the top of a `build.rs`"));
-                        }
-                        db.note("see <https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#check-cfg> for more information about checking conditional configuration");
-                    } else {
-                        db.help(format!("to expect this configuration use `--check-cfg={inst}`"));
-                        db.note("see <https://doc.rust-lang.org/nightly/unstable-book/compiler-flags/check-cfg.html> for more information about checking conditional configuration");
-                    }
-                },
-                BuiltinLintDiagnostics::DeprecatedWhereclauseLocation(new_span, suggestion) => {
-                    db.multipart_suggestion(
-                        "move it to the end of the type declaration",
-                        vec![(db.span.primary_span().unwrap(), "".to_string()), (new_span, suggestion)],
-                        Applicability::MachineApplicable,
-                    );
-                    db.note(
-                        "see issue #89122 <https://github.com/rust-lang/rust/issues/89122> for more information",
-                    );
-                },
-                BuiltinLintDiagnostics::SingleUseLifetime {
-                    param_span,
-                    use_span: Some((use_span, elide)),
-                    deletion_span,
-                } => {
-                    debug!(?param_span, ?use_span, ?deletion_span);
-                    db.span_label(param_span, "this lifetime...");
-                    db.span_label(use_span, "...is used only here");
-                    if let Some(deletion_span) = deletion_span {
-                        let msg = "elide the single-use lifetime";
-                        let (use_span, replace_lt) = if elide {
-                            let use_span = sess.source_map().span_extend_while(
-                                use_span,
-                                char::is_whitespace,
-                            ).unwrap_or(use_span);
-                            (use_span, String::new())
-                        } else {
-                            (use_span, "'_".to_owned())
-                        };
-                        debug!(?deletion_span, ?use_span);
-
-                        // issue 107998 for the case such as a wrong function pointer type
-                        // `deletion_span` is empty and there is no need to report lifetime uses here
-                        let suggestions = if deletion_span.is_empty() {
-                            vec![(use_span, replace_lt)]
-                        } else {
-                            vec![(deletion_span, String::new()), (use_span, replace_lt)]
-                        };
-                        db.multipart_suggestion(
-                            msg,
-                            suggestions,
-                            Applicability::MachineApplicable,
-                        );
-                    }
-                },
-                BuiltinLintDiagnostics::SingleUseLifetime {
-                    param_span: _,
-                    use_span: None,
-                    deletion_span,
-                } => {
-                    debug!(?deletion_span);
-                    if let Some(deletion_span) = deletion_span {
-                        db.span_suggestion(
-                            deletion_span,
-                            "elide the unused lifetime",
-                            "",
-                            Applicability::MachineApplicable,
-                        );
-                    }
-                },
-                BuiltinLintDiagnostics::NamedArgumentUsedPositionally{ position_sp_to_replace, position_sp_for_msg, named_arg_sp, named_arg_name, is_formatting_arg} => {
-                    db.span_label(named_arg_sp, "this named argument is referred to by position in formatting string");
-                    if let Some(positional_arg_for_msg) = position_sp_for_msg {
-                        let msg = format!("this formatting argument uses named argument `{named_arg_name}` by position");
-                        db.span_label(positional_arg_for_msg, msg);
-                    }
-
-                    if let Some(positional_arg_to_replace) = position_sp_to_replace {
-                        let name = if is_formatting_arg { named_arg_name + "$" } else { named_arg_name };
-                        let span_to_replace = if let Ok(positional_arg_content) =
-                            self.sess().source_map().span_to_snippet(positional_arg_to_replace) && positional_arg_content.starts_with(':') {
-                            positional_arg_to_replace.shrink_to_lo()
-                        } else {
-                            positional_arg_to_replace
-                        };
-                        db.span_suggestion_verbose(
-                            span_to_replace,
-                            "use the named argument by name to avoid ambiguity",
-                            name,
-                            Applicability::MaybeIncorrect,
-                        );
-                    }
-                }
-                BuiltinLintDiagnostics::ByteSliceInPackedStructWithDerive => {
-                    db.help("consider implementing the trait by hand, or remove the `packed` attribute");
-                }
-                BuiltinLintDiagnostics::UnusedExternCrate { removal_span }=> {
-                    db.span_suggestion(
-                        removal_span,
-                        "remove it",
-                        "",
-                        Applicability::MachineApplicable,
-                    );
-                }
-                BuiltinLintDiagnostics::ExternCrateNotIdiomatic { vis_span, ident_span }=> {
-                    let suggestion_span = vis_span.between(ident_span);
-                    db.span_suggestion_verbose(
-                        suggestion_span,
-                        "convert it to a `use`",
-                        if vis_span.is_empty() { "use " } else { " use " },
-                        Applicability::MachineApplicable,
-                    );
-                }
-                BuiltinLintDiagnostics::AmbiguousGlobImports { diag } => {
-                    rustc_errors::report_ambiguity_error(db, diag);
-                }
-                BuiltinLintDiagnostics::AmbiguousGlobReexports { name, namespace, first_reexport_span, duplicate_reexport_span } => {
-                    db.span_label(first_reexport_span, format!("the name `{name}` in the {namespace} namespace is first re-exported here"));
-                    db.span_label(duplicate_reexport_span, format!("but the name `{name}` in the {namespace} namespace is also re-exported here"));
-                }
-                BuiltinLintDiagnostics::HiddenGlobReexports { name, namespace, glob_reexport_span, private_item_span } => {
-                    db.span_note(glob_reexport_span, format!("the name `{name}` in the {namespace} namespace is supposed to be publicly re-exported here"));
-                    db.span_note(private_item_span, "but the private item here shadows it".to_owned());
-                }
-                BuiltinLintDiagnostics::UnusedQualifications { removal_span } => {
-                    db.span_suggestion_verbose(
-                        removal_span,
-                        "remove the unnecessary path segments",
-                        "",
-                        Applicability::MachineApplicable
-                    );
-                }
-                BuiltinLintDiagnostics::AssociatedConstElidedLifetime { elided, span } => {
-                    db.span_suggestion_verbose(
-                        if elided { span.shrink_to_hi() } else { span },
-                        "use the `'static` lifetime",
-                        if elided { "'static " } else { "'static" },
-                        Applicability::MachineApplicable
-                    );
-                },
-                BuiltinLintDiagnostics::RedundantImportVisibility { max_vis, span } => {
-                    db.span_note(span, format!("the most public imported item is `{max_vis}`"));
-                    db.help("reduce the glob import's visibility or increase visibility of imported items");
-                }
-            }
+            diagnostics::builtin(self.sess(), diagnostic, db);
             // Rewrap `db`, and pass control to the user.
             decorate(db)
         });
diff --git a/compiler/rustc_lint/src/context/diagnostics.rs b/compiler/rustc_lint/src/context/diagnostics.rs
new file mode 100644
index 00000000000..cfb0dff31f6
--- /dev/null
+++ b/compiler/rustc_lint/src/context/diagnostics.rs
@@ -0,0 +1,532 @@
+use rustc_ast::util::unicode::TEXT_FLOW_CONTROL_CHARS;
+use rustc_errors::{add_elided_lifetime_in_path_suggestion, DiagnosticBuilder};
+use rustc_errors::{Applicability, SuggestionStyle};
+use rustc_middle::middle::stability;
+use rustc_session::config::ExpectedValues;
+use rustc_session::lint::BuiltinLintDiagnostics;
+use rustc_session::Session;
+use rustc_span::edit_distance::find_best_match_for_name;
+use rustc_span::symbol::{sym, Symbol};
+use rustc_span::BytePos;
+
+pub(super) fn builtin(
+    sess: &Session,
+    diagnostic: BuiltinLintDiagnostics,
+    db: &mut DiagnosticBuilder<'_, ()>,
+) {
+    match diagnostic {
+        BuiltinLintDiagnostics::UnicodeTextFlow(span, content) => {
+            let spans: Vec<_> = content
+                .char_indices()
+                .filter_map(|(i, c)| {
+                    TEXT_FLOW_CONTROL_CHARS.contains(&c).then(|| {
+                        let lo = span.lo() + BytePos(2 + i as u32);
+                        (c, span.with_lo(lo).with_hi(lo + BytePos(c.len_utf8() as u32)))
+                    })
+                })
+                .collect();
+            let (an, s) = match spans.len() {
+                1 => ("an ", ""),
+                _ => ("", "s"),
+            };
+            db.span_label(
+                span,
+                format!(
+                    "this comment contains {an}invisible unicode text flow control codepoint{s}",
+                ),
+            );
+            for (c, span) in &spans {
+                db.span_label(*span, format!("{c:?}"));
+            }
+            db.note(
+                "these kind of unicode codepoints change the way text flows on \
+                         applications that support them, but can cause confusion because they \
+                         change the order of characters on the screen",
+            );
+            if !spans.is_empty() {
+                db.multipart_suggestion_with_style(
+                    "if their presence wasn't intentional, you can remove them",
+                    spans.into_iter().map(|(_, span)| (span, "".to_string())).collect(),
+                    Applicability::MachineApplicable,
+                    SuggestionStyle::HideCodeAlways,
+                );
+            }
+        }
+        BuiltinLintDiagnostics::Normal => (),
+        BuiltinLintDiagnostics::AbsPathWithModule(span) => {
+            let (sugg, app) = match sess.source_map().span_to_snippet(span) {
+                Ok(ref s) => {
+                    // FIXME(Manishearth) ideally the emitting code
+                    // can tell us whether or not this is global
+                    let opt_colon = if s.trim_start().starts_with("::") { "" } else { "::" };
+
+                    (format!("crate{opt_colon}{s}"), Applicability::MachineApplicable)
+                }
+                Err(_) => ("crate::<path>".to_string(), Applicability::HasPlaceholders),
+            };
+            db.span_suggestion(span, "use `crate`", sugg, app);
+        }
+        BuiltinLintDiagnostics::ProcMacroDeriveResolutionFallback(span) => {
+            db.span_label(
+                span,
+                "names from parent modules are not accessible without an explicit import",
+            );
+        }
+        BuiltinLintDiagnostics::MacroExpandedMacroExportsAccessedByAbsolutePaths(span_def) => {
+            db.span_note(span_def, "the macro is defined here");
+        }
+        BuiltinLintDiagnostics::ElidedLifetimesInPaths(
+            n,
+            path_span,
+            incl_angl_brckt,
+            insertion_span,
+        ) => {
+            add_elided_lifetime_in_path_suggestion(
+                sess.source_map(),
+                db,
+                n,
+                path_span,
+                incl_angl_brckt,
+                insertion_span,
+            );
+        }
+        BuiltinLintDiagnostics::UnknownCrateTypes(span, note, sugg) => {
+            db.span_suggestion(span, note, sugg, Applicability::MaybeIncorrect);
+        }
+        BuiltinLintDiagnostics::UnusedImports(message, replaces, in_test_module) => {
+            if !replaces.is_empty() {
+                db.tool_only_multipart_suggestion(
+                    message,
+                    replaces,
+                    Applicability::MachineApplicable,
+                );
+            }
+
+            if let Some(span) = in_test_module {
+                db.span_help(
+                    sess.source_map().guess_head_span(span),
+                    "consider adding a `#[cfg(test)]` to the containing module",
+                );
+            }
+        }
+        BuiltinLintDiagnostics::RedundantImport(spans, ident) => {
+            for (span, is_imported) in spans {
+                let introduced = if is_imported { "imported" } else { "defined" };
+                db.span_label(span, format!("the item `{ident}` is already {introduced} here"));
+            }
+        }
+        BuiltinLintDiagnostics::DeprecatedMacro(suggestion, span) => {
+            stability::deprecation_suggestion(db, "macro", suggestion, span)
+        }
+        BuiltinLintDiagnostics::UnusedDocComment(span) => {
+            db.span_label(span, "rustdoc does not generate documentation for macro invocations");
+            db.help("to document an item produced by a macro, \
+                                  the macro must produce the documentation as part of its expansion");
+        }
+        BuiltinLintDiagnostics::PatternsInFnsWithoutBody(span, ident) => {
+            db.span_suggestion(
+                span,
+                "remove `mut` from the parameter",
+                ident,
+                Applicability::MachineApplicable,
+            );
+        }
+        BuiltinLintDiagnostics::MissingAbi(span, default_abi) => {
+            db.span_label(span, "ABI should be specified here");
+            db.help(format!("the default ABI is {}", default_abi.name()));
+        }
+        BuiltinLintDiagnostics::LegacyDeriveHelpers(span) => {
+            db.span_label(span, "the attribute is introduced here");
+        }
+        BuiltinLintDiagnostics::ProcMacroBackCompat(note) => {
+            db.note(note);
+        }
+        BuiltinLintDiagnostics::OrPatternsBackCompat(span, suggestion) => {
+            db.span_suggestion(
+                span,
+                "use pat_param to preserve semantics",
+                suggestion,
+                Applicability::MachineApplicable,
+            );
+        }
+        BuiltinLintDiagnostics::ReservedPrefix(span) => {
+            db.span_label(span, "unknown prefix");
+            db.span_suggestion_verbose(
+                span.shrink_to_hi(),
+                "insert whitespace here to avoid this being parsed as a prefix in Rust 2021",
+                " ",
+                Applicability::MachineApplicable,
+            );
+        }
+        BuiltinLintDiagnostics::UnusedBuiltinAttribute { attr_name, macro_name, invoc_span } => {
+            db.span_note(
+                        invoc_span,
+                        format!("the built-in attribute `{attr_name}` will be ignored, since it's applied to the macro invocation `{macro_name}`")
+                    );
+        }
+        BuiltinLintDiagnostics::TrailingMacro(is_trailing, name) => {
+            if is_trailing {
+                db.note("macro invocations at the end of a block are treated as expressions");
+                db.note(format!("to ignore the value produced by the macro, add a semicolon after the invocation of `{name}`"));
+            }
+        }
+        BuiltinLintDiagnostics::BreakWithLabelAndLoop(span) => {
+            db.multipart_suggestion(
+                "wrap this expression in parentheses",
+                vec![
+                    (span.shrink_to_lo(), "(".to_string()),
+                    (span.shrink_to_hi(), ")".to_string()),
+                ],
+                Applicability::MachineApplicable,
+            );
+        }
+        BuiltinLintDiagnostics::NamedAsmLabel(help) => {
+            db.help(help);
+            db.note("see the asm section of Rust By Example <https://doc.rust-lang.org/nightly/rust-by-example/unsafe/asm.html#labels> for more information");
+        }
+        BuiltinLintDiagnostics::UnexpectedCfgName((name, name_span), value) => {
+            let possibilities: Vec<Symbol> =
+                sess.parse_sess.check_config.expecteds.keys().copied().collect();
+            let is_from_cargo = std::env::var_os("CARGO").is_some();
+            let mut is_feature_cfg = name == sym::feature;
+
+            if is_feature_cfg && is_from_cargo {
+                db.help("consider defining some features in `Cargo.toml`");
+            // Suggest the most probable if we found one
+            } else if let Some(best_match) = find_best_match_for_name(&possibilities, name, None) {
+                if let Some(ExpectedValues::Some(best_match_values)) =
+                    sess.parse_sess.check_config.expecteds.get(&best_match)
+                {
+                    let mut possibilities =
+                        best_match_values.iter().flatten().map(Symbol::as_str).collect::<Vec<_>>();
+                    possibilities.sort();
+
+                    let mut should_print_possibilities = true;
+                    if let Some((value, value_span)) = value {
+                        if best_match_values.contains(&Some(value)) {
+                            db.span_suggestion(
+                                name_span,
+                                "there is a config with a similar name and value",
+                                best_match,
+                                Applicability::MaybeIncorrect,
+                            );
+                            should_print_possibilities = false;
+                        } else if best_match_values.contains(&None) {
+                            db.span_suggestion(
+                                name_span.to(value_span),
+                                "there is a config with a similar name and no value",
+                                best_match,
+                                Applicability::MaybeIncorrect,
+                            );
+                            should_print_possibilities = false;
+                        } else if let Some(first_value) = possibilities.first() {
+                            db.span_suggestion(
+                                name_span.to(value_span),
+                                "there is a config with a similar name and different values",
+                                format!("{best_match} = \"{first_value}\""),
+                                Applicability::MaybeIncorrect,
+                            );
+                        } else {
+                            db.span_suggestion(
+                                name_span.to(value_span),
+                                "there is a config with a similar name and different values",
+                                best_match,
+                                Applicability::MaybeIncorrect,
+                            );
+                        };
+                    } else {
+                        db.span_suggestion(
+                            name_span,
+                            "there is a config with a similar name",
+                            best_match,
+                            Applicability::MaybeIncorrect,
+                        );
+                    }
+
+                    if !possibilities.is_empty() && should_print_possibilities {
+                        let possibilities = possibilities.join("`, `");
+                        db.help(format!(
+                            "expected values for `{best_match}` are: `{possibilities}`"
+                        ));
+                    }
+                } else {
+                    db.span_suggestion(
+                        name_span,
+                        "there is a config with a similar name",
+                        best_match,
+                        Applicability::MaybeIncorrect,
+                    );
+                }
+
+                is_feature_cfg |= best_match == sym::feature;
+            } else if !possibilities.is_empty() {
+                let mut possibilities =
+                    possibilities.iter().map(Symbol::as_str).collect::<Vec<_>>();
+                possibilities.sort();
+                let possibilities = possibilities.join("`, `");
+
+                // The list of expected names can be long (even by default) and
+                // so the diagnostic produced can take a lot of space. To avoid
+                // cloging the user output we only want to print that diagnostic
+                // once.
+                db.help_once(format!("expected names are: `{possibilities}`"));
+            }
+
+            let inst = if let Some((value, _value_span)) = value {
+                let pre = if is_from_cargo { "\\" } else { "" };
+                format!("cfg({name}, values({pre}\"{value}{pre}\"))")
+            } else {
+                format!("cfg({name})")
+            };
+
+            if is_from_cargo {
+                if !is_feature_cfg {
+                    db.help(format!("consider using a Cargo feature instead or adding `println!(\"cargo:rustc-check-cfg={inst}\");` to the top of a `build.rs`"));
+                }
+                db.note("see <https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#check-cfg> for more information about checking conditional configuration");
+            } else {
+                db.help(format!("to expect this configuration use `--check-cfg={inst}`"));
+                db.note("see <https://doc.rust-lang.org/nightly/unstable-book/compiler-flags/check-cfg.html> for more information about checking conditional configuration");
+            }
+        }
+        BuiltinLintDiagnostics::UnexpectedCfgValue((name, name_span), value) => {
+            let Some(ExpectedValues::Some(values)) =
+                &sess.parse_sess.check_config.expecteds.get(&name)
+            else {
+                bug!(
+                    "it shouldn't be possible to have a diagnostic on a value whose name is not in values"
+                );
+            };
+            let mut have_none_possibility = false;
+            let possibilities: Vec<Symbol> = values
+                .iter()
+                .inspect(|a| have_none_possibility |= a.is_none())
+                .copied()
+                .flatten()
+                .collect();
+            let is_from_cargo = std::env::var_os("CARGO").is_some();
+
+            // Show the full list if all possible values for a given name, but don't do it
+            // for names as the possibilities could be very long
+            if !possibilities.is_empty() {
+                {
+                    let mut possibilities =
+                        possibilities.iter().map(Symbol::as_str).collect::<Vec<_>>();
+                    possibilities.sort();
+
+                    let possibilities = possibilities.join("`, `");
+                    let none = if have_none_possibility { "(none), " } else { "" };
+
+                    db.note(format!("expected values for `{name}` are: {none}`{possibilities}`"));
+                }
+
+                if let Some((value, value_span)) = value {
+                    // Suggest the most probable if we found one
+                    if let Some(best_match) = find_best_match_for_name(&possibilities, value, None)
+                    {
+                        db.span_suggestion(
+                            value_span,
+                            "there is a expected value with a similar name",
+                            format!("\"{best_match}\""),
+                            Applicability::MaybeIncorrect,
+                        );
+                    }
+                } else if let &[first_possibility] = &possibilities[..] {
+                    db.span_suggestion(
+                        name_span.shrink_to_hi(),
+                        "specify a config value",
+                        format!(" = \"{first_possibility}\""),
+                        Applicability::MaybeIncorrect,
+                    );
+                }
+            } else if have_none_possibility {
+                db.note(format!("no expected value for `{name}`"));
+                if let Some((_value, value_span)) = value {
+                    db.span_suggestion(
+                        name_span.shrink_to_hi().to(value_span),
+                        "remove the value",
+                        "",
+                        Applicability::MaybeIncorrect,
+                    );
+                }
+            }
+
+            let inst = if let Some((value, _value_span)) = value {
+                let pre = if is_from_cargo { "\\" } else { "" };
+                format!("cfg({name}, values({pre}\"{value}{pre}\"))")
+            } else {
+                format!("cfg({name})")
+            };
+
+            if is_from_cargo {
+                if name == sym::feature {
+                    if let Some((value, _value_span)) = value {
+                        db.help(format!("consider adding `{value}` as a feature in `Cargo.toml`"));
+                    }
+                } else {
+                    db.help(format!("consider using a Cargo feature instead or adding `println!(\"cargo:rustc-check-cfg={inst}\");` to the top of a `build.rs`"));
+                }
+                db.note("see <https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#check-cfg> for more information about checking conditional configuration");
+            } else {
+                db.help(format!("to expect this configuration use `--check-cfg={inst}`"));
+                db.note("see <https://doc.rust-lang.org/nightly/unstable-book/compiler-flags/check-cfg.html> for more information about checking conditional configuration");
+            }
+        }
+        BuiltinLintDiagnostics::DeprecatedWhereclauseLocation(new_span, suggestion) => {
+            db.multipart_suggestion(
+                "move it to the end of the type declaration",
+                vec![(db.span.primary_span().unwrap(), "".to_string()), (new_span, suggestion)],
+                Applicability::MachineApplicable,
+            );
+            db.note(
+                        "see issue #89122 <https://github.com/rust-lang/rust/issues/89122> for more information",
+                    );
+        }
+        BuiltinLintDiagnostics::SingleUseLifetime {
+            param_span,
+            use_span: Some((use_span, elide)),
+            deletion_span,
+        } => {
+            debug!(?param_span, ?use_span, ?deletion_span);
+            db.span_label(param_span, "this lifetime...");
+            db.span_label(use_span, "...is used only here");
+            if let Some(deletion_span) = deletion_span {
+                let msg = "elide the single-use lifetime";
+                let (use_span, replace_lt) = if elide {
+                    let use_span = sess
+                        .source_map()
+                        .span_extend_while(use_span, char::is_whitespace)
+                        .unwrap_or(use_span);
+                    (use_span, String::new())
+                } else {
+                    (use_span, "'_".to_owned())
+                };
+                debug!(?deletion_span, ?use_span);
+
+                // issue 107998 for the case such as a wrong function pointer type
+                // `deletion_span` is empty and there is no need to report lifetime uses here
+                let suggestions = if deletion_span.is_empty() {
+                    vec![(use_span, replace_lt)]
+                } else {
+                    vec![(deletion_span, String::new()), (use_span, replace_lt)]
+                };
+                db.multipart_suggestion(msg, suggestions, Applicability::MachineApplicable);
+            }
+        }
+        BuiltinLintDiagnostics::SingleUseLifetime {
+            param_span: _,
+            use_span: None,
+            deletion_span,
+        } => {
+            debug!(?deletion_span);
+            if let Some(deletion_span) = deletion_span {
+                db.span_suggestion(
+                    deletion_span,
+                    "elide the unused lifetime",
+                    "",
+                    Applicability::MachineApplicable,
+                );
+            }
+        }
+        BuiltinLintDiagnostics::NamedArgumentUsedPositionally {
+            position_sp_to_replace,
+            position_sp_for_msg,
+            named_arg_sp,
+            named_arg_name,
+            is_formatting_arg,
+        } => {
+            db.span_label(
+                named_arg_sp,
+                "this named argument is referred to by position in formatting string",
+            );
+            if let Some(positional_arg_for_msg) = position_sp_for_msg {
+                let msg = format!(
+                    "this formatting argument uses named argument `{named_arg_name}` by position"
+                );
+                db.span_label(positional_arg_for_msg, msg);
+            }
+
+            if let Some(positional_arg_to_replace) = position_sp_to_replace {
+                let name = if is_formatting_arg { named_arg_name + "$" } else { named_arg_name };
+                let span_to_replace = if let Ok(positional_arg_content) =
+                    sess.source_map().span_to_snippet(positional_arg_to_replace)
+                    && positional_arg_content.starts_with(':')
+                {
+                    positional_arg_to_replace.shrink_to_lo()
+                } else {
+                    positional_arg_to_replace
+                };
+                db.span_suggestion_verbose(
+                    span_to_replace,
+                    "use the named argument by name to avoid ambiguity",
+                    name,
+                    Applicability::MaybeIncorrect,
+                );
+            }
+        }
+        BuiltinLintDiagnostics::ByteSliceInPackedStructWithDerive => {
+            db.help("consider implementing the trait by hand, or remove the `packed` attribute");
+        }
+        BuiltinLintDiagnostics::UnusedExternCrate { removal_span } => {
+            db.span_suggestion(removal_span, "remove it", "", Applicability::MachineApplicable);
+        }
+        BuiltinLintDiagnostics::ExternCrateNotIdiomatic { vis_span, ident_span } => {
+            let suggestion_span = vis_span.between(ident_span);
+            db.span_suggestion_verbose(
+                suggestion_span,
+                "convert it to a `use`",
+                if vis_span.is_empty() { "use " } else { " use " },
+                Applicability::MachineApplicable,
+            );
+        }
+        BuiltinLintDiagnostics::AmbiguousGlobImports { diag } => {
+            rustc_errors::report_ambiguity_error(db, diag);
+        }
+        BuiltinLintDiagnostics::AmbiguousGlobReexports {
+            name,
+            namespace,
+            first_reexport_span,
+            duplicate_reexport_span,
+        } => {
+            db.span_label(
+                first_reexport_span,
+                format!("the name `{name}` in the {namespace} namespace is first re-exported here"),
+            );
+            db.span_label(
+                duplicate_reexport_span,
+                format!(
+                    "but the name `{name}` in the {namespace} namespace is also re-exported here"
+                ),
+            );
+        }
+        BuiltinLintDiagnostics::HiddenGlobReexports {
+            name,
+            namespace,
+            glob_reexport_span,
+            private_item_span,
+        } => {
+            db.span_note(glob_reexport_span, format!("the name `{name}` in the {namespace} namespace is supposed to be publicly re-exported here"));
+            db.span_note(private_item_span, "but the private item here shadows it".to_owned());
+        }
+        BuiltinLintDiagnostics::UnusedQualifications { removal_span } => {
+            db.span_suggestion_verbose(
+                removal_span,
+                "remove the unnecessary path segments",
+                "",
+                Applicability::MachineApplicable,
+            );
+        }
+        BuiltinLintDiagnostics::AssociatedConstElidedLifetime { elided, span } => {
+            db.span_suggestion_verbose(
+                if elided { span.shrink_to_hi() } else { span },
+                "use the `'static` lifetime",
+                if elided { "'static " } else { "'static" },
+                Applicability::MachineApplicable,
+            );
+        }
+        BuiltinLintDiagnostics::RedundantImportVisibility { max_vis, span } => {
+            db.span_note(span, format!("the most public imported item is `{max_vis}`"));
+            db.help("reduce the glob import's visibility or increase visibility of imported items");
+        }
+    }
+}