about summary refs log tree commit diff
path: root/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler')
-rw-r--r--compiler/rustc_ast_passes/messages.ftl5
-rw-r--r--compiler/rustc_ast_passes/src/ast_validation.rs15
-rw-r--r--compiler/rustc_ast_passes/src/errors.rs2
-rw-r--r--compiler/rustc_attr/src/builtin.rs6
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/generic/mod.rs7
-rw-r--r--compiler/rustc_builtin_macros/src/format.rs1
-rw-r--r--compiler/rustc_expand/src/base.rs1
-rw-r--r--compiler/rustc_expand/src/expand.rs2
-rw-r--r--compiler/rustc_expand/src/mbe/macro_rules.rs2
-rw-r--r--compiler/rustc_interface/src/util.rs1
-rw-r--r--compiler/rustc_lint/messages.ftl11
-rw-r--r--compiler/rustc_lint/src/context.rs3
-rw-r--r--compiler/rustc_lint/src/context/diagnostics.rs150
-rw-r--r--compiler/rustc_lint/src/early.rs10
-rw-r--r--compiler/rustc_lint_defs/src/lib.rs49
-rw-r--r--compiler/rustc_middle/src/middle/stability.rs4
-rw-r--r--compiler/rustc_parse/src/lexer/mod.rs4
-rw-r--r--compiler/rustc_parse/src/parser/expr.rs1
-rw-r--r--compiler/rustc_resolve/src/check_unused.rs23
-rw-r--r--compiler/rustc_resolve/src/diagnostics.rs6
-rw-r--r--compiler/rustc_resolve/src/ident.rs13
-rw-r--r--compiler/rustc_resolve/src/imports.rs9
-rw-r--r--compiler/rustc_resolve/src/late.rs7
-rw-r--r--compiler/rustc_resolve/src/late/diagnostics.rs4
-rw-r--r--compiler/rustc_resolve/src/macros.rs1
-rw-r--r--compiler/rustc_session/src/parse.rs5
26 files changed, 212 insertions, 130 deletions
diff --git a/compiler/rustc_ast_passes/messages.ftl b/compiler/rustc_ast_passes/messages.ftl
index a3731e94276..31a184fe921 100644
--- a/compiler/rustc_ast_passes/messages.ftl
+++ b/compiler/rustc_ast_passes/messages.ftl
@@ -55,9 +55,6 @@ ast_passes_const_without_body =
 ast_passes_constraint_on_negative_bound =
     associated type constraints not allowed on negative bounds
 
-ast_passes_deprecated_where_clause_location =
-    where clause not allowed here
-
 ast_passes_equality_in_where = equality constraints are not yet supported in `where` clauses
     .label = not supported
     .suggestion = if `{$ident}` is an associated type you're trying to set, use the associated type binding syntax
@@ -80,8 +77,6 @@ ast_passes_extern_types_cannot = `type`s inside `extern` blocks cannot have {$de
     .suggestion = remove the {$remove_descr}
     .label = `extern` block begins here
 
-ast_passes_extern_without_abi = extern declarations without an explicit ABI are deprecated
-
 ast_passes_feature_on_non_nightly = `#![feature]` may not be used on the {$channel} release channel
     .suggestion = remove the attribute
     .stable_since = the feature `{$name}` has been stable since `{$since}` and no longer requires an attribute to enable
diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs
index 9d07683f8d6..c287f6e4b3d 100644
--- a/compiler/rustc_ast_passes/src/ast_validation.rs
+++ b/compiler/rustc_ast_passes/src/ast_validation.rs
@@ -27,7 +27,6 @@ use std::ops::{Deref, DerefMut};
 use thin_vec::thin_vec;
 
 use crate::errors;
-use crate::fluent_generated as fluent;
 
 /// Is `self` allowed semantically as the first parameter in an `FnDecl`?
 enum SelfSemantic {
@@ -770,7 +769,6 @@ impl<'a> AstValidator<'a> {
                 MISSING_ABI,
                 id,
                 span,
-                fluent::ast_passes_extern_without_abi,
                 BuiltinLintDiag::MissingAbi(span, abi::Abi::FALLBACK),
             )
         }
@@ -1428,17 +1426,15 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
             Self::check_decl_no_pat(&sig.decl, |span, ident, mut_ident| {
                 if mut_ident && matches!(ctxt, FnCtxt::Assoc(_)) {
                     if let Some(ident) = ident {
-                        let msg = match ctxt {
-                            FnCtxt::Foreign => fluent::ast_passes_pattern_in_foreign,
-                            _ => fluent::ast_passes_pattern_in_bodiless,
-                        };
-                        let diag = BuiltinLintDiag::PatternsInFnsWithoutBody(span, ident);
                         self.lint_buffer.buffer_lint_with_diagnostic(
                             PATTERNS_IN_FNS_WITHOUT_BODY,
                             id,
                             span,
-                            msg,
-                            diag,
+                            BuiltinLintDiag::PatternsInFnsWithoutBody {
+                                span,
+                                ident,
+                                is_foreign: matches!(ctxt, FnCtxt::Foreign),
+                            },
                         )
                     }
                 } else {
@@ -1514,7 +1510,6 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                 DEPRECATED_WHERE_CLAUSE_LOCATION,
                 item.id,
                 err.span,
-                fluent::ast_passes_deprecated_where_clause_location,
                 BuiltinLintDiag::DeprecatedWhereclauseLocation(sugg),
             );
         }
diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs
index 25a125f8393..a95a7bdaf6d 100644
--- a/compiler/rustc_ast_passes/src/errors.rs
+++ b/compiler/rustc_ast_passes/src/errors.rs
@@ -669,6 +669,7 @@ pub struct ConstAndCVariadic {
 
 #[derive(Diagnostic)]
 #[diag(ast_passes_pattern_in_foreign, code = E0130)]
+// FIXME: deduplicate with rustc_lint (`BuiltinLintDiag::PatternsInFnsWithoutBody`)
 pub struct PatternInForeign {
     #[primary_span]
     #[label]
@@ -677,6 +678,7 @@ pub struct PatternInForeign {
 
 #[derive(Diagnostic)]
 #[diag(ast_passes_pattern_in_bodiless, code = E0642)]
+// FIXME: deduplicate with rustc_lint (`BuiltinLintDiag::PatternsInFnsWithoutBody`)
 pub struct PatternInBodiless {
     #[primary_span]
     #[label]
diff --git a/compiler/rustc_attr/src/builtin.rs b/compiler/rustc_attr/src/builtin.rs
index c08bf287733..1ac2e15c327 100644
--- a/compiler/rustc_attr/src/builtin.rs
+++ b/compiler/rustc_attr/src/builtin.rs
@@ -532,11 +532,6 @@ pub fn cfg_matches(
                     UNEXPECTED_CFGS,
                     cfg.span,
                     lint_node_id,
-                    if let Some(value) = cfg.value {
-                        format!("unexpected `cfg` condition value: `{value}`")
-                    } else {
-                        format!("unexpected `cfg` condition value: (none)")
-                    },
                     BuiltinLintDiag::UnexpectedCfgValue(
                         (cfg.name, cfg.name_span),
                         cfg.value.map(|v| (v, cfg.value_span.unwrap())),
@@ -548,7 +543,6 @@ pub fn cfg_matches(
                     UNEXPECTED_CFGS,
                     cfg.span,
                     lint_node_id,
-                    format!("unexpected `cfg` condition name: `{}`", cfg.name),
                     BuiltinLintDiag::UnexpectedCfgName(
                         (cfg.name, cfg.name_span),
                         cfg.value.map(|v| (v, cfg.value_span.unwrap())),
diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
index 46949f731aa..18dca4b2615 100644
--- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
@@ -1625,10 +1625,9 @@ impl<'a> TraitDef<'a> {
                                 BYTE_SLICE_IN_PACKED_STRUCT_WITH_DERIVE,
                                 sp,
                                 ast::CRATE_NODE_ID,
-                                format!(
-                                    "{ty} slice in a packed struct that derives a built-in trait"
-                                ),
-                                rustc_lint_defs::BuiltinLintDiag::ByteSliceInPackedStructWithDerive,
+                                rustc_lint_defs::BuiltinLintDiag::ByteSliceInPackedStructWithDerive {
+                                    ty: ty.to_string(),
+                                },
                             );
                         } else {
                             // Wrap the expression in `{...}`, causing a copy.
diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs
index a5fc74f1d66..5cb0407bd59 100644
--- a/compiler/rustc_builtin_macros/src/format.rs
+++ b/compiler/rustc_builtin_macros/src/format.rs
@@ -556,7 +556,6 @@ fn make_format_args(
             let arg_name = args.explicit_args()[index].kind.ident().unwrap();
             ecx.buffered_early_lint.push(BufferedEarlyLint {
                 span: arg_name.span.into(),
-                msg: format!("named argument `{}` is not used by name", arg_name.name).into(),
                 node_id: rustc_ast::CRATE_NODE_ID,
                 lint_id: LintId::of(NAMED_ARGUMENTS_USED_POSITIONALLY),
                 diagnostic: BuiltinLintDiag::NamedArgumentUsedPositionally {
diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs
index 12868a66605..c7ca515afc4 100644
--- a/compiler/rustc_expand/src/base.rs
+++ b/compiler/rustc_expand/src/base.rs
@@ -1370,7 +1370,6 @@ fn pretty_printing_compatibility_hack(item: &Item, sess: &Session) -> bool {
                                         PROC_MACRO_BACK_COMPAT,
                                         item.ident.span,
                                         ast::CRATE_NODE_ID,
-                                        "using an old version of `rental`",
                                         BuiltinLintDiag::ProcMacroBackCompat(
                                         "older versions of the `rental` crate will stop compiling in future versions of Rust; \
                                         please update to `rental` v0.5.6, or switch to one of the `rental` alternatives".to_string()
diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs
index a049ac251e1..02e4f5e0f9e 100644
--- a/compiler/rustc_expand/src/expand.rs
+++ b/compiler/rustc_expand/src/expand.rs
@@ -1803,7 +1803,6 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
                     UNUSED_DOC_COMMENTS,
                     current_span,
                     self.cx.current_expansion.lint_node_id,
-                    "unused doc comment",
                     BuiltinLintDiag::UnusedDocComment(attr.span),
                 );
             } else if rustc_attr::is_builtin_attr(attr) {
@@ -1815,7 +1814,6 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
                         UNUSED_ATTRIBUTES,
                         attr.span,
                         self.cx.current_expansion.lint_node_id,
-                        format!("unused attribute `{attr_name}`"),
                         BuiltinLintDiag::UnusedBuiltinAttribute {
                             attr_name,
                             macro_name: pprust::path_to_string(&call.path),
diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs
index 8f18055f838..fee7ba099aa 100644
--- a/compiler/rustc_expand/src/mbe/macro_rules.rs
+++ b/compiler/rustc_expand/src/mbe/macro_rules.rs
@@ -83,7 +83,6 @@ impl<'a> ParserAnyMacro<'a> {
                     SEMICOLON_IN_EXPRESSIONS_FROM_MACROS,
                     parser.token.span,
                     lint_node_id,
-                    "trailing semicolon in macro used in expression position",
                     BuiltinLintDiag::TrailingMacro(is_trailing_mac, macro_ident),
                 );
             }
@@ -1158,7 +1157,6 @@ fn check_matcher_core<'tt>(
                             RUST_2021_INCOMPATIBLE_OR_PATTERNS,
                             span,
                             ast::CRATE_NODE_ID,
-                            "the meaning of the `pat` fragment specifier is changing in Rust 2021, which may affect this macro",
                             BuiltinLintDiag::OrPatternsBackCompat(span, suggestion),
                         );
                     }
diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs
index ce2382b9501..de7005a541b 100644
--- a/compiler/rustc_interface/src/util.rs
+++ b/compiler/rustc_interface/src/util.rs
@@ -409,7 +409,6 @@ pub(crate) fn check_attr_crate_type(
                             lint::builtin::UNKNOWN_CRATE_TYPES,
                             ast::CRATE_NODE_ID,
                             span,
-                            "invalid `crate_type` value",
                             BuiltinLintDiag::UnknownCrateTypes(
                                 span,
                                 "did you mean".to_string(),
diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl
index 6ad13c01812..e1b0aec6c96 100644
--- a/compiler/rustc_lint/messages.ftl
+++ b/compiler/rustc_lint/messages.ftl
@@ -180,6 +180,9 @@ lint_deprecated_lint_name =
     .suggestion = change it to
     .help = change it to {$replace}
 
+lint_deprecated_where_clause_location =
+    where clause not allowed here
+
 lint_diag_out_of_impl =
     diagnostics should only be created in `Diagnostic`/`Subdiagnostic`/`LintDiagnostic` impls
 
@@ -209,6 +212,8 @@ lint_expectation = this lint expectation is unfulfilled
     .note = the `unfulfilled_lint_expectations` lint can't be expected and will always produce this message
     .rationale = {$rationale}
 
+lint_extern_without_abi = extern declarations without an explicit ABI are deprecated
+
 lint_for_loops_over_fallibles =
     for loop over {$article} `{$ty}`. This is more readably written as an `if let` statement
     .suggestion = consider using `if let` to clear intent
@@ -521,6 +526,12 @@ lint_path_statement_drop = path statement drops value
 
 lint_path_statement_no_effect = path statement with no effect
 
+lint_pattern_in_bodiless = patterns aren't allowed in functions without bodies
+    .label = pattern not allowed in function without body
+
+lint_pattern_in_foreign = patterns aren't allowed in foreign function declarations
+    .label = pattern not allowed in foreign function
+
 lint_ptr_null_checks_fn_ptr = function pointers are not nullable, so checking them for null will always return false
     .help = wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value
     .label = expression has type `{$orig_ty}`
diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs
index 62ba9ef5c11..563d96d9519 100644
--- a/compiler/rustc_lint/src/context.rs
+++ b/compiler/rustc_lint/src/context.rs
@@ -539,12 +539,11 @@ pub trait LintContext {
         &self,
         lint: &'static Lint,
         span: Option<impl Into<MultiSpan>>,
-        msg: impl Into<DiagMessage>,
         decorate: impl for<'a, 'b> FnOnce(&'b mut Diag<'a, ()>),
         diagnostic: BuiltinLintDiag,
     ) {
         // We first generate a blank diagnostic.
-        self.opt_span_lint(lint, span, msg, |db| {
+        self.opt_span_lint(lint, span, diagnostics::builtin_message(&diagnostic), |db| {
             // Now, set up surrounding context.
             diagnostics::builtin(self.sess(), diagnostic, db);
             // Rewrap `db`, and pass control to the user.
diff --git a/compiler/rustc_lint/src/context/diagnostics.rs b/compiler/rustc_lint/src/context/diagnostics.rs
index b8aac6c15d9..df012b2d10b 100644
--- a/compiler/rustc_lint/src/context/diagnostics.rs
+++ b/compiler/rustc_lint/src/context/diagnostics.rs
@@ -2,7 +2,7 @@
 #![allow(rustc::untranslatable_diagnostic)]
 
 use rustc_ast::util::unicode::TEXT_FLOW_CONTROL_CHARS;
-use rustc_errors::{elided_lifetime_in_path_suggestion, Diag};
+use rustc_errors::{elided_lifetime_in_path_suggestion, pluralize, Diag, DiagMessage};
 use rustc_errors::{Applicability, SuggestionStyle};
 use rustc_middle::middle::stability;
 use rustc_session::lint::BuiltinLintDiag;
@@ -50,7 +50,7 @@ pub(super) fn builtin(sess: &Session, diagnostic: BuiltinLintDiag, diag: &mut Di
                 );
             }
         }
-        BuiltinLintDiag::Normal => (),
+        BuiltinLintDiag::Normal(_) => (),
         BuiltinLintDiag::AbsPathWithModule(span) => {
             let (sugg, app) = match sess.source_map().span_to_snippet(span) {
                 Ok(ref s) => {
@@ -64,7 +64,7 @@ pub(super) fn builtin(sess: &Session, diagnostic: BuiltinLintDiag, diag: &mut Di
             };
             diag.span_suggestion(span, "use `crate`", sugg, app);
         }
-        BuiltinLintDiag::ProcMacroDeriveResolutionFallback(span) => {
+        BuiltinLintDiag::ProcMacroDeriveResolutionFallback { span, .. } => {
             diag.span_label(
                 span,
                 "names from parent modules are not accessible without an explicit import",
@@ -88,16 +88,16 @@ pub(super) fn builtin(sess: &Session, diagnostic: BuiltinLintDiag, diag: &mut Di
         BuiltinLintDiag::UnknownCrateTypes(span, note, sugg) => {
             diag.span_suggestion(span, note, sugg, Applicability::MaybeIncorrect);
         }
-        BuiltinLintDiag::UnusedImports(message, replaces, in_test_module) => {
-            if !replaces.is_empty() {
+        BuiltinLintDiag::UnusedImports { fix_msg, fixes, test_module_span, .. } => {
+            if !fixes.is_empty() {
                 diag.tool_only_multipart_suggestion(
-                    message,
-                    replaces,
+                    fix_msg,
+                    fixes,
                     Applicability::MachineApplicable,
                 );
             }
 
-            if let Some(span) = in_test_module {
+            if let Some(span) = test_module_span {
                 diag.span_help(
                     sess.source_map().guess_head_span(span),
                     "if this is a test module, consider adding a `#[cfg(test)]` to the containing module",
@@ -114,7 +114,7 @@ pub(super) fn builtin(sess: &Session, diagnostic: BuiltinLintDiag, diag: &mut Di
                 );
             }
         }
-        BuiltinLintDiag::DeprecatedMacro(suggestion, span) => {
+        BuiltinLintDiag::DeprecatedMacro { suggestion, span, .. } => {
             stability::deprecation_suggestion(diag, "macro", suggestion, span)
         }
         BuiltinLintDiag::UnusedDocComment(span) => {
@@ -122,7 +122,7 @@ pub(super) fn builtin(sess: &Session, diagnostic: BuiltinLintDiag, diag: &mut Di
             diag.help("to document an item produced by a macro, \
                                   the macro must produce the documentation as part of its expansion");
         }
-        BuiltinLintDiag::PatternsInFnsWithoutBody(span, ident) => {
+        BuiltinLintDiag::PatternsInFnsWithoutBody { span, ident, .. } => {
             diag.span_suggestion(
                 span,
                 "remove `mut` from the parameter",
@@ -148,7 +148,7 @@ pub(super) fn builtin(sess: &Session, diagnostic: BuiltinLintDiag, diag: &mut Di
                 Applicability::MachineApplicable,
             );
         }
-        BuiltinLintDiag::ReservedPrefix(span) => {
+        BuiltinLintDiag::ReservedPrefix(span, _) => {
             diag.span_label(span, "unknown prefix");
             diag.span_suggestion_verbose(
                 span.shrink_to_hi(),
@@ -206,6 +206,7 @@ pub(super) fn builtin(sess: &Session, diagnostic: BuiltinLintDiag, diag: &mut Di
             param_span,
             use_span: Some((use_span, elide)),
             deletion_span,
+            ..
         } => {
             debug!(?param_span, ?use_span, ?deletion_span);
             diag.span_label(param_span, "this lifetime...");
@@ -230,7 +231,9 @@ pub(super) fn builtin(sess: &Session, diagnostic: BuiltinLintDiag, diag: &mut Di
                 diag.multipart_suggestion(msg, suggestions, Applicability::MachineApplicable);
             }
         }
-        BuiltinLintDiag::SingleUseLifetime { param_span: _, use_span: None, deletion_span } => {
+        BuiltinLintDiag::SingleUseLifetime {
+            param_span: _, use_span: None, deletion_span, ..
+        } => {
             debug!(?deletion_span);
             if let Some(deletion_span) = deletion_span {
                 diag.span_suggestion(
@@ -277,7 +280,7 @@ pub(super) fn builtin(sess: &Session, diagnostic: BuiltinLintDiag, diag: &mut Di
                 );
             }
         }
-        BuiltinLintDiag::ByteSliceInPackedStructWithDerive => {
+        BuiltinLintDiag::ByteSliceInPackedStructWithDerive { .. } => {
             diag.help("consider implementing the trait by hand, or remove the `packed` attribute");
         }
         BuiltinLintDiag::UnusedExternCrate { removal_span } => {
@@ -337,7 +340,7 @@ pub(super) fn builtin(sess: &Session, diagnostic: BuiltinLintDiag, diag: &mut Di
                 Applicability::MachineApplicable,
             );
         }
-        BuiltinLintDiag::RedundantImportVisibility { max_vis, span } => {
+        BuiltinLintDiag::RedundantImportVisibility { max_vis, span, .. } => {
             diag.span_note(span, format!("the most public imported item is `{max_vis}`"));
             diag.help(
                 "reduce the glob import's visibility or increase visibility of imported items",
@@ -353,3 +356,122 @@ pub(super) fn builtin(sess: &Session, diagnostic: BuiltinLintDiag, diag: &mut Di
         }
     }
 }
+
+pub(super) fn builtin_message(diagnostic: &BuiltinLintDiag) -> DiagMessage {
+    match diagnostic {
+        BuiltinLintDiag::Normal(msg) => msg.clone(),
+        BuiltinLintDiag::AbsPathWithModule(_) => {
+            "absolute paths must start with `self`, `super`, `crate`, or an \
+                external crate name in the 2018 edition"
+                .into()
+        }
+        BuiltinLintDiag::ProcMacroDeriveResolutionFallback { ns, ident, .. } => {
+            format!("cannot find {} `{}` in this scope", ns.descr(), ident).into()
+        }
+        BuiltinLintDiag::MacroExpandedMacroExportsAccessedByAbsolutePaths(_) => {
+            "macro-expanded `macro_export` macros from the current crate cannot \
+                be referred to by absolute paths"
+                .into()
+        }
+        BuiltinLintDiag::ElidedLifetimesInPaths(_, _, _, _) => {
+            "hidden lifetime parameters in types are deprecated".into()
+        }
+        BuiltinLintDiag::UnknownCrateTypes(_, _, _) => "invalid `crate_type` value".into(),
+        BuiltinLintDiag::UnusedImports { span_snippets, .. } => format!(
+            "unused import{}{}",
+            pluralize!(span_snippets.len()),
+            if !span_snippets.is_empty() {
+                format!(": {}", span_snippets.join(", "))
+            } else {
+                String::new()
+            }
+        )
+        .into(),
+        BuiltinLintDiag::RedundantImport(_, source) => {
+            format!("the item `{source}` is imported redundantly").into()
+        }
+        BuiltinLintDiag::DeprecatedMacro { message, .. } => message.clone().into(),
+        BuiltinLintDiag::MissingAbi(_, _) => crate::fluent_generated::lint_extern_without_abi,
+        BuiltinLintDiag::UnusedDocComment(_) => "unused doc comment".into(),
+        BuiltinLintDiag::UnusedBuiltinAttribute { attr_name, .. } => {
+            format!("unused attribute `{attr_name}`").into()
+        }
+        BuiltinLintDiag::PatternsInFnsWithoutBody { is_foreign, .. } => {
+            if *is_foreign {
+                crate::fluent_generated::lint_pattern_in_foreign
+            } else {
+                crate::fluent_generated::lint_pattern_in_bodiless
+            }
+        }
+        BuiltinLintDiag::LegacyDeriveHelpers(_) => {
+            "derive helper attribute is used before it is introduced".into()
+        }
+        BuiltinLintDiag::ProcMacroBackCompat(_) => "using an old version of `rental`".into(),
+        BuiltinLintDiag::OrPatternsBackCompat(_, _) => {
+            "the meaning of the `pat` fragment specifier is changing in Rust 2021, \
+            which may affect this macro"
+                .into()
+        }
+        BuiltinLintDiag::ReservedPrefix(_, prefix) => {
+            format!("prefix `{prefix}` is unknown").into()
+        }
+        BuiltinLintDiag::TrailingMacro(_, _) => {
+            "trailing semicolon in macro used in expression position".into()
+        }
+        BuiltinLintDiag::BreakWithLabelAndLoop(_) => {
+            "this labeled break expression is easy to confuse with an unlabeled break with a \
+            labeled value expression"
+                .into()
+        }
+        BuiltinLintDiag::UnicodeTextFlow(_, _) => {
+            "unicode codepoint changing visible direction of text present in comment".into()
+        }
+        BuiltinLintDiag::UnexpectedCfgName((name, _), _) => {
+            format!("unexpected `cfg` condition name: `{}`", name).into()
+        }
+        BuiltinLintDiag::UnexpectedCfgValue(_, v) => if let Some((value, _)) = v {
+            format!("unexpected `cfg` condition value: `{value}`")
+        } else {
+            format!("unexpected `cfg` condition value: (none)")
+        }
+        .into(),
+        BuiltinLintDiag::DeprecatedWhereclauseLocation(_) => {
+            crate::fluent_generated::lint_deprecated_where_clause_location
+        }
+        BuiltinLintDiag::SingleUseLifetime { use_span, ident, .. } => {
+            if use_span.is_some() {
+                format!("lifetime parameter `{}` only used once", ident).into()
+            } else {
+                format!("lifetime parameter `{}` never used", ident).into()
+            }
+        }
+        BuiltinLintDiag::NamedArgumentUsedPositionally { named_arg_name, .. } => {
+            format!("named argument `{}` is not used by name", named_arg_name).into()
+        }
+        BuiltinLintDiag::ByteSliceInPackedStructWithDerive { ty } => {
+            format!("{ty} slice in a packed struct that derives a built-in trait").into()
+        }
+        BuiltinLintDiag::UnusedExternCrate { .. } => "unused extern crate".into(),
+        BuiltinLintDiag::ExternCrateNotIdiomatic { .. } => {
+            "`extern crate` is not idiomatic in the new edition".into()
+        }
+        BuiltinLintDiag::AmbiguousGlobImports { diag } => diag.msg.clone().into(),
+        BuiltinLintDiag::AmbiguousGlobReexports { .. } => "ambiguous glob re-exports".into(),
+        BuiltinLintDiag::HiddenGlobReexports { .. } => {
+            "private item shadows public glob re-export".into()
+        }
+        BuiltinLintDiag::UnusedQualifications { .. } => "unnecessary qualification".into(),
+        BuiltinLintDiag::AssociatedConstElidedLifetime { elided, .. } => if *elided {
+            "`&` without an explicit lifetime name cannot be used here"
+        } else {
+            "`'_` cannot be used here"
+        }
+        .into(),
+        BuiltinLintDiag::RedundantImportVisibility { import_vis, .. } => format!(
+            "glob import doesn't reexport anything with visibility `{}` \
+            because no imported item is public enough",
+            import_vis
+        )
+        .into(),
+    }
+}
diff --git a/compiler/rustc_lint/src/early.rs b/compiler/rustc_lint/src/early.rs
index 3f627baf770..80cb70ba277 100644
--- a/compiler/rustc_lint/src/early.rs
+++ b/compiler/rustc_lint/src/early.rs
@@ -44,14 +44,8 @@ impl<'a, T: EarlyLintPass> EarlyContextAndPass<'a, T> {
     #[allow(rustc::diagnostic_outside_of_impl)]
     fn inlined_check_id(&mut self, id: ast::NodeId) {
         for early_lint in self.context.buffered.take(id) {
-            let BufferedEarlyLint { span, msg, node_id: _, lint_id, diagnostic } = early_lint;
-            self.context.span_lint_with_diagnostics(
-                lint_id.lint,
-                Some(span),
-                msg,
-                |_| {},
-                diagnostic,
-            );
+            let BufferedEarlyLint { span, node_id: _, lint_id, diagnostic } = early_lint;
+            self.context.span_lint_with_diagnostics(lint_id.lint, Some(span), |_| {}, diagnostic);
         }
     }
 
diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs
index 7da65ba0eec..a0b24d02561 100644
--- a/compiler/rustc_lint_defs/src/lib.rs
+++ b/compiler/rustc_lint_defs/src/lib.rs
@@ -6,6 +6,7 @@ use rustc_data_structures::stable_hasher::{
     HashStable, StableCompare, StableHasher, ToStableHashKey,
 };
 use rustc_error_messages::{DiagMessage, MultiSpan};
+use rustc_hir::def::Namespace;
 use rustc_hir::HashStableContext;
 use rustc_hir::HirId;
 use rustc_macros::{Decodable, Encodable, HashStable_Generic};
@@ -569,15 +570,28 @@ pub struct AmbiguityErrorDiag {
 // becomes hacky (and it gets allocated).
 #[derive(Debug)]
 pub enum BuiltinLintDiag {
-    Normal,
+    Normal(DiagMessage),
     AbsPathWithModule(Span),
-    ProcMacroDeriveResolutionFallback(Span),
+    ProcMacroDeriveResolutionFallback {
+        span: Span,
+        ns: Namespace,
+        ident: Ident,
+    },
     MacroExpandedMacroExportsAccessedByAbsolutePaths(Span),
     ElidedLifetimesInPaths(usize, Span, bool, Span),
     UnknownCrateTypes(Span, String, String),
-    UnusedImports(String, Vec<(Span, String)>, Option<Span>),
+    UnusedImports {
+        fix_msg: String,
+        fixes: Vec<(Span, String)>,
+        test_module_span: Option<Span>,
+        span_snippets: Vec<String>,
+    },
     RedundantImport(Vec<(Span, bool)>, Ident),
-    DeprecatedMacro(Option<Symbol>, Span),
+    DeprecatedMacro {
+        suggestion: Option<Symbol>,
+        span: Span,
+        message: String,
+    },
     MissingAbi(Span, Abi),
     UnusedDocComment(Span),
     UnusedBuiltinAttribute {
@@ -585,11 +599,15 @@ pub enum BuiltinLintDiag {
         macro_name: String,
         invoc_span: Span,
     },
-    PatternsInFnsWithoutBody(Span, Ident),
+    PatternsInFnsWithoutBody {
+        span: Span,
+        ident: Ident,
+        is_foreign: bool,
+    },
     LegacyDeriveHelpers(Span),
     ProcMacroBackCompat(String),
     OrPatternsBackCompat(Span, String),
-    ReservedPrefix(Span),
+    ReservedPrefix(Span, String),
     TrailingMacro(bool, Ident),
     BreakWithLabelAndLoop(Span),
     UnicodeTextFlow(Span, String),
@@ -605,6 +623,7 @@ pub enum BuiltinLintDiag {
         /// Span of the single use, or None if the lifetime is never used.
         /// If true, the lifetime will be fully elided.
         use_span: Option<(Span, bool)>,
+        ident: Ident,
     },
     NamedArgumentUsedPositionally {
         /// Span where the named argument is used by position and will be replaced with the named
@@ -619,7 +638,10 @@ pub enum BuiltinLintDiag {
         /// Indicates if the named argument is used as a width/precision for formatting
         is_formatting_arg: bool,
     },
-    ByteSliceInPackedStructWithDerive,
+    ByteSliceInPackedStructWithDerive {
+        // FIXME: enum of byte/string
+        ty: String,
+    },
     UnusedExternCrate {
         removal_span: Span,
     },
@@ -661,6 +683,7 @@ pub enum BuiltinLintDiag {
     RedundantImportVisibility {
         span: Span,
         max_vis: String,
+        import_vis: String,
     },
     MaybeTypo {
         span: Span,
@@ -675,9 +698,6 @@ pub struct BufferedEarlyLint {
     /// The span of code that we are linting on.
     pub span: MultiSpan,
 
-    /// The lint message.
-    pub msg: DiagMessage,
-
     /// The `NodeId` of the AST node that generated the lint.
     pub node_id: NodeId,
 
@@ -705,12 +725,10 @@ impl LintBuffer {
         lint: &'static Lint,
         node_id: NodeId,
         span: MultiSpan,
-        msg: impl Into<DiagMessage>,
         diagnostic: BuiltinLintDiag,
     ) {
         let lint_id = LintId::of(lint);
-        let msg = msg.into();
-        self.add_early_lint(BufferedEarlyLint { lint_id, node_id, span, msg, diagnostic });
+        self.add_early_lint(BufferedEarlyLint { lint_id, node_id, span, diagnostic });
     }
 
     pub fn take(&mut self, id: NodeId) -> Vec<BufferedEarlyLint> {
@@ -725,7 +743,7 @@ impl LintBuffer {
         sp: impl Into<MultiSpan>,
         msg: impl Into<DiagMessage>,
     ) {
-        self.add_lint(lint, id, sp.into(), msg, BuiltinLintDiag::Normal)
+        self.add_lint(lint, id, sp.into(), BuiltinLintDiag::Normal(msg.into()))
     }
 
     pub fn buffer_lint_with_diagnostic(
@@ -733,10 +751,9 @@ impl LintBuffer {
         lint: &'static Lint,
         id: NodeId,
         sp: impl Into<MultiSpan>,
-        msg: impl Into<DiagMessage>,
         diagnostic: BuiltinLintDiag,
     ) {
-        self.add_lint(lint, id, sp.into(), msg, diagnostic)
+        self.add_lint(lint, id, sp.into(), diagnostic)
     }
 }
 
diff --git a/compiler/rustc_middle/src/middle/stability.rs b/compiler/rustc_middle/src/middle/stability.rs
index 67bd53f53da..75ed408e644 100644
--- a/compiler/rustc_middle/src/middle/stability.rs
+++ b/compiler/rustc_middle/src/middle/stability.rs
@@ -200,8 +200,8 @@ pub fn early_report_deprecation(
         return;
     }
 
-    let diag = BuiltinLintDiag::DeprecatedMacro(suggestion, span);
-    lint_buffer.buffer_lint_with_diagnostic(lint, node_id, span, message, diag);
+    let diag = BuiltinLintDiag::DeprecatedMacro { suggestion, span, message };
+    lint_buffer.buffer_lint_with_diagnostic(lint, node_id, span, diag);
 }
 
 fn late_report_deprecation(
diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs
index d2d200a91af..cdb1cf62cf7 100644
--- a/compiler/rustc_parse/src/lexer/mod.rs
+++ b/compiler/rustc_parse/src/lexer/mod.rs
@@ -374,7 +374,6 @@ impl<'psess, 'src> StringReader<'psess, 'src> {
                 TEXT_DIRECTION_CODEPOINT_IN_COMMENT,
                 span,
                 ast::CRATE_NODE_ID,
-                "unicode codepoint changing visible direction of text present in comment",
                 BuiltinLintDiag::UnicodeTextFlow(span, content.to_string()),
             );
         }
@@ -727,8 +726,7 @@ impl<'psess, 'src> StringReader<'psess, 'src> {
                 RUST_2021_PREFIXES_INCOMPATIBLE_SYNTAX,
                 prefix_span,
                 ast::CRATE_NODE_ID,
-                format!("prefix `{prefix}` is unknown"),
-                BuiltinLintDiag::ReservedPrefix(prefix_span),
+                BuiltinLintDiag::ReservedPrefix(prefix_span, prefix.to_string()),
             );
         }
     }
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs
index d2d21624150..be4a52a9bc4 100644
--- a/compiler/rustc_parse/src/parser/expr.rs
+++ b/compiler/rustc_parse/src/parser/expr.rs
@@ -1917,7 +1917,6 @@ impl<'a> Parser<'a> {
                         BREAK_WITH_LABEL_AND_LOOP,
                         lo.to(expr.span),
                         ast::CRATE_NODE_ID,
-                        "this labeled break expression is easy to confuse with an unlabeled break with a labeled value expression",
                         BuiltinLintDiag::BreakWithLabelAndLoop(expr.span),
                     );
                 }
diff --git a/compiler/rustc_resolve/src/check_unused.rs b/compiler/rustc_resolve/src/check_unused.rs
index 180e7f6def3..2a8eb940ccf 100644
--- a/compiler/rustc_resolve/src/check_unused.rs
+++ b/compiler/rustc_resolve/src/check_unused.rs
@@ -32,7 +32,7 @@ use rustc_ast as ast;
 use rustc_ast::visit::{self, Visitor};
 use rustc_data_structures::fx::{FxHashMap, FxIndexMap, FxIndexSet};
 use rustc_data_structures::unord::UnordSet;
-use rustc_errors::{pluralize, MultiSpan};
+use rustc_errors::MultiSpan;
 use rustc_hir::def::{DefKind, Res};
 use rustc_session::lint::builtin::{MACRO_USE_EXTERN_CRATE, UNUSED_EXTERN_CRATES};
 use rustc_session::lint::builtin::{UNUSED_IMPORTS, UNUSED_QUALIFICATIONS};
@@ -155,7 +155,6 @@ impl<'a, 'b, 'tcx> UnusedImportCheckVisitor<'a, 'b, 'tcx> {
                         UNUSED_EXTERN_CRATES,
                         extern_crate.id,
                         span,
-                        "unused extern crate",
                         BuiltinLintDiag::UnusedExternCrate {
                             removal_span: extern_crate.span_with_attributes,
                         },
@@ -208,7 +207,6 @@ impl<'a, 'b, 'tcx> UnusedImportCheckVisitor<'a, 'b, 'tcx> {
                 UNUSED_EXTERN_CRATES,
                 extern_crate.id,
                 extern_crate.span,
-                "`extern crate` is not idiomatic in the new edition",
                 BuiltinLintDiag::ExternCrateNotIdiomatic { vis_span, ident_span },
             );
         }
@@ -459,16 +457,6 @@ impl Resolver<'_, '_> {
                 .collect::<Vec<String>>();
             span_snippets.sort();
 
-            let msg = format!(
-                "unused import{}{}",
-                pluralize!(ms.primary_spans().len()),
-                if !span_snippets.is_empty() {
-                    format!(": {}", span_snippets.join(", "))
-                } else {
-                    String::new()
-                }
-            );
-
             let fix_msg = if fixes.len() == 1 && fixes[0].0 == unused.item_span {
                 "remove the whole `use` item"
             } else if ms.primary_spans().len() > 1 {
@@ -505,8 +493,12 @@ impl Resolver<'_, '_> {
                 UNUSED_IMPORTS,
                 unused.use_tree_id,
                 ms,
-                msg,
-                BuiltinLintDiag::UnusedImports(fix_msg.into(), fixes, test_module_span),
+                BuiltinLintDiag::UnusedImports {
+                    fix_msg: fix_msg.into(),
+                    fixes,
+                    test_module_span,
+                    span_snippets,
+                },
             );
         }
 
@@ -556,7 +548,6 @@ impl Resolver<'_, '_> {
                 UNUSED_QUALIFICATIONS,
                 unn_qua.node_id,
                 unn_qua.path_span,
-                "unnecessary qualification",
                 BuiltinLintDiag::UnusedQualifications { removal_span: unn_qua.removal_span },
             );
         }
diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs
index b28312fa473..791d777b3d4 100644
--- a/compiler/rustc_resolve/src/diagnostics.rs
+++ b/compiler/rustc_resolve/src/diagnostics.rs
@@ -128,13 +128,10 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
         self.report_with_use_injections(krate);
 
         for &(span_use, span_def) in &self.macro_expanded_macro_export_errors {
-            let msg = "macro-expanded `macro_export` macros from the current crate \
-                       cannot be referred to by absolute paths";
             self.lint_buffer.buffer_lint_with_diagnostic(
                 MACRO_EXPANDED_MACRO_EXPORTS_ACCESSED_BY_ABSOLUTE_PATHS,
                 CRATE_NODE_ID,
                 span_use,
-                msg,
                 BuiltinLintDiag::MacroExpandedMacroExportsAccessedByAbsolutePaths(span_def),
             );
         }
@@ -149,7 +146,6 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                     AMBIGUOUS_GLOB_IMPORTS,
                     import.root_id,
                     ambiguity_error.ident.span,
-                    diag.msg.to_string(),
                     BuiltinLintDiag::AmbiguousGlobImports { diag },
                 );
             } else {
@@ -530,8 +526,6 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
             ABSOLUTE_PATHS_NOT_STARTING_WITH_CRATE,
             node_id,
             root_span,
-            "absolute paths must start with `self`, `super`, \
-             `crate`, or an external crate name in the 2018 edition",
             diag,
         );
     }
diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs
index f88725830f1..d1fd19e9953 100644
--- a/compiler/rustc_resolve/src/ident.rs
+++ b/compiler/rustc_resolve/src/ident.rs
@@ -528,14 +528,11 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                                         PROC_MACRO_DERIVE_RESOLUTION_FALLBACK,
                                         lint_id,
                                         orig_ident.span,
-                                        format!(
-                                            "cannot find {} `{}` in this scope",
-                                            ns.descr(),
-                                            ident
-                                        ),
-                                        BuiltinLintDiag::ProcMacroDeriveResolutionFallback(
-                                            orig_ident.span,
-                                        ),
+                                        BuiltinLintDiag::ProcMacroDeriveResolutionFallback {
+                                            span: orig_ident.span,
+                                            ns,
+                                            ident,
+                                        },
                                     );
                                 }
                                 let misc_flags = if module == this.graph_root {
diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs
index f53bcb0e9d0..55a0382a8e8 100644
--- a/compiler/rustc_resolve/src/imports.rs
+++ b/compiler/rustc_resolve/src/imports.rs
@@ -623,7 +623,6 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                             AMBIGUOUS_GLOB_REEXPORTS,
                             import.root_id,
                             import.root_span,
-                            "ambiguous glob re-exports",
                             BuiltinLintDiag::AmbiguousGlobReexports {
                                 name: key.ident.to_string(),
                                 namespace: key.ns.descr().to_string(),
@@ -659,7 +658,6 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                                 HIDDEN_GLOB_REEXPORTS,
                                 binding_id,
                                 binding.span,
-                                "private item shadows public glob re-export",
                                 BuiltinLintDiag::HiddenGlobReexports {
                                     name: key.ident.name.to_string(),
                                     namespace: key.ns.descr().to_owned(),
@@ -1015,17 +1013,13 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                         && !max_vis.is_at_least(import_vis, self.tcx)
                     {
                         let def_id = self.local_def_id(id);
-                        let msg = format!(
-                            "glob import doesn't reexport anything with visibility `{}` because no imported item is public enough",
-                            import_vis.to_string(def_id, self.tcx)
-                        );
                         self.lint_buffer.buffer_lint_with_diagnostic(
                             UNUSED_IMPORTS,
                             id,
                             import.span,
-                            msg,
                             BuiltinLintDiag::RedundantImportVisibility {
                                 max_vis: max_vis.to_string(def_id, self.tcx),
+                                import_vis: import_vis.to_string(def_id, self.tcx),
                                 span: import.span,
                             },
                         );
@@ -1397,7 +1391,6 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                 UNUSED_IMPORTS,
                 id,
                 import.span,
-                format!("the item `{source}` is imported redundantly"),
                 BuiltinLintDiag::RedundantImport(redundant_spans, source),
             );
             */
diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs
index 0f585aafdd5..9442741b6cc 100644
--- a/compiler/rustc_resolve/src/late.rs
+++ b/compiler/rustc_resolve/src/late.rs
@@ -1675,16 +1675,10 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
                     return;
                 }
                 LifetimeRibKind::AnonymousWarn(node_id) => {
-                    let msg = if elided {
-                        "`&` without an explicit lifetime name cannot be used here"
-                    } else {
-                        "`'_` cannot be used here"
-                    };
                     self.r.lint_buffer.buffer_lint_with_diagnostic(
                         lint::builtin::ELIDED_LIFETIMES_IN_ASSOCIATED_CONSTANT,
                         node_id,
                         lifetime.ident.span,
-                        msg,
                         lint::BuiltinLintDiag::AssociatedConstElidedLifetime {
                             elided,
                             span: lifetime.ident.span,
@@ -1970,7 +1964,6 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
                     lint::builtin::ELIDED_LIFETIMES_IN_PATHS,
                     segment_id,
                     elided_lifetime_span,
-                    "hidden lifetime parameters in types are deprecated",
                     lint::BuiltinLintDiag::ElidedLifetimesInPaths(
                         expected_lifetimes,
                         path_span,
diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs
index 1958fdf1cbc..ce14c98b0d6 100644
--- a/compiler/rustc_resolve/src/late/diagnostics.rs
+++ b/compiler/rustc_resolve/src/late/diagnostics.rs
@@ -2655,11 +2655,11 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
                         lint::builtin::SINGLE_USE_LIFETIMES,
                         param.id,
                         param.ident.span,
-                        format!("lifetime parameter `{}` only used once", param.ident),
                         lint::BuiltinLintDiag::SingleUseLifetime {
                             param_span: param.ident.span,
                             use_span: Some((use_span, elidable)),
                             deletion_span,
+                            ident: param.ident,
                         },
                     );
                 }
@@ -2673,11 +2673,11 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
                             lint::builtin::UNUSED_LIFETIMES,
                             param.id,
                             param.ident.span,
-                            format!("lifetime parameter `{}` never used", param.ident),
                             lint::BuiltinLintDiag::SingleUseLifetime {
                                 param_span: param.ident.span,
                                 use_span: None,
                                 deletion_span,
+                                ident: param.ident,
                             },
                         );
                     }
diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs
index f8d245f94e5..0e0e6ffc136 100644
--- a/compiler/rustc_resolve/src/macros.rs
+++ b/compiler/rustc_resolve/src/macros.rs
@@ -786,7 +786,6 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                             LEGACY_DERIVE_HELPERS,
                             node_id,
                             ident.span,
-                            "derive helper attribute is used before it is introduced",
                             BuiltinLintDiag::LegacyDeriveHelpers(binding.span),
                         );
                     }
diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs
index f6053f43fbd..59281f96126 100644
--- a/compiler/rustc_session/src/parse.rs
+++ b/compiler/rustc_session/src/parse.rs
@@ -311,9 +311,8 @@ impl ParseSess {
             buffered_lints.push(BufferedEarlyLint {
                 span: span.into(),
                 node_id,
-                msg: msg.into(),
                 lint_id: LintId::of(lint),
-                diagnostic: BuiltinLintDiag::Normal,
+                diagnostic: BuiltinLintDiag::Normal(msg.into()),
             });
         });
     }
@@ -323,14 +322,12 @@ impl ParseSess {
         lint: &'static Lint,
         span: impl Into<MultiSpan>,
         node_id: NodeId,
-        msg: impl Into<DiagMessage>,
         diagnostic: BuiltinLintDiag,
     ) {
         self.buffered_lints.with_lock(|buffered_lints| {
             buffered_lints.push(BufferedEarlyLint {
                 span: span.into(),
                 node_id,
-                msg: msg.into(),
                 lint_id: LintId::of(lint),
                 diagnostic,
             });