about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_error_messages/locales/en-US/lint.ftl4
-rw-r--r--compiler/rustc_lint/src/builtin.rs16
-rw-r--r--compiler/rustc_lint/src/expect.rs6
-rw-r--r--compiler/rustc_lint/src/levels.rs13
-rw-r--r--compiler/rustc_lint/src/lints.rs174
-rw-r--r--compiler/rustc_lint/src/types.rs26
6 files changed, 99 insertions, 140 deletions
diff --git a/compiler/rustc_error_messages/locales/en-US/lint.ftl b/compiler/rustc_error_messages/locales/en-US/lint.ftl
index 5330ce504b2..1518adf8e4e 100644
--- a/compiler/rustc_error_messages/locales/en-US/lint.ftl
+++ b/compiler/rustc_error_messages/locales/en-US/lint.ftl
@@ -15,6 +15,7 @@ lint_enum_intrinsics_mem_variant =
 
 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_for_loops_over_fallibles =
     for loop over {$article} `{$ty}`. This is more readably written as an `if let` statement
@@ -39,7 +40,8 @@ lint_deprecated_lint_name =
     lint name `{$name}` is deprecated and may not have an effect in the future.
     .suggestion = change it to
 
-lint_renamed_or_removed_lint_suggestion = use the new name
+lint_renamed_or_removed_lint = {$msg}
+    .suggestion = use the new name
 
 lint_unknown_lint =
     unknown lint: `{$name}`
diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs
index 8a34afb1ff5..1dae563577a 100644
--- a/compiler/rustc_lint/src/builtin.rs
+++ b/compiler/rustc_lint/src/builtin.rs
@@ -26,7 +26,8 @@ use crate::{
         BuiltinAnonymousParams, BuiltinBoxPointers, BuiltinConstNoMangle,
         BuiltinDeprecatedAttrUsed, BuiltinDerefNullptr, BuiltinEllipsisInclusiveRangePatternsLint,
         BuiltinExplicitOutlives, BuiltinExplicitOutlivesSuggestion, BuiltinIncompleteFeatures,
-        BuiltinKeywordIdents, BuiltinMissingCopyImpl, BuiltinMissingDebugImpl, BuiltinMissingDoc,
+        BuiltinIncompleteFeaturesHelp, BuiltinIncompleteFeaturesNote, BuiltinKeywordIdents,
+        BuiltinMissingCopyImpl, BuiltinMissingDebugImpl, BuiltinMissingDoc,
         BuiltinMutablesTransmutes, BuiltinNoMangleGeneric, BuiltinNonShorthandFieldPatterns,
         BuiltinSpecialModuleNameUsed, BuiltinTrivialBounds, BuiltinUnexpectedCliConfigName,
         BuiltinUnexpectedCliConfigValue, BuiltinUnnameableTestItems, BuiltinUnreachablePub,
@@ -2379,14 +2380,17 @@ impl EarlyLintPass for IncompleteFeatures {
             .chain(features.declared_lib_features.iter().map(|(name, span)| (name, span)))
             .filter(|(&name, _)| features.incomplete(name))
             .for_each(|(&name, &span)| {
+                let note = rustc_feature::find_feature_issue(name, GateIssue::Language)
+                    .map(|n| BuiltinIncompleteFeaturesNote { n });
+                let help = if HAS_MIN_FEATURES.contains(&name) {
+                    Some(BuiltinIncompleteFeaturesHelp)
+                } else {
+                    None
+                };
                 cx.emit_spanned_lint(
                     INCOMPLETE_FEATURES,
                     span,
-                    BuiltinIncompleteFeatures {
-                        name,
-                        note: rustc_feature::find_feature_issue(name, GateIssue::Language),
-                        help: HAS_MIN_FEATURES.contains(&name).then_some(()),
-                    },
+                    BuiltinIncompleteFeatures { name, note, help },
                 );
             });
     }
diff --git a/compiler/rustc_lint/src/expect.rs b/compiler/rustc_lint/src/expect.rs
index c0e62a8d9fc..8985ccee0cd 100644
--- a/compiler/rustc_lint/src/expect.rs
+++ b/compiler/rustc_lint/src/expect.rs
@@ -1,6 +1,6 @@
 #![deny(rustc::untranslatable_diagnostic)]
 #![deny(rustc::diagnostic_outside_of_impl)]
-use crate::lints::Expectation;
+use crate::lints::{Expectation, ExpectationNote};
 use rustc_middle::ty::query::Providers;
 use rustc_middle::ty::TyCtxt;
 use rustc_session::lint::builtin::UNFULFILLED_LINT_EXPECTATIONS;
@@ -29,11 +29,13 @@ fn check_expectations(tcx: TyCtxt<'_>, tool_filter: Option<Symbol>) {
             if !fulfilled_expectations.contains(&id)
                 && tool_filter.map_or(true, |filter| expectation.lint_tool == Some(filter))
             {
+                let rationale = expectation.reason.map(|rationale| ExpectationNote { rationale });
+                let note = expectation.is_unfulfilled_lint_expectations.then_some(());
                 tcx.emit_spanned_lint(
                     UNFULFILLED_LINT_EXPECTATIONS,
                     *hir_id,
                     expectation.emission_span,
-                    Expectation { expectation },
+                    Expectation { rationale, note },
                 );
             }
         } else {
diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs
index b335f330f5d..500b1f36558 100644
--- a/compiler/rustc_lint/src/levels.rs
+++ b/compiler/rustc_lint/src/levels.rs
@@ -4,7 +4,7 @@ use crate::context::{CheckLintNameResult, LintStore};
 use crate::late::unerased_lint_store;
 use crate::lints::{
     DeprecatedLintName, IgnoredUnlessCrateSpecified, OverruledAtributeLint, RenamedOrRemovedLint,
-    UnknownLint,
+    RenamedOrRemovedLintSuggestion, UnknownLint, UnknownLintSuggestion,
 };
 use rustc_ast as ast;
 use rustc_ast_pretty::pprust;
@@ -887,10 +887,15 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
                     _ if !self.warn_about_weird_lints => {}
 
                     CheckLintNameResult::Warning(msg, renamed) => {
+                        let suggestion =
+                            renamed.as_ref().map(|replace| RenamedOrRemovedLintSuggestion {
+                                suggestion: sp,
+                                replace: replace.as_str(),
+                            });
                         self.emit_spanned_lint(
                             RENAMED_AND_REMOVED_LINTS,
                             sp.into(),
-                            RenamedOrRemovedLint { msg, suggestion: sp, renamed },
+                            RenamedOrRemovedLint { msg, suggestion },
                         );
                     }
                     CheckLintNameResult::NoLint(suggestion) => {
@@ -899,10 +904,12 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
                         } else {
                             name.to_string()
                         };
+                        let suggestion = suggestion
+                            .map(|replace| UnknownLintSuggestion { suggestion: sp, replace });
                         self.emit_spanned_lint(
                             UNKNOWN_LINTS,
                             sp.into(),
-                            UnknownLint { name, suggestion: sp, replace: suggestion },
+                            UnknownLint { name, suggestion },
                         );
                     }
                 }
diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs
index a3549346604..faeb396ce8c 100644
--- a/compiler/rustc_lint/src/lints.rs
+++ b/compiler/rustc_lint/src/lints.rs
@@ -3,10 +3,7 @@ use std::num::NonZeroU32;
 use rustc_errors::{fluent, AddToDiagnostic, Applicability, DecorateLint, DiagnosticMessage};
 use rustc_hir::def_id::DefId;
 use rustc_macros::{LintDiagnostic, Subdiagnostic};
-use rustc_middle::{
-    lint::LintExpectation,
-    ty::{Predicate, Ty, TyCtxt},
-};
+use rustc_middle::ty::{Predicate, Ty, TyCtxt};
 use rustc_span::{edition::Edition, symbol::Ident, Span, Symbol};
 
 use crate::{errors::OverruledAttributeSub, LateContext};
