about summary refs log tree commit diff
path: root/compiler/rustc_lint/src
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_lint/src')
-rw-r--r--compiler/rustc_lint/src/builtin.rs20
-rw-r--r--compiler/rustc_lint/src/context.rs35
-rw-r--r--compiler/rustc_lint/src/context/diagnostics.rs383
-rw-r--r--compiler/rustc_lint/src/impl_trait_overcaptures.rs23
-rw-r--r--compiler/rustc_lint/src/levels.rs42
-rw-r--r--compiler/rustc_lint/src/lints.rs178
-rw-r--r--compiler/rustc_lint/src/non_fmt_panic.rs3
-rw-r--r--compiler/rustc_lint/src/non_local_def.rs216
8 files changed, 505 insertions, 395 deletions
diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs
index 85d54ce563d..ba42eae3441 100644
--- a/compiler/rustc_lint/src/builtin.rs
+++ b/compiler/rustc_lint/src/builtin.rs
@@ -1950,14 +1950,22 @@ declare_lint_pass!(ExplicitOutlivesRequirements => [EXPLICIT_OUTLIVES_REQUIREMEN
 
 impl ExplicitOutlivesRequirements {
     fn lifetimes_outliving_lifetime<'tcx>(
+        tcx: TyCtxt<'tcx>,
         inferred_outlives: &'tcx [(ty::Clause<'tcx>, Span)],
-        def_id: DefId,
+        item: DefId,
+        lifetime: DefId,
     ) -> Vec<ty::Region<'tcx>> {
+        let item_generics = tcx.generics_of(item);
+
         inferred_outlives
             .iter()
             .filter_map(|(clause, _)| match clause.kind().skip_binder() {
                 ty::ClauseKind::RegionOutlives(ty::OutlivesPredicate(a, b)) => match *a {
-                    ty::ReEarlyParam(ebr) if ebr.def_id == def_id => Some(b),
+                    ty::ReEarlyParam(ebr)
+                        if item_generics.region_param(ebr, tcx).def_id == lifetime =>
+                    {
+                        Some(b)
+                    }
                     _ => None,
                 },
                 _ => None,
@@ -1986,9 +1994,12 @@ impl ExplicitOutlivesRequirements {
         bounds: &hir::GenericBounds<'_>,
         inferred_outlives: &[ty::Region<'tcx>],
         predicate_span: Span,
+        item: DefId,
     ) -> Vec<(usize, Span)> {
         use rustc_middle::middle::resolve_bound_vars::ResolvedArg;
 
+        let item_generics = tcx.generics_of(item);
+
         bounds
             .iter()
             .enumerate()
@@ -2000,7 +2011,7 @@ impl ExplicitOutlivesRequirements {
                 let is_inferred = match tcx.named_bound_var(lifetime.hir_id) {
                     Some(ResolvedArg::EarlyBound(def_id)) => inferred_outlives
                         .iter()
-                        .any(|r| matches!(**r, ty::ReEarlyParam(ebr) if { ebr.def_id == def_id })),
+                        .any(|r| matches!(**r, ty::ReEarlyParam(ebr) if { item_generics.region_param(ebr, tcx).def_id == def_id })),
                     _ => false,
                 };
 
@@ -2109,7 +2120,9 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitOutlivesRequirements {
                             {
                                 (
                                     Self::lifetimes_outliving_lifetime(
+                                        cx.tcx,
                                         inferred_outlives,
+                                        item.owner_id.to_def_id(),
                                         region_def_id,
                                     ),
                                     &predicate.bounds,
@@ -2152,6 +2165,7 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitOutlivesRequirements {
                     bounds,
                     &relevant_lifetimes,
                     predicate_span,
+                    item.owner_id.to_def_id(),
                 );
                 bound_count += bound_spans.len();
 
diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs
index c23a67f6131..56f07a2b5e9 100644
--- a/compiler/rustc_lint/src/context.rs
+++ b/compiler/rustc_lint/src/context.rs
@@ -21,7 +21,7 @@ use crate::passes::{EarlyLintPassObject, LateLintPassObject};
 use rustc_data_structures::fx::FxIndexMap;
 use rustc_data_structures::sync;
 use rustc_data_structures::unord::UnordMap;
-use rustc_errors::{Diag, DiagMessage, LintDiagnostic, MultiSpan};
+use rustc_errors::{Diag, LintDiagnostic, MultiSpan};
 use rustc_feature::Features;
 use rustc_hir as hir;
 use rustc_hir::def::Res;
@@ -539,7 +539,9 @@ impl EarlyContext<'_> {
         span: MultiSpan,
         diagnostic: BuiltinLintDiag,
     ) {
-        diagnostics::emit_buffered_lint(self, lint, span, diagnostic)
+        self.opt_span_lint(lint, Some(span), |diag| {
+            diagnostics::decorate_lint(self.sess(), diagnostic, diag);
+        });
     }
 }
 
@@ -556,7 +558,6 @@ pub trait LintContext {
         &self,
         lint: &'static Lint,
         span: Option<S>,
-        msg: impl Into<DiagMessage>,
         decorate: impl for<'a, 'b> FnOnce(&'b mut Diag<'a, ()>),
     );
 
@@ -568,8 +569,8 @@ pub trait LintContext {
         span: S,
         decorator: impl for<'a> LintDiagnostic<'a, ()>,
     ) {
-        self.opt_span_lint(lint, Some(span), decorator.msg(), |diag| {
-            decorator.decorate_lint(diag);
+        self.opt_span_lint(lint, Some(span), |lint| {
+            decorator.decorate_lint(lint);
         });
     }
 
@@ -581,17 +582,16 @@ pub trait LintContext {
         &self,
         lint: &'static Lint,
         span: S,
-        msg: impl Into<DiagMessage>,
         decorate: impl for<'a, 'b> FnOnce(&'b mut Diag<'a, ()>),
     ) {
-        self.opt_span_lint(lint, Some(span), msg, decorate);
+        self.opt_span_lint(lint, Some(span), decorate);
     }
 
     /// Emit a lint from a lint struct (some type that implements `LintDiagnostic`, typically
     /// generated by `#[derive(LintDiagnostic)]`).
     fn emit_lint(&self, lint: &'static Lint, decorator: impl for<'a> LintDiagnostic<'a, ()>) {
-        self.opt_span_lint(lint, None as Option<Span>, decorator.msg(), |diag| {
-            decorator.decorate_lint(diag);
+        self.opt_span_lint(lint, None as Option<Span>, |lint| {
+            decorator.decorate_lint(lint);
         });
     }
 
@@ -599,13 +599,8 @@ pub trait LintContext {
     ///
     /// [`lint_level`]: rustc_middle::lint::lint_level#decorate-signature
     #[rustc_lint_diagnostics]
-    fn lint(
-        &self,
-        lint: &'static Lint,
-        msg: impl Into<DiagMessage>,
-        decorate: impl for<'a, 'b> FnOnce(&'b mut Diag<'a, ()>),
-    ) {
-        self.opt_span_lint(lint, None as Option<Span>, msg, decorate);
+    fn lint(&self, lint: &'static Lint, decorate: impl for<'a, 'b> FnOnce(&'b mut Diag<'a, ()>)) {
+        self.opt_span_lint(lint, None as Option<Span>, decorate);
     }
 
     /// This returns the lint level for the given lint at the current location.
@@ -668,14 +663,13 @@ impl<'tcx> LintContext for LateContext<'tcx> {
         &self,
         lint: &'static Lint,
         span: Option<S>,
-        msg: impl Into<DiagMessage>,
         decorate: impl for<'a, 'b> FnOnce(&'b mut Diag<'a, ()>),
     ) {
         let hir_id = self.last_node_with_lint_attrs;
 
         match span {
-            Some(s) => self.tcx.node_span_lint(lint, hir_id, s, msg, decorate),
-            None => self.tcx.node_lint(lint, hir_id, msg, decorate),
+            Some(s) => self.tcx.node_span_lint(lint, hir_id, s, decorate),
+            None => self.tcx.node_lint(lint, hir_id, decorate),
         }
     }
 
@@ -695,10 +689,9 @@ impl LintContext for EarlyContext<'_> {
         &self,
         lint: &'static Lint,
         span: Option<S>,
-        msg: impl Into<DiagMessage>,
         decorate: impl for<'a, 'b> FnOnce(&'b mut Diag<'a, ()>),
     ) {
-        self.builder.opt_span_lint(lint, span.map(|s| s.into()), msg, decorate)
+        self.builder.opt_span_lint(lint, span.map(|s| s.into()), decorate)
     }
 
     fn get_lint_level(&self, lint: &'static Lint) -> Level {
diff --git a/compiler/rustc_lint/src/context/diagnostics.rs b/compiler/rustc_lint/src/context/diagnostics.rs
index 23c5e0a9f5f..26f34486a3d 100644
--- a/compiler/rustc_lint/src/context/diagnostics.rs
+++ b/compiler/rustc_lint/src/context/diagnostics.rs
@@ -4,24 +4,19 @@
 use std::borrow::Cow;
 
 use rustc_ast::util::unicode::TEXT_FLOW_CONTROL_CHARS;
-use rustc_errors::Applicability;
-use rustc_errors::{elided_lifetime_in_path_suggestion, DiagArgValue, MultiSpan};
+use rustc_errors::elided_lifetime_in_path_suggestion;
+use rustc_errors::{Applicability, Diag, DiagArgValue, LintDiagnostic};
 use rustc_middle::middle::stability;
-use rustc_session::lint::{BuiltinLintDiag, Lint};
+use rustc_session::lint::BuiltinLintDiag;
+use rustc_session::Session;
 use rustc_span::BytePos;
 use tracing::debug;
 
-use crate::{lints, EarlyContext, LintContext as _};
+use crate::lints;
 
 mod check_cfg;
 
-pub(super) fn emit_buffered_lint(
-    ctx: &EarlyContext<'_>,
-    lint: &'static Lint,
-    span: MultiSpan,
-    diagnostic: BuiltinLintDiag,
-) {
-    let sess = ctx.sess();
+pub(super) fn decorate_lint(sess: &Session, diagnostic: BuiltinLintDiag, diag: &mut Diag<'_, ()>) {
     match diagnostic {
         BuiltinLintDiag::UnicodeTextFlow(comment_span, content) => {
             let spans: Vec<_> = content
@@ -40,16 +35,14 @@ pub(super) fn emit_buffered_lint(
             let suggestions = (!spans.is_empty()).then_some(lints::UnicodeTextFlowSuggestion {
                 spans: spans.iter().map(|(_c, span)| *span).collect(),
             });
-            ctx.emit_span_lint(
-                lint,
-                span,
-                lints::UnicodeTextFlow {
-                    comment_span,
-                    characters,
-                    suggestions,
-                    num_codepoints: spans.len(),
-                },
-            )
+
+            lints::UnicodeTextFlow {
+                comment_span,
+                characters,
+                suggestions,
+                num_codepoints: spans.len(),
+            }
+            .decorate_lint(diag);
         }
         BuiltinLintDiag::AbsPathWithModule(mod_span) => {
             let (replacement, applicability) = match sess.source_map().span_to_snippet(mod_span) {
@@ -62,48 +55,35 @@ pub(super) fn emit_buffered_lint(
                 }
                 Err(_) => ("crate::<path>".to_string(), Applicability::HasPlaceholders),
             };
-            ctx.emit_span_lint(
-                lint,
-                span,
-                lints::AbsPathWithModule {
-                    sugg: lints::AbsPathWithModuleSugg {
-                        span: mod_span,
-                        applicability,
-                        replacement,
-                    },
-                },
-            );
-        }
-        BuiltinLintDiag::ProcMacroDeriveResolutionFallback { span: macro_span, ns, ident } => ctx
-            .emit_span_lint(
-                lint,
-                span,
-                lints::ProcMacroDeriveResolutionFallback { span: macro_span, ns, ident },
-            ),
-        BuiltinLintDiag::MacroExpandedMacroExportsAccessedByAbsolutePaths(span_def) => ctx
-            .emit_span_lint(
-                lint,
-                span,
-                lints::MacroExpandedMacroExportsAccessedByAbsolutePaths { definition: span_def },
-            ),
+            lints::AbsPathWithModule {
+                sugg: lints::AbsPathWithModuleSugg { span: mod_span, applicability, replacement },
+            }
+            .decorate_lint(diag);
+        }
+        BuiltinLintDiag::ProcMacroDeriveResolutionFallback { span: macro_span, ns, ident } => {
+            lints::ProcMacroDeriveResolutionFallback { span: macro_span, ns, ident }
+                .decorate_lint(diag)
+        }
+        BuiltinLintDiag::MacroExpandedMacroExportsAccessedByAbsolutePaths(span_def) => {
+            lints::MacroExpandedMacroExportsAccessedByAbsolutePaths { definition: span_def }
+                .decorate_lint(diag)
+        }
+
         BuiltinLintDiag::ElidedLifetimesInPaths(n, path_span, incl_angl_brckt, insertion_span) => {
-            ctx.emit_span_lint(
-                lint,
-                span,
-                lints::ElidedLifetimesInPaths {
-                    subdiag: elided_lifetime_in_path_suggestion(
-                        sess.source_map(),
-                        n,
-                        path_span,
-                        incl_angl_brckt,
-                        insertion_span,
-                    ),
-                },
-            );
+            lints::ElidedLifetimesInPaths {
+                subdiag: elided_lifetime_in_path_suggestion(
+                    sess.source_map(),
+                    n,
+                    path_span,
+                    incl_angl_brckt,
+                    insertion_span,
+                ),
+            }
+            .decorate_lint(diag);
         }
         BuiltinLintDiag::UnknownCrateTypes { span, candidate } => {
             let sugg = candidate.map(|candidate| lints::UnknownCrateTypesSub { span, candidate });
-            ctx.emit_span_lint(lint, span, lints::UnknownCrateTypes { sugg });
+            lints::UnknownCrateTypes { sugg }.decorate_lint(diag);
         }
         BuiltinLintDiag::UnusedImports {
             remove_whole_use,
@@ -120,18 +100,15 @@ pub(super) fn emit_buffered_lint(
             let test_module_span =
                 test_module_span.map(|span| sess.source_map().guess_head_span(span));
 
-            ctx.emit_span_lint(
-                lint,
-                span,
-                lints::UnusedImports {
-                    sugg,
-                    test_module_span,
-                    num_snippets: span_snippets.len(),
-                    span_snippets: DiagArgValue::StrListSepByAnd(
-                        span_snippets.into_iter().map(Cow::Owned).collect(),
-                    ),
-                },
-            );
+            lints::UnusedImports {
+                sugg,
+                test_module_span,
+                num_snippets: span_snippets.len(),
+                span_snippets: DiagArgValue::StrListSepByAnd(
+                    span_snippets.into_iter().map(Cow::Owned).collect(),
+                ),
+            }
+            .decorate_lint(diag);
         }
         BuiltinLintDiag::RedundantImport(spans, ident) => {
             let subs = spans
@@ -145,7 +122,7 @@ pub(super) fn emit_buffered_lint(
                     })(span)
                 })
                 .collect();
-            ctx.emit_span_lint(lint, span, lints::RedundantImport { subs, ident });
+            lints::RedundantImport { subs, ident }.decorate_lint(diag);
         }
         BuiltinLintDiag::DeprecatedMacro {
             suggestion,
@@ -159,90 +136,63 @@ pub(super) fn emit_buffered_lint(
                 kind: "macro".to_owned(),
                 suggestion,
             });
-            ctx.emit_span_lint(
-                lint,
-                span,
-                stability::Deprecated { sub, kind: "macro".to_owned(), path, note, since_kind },
-            );
+
+            stability::Deprecated { sub, kind: "macro".to_owned(), path, note, since_kind }
+                .decorate_lint(diag);
         }
         BuiltinLintDiag::UnusedDocComment(attr_span) => {
-            ctx.emit_span_lint(lint, span, lints::UnusedDocComment { span: attr_span });
+            lints::UnusedDocComment { span: attr_span }.decorate_lint(diag);
         }
         BuiltinLintDiag::PatternsInFnsWithoutBody { span: remove_span, ident, is_foreign } => {
             let sub = lints::PatternsInFnsWithoutBodySub { ident, span: remove_span };
-
-            ctx.emit_span_lint(
-                lint,
-                span,
-                if is_foreign {
-                    lints::PatternsInFnsWithoutBody::Foreign { sub }
-                } else {
-                    lints::PatternsInFnsWithoutBody::Bodiless { sub }
-                },
-            );
+            if is_foreign {
+                lints::PatternsInFnsWithoutBody::Foreign { sub }
+            } else {
+                lints::PatternsInFnsWithoutBody::Bodiless { sub }
+            }
+            .decorate_lint(diag);
         }
         BuiltinLintDiag::MissingAbi(label_span, default_abi) => {
-            ctx.emit_span_lint(
-                lint,
-                span,
-                lints::MissingAbi { span: label_span, default_abi: default_abi.name() },
-            );
+            lints::MissingAbi { span: label_span, default_abi: default_abi.name() }
+                .decorate_lint(diag);
         }
         BuiltinLintDiag::LegacyDeriveHelpers(label_span) => {
-            ctx.emit_span_lint(lint, span, lints::LegacyDeriveHelpers { span: label_span });
+            lints::LegacyDeriveHelpers { span: label_span }.decorate_lint(diag);
         }
         BuiltinLintDiag::ProcMacroBackCompat { crate_name, fixed_version } => {
-            ctx.emit_span_lint(
-                lint,
-                span,
-                lints::ProcMacroBackCompat { crate_name, fixed_version },
-            );
+            lints::ProcMacroBackCompat { crate_name, fixed_version }.decorate_lint(diag);
         }
         BuiltinLintDiag::OrPatternsBackCompat(suggestion_span, suggestion) => {
-            ctx.emit_span_lint(
-                lint,
-                span,
-                lints::OrPatternsBackCompat { span: suggestion_span, suggestion },
-            );
+            lints::OrPatternsBackCompat { span: suggestion_span, suggestion }.decorate_lint(diag);
         }
         BuiltinLintDiag::ReservedPrefix(label_span, prefix) => {
-            ctx.emit_span_lint(
-                lint,
-                span,
-                lints::ReservedPrefix {
-                    label: label_span,
-                    suggestion: label_span.shrink_to_hi(),
-                    prefix,
-                },
-            );
+            lints::ReservedPrefix {
+                label: label_span,
+                suggestion: label_span.shrink_to_hi(),
+                prefix,
+            }
+            .decorate_lint(diag);
         }
         BuiltinLintDiag::UnusedBuiltinAttribute { attr_name, macro_name, invoc_span } => {
-            ctx.emit_span_lint(
-                lint,
-                span,
-                lints::UnusedBuiltinAttribute { invoc_span, attr_name, macro_name },
-            );
+            lints::UnusedBuiltinAttribute { invoc_span, attr_name, macro_name }.decorate_lint(diag);
         }
         BuiltinLintDiag::TrailingMacro(is_trailing, name) => {
-            ctx.emit_span_lint(lint, span, lints::TrailingMacro { is_trailing, name });
+            lints::TrailingMacro { is_trailing, name }.decorate_lint(diag);
         }
         BuiltinLintDiag::BreakWithLabelAndLoop(sugg_span) => {
-            ctx.emit_span_lint(
-                lint,
-                span,
-                lints::BreakWithLabelAndLoop {
-                    sub: lints::BreakWithLabelAndLoopSub {
-                        left: sugg_span.shrink_to_lo(),
-                        right: sugg_span.shrink_to_hi(),
-                    },
+            lints::BreakWithLabelAndLoop {
+                sub: lints::BreakWithLabelAndLoopSub {
+                    left: sugg_span.shrink_to_lo(),
+                    right: sugg_span.shrink_to_hi(),
                 },
-            );
+            }
+            .decorate_lint(diag);
         }
         BuiltinLintDiag::UnexpectedCfgName(name, value) => {
-            ctx.emit_span_lint(lint, span, check_cfg::unexpected_cfg_name(sess, name, value));
+            check_cfg::unexpected_cfg_name(sess, name, value).decorate_lint(diag);
         }
         BuiltinLintDiag::UnexpectedCfgValue(name, value) => {
-            ctx.emit_span_lint(lint, span, check_cfg::unexpected_cfg_value(sess, name, value));
+            check_cfg::unexpected_cfg_value(sess, name, value).decorate_lint(diag);
         }
         BuiltinLintDiag::DeprecatedWhereclauseLocation(left_sp, sugg) => {
             let suggestion = match sugg {
@@ -253,7 +203,7 @@ pub(super) fn emit_buffered_lint(
                 },
                 None => lints::DeprecatedWhereClauseLocationSugg::RemoveWhere { span: left_sp },
             };
-            ctx.emit_span_lint(lint, span, lints::DeprecatedWhereClauseLocation { suggestion });
+            lints::DeprecatedWhereClauseLocation { suggestion }.decorate_lint(diag);
         }
         BuiltinLintDiag::SingleUseLifetime {
             param_span,
@@ -280,15 +230,12 @@ pub(super) fn emit_buffered_lint(
                 None
             };
 
-            ctx.emit_span_lint(
-                lint,
-                span,
-                lints::SingleUseLifetime { suggestion, param_span, use_span, ident },
-            );
+            lints::SingleUseLifetime { suggestion, param_span, use_span, ident }
+                .decorate_lint(diag);
         }
         BuiltinLintDiag::SingleUseLifetime { use_span: None, deletion_span, ident, .. } => {
             debug!(?deletion_span);
-            ctx.emit_span_lint(lint, span, lints::UnusedLifetime { deletion_span, ident });
+            lints::UnusedLifetime { deletion_span, ident }.decorate_lint(diag);
         }
         BuiltinLintDiag::NamedArgumentUsedPositionally {
             position_sp_to_replace,
@@ -316,35 +263,29 @@ pub(super) fn emit_buffered_lint(
                 (None, String::new())
             };
 
-            ctx.emit_span_lint(
-                lint,
-                span,
-                lints::NamedArgumentUsedPositionally {
-                    named_arg_sp,
-                    position_label_sp: position_sp_for_msg,
-                    suggestion,
-                    name,
-                    named_arg_name,
-                },
-            )
+            lints::NamedArgumentUsedPositionally {
+                named_arg_sp,
+                position_label_sp: position_sp_for_msg,
+                suggestion,
+                name,
+                named_arg_name,
+            }
+            .decorate_lint(diag);
         }
         BuiltinLintDiag::ByteSliceInPackedStructWithDerive { ty } => {
-            ctx.emit_span_lint(lint, span, lints::ByteSliceInPackedStructWithDerive { ty })
+            lints::ByteSliceInPackedStructWithDerive { ty }.decorate_lint(diag);
         }
         BuiltinLintDiag::UnusedExternCrate { removal_span } => {
-            ctx.emit_span_lint(lint, span, lints::UnusedExternCrate { removal_span })
+            lints::UnusedExternCrate { removal_span }.decorate_lint(diag);
         }
         BuiltinLintDiag::ExternCrateNotIdiomatic { vis_span, ident_span } => {
             let suggestion_span = vis_span.between(ident_span);
             let code = if vis_span.is_empty() { "use " } else { " use " };
-            ctx.emit_span_lint(
-                lint,
-                span,
-                lints::ExternCrateNotIdiomatic { span: suggestion_span, code },
-            );
+
+            lints::ExternCrateNotIdiomatic { span: suggestion_span, code }.decorate_lint(diag);
         }
         BuiltinLintDiag::AmbiguousGlobImports { diag: ambiguity } => {
-            ctx.emit_span_lint(lint, span, lints::AmbiguousGlobImports { ambiguity });
+            lints::AmbiguousGlobImports { ambiguity }.decorate_lint(diag);
         }
         BuiltinLintDiag::AmbiguousGlobReexports {
             name,
@@ -352,16 +293,13 @@ pub(super) fn emit_buffered_lint(
             first_reexport_span,
             duplicate_reexport_span,
         } => {
-            ctx.emit_span_lint(
-                lint,
-                span,
-                lints::AmbiguousGlobReexports {
-                    first_reexport: first_reexport_span,
-                    duplicate_reexport: duplicate_reexport_span,
-                    name,
-                    namespace,
-                },
-            );
+            lints::AmbiguousGlobReexports {
+                first_reexport: first_reexport_span,
+                duplicate_reexport: duplicate_reexport_span,
+                name,
+                namespace,
+            }
+            .decorate_lint(diag);
         }
         BuiltinLintDiag::HiddenGlobReexports {
             name,
@@ -369,127 +307,112 @@ pub(super) fn emit_buffered_lint(
             glob_reexport_span,
             private_item_span,
         } => {
-            ctx.emit_span_lint(
-                lint,
-                span,
-                lints::HiddenGlobReexports {
-                    glob_reexport: glob_reexport_span,
-                    private_item: private_item_span,
+            lints::HiddenGlobReexports {
+                glob_reexport: glob_reexport_span,
+                private_item: private_item_span,
 
-                    name,
-                    namespace,
-                },
-            );
+                name,
+                namespace,
+            }
+            .decorate_lint(diag);
         }
         BuiltinLintDiag::UnusedQualifications { removal_span } => {
-            ctx.emit_span_lint(lint, span, lints::UnusedQualifications { removal_span });
+            lints::UnusedQualifications { removal_span }.decorate_lint(diag);
         }
         BuiltinLintDiag::AssociatedConstElidedLifetime { elided, span: lt_span } => {
             let lt_span = if elided { lt_span.shrink_to_hi() } else { lt_span };
             let code = if elided { "'static " } else { "'static" };
-            ctx.emit_span_lint(
-                lint,
-                span,
-                lints::AssociatedConstElidedLifetime { span: lt_span, code, elided },
-            );
+            lints::AssociatedConstElidedLifetime { span: lt_span, code, elided }
+                .decorate_lint(diag);
         }
         BuiltinLintDiag::RedundantImportVisibility { max_vis, span: vis_span, import_vis } => {
-            ctx.emit_span_lint(
-                lint,
-                span,
-                lints::RedundantImportVisibility { span: vis_span, help: (), max_vis, import_vis },
-            );
+            lints::RedundantImportVisibility { span: vis_span, help: (), max_vis, import_vis }
+                .decorate_lint(diag);
         }
         BuiltinLintDiag::UnknownDiagnosticAttribute { span: typo_span, typo_name } => {
             let typo = typo_name.map(|typo_name| lints::UnknownDiagnosticAttributeTypoSugg {
                 span: typo_span,
                 typo_name,
             });
-            ctx.emit_span_lint(lint, span, lints::UnknownDiagnosticAttribute { typo });
+            lints::UnknownDiagnosticAttribute { typo }.decorate_lint(diag);
         }
         BuiltinLintDiag::MacroUseDeprecated => {
-            ctx.emit_span_lint(lint, span, lints::MacroUseDeprecated)
+            lints::MacroUseDeprecated.decorate_lint(diag);
         }
-        BuiltinLintDiag::UnusedMacroUse => ctx.emit_span_lint(lint, span, lints::UnusedMacroUse),
+        BuiltinLintDiag::UnusedMacroUse => lints::UnusedMacroUse.decorate_lint(diag),
         BuiltinLintDiag::PrivateExternCrateReexport(ident) => {
-            ctx.emit_span_lint(lint, span, lints::PrivateExternCrateReexport { ident })
+            lints::PrivateExternCrateReexport { ident }.decorate_lint(diag);
         }
-        BuiltinLintDiag::UnusedLabel => ctx.emit_span_lint(lint, span, lints::UnusedLabel),
+        BuiltinLintDiag::UnusedLabel => lints::UnusedLabel.decorate_lint(diag),
         BuiltinLintDiag::MacroIsPrivate(ident) => {
-            ctx.emit_span_lint(lint, span, lints::MacroIsPrivate { ident })
+            lints::MacroIsPrivate { ident }.decorate_lint(diag);
         }
         BuiltinLintDiag::UnusedMacroDefinition(name) => {
-            ctx.emit_span_lint(lint, span, lints::UnusedMacroDefinition { name })
+            lints::UnusedMacroDefinition { name }.decorate_lint(diag);
         }
         BuiltinLintDiag::MacroRuleNeverUsed(n, name) => {
-            ctx.emit_span_lint(lint, span, lints::MacroRuleNeverUsed { n: n + 1, name })
+            lints::MacroRuleNeverUsed { n: n + 1, name }.decorate_lint(diag);
         }
         BuiltinLintDiag::UnstableFeature(msg) => {
-            ctx.emit_span_lint(lint, span, lints::UnstableFeature { msg })
+            lints::UnstableFeature { msg }.decorate_lint(diag);
         }
         BuiltinLintDiag::AvoidUsingIntelSyntax => {
-            ctx.emit_span_lint(lint, span, lints::AvoidIntelSyntax)
+            lints::AvoidIntelSyntax.decorate_lint(diag);
         }
         BuiltinLintDiag::AvoidUsingAttSyntax => {
-            ctx.emit_span_lint(lint, span, lints::AvoidAttSyntax)
+            lints::AvoidAttSyntax.decorate_lint(diag);
         }
         BuiltinLintDiag::IncompleteInclude => {
-            ctx.emit_span_lint(lint, span, lints::IncompleteInclude)
+            lints::IncompleteInclude.decorate_lint(diag);
         }
         BuiltinLintDiag::UnnameableTestItems => {
-            ctx.emit_span_lint(lint, span, lints::UnnameableTestItems)
+            lints::UnnameableTestItems.decorate_lint(diag);
         }
         BuiltinLintDiag::DuplicateMacroAttribute => {
-            ctx.emit_span_lint(lint, span, lints::DuplicateMacroAttribute)
+            lints::DuplicateMacroAttribute.decorate_lint(diag);
         }
         BuiltinLintDiag::CfgAttrNoAttributes => {
-            ctx.emit_span_lint(lint, span, lints::CfgAttrNoAttributes)
+            lints::CfgAttrNoAttributes.decorate_lint(diag);
         }
         BuiltinLintDiag::CrateTypeInCfgAttr => {
-            ctx.emit_span_lint(lint, span, lints::CrateTypeInCfgAttr)
+            lints::CrateTypeInCfgAttr.decorate_lint(diag);
         }
         BuiltinLintDiag::CrateNameInCfgAttr => {
-            ctx.emit_span_lint(lint, span, lints::CrateNameInCfgAttr)
+            lints::CrateNameInCfgAttr.decorate_lint(diag);
         }
         BuiltinLintDiag::MissingFragmentSpecifier => {
-            ctx.emit_span_lint(lint, span, lints::MissingFragmentSpecifier)
+            lints::MissingFragmentSpecifier.decorate_lint(diag);
         }
         BuiltinLintDiag::MetaVariableStillRepeating(name) => {
-            ctx.emit_span_lint(lint, span, lints::MetaVariableStillRepeating { name })
+            lints::MetaVariableStillRepeating { name }.decorate_lint(diag);
         }
         BuiltinLintDiag::MetaVariableWrongOperator => {
-            ctx.emit_span_lint(lint, span, lints::MetaVariableWrongOperator)
+            lints::MetaVariableWrongOperator.decorate_lint(diag);
         }
         BuiltinLintDiag::DuplicateMatcherBinding => {
-            ctx.emit_span_lint(lint, span, lints::DuplicateMatcherBinding)
+            lints::DuplicateMatcherBinding.decorate_lint(diag);
         }
         BuiltinLintDiag::UnknownMacroVariable(name) => {
-            ctx.emit_span_lint(lint, span, lints::UnknownMacroVariable { name })
-        }
-        BuiltinLintDiag::UnusedCrateDependency { extern_crate, local_crate } => ctx.emit_span_lint(
-            lint,
-            span,
-            lints::UnusedCrateDependency { extern_crate, local_crate },
-        ),
-        BuiltinLintDiag::WasmCAbi => ctx.emit_span_lint(lint, span, lints::WasmCAbi),
-        BuiltinLintDiag::IllFormedAttributeInput { suggestions } => ctx.emit_span_lint(
-            lint,
-            span,
+            lints::UnknownMacroVariable { name }.decorate_lint(diag);
+        }
+        BuiltinLintDiag::UnusedCrateDependency { extern_crate, local_crate } => {
+            lints::UnusedCrateDependency { extern_crate, local_crate }.decorate_lint(diag)
+        }
+        BuiltinLintDiag::WasmCAbi => lints::WasmCAbi.decorate_lint(diag),
+        BuiltinLintDiag::IllFormedAttributeInput { suggestions } => {
             lints::IllFormedAttributeInput {
                 num_suggestions: suggestions.len(),
                 suggestions: DiagArgValue::StrListSepByAnd(
                     suggestions.into_iter().map(|s| format!("`{s}`").into()).collect(),
                 ),
-            },
-        ),
-        BuiltinLintDiag::InnerAttributeUnstable { is_macro } => ctx.emit_span_lint(
-            lint,
-            span,
-            if is_macro {
-                lints::InnerAttributeUnstable::InnerMacroAttribute
-            } else {
-                lints::InnerAttributeUnstable::CustomInnerAttribute
-            },
-        ),
+            }
+            .decorate_lint(diag)
+        }
+        BuiltinLintDiag::InnerAttributeUnstable { is_macro } => if is_macro {
+            lints::InnerAttributeUnstable::InnerMacroAttribute
+        } else {
+            lints::InnerAttributeUnstable::CustomInnerAttribute
+        }
+        .decorate_lint(diag),
     }
 }
diff --git a/compiler/rustc_lint/src/impl_trait_overcaptures.rs b/compiler/rustc_lint/src/impl_trait_overcaptures.rs
index 30bf80b915b..a311c274a6b 100644
--- a/compiler/rustc_lint/src/impl_trait_overcaptures.rs
+++ b/compiler/rustc_lint/src/impl_trait_overcaptures.rs
@@ -5,11 +5,11 @@ use rustc_hir as hir;
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_macros::LintDiagnostic;
+use rustc_middle::bug;
 use rustc_middle::middle::resolve_bound_vars::ResolvedArg;
 use rustc_middle::ty::{
     self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor,
 };
-use rustc_middle::{bug, span_bug};
 use rustc_session::{declare_lint, declare_lint_pass};
 use rustc_span::{sym, BytePos, Span};
 
@@ -303,20 +303,12 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for VisitOpaqueTypes<'tcx> {
                             ResolvedArg::EarlyBound(def_id) | ResolvedArg::LateBound(_, _, def_id),
                         ) => {
                             if self.tcx.def_kind(self.tcx.parent(def_id)) == DefKind::OpaqueTy {
-                                let (ty::ReEarlyParam(ty::EarlyParamRegion { def_id, .. })
-                                | ty::ReLateParam(ty::LateParamRegion {
-                                    bound_region: ty::BoundRegionKind::BrNamed(def_id, _),
-                                    ..
-                                })) = self
+                                let def_id = self
                                     .tcx
                                     .map_opaque_lifetime_to_parent_lifetime(def_id.expect_local())
-                                    .kind()
-                                else {
-                                    span_bug!(
-                                        self.tcx.def_span(def_id),
-                                        "variable should have been duplicated from a parent"
-                                    );
-                                };
+                                    .opt_param_def_id(self.tcx, self.parent_def_id.to_def_id())
+                                    .expect("variable should have been duplicated from parent");
+
                                 explicitly_captured.insert(def_id);
                             } else {
                                 explicitly_captured.insert(def_id);
@@ -369,6 +361,7 @@ struct ImplTraitOvercapturesLint<'tcx> {
 
 impl<'a> LintDiagnostic<'a, ()> for ImplTraitOvercapturesLint<'_> {
     fn decorate_lint<'b>(self, diag: &'b mut rustc_errors::Diag<'a, ()>) {
+        diag.primary_message(fluent::lint_impl_trait_overcaptures);
         diag.arg("self_ty", self.self_ty.to_string())
             .arg("num_captured", self.num_captured)
             .span_note(self.uncaptured_spans, fluent::lint_note)
@@ -382,10 +375,6 @@ impl<'a> LintDiagnostic<'a, ()> for ImplTraitOvercapturesLint<'_> {
             );
         }
     }
-
-    fn msg(&self) -> rustc_errors::DiagMessage {
-        fluent::lint_impl_trait_overcaptures
-    }
 }
 
 #[derive(LintDiagnostic)]
diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs
index 98e321076c5..84645e0ce7f 100644
--- a/compiler/rustc_lint/src/levels.rs
+++ b/compiler/rustc_lint/src/levels.rs
@@ -16,7 +16,7 @@ use crate::{
 use rustc_ast as ast;
 use rustc_ast_pretty::pprust;
 use rustc_data_structures::fx::FxIndexMap;
-use rustc_errors::{Diag, DiagMessage, LintDiagnostic, MultiSpan};
+use rustc_errors::{Diag, LintDiagnostic, MultiSpan};
 use rustc_feature::{Features, GateIssue};
 use rustc_hir as hir;
 use rustc_hir::intravisit::{self, Visitor};
@@ -1064,26 +1064,19 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
             // FIXME: make this translatable
             #[allow(rustc::diagnostic_outside_of_impl)]
             #[allow(rustc::untranslatable_diagnostic)]
-            lint_level(
-                self.sess,
-                lint,
-                level,
-                src,
-                Some(span.into()),
-                fluent::lint_unknown_gated_lint,
-                |lint| {
-                    lint.arg("name", lint_id.lint.name_lower());
-                    lint.note(fluent::lint_note);
-                    rustc_session::parse::add_feature_diagnostics_for_issue(
-                        lint,
-                        &self.sess,
-                        feature,
-                        GateIssue::Language,
-                        lint_from_cli,
-                        None,
-                    );
-                },
-            );
+            lint_level(self.sess, lint, level, src, Some(span.into()), |lint| {
+                lint.primary_message(fluent::lint_unknown_gated_lint);
+                lint.arg("name", lint_id.lint.name_lower());
+                lint.note(fluent::lint_note);
+                rustc_session::parse::add_feature_diagnostics_for_issue(
+                    lint,
+                    &self.sess,
+                    feature,
+                    GateIssue::Language,
+                    lint_from_cli,
+                    None,
+                );
+            });
         }
 
         false
@@ -1104,11 +1097,10 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
         &self,
         lint: &'static Lint,
         span: Option<MultiSpan>,
-        msg: impl Into<DiagMessage>,
         decorate: impl for<'a, 'b> FnOnce(&'b mut Diag<'a, ()>),
     ) {
         let (level, src) = self.lint_level(lint);
-        lint_level(self.sess, lint, level, src, span, msg, decorate)
+        lint_level(self.sess, lint, level, src, span, decorate)
     }
 
     #[track_caller]
@@ -1119,7 +1111,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
         decorate: impl for<'a> LintDiagnostic<'a, ()>,
     ) {
         let (level, src) = self.lint_level(lint);
-        lint_level(self.sess, lint, level, src, Some(span), decorate.msg(), |lint| {
+        lint_level(self.sess, lint, level, src, Some(span), |lint| {
             decorate.decorate_lint(lint);
         });
     }
@@ -1127,7 +1119,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
     #[track_caller]
     pub fn emit_lint(&self, lint: &'static Lint, decorate: impl for<'a> LintDiagnostic<'a, ()>) {
         let (level, src) = self.lint_level(lint);
-        lint_level(self.sess, lint, level, src, None, decorate.msg(), |lint| {
+        lint_level(self.sess, lint, level, src, None, |lint| {
             decorate.decorate_lint(lint);
         });
     }
diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs
index 2f44bc4764e..2edfb8d3df4 100644
--- a/compiler/rustc_lint/src/lints.rs
+++ b/compiler/rustc_lint/src/lints.rs
@@ -6,7 +6,7 @@ use crate::errors::RequestedLevel;
 use crate::fluent_generated as fluent;
 use rustc_errors::{
     codes::*, Applicability, Diag, DiagArgValue, DiagMessage, DiagStyledString,
-    ElidedLifetimeInPathSubdiag, EmissionGuarantee, LintDiagnostic, SubdiagMessageOp,
+    ElidedLifetimeInPathSubdiag, EmissionGuarantee, LintDiagnostic, MultiSpan, SubdiagMessageOp,
     Subdiagnostic, SuggestionStyle,
 };
 use rustc_hir::{def::Namespace, def_id::DefId};
@@ -145,12 +145,9 @@ pub struct BuiltinMissingDebugImpl<'a> {
 // Needed for def_path_str
 impl<'a> LintDiagnostic<'a, ()> for BuiltinMissingDebugImpl<'_> {
     fn decorate_lint<'b>(self, diag: &'b mut rustc_errors::Diag<'a, ()>) {
+        diag.primary_message(fluent::lint_builtin_missing_debug_impl);
         diag.arg("debug", self.tcx.def_path_str(self.def_id));
     }
-
-    fn msg(&self) -> DiagMessage {
-        fluent::lint_builtin_missing_debug_impl
-    }
 }
 
 #[derive(LintDiagnostic)]
@@ -250,6 +247,7 @@ pub struct BuiltinUngatedAsyncFnTrackCaller<'a> {
 
 impl<'a> LintDiagnostic<'a, ()> for BuiltinUngatedAsyncFnTrackCaller<'_> {
     fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, ()>) {
+        diag.primary_message(fluent::lint_ungated_async_fn_track_caller);
         diag.span_label(self.label, fluent::lint_label);
         rustc_session::parse::add_feature_diagnostics(
             diag,
@@ -257,10 +255,6 @@ impl<'a> LintDiagnostic<'a, ()> for BuiltinUngatedAsyncFnTrackCaller<'_> {
             sym::async_fn_track_caller,
         );
     }
-
-    fn msg(&self) -> DiagMessage {
-        fluent::lint_ungated_async_fn_track_caller
-    }
 }
 
 #[derive(LintDiagnostic)]
@@ -432,6 +426,7 @@ pub struct BuiltinUnpermittedTypeInit<'a> {
 
 impl<'a> LintDiagnostic<'a, ()> for BuiltinUnpermittedTypeInit<'_> {
     fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, ()>) {
+        diag.primary_message(self.msg);
         diag.arg("ty", self.ty);
         diag.span_label(self.label, fluent::lint_builtin_unpermitted_type_init_label);
         if let InhabitedPredicate::True = self.ty.inhabited_predicate(self.tcx) {
@@ -443,10 +438,6 @@ impl<'a> LintDiagnostic<'a, ()> for BuiltinUnpermittedTypeInit<'_> {
         }
         self.sub.add_to_diag(diag);
     }
-
-    fn msg(&self) -> DiagMessage {
-        self.msg.clone()
-    }
 }
 
 // FIXME(davidtwco): make translatable
@@ -1169,6 +1160,7 @@ pub struct NonFmtPanicUnused {
 // Used because of two suggestions based on one Option<Span>
 impl<'a> LintDiagnostic<'a, ()> for NonFmtPanicUnused {
     fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, ()>) {
+        diag.primary_message(fluent::lint_non_fmt_panic_unused);
         diag.arg("count", self.count);
         diag.note(fluent::lint_note);
         if let Some(span) = self.suggestion {
@@ -1186,10 +1178,6 @@ impl<'a> LintDiagnostic<'a, ()> for NonFmtPanicUnused {
             );
         }
     }
-
-    fn msg(&self) -> DiagMessage {
-        fluent::lint_non_fmt_panic_unused
-    }
 }
 
 #[derive(LintDiagnostic)]
@@ -1341,40 +1329,126 @@ pub struct SuspiciousDoubleRefCloneDiag<'a> {
 }
 
 // non_local_defs.rs
-#[derive(LintDiagnostic)]
 pub enum NonLocalDefinitionsDiag {
-    #[diag(lint_non_local_definitions_impl)]
-    #[help]
-    #[note(lint_non_local)]
-    #[note(lint_exception)]
-    #[note(lint_non_local_definitions_deprecation)]
     Impl {
         depth: u32,
         body_kind_descr: &'static str,
         body_name: String,
-        #[subdiagnostic]
         cargo_update: Option<NonLocalDefinitionsCargoUpdateNote>,
-        #[suggestion(lint_const_anon, code = "_", applicability = "machine-applicable")]
-        const_anon: Option<Span>,
+        const_anon: Option<Option<Span>>,
+        move_to: Option<(Span, Vec<Span>)>,
+        may_remove: Option<(Span, String)>,
+        has_trait: bool,
+        self_ty_str: String,
+        of_trait_str: Option<String>,
     },
-    #[diag(lint_non_local_definitions_macro_rules)]
     MacroRules {
         depth: u32,
         body_kind_descr: &'static str,
         body_name: String,
-        #[help]
         help: Option<()>,
-        #[help(lint_help_doctest)]
         doctest_help: Option<()>,
-        #[note(lint_non_local)]
-        #[note(lint_exception)]
-        #[note(lint_non_local_definitions_deprecation)]
-        notes: (),
-        #[subdiagnostic]
         cargo_update: Option<NonLocalDefinitionsCargoUpdateNote>,
     },
 }
 
+impl<'a> LintDiagnostic<'a, ()> for NonLocalDefinitionsDiag {
+    fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, ()>) {
+        match self {
+            NonLocalDefinitionsDiag::Impl {
+                depth,
+                body_kind_descr,
+                body_name,
+                cargo_update,
+                const_anon,
+                move_to,
+                may_remove,
+                has_trait,
+                self_ty_str,
+                of_trait_str,
+            } => {
+                diag.primary_message(fluent::lint_non_local_definitions_impl);
+                diag.arg("depth", depth);
+                diag.arg("body_kind_descr", body_kind_descr);
+                diag.arg("body_name", body_name);
+                diag.arg("self_ty_str", self_ty_str);
+                if let Some(of_trait_str) = of_trait_str {
+                    diag.arg("of_trait_str", of_trait_str);
+                }
+
+                if has_trait {
+                    diag.note(fluent::lint_bounds);
+                    diag.note(fluent::lint_with_trait);
+                } else {
+                    diag.note(fluent::lint_without_trait);
+                }
+
+                if let Some((move_help, may_move)) = move_to {
+                    let mut ms = MultiSpan::from_span(move_help);
+                    for sp in may_move {
+                        ms.push_span_label(sp, fluent::lint_non_local_definitions_may_move);
+                    }
+                    diag.span_help(ms, fluent::lint_non_local_definitions_impl_move_help);
+                }
+
+                if let Some((span, part)) = may_remove {
+                    diag.arg("may_remove_part", part);
+                    diag.span_suggestion(
+                        span,
+                        fluent::lint_remove_help,
+                        "",
+                        Applicability::MaybeIncorrect,
+                    );
+                }
+
+                if let Some(cargo_update) = cargo_update {
+                    diag.subdiagnostic(&diag.dcx, cargo_update);
+                }
+                if let Some(const_anon) = const_anon {
+                    diag.note(fluent::lint_exception);
+                    if let Some(const_anon) = const_anon {
+                        diag.span_suggestion(
+                            const_anon,
+                            fluent::lint_const_anon,
+                            "_",
+                            Applicability::MachineApplicable,
+                        );
+                    }
+                }
+
+                diag.note(fluent::lint_non_local_definitions_deprecation);
+            }
+            NonLocalDefinitionsDiag::MacroRules {
+                depth,
+                body_kind_descr,
+                body_name,
+                help,
+                doctest_help,
+                cargo_update,
+            } => {
+                diag.primary_message(fluent::lint_non_local_definitions_macro_rules);
+                diag.arg("depth", depth);
+                diag.arg("body_kind_descr", body_kind_descr);
+                diag.arg("body_name", body_name);
+
+                if let Some(()) = help {
+                    diag.help(fluent::lint_help);
+                }
+                if let Some(()) = doctest_help {
+                    diag.help(fluent::lint_help_doctest);
+                }
+
+                diag.note(fluent::lint_non_local);
+                diag.note(fluent::lint_non_local_definitions_deprecation);
+
+                if let Some(cargo_update) = cargo_update {
+                    diag.subdiagnostic(&diag.dcx, cargo_update);
+                }
+            }
+        }
+    }
+}
+
 #[derive(Subdiagnostic)]
 #[note(lint_non_local_definitions_cargo_update)]
 pub struct NonLocalDefinitionsCargoUpdateNote {
@@ -1411,13 +1485,10 @@ pub struct DropTraitConstraintsDiag<'a> {
 // Needed for def_path_str
 impl<'a> LintDiagnostic<'a, ()> for DropTraitConstraintsDiag<'_> {
     fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, ()>) {
+        diag.primary_message(fluent::lint_drop_trait_constraints);
         diag.arg("predicate", self.predicate);
         diag.arg("needs_drop", self.tcx.def_path_str(self.def_id));
     }
-
-    fn msg(&self) -> DiagMessage {
-        fluent::lint_drop_trait_constraints
-    }
 }
 
 pub struct DropGlue<'a> {
@@ -1428,12 +1499,9 @@ pub struct DropGlue<'a> {
 // Needed for def_path_str
 impl<'a> LintDiagnostic<'a, ()> for DropGlue<'_> {
     fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, ()>) {
+        diag.primary_message(fluent::lint_drop_glue);
         diag.arg("needs_drop", self.tcx.def_path_str(self.def_id));
     }
-
-    fn msg(&self) -> DiagMessage {
-        fluent::lint_drop_glue
-    }
 }
 
 // types.rs
@@ -1711,6 +1779,7 @@ pub struct ImproperCTypes<'a> {
 // Used because of the complexity of Option<DiagMessage>, DiagMessage, and Option<Span>
 impl<'a> LintDiagnostic<'a, ()> for ImproperCTypes<'_> {
     fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, ()>) {
+        diag.primary_message(fluent::lint_improper_ctypes);
         diag.arg("ty", self.ty);
         diag.arg("desc", self.desc);
         diag.span_label(self.label, fluent::lint_label);
@@ -1722,10 +1791,6 @@ impl<'a> LintDiagnostic<'a, ()> for ImproperCTypes<'_> {
             diag.span_note(note, fluent::lint_note);
         }
     }
-
-    fn msg(&self) -> DiagMessage {
-        fluent::lint_improper_ctypes
-    }
 }
 
 #[derive(LintDiagnostic)]
@@ -1854,6 +1919,7 @@ pub enum UnusedDefSuggestion {
 // Needed because of def_path_str
 impl<'a> LintDiagnostic<'a, ()> for UnusedDef<'_, '_> {
     fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, ()>) {
+        diag.primary_message(fluent::lint_unused_def);
         diag.arg("pre", self.pre);
         diag.arg("post", self.post);
         diag.arg("def", self.cx.tcx.def_path_str(self.def_id));
@@ -1865,10 +1931,6 @@ impl<'a> LintDiagnostic<'a, ()> for UnusedDef<'_, '_> {
             diag.subdiagnostic(diag.dcx, sugg);
         }
     }
-
-    fn msg(&self) -> DiagMessage {
-        fluent::lint_unused_def
-    }
 }
 
 #[derive(LintDiagnostic)]
@@ -1937,15 +1999,12 @@ pub struct AsyncFnInTraitDiag {
 
 impl<'a> LintDiagnostic<'a, ()> for AsyncFnInTraitDiag {
     fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, ()>) {
+        diag.primary_message(fluent::lint_async_fn_in_trait);
         diag.note(fluent::lint_note);
         if let Some(sugg) = self.sugg {
             diag.multipart_suggestion(fluent::lint_suggestion, sugg, Applicability::MaybeIncorrect);
         }
     }
-
-    fn msg(&self) -> DiagMessage {
-        fluent::lint_async_fn_in_trait
-    }
 }
 
 #[derive(LintDiagnostic)]
@@ -2273,10 +2332,8 @@ pub struct UnstableFeature {
 }
 
 impl<'a> LintDiagnostic<'a, ()> for UnstableFeature {
-    fn decorate_lint<'b>(self, _diag: &'b mut Diag<'a, ()>) {}
-
-    fn msg(&self) -> DiagMessage {
-        self.msg.clone()
+    fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, ()>) {
+        diag.primary_message(self.msg);
     }
 }
 
@@ -2738,12 +2795,9 @@ pub struct AmbiguousGlobImports {
 
 impl<'a, G: EmissionGuarantee> LintDiagnostic<'a, G> for AmbiguousGlobImports {
     fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, G>) {
+        diag.primary_message(self.ambiguity.msg.clone());
         rustc_errors::report_ambiguity_error(diag, self.ambiguity);
     }
-
-    fn msg(&self) -> DiagMessage {
-        DiagMessage::Str(self.ambiguity.msg.clone().into())
-    }
 }
 
 #[derive(LintDiagnostic)]
diff --git a/compiler/rustc_lint/src/non_fmt_panic.rs b/compiler/rustc_lint/src/non_fmt_panic.rs
index a6057afcbd6..c9d67854112 100644
--- a/compiler/rustc_lint/src/non_fmt_panic.rs
+++ b/compiler/rustc_lint/src/non_fmt_panic.rs
@@ -123,7 +123,8 @@ fn check_panic<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>, arg: &'tc
     }
 
     #[allow(rustc::diagnostic_outside_of_impl)]
-    cx.span_lint(NON_FMT_PANICS, arg_span, fluent::lint_non_fmt_panic, |lint| {
+    cx.span_lint(NON_FMT_PANICS, arg_span, |lint| {
+        lint.primary_message(fluent::lint_non_fmt_panic);
         lint.arg("name", symbol);
         lint.note(fluent::lint_note);
         lint.note(fluent::lint_more_info_note);
diff --git a/compiler/rustc_lint/src/non_local_def.rs b/compiler/rustc_lint/src/non_local_def.rs
index b3e93748a16..42b03f47a5b 100644
--- a/compiler/rustc_lint/src/non_local_def.rs
+++ b/compiler/rustc_lint/src/non_local_def.rs
@@ -1,3 +1,6 @@
+use rustc_errors::MultiSpan;
+use rustc_hir::intravisit::{self, Visitor};
+use rustc_hir::HirId;
 use rustc_hir::{def::DefKind, Body, Item, ItemKind, Node, TyKind};
 use rustc_hir::{Path, QPath};
 use rustc_infer::infer::InferCtxt;
@@ -7,12 +10,13 @@ use rustc_middle::ty::{EarlyBinder, TraitRef, TypeSuperFoldable};
 use rustc_session::{declare_lint, impl_lint_pass};
 use rustc_span::def_id::{DefId, LOCAL_CRATE};
 use rustc_span::Span;
-use rustc_span::{sym, symbol::kw, ExpnKind, MacroKind};
+use rustc_span::{sym, symbol::kw, ExpnKind, MacroKind, Symbol};
 use rustc_trait_selection::infer::TyCtxtInferExt;
 use rustc_trait_selection::traits::error_reporting::ambiguity::{
     compute_applicable_impls_for_diagnostics, CandidateSource,
 };
 
+use crate::fluent_generated as fluent;
 use crate::lints::{NonLocalDefinitionsCargoUpdateNote, NonLocalDefinitionsDiag};
 use crate::{LateContext, LateLintPass, LintContext};
 
@@ -134,35 +138,8 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions {
                 };
 
                 // Part 1: Is the Self type local?
-                let self_ty_has_local_parent = match impl_.self_ty.kind {
-                    TyKind::Path(QPath::Resolved(_, ty_path)) => {
-                        path_has_local_parent(ty_path, cx, parent, parent_parent)
-                    }
-                    TyKind::TraitObject([principle_poly_trait_ref, ..], _, _) => {
-                        path_has_local_parent(
-                            principle_poly_trait_ref.trait_ref.path,
-                            cx,
-                            parent,
-                            parent_parent,
-                        )
-                    }
-                    TyKind::TraitObject([], _, _)
-                    | TyKind::InferDelegation(_, _)
-                    | TyKind::Slice(_)
-                    | TyKind::Array(_, _)
-                    | TyKind::Ptr(_)
-                    | TyKind::Ref(_, _)
-                    | TyKind::BareFn(_)
-                    | TyKind::Never
-                    | TyKind::Tup(_)
-                    | TyKind::Path(_)
-                    | TyKind::Pat(..)
-                    | TyKind::AnonAdt(_)
-                    | TyKind::OpaqueDef(_, _, _)
-                    | TyKind::Typeof(_)
-                    | TyKind::Infer
-                    | TyKind::Err(_) => false,
-                };
+                let self_ty_has_local_parent =
+                    ty_has_local_parent(&impl_.self_ty.kind, cx, parent, parent_parent);
 
                 if self_ty_has_local_parent {
                     return;
@@ -202,8 +179,7 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions {
                 // Get the span of the parent const item ident (if it's a not a const anon).
                 //
                 // Used to suggest changing the const item to a const anon.
-                let span_for_const_anon_suggestion = if self.body_depth == 1
-                    && parent_def_kind == DefKind::Const
+                let span_for_const_anon_suggestion = if parent_def_kind == DefKind::Const
                     && parent_opt_item_name != Some(kw::Underscore)
                     && let Some(parent) = parent.as_local()
                     && let Node::Item(item) = cx.tcx.hir_node_by_def_id(parent)
@@ -215,9 +191,76 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions {
                     None
                 };
 
+                let mut collector = PathCollector { paths: Vec::new() };
+                collector.visit_ty(&impl_.self_ty);
+                if let Some(of_trait) = &impl_.of_trait {
+                    collector.visit_trait_ref(of_trait);
+                }
+                collector.visit_generics(&impl_.generics);
+
+                let mut may_move: Vec<Span> = collector
+                    .paths
+                    .into_iter()
+                    .filter_map(|path| {
+                        if let Some(did) = path.res.opt_def_id()
+                            && did_has_local_parent(did, cx.tcx, parent, parent_parent)
+                        {
+                            Some(cx.tcx.def_span(did))
+                        } else {
+                            None
+                        }
+                    })
+                    .collect();
+                may_move.sort();
+                may_move.dedup();
+
+                let const_anon = matches!(parent_def_kind, DefKind::Const | DefKind::Static { .. })
+                    .then_some(span_for_const_anon_suggestion);
+
+                let may_remove = match &impl_.self_ty.kind {
+                    TyKind::Ptr(mut_ty) | TyKind::Ref(_, mut_ty)
+                        if ty_has_local_parent(&mut_ty.ty.kind, cx, parent, parent_parent) =>
+                    {
+                        let type_ =
+                            if matches!(impl_.self_ty.kind, TyKind::Ptr(_)) { "*" } else { "&" };
+                        let part = format!("{}{}", type_, mut_ty.mutbl.prefix_str());
+                        Some((impl_.self_ty.span.shrink_to_lo().until(mut_ty.ty.span), part))
+                    }
+                    _ => None,
+                };
+
+                let impl_span = item.span.shrink_to_lo().to(impl_.self_ty.span);
+                let mut ms = MultiSpan::from_span(impl_span);
+
+                let (self_ty_span, self_ty_str) =
+                    self_ty_kind_for_diagnostic(&impl_.self_ty, cx.tcx);
+
+                ms.push_span_label(
+                    self_ty_span,
+                    fluent::lint_non_local_definitions_self_ty_not_local,
+                );
+                let of_trait_str = if let Some(of_trait) = &impl_.of_trait {
+                    ms.push_span_label(
+                        path_span_without_args(&of_trait.path),
+                        fluent::lint_non_local_definitions_of_trait_not_local,
+                    );
+                    Some(path_name_to_string(&of_trait.path))
+                } else {
+                    None
+                };
+                let move_to = if may_move.is_empty() {
+                    ms.push_span_label(
+                        cx.tcx.def_span(parent),
+                        fluent::lint_non_local_definitions_impl_move_help,
+                    );
+                    None
+                } else {
+                    Some((cx.tcx.def_span(parent), may_move))
+                };
+
                 cx.emit_span_lint(
                     NON_LOCAL_DEFINITIONS,
-                    item.span,
+                    ms,
                     NonLocalDefinitionsDiag::Impl {
                         depth: self.body_depth,
                         body_kind_descr: cx.tcx.def_kind_descr(parent_def_kind, parent),
@@ -225,7 +268,12 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions {
                             .map(|s| s.to_ident_string())
                             .unwrap_or_else(|| "<unnameable>".to_string()),
                         cargo_update: cargo_update(),
-                        const_anon: span_for_const_anon_suggestion,
+                        const_anon,
+                        self_ty_str,
+                        of_trait_str,
+                        move_to,
+                        may_remove,
+                        has_trait: impl_.of_trait.is_some(),
                     },
                 )
             }
@@ -250,7 +298,6 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions {
                         cargo_update: cargo_update(),
                         help: (!is_at_toplevel_doctest).then_some(()),
                         doctest_help: is_at_toplevel_doctest.then_some(()),
-                        notes: (),
                     },
                 )
             }
@@ -270,7 +317,7 @@ fn impl_trait_ref_has_enough_non_local_candidates<'tcx>(
     tcx: TyCtxt<'tcx>,
     infer_span: Span,
     trait_def_id: DefId,
-    binder: EarlyBinder<TraitRef<'tcx>>,
+    binder: EarlyBinder<'tcx, TraitRef<'tcx>>,
     mut did_has_local_parent: impl FnMut(DefId) -> bool,
 ) -> bool {
     let infcx = tcx
@@ -343,6 +390,54 @@ impl<'a, 'tcx, F: FnMut(DefId) -> bool> TypeFolder<TyCtxt<'tcx>>
     }
 }
 
+/// Simple hir::Path collector
+struct PathCollector<'tcx> {
+    paths: Vec<Path<'tcx>>,
+}
+
+impl<'tcx> Visitor<'tcx> for PathCollector<'tcx> {
+    fn visit_path(&mut self, path: &Path<'tcx>, _id: HirId) {
+        self.paths.push(path.clone()); // need to clone, bc of the restricted lifetime
+        intravisit::walk_path(self, path)
+    }
+}
+
+/// Given a `Ty` we check if the (outermost) type is local.
+fn ty_has_local_parent(
+    ty_kind: &TyKind<'_>,
+    cx: &LateContext<'_>,
+    impl_parent: DefId,
+    impl_parent_parent: Option<DefId>,
+) -> bool {
+    match ty_kind {
+        TyKind::Path(QPath::Resolved(_, ty_path)) => {
+            path_has_local_parent(ty_path, cx, impl_parent, impl_parent_parent)
+        }
+        TyKind::TraitObject([principle_poly_trait_ref, ..], _, _) => path_has_local_parent(
+            principle_poly_trait_ref.trait_ref.path,
+            cx,
+            impl_parent,
+            impl_parent_parent,
+        ),
+        TyKind::TraitObject([], _, _)
+        | TyKind::InferDelegation(_, _)
+        | TyKind::Slice(_)
+        | TyKind::Array(_, _)
+        | TyKind::Ptr(_)
+        | TyKind::Ref(_, _)
+        | TyKind::BareFn(_)
+        | TyKind::Never
+        | TyKind::Tup(_)
+        | TyKind::Path(_)
+        | TyKind::Pat(..)
+        | TyKind::AnonAdt(_)
+        | TyKind::OpaqueDef(_, _, _)
+        | TyKind::Typeof(_)
+        | TyKind::Infer
+        | TyKind::Err(_) => false,
+    }
+}
+
 /// Given a path and a parent impl def id, this checks if the if parent resolution
 /// def id correspond to the def id of the parent impl definition.
 ///
@@ -384,3 +479,52 @@ fn did_has_local_parent(
             false
         }
 }
+
+/// Return for a given `Path` the span until the last args
+fn path_span_without_args(path: &Path<'_>) -> Span {
+    if let Some(args) = &path.segments.last().unwrap().args {
+        path.span.until(args.span_ext)
+    } else {
+        path.span
+    }
+}
+
+/// Return a "error message-able" ident for the last segment of the `Path`
+fn path_name_to_string(path: &Path<'_>) -> String {
+    path.segments.last().unwrap().ident.name.to_ident_string()
+}
+
+/// Compute the `Span` and visual representation for the `Self` we want to point at;
+/// It follows part of the actual logic of non-local, and if possible return the least
+/// amount possible for the span and representation.
+fn self_ty_kind_for_diagnostic(ty: &rustc_hir::Ty<'_>, tcx: TyCtxt<'_>) -> (Span, String) {
+    match ty.kind {
+        TyKind::Path(QPath::Resolved(_, ty_path)) => (
+            path_span_without_args(ty_path),
+            ty_path
+                .res
+                .opt_def_id()
+                .map(|did| tcx.opt_item_name(did))
+                .flatten()
+                .as_ref()
+                .map(|s| Symbol::as_str(s))
+                .unwrap_or("<unnameable>")
+                .to_string(),
+        ),
+        TyKind::TraitObject([principle_poly_trait_ref, ..], _, _) => {
+            let path = &principle_poly_trait_ref.trait_ref.path;
+            (
+                path_span_without_args(path),
+                path.res
+                    .opt_def_id()
+                    .map(|did| tcx.opt_item_name(did))
+                    .flatten()
+                    .as_ref()
+                    .map(|s| Symbol::as_str(s))
+                    .unwrap_or("<unnameable>")
+                    .to_string(),
+            )
+        }
+        _ => (ty.span, rustc_hir_pretty::ty_to_string(&tcx, ty)),
+    }
+}