@@ -80,6 +77,7 @@ pub struct BuiltinMissingDebugImpl<'a> {
     pub def_id: DefId,
 }
 
+// Needed for def_path_str
 impl<'a> DecorateLint<'a, ()> for BuiltinMissingDebugImpl<'_> {
     fn decorate_lint<'b>(
         self,
@@ -225,31 +223,24 @@ pub struct BuiltinExplicitOutlivesSuggestion {
     pub applicability: Applicability,
 }
 
+#[derive(LintDiagnostic)]
+#[diag(lint_builtin_incomplete_features)]
 pub struct BuiltinIncompleteFeatures {
     pub name: Symbol,
-    pub note: Option<NonZeroU32>,
-    pub help: Option<()>,
+    #[subdiagnostic]
+    pub note: Option<BuiltinIncompleteFeaturesNote>,
+    #[subdiagnostic]
+    pub help: Option<BuiltinIncompleteFeaturesHelp>,
 }
 
-impl<'a> DecorateLint<'a, ()> for BuiltinIncompleteFeatures {
-    fn decorate_lint<'b>(
-        self,
-        diag: &'b mut rustc_errors::DiagnosticBuilder<'a, ()>,
-    ) -> &'b mut rustc_errors::DiagnosticBuilder<'a, ()> {
-        diag.set_arg("name", self.name);
-        if let Some(n) = self.note {
-            diag.set_arg("n", n);
-            diag.note(fluent::note);
-        }
-        if let Some(_) = self.help {
-            diag.help(fluent::help);
-        }
-        diag
-    }
+#[derive(Subdiagnostic)]
+#[help(help)]
+pub struct BuiltinIncompleteFeaturesHelp;
 
-    fn msg(&self) -> DiagnosticMessage {
-        fluent::lint_builtin_incomplete_features
-    }
+#[derive(Subdiagnostic)]
+#[note(note)]
+pub struct BuiltinIncompleteFeaturesNote {
+    pub n: NonZeroU32,
 }
 
 // FIXME: migrate "the type `{}` does not permit {}"
@@ -308,29 +299,19 @@ pub struct EnumIntrinsicsMemVariant<'a> {
 }
 
 // expect.rs
-pub struct Expectation<'a> {
-    pub expectation: &'a LintExpectation,
+#[derive(LintDiagnostic)]
+#[diag(lint_expectation)]
+pub struct Expectation {
+    #[subdiagnostic]
+    pub rationale: Option<ExpectationNote>,
+    #[note]
+    pub note: Option<()>,
 }
 
-impl<'a> DecorateLint<'a, ()> for Expectation<'_> {
-    fn decorate_lint<'b>(
-        self,
-        diag: &'b mut rustc_errors::DiagnosticBuilder<'a, ()>,
-    ) -> &'b mut rustc_errors::DiagnosticBuilder<'a, ()> {
-        if let Some(rationale) = self.expectation.reason {
-            diag.note(rationale.as_str());
-        }
-
-        if self.expectation.is_unfulfilled_lint_expectations {
-            diag.note(fluent::note);
-        }
-
-        diag
-    }
-
-    fn msg(&self) -> DiagnosticMessage {
-        fluent::lint_expectation
-    }
+#[derive(Subdiagnostic)]
+#[note(rationale)]
+pub struct ExpectationNote {
+    pub rationale: Symbol,
 }
 
 // for_loops_over_fallibles.rs
@@ -511,59 +492,37 @@ pub struct DeprecatedLintName<'a> {
     pub replace: &'a str,
 }
 
+// FIXME: Non-translatable msg
+#[derive(LintDiagnostic)]
+#[diag(lint_renamed_or_removed_lint)]
 pub struct RenamedOrRemovedLint<'a> {
     pub msg: &'a str,
-    pub suggestion: Span,
-    pub renamed: &'a Option<String>,
+    #[subdiagnostic]
+    pub suggestion: Option<RenamedOrRemovedLintSuggestion<'a>>,
 }
 
-impl<'a> DecorateLint<'a, ()> for RenamedOrRemovedLint<'_> {
-    fn decorate_lint<'b>(
-        self,
-        diag: &'b mut rustc_errors::DiagnosticBuilder<'a, ()>,
-    ) -> &'b mut rustc_errors::DiagnosticBuilder<'a, ()> {
-        if let Some(new_name) = self.renamed {
-            diag.span_suggestion(
-                self.suggestion,
-                fluent::lint_renamed_or_removed_lint_suggestion,
-                new_name,
-                Applicability::MachineApplicable,
-            );
-        };
-        diag
-    }
-
-    fn msg(&self) -> rustc_errors::DiagnosticMessage {
-        rustc_errors::DiagnosticMessage::Str(self.msg.to_string())
-    }
+#[derive(Subdiagnostic)]
+#[suggestion(suggestion, code = "{replace}", applicability = "machine-applicable")]
+pub struct RenamedOrRemovedLintSuggestion<'a> {
+    #[primary_span]
+    pub suggestion: Span,
+    pub replace: &'a str,
 }
 
-pub struct UnknownLint<'a> {
+#[derive(LintDiagnostic)]
+#[diag(lint_unknown_lint)]
+pub struct UnknownLint {
     pub name: String,
-    pub suggestion: Span,
-    pub replace: &'a Option<Symbol>,
+    #[subdiagnostic]
+    pub suggestion: Option<UnknownLintSuggestion>,
 }
 
-impl<'a> DecorateLint<'a, ()> for UnknownLint<'_> {
-    fn decorate_lint<'b>(
-        self,
-        diag: &'b mut rustc_errors::DiagnosticBuilder<'a, ()>,
-    ) -> &'b mut rustc_errors::DiagnosticBuilder<'a, ()> {
-        diag.set_arg("name", self.name);
-        if let Some(replace) = self.replace {
-            diag.span_suggestion(
-                self.suggestion,
-                fluent::suggestion,
-                replace,
-                Applicability::MaybeIncorrect,
-            );
-        };
-        diag
-    }
-
-    fn msg(&self) -> rustc_errors::DiagnosticMessage {
-        fluent::lint_unknown_lint
-    }
+#[derive(Subdiagnostic)]
+#[suggestion(suggestion, code = "{replace}", applicability = "maybe-incorrect")]
+pub struct UnknownLintSuggestion {
+    #[primary_span]
+    pub suggestion: Span,
+    pub replace: Symbol,
 }
 
 #[derive(LintDiagnostic)]
@@ -618,6 +577,7 @@ pub struct NonFmtPanicUnused {
     pub suggestion: Option<Span>,
 }
 
+// Used because of two suggestions based on one Option<Span>
 impl<'a> DecorateLint<'a, ()> for NonFmtPanicUnused {
     fn decorate_lint<'b>(
         self,
@@ -803,6 +763,7 @@ pub struct DropTraitConstraintsDiag<'a> {
     pub def_id: DefId,
 }
 
+// Needed for def_path_str
 impl<'a> DecorateLint<'a, ()> for DropTraitConstraintsDiag<'_> {
     fn decorate_lint<'b>(
         self,
@@ -822,6 +783,7 @@ pub struct DropGlue<'a> {
     pub def_id: DefId,
 }
 
+// Needed for def_path_str
 impl<'a> DecorateLint<'a, ()> for DropGlue<'_> {
     fn decorate_lint<'b>(
         self,
@@ -902,35 +864,22 @@ pub enum OverflowingBinHexSub<'a> {
     Help { suggestion_ty: &'a str },
 }
 
+#[derive(LintDiagnostic)]
+#[diag(lint_overflowing_int)]
+#[note]
 pub struct OverflowingInt<'a> {
     pub ty: &'a str,
     pub lit: String,
     pub min: i128,
     pub max: u128,
-    pub suggestion_ty: Option<&'a str>,
+    #[subdiagnostic]
+    pub help: Option<OverflowingIntHelp<'a>>,
 }
 
-// FIXME: refactor with `Option<&'a str>` in macro
-impl<'a> DecorateLint<'a, ()> for OverflowingInt<'_> {
-    fn decorate_lint<'b>(
-        self,
-        diag: &'b mut rustc_errors::DiagnosticBuilder<'a, ()>,
-    ) -> &'b mut rustc_errors::DiagnosticBuilder<'a, ()> {
-        diag.set_arg("ty", self.ty);
-        diag.set_arg("lit", self.lit);
-        diag.set_arg("min", self.min);
-        diag.set_arg("max", self.max);
-        diag.note(fluent::note);
-        if let Some(suggestion_ty) = self.suggestion_ty {
-            diag.set_arg("suggestion_ty", suggestion_ty);
-            diag.help(fluent::help);
-        }
-        diag
-    }
-
-    fn msg(&self) -> rustc_errors::DiagnosticMessage {
-        fluent::lint_overflowing_int
-    }
+#[derive(Subdiagnostic)]
+#[help(help)]
+pub struct OverflowingIntHelp<'a> {
+    pub suggestion_ty: &'a str,
 }
 
 #[derive(LintDiagnostic)]
@@ -972,6 +921,7 @@ pub struct ImproperCTypes<'a> {
     pub span_note: Option<Span>,
 }
 
+// Used because of the complexity of Option<DiagnosticMessage>, DiagnosticMessage, and Option<Span>
 impl<'a> DecorateLint<'a, ()> for ImproperCTypes<'_> {
     fn decorate_lint<'b>(
         self,
@@ -1074,7 +1024,7 @@ pub struct UnusedDef<'a, 'b> {
     pub note: Option<Symbol>,
 }
 
-// FIXME: refactor with `Option<String>` in macro
+// Needed because of def_path_str
 impl<'a> DecorateLint<'a, ()> for UnusedDef<'_, '_> {
     fn decorate_lint<'b>(
         self,
diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs
index a112292eb14..1300a2fe27a 100644
--- a/compiler/rustc_lint/src/types.rs
+++ b/compiler/rustc_lint/src/types.rs
@@ -3,7 +3,7 @@
 use crate::lints::{
     AtomicOrderingFence, AtomicOrderingLoad, AtomicOrderingStore, ImproperCTypes,
     InvalidAtomicOrderingDiag, OnlyCastu8ToChar, OverflowingBinHex, OverflowingBinHexSign,
-    OverflowingBinHexSub, OverflowingInt, OverflowingLiteral, OverflowingUInt,
+    OverflowingBinHexSub, OverflowingInt, OverflowingIntHelp, OverflowingLiteral, OverflowingUInt,
     RangeEndpointOutOfRange, UnusedComparisons, VariantSizeDifferencesDiag,
 };
 use crate::{LateContext, LateLintPass, LintContext};
@@ -339,24 +339,18 @@ fn lint_int_literal<'tcx>(
             return;
         }
 
+        let lit = cx
+            .sess()
+            .source_map()
+            .span_to_snippet(lit.span)
+            .expect("must get snippet from literal");
+        let help = get_type_suggestion(cx.typeck_results().node_type(e.hir_id), v, negative)
+            .map(|suggestion_ty| OverflowingIntHelp { suggestion_ty });
+
         cx.emit_spanned_lint(
             OVERFLOWING_LITERALS,
             e.span,
-            OverflowingInt {
-                ty: t.name_str(),
-                lit: cx
-                    .sess()
-                    .source_map()
-                    .span_to_snippet(lit.span)
-                    .expect("must get snippet from literal"),
-                min,
-                max,
-                suggestion_ty: get_type_suggestion(
-                    cx.typeck_results().node_type(e.hir_id),
-                    v,
-                    negative,
-                ),
-            },
+            OverflowingInt { ty: t.name_str(), lit, min, max, help },
         );
     }
 }