about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2025-07-13 01:11:56 +0000
committerbors <bors@rust-lang.org>2025-07-13 01:11:56 +0000
commitb1d2f2c64c6254b2a935d6a2d29b68f4511cf500 (patch)
tree6892fe2762a0422917f6bfde0db6bdc97767b5a3
parent288e94c4ba406d612a556520442683d0f1ef5dbb (diff)
parenta7bf5c4fa2d83669511e88657c194edaec82a872 (diff)
downloadrust-b1d2f2c64c6254b2a935d6a2d29b68f4511cf500.tar.gz
rust-b1d2f2c64c6254b2a935d6a2d29b68f4511cf500.zip
Auto merge of #140717 - mejrs:diagnostic_lints, r=oli-obk
Split up the `unknown_or_malformed_diagnostic_attributes` lint

This splits up the lint into the following lint group:
- `unknown_diagnostic_attributes` - triggers if the attribute is unknown to the current compiler
- `misplaced_diagnostic_attributes` - triggers if the attribute exists but it is not placed on the item kind it's meant for
- `malformed_diagnostic_attributes` - triggers if the attribute's syntax or options are invalid
- `malformed_diagnostic_format_literals` - triggers if the format string literal is invalid, for example if it has unpaired curly braces or invalid parameters
- this pr doesn't create it, but future lints for things like deprecations can also go here.

This PR does not start emitting lints in places that previously did not.

## Motivation

I want to have finer control over what `unknown_or_malformed_diagnostic_attributes` does

I have a project with fairly low msrv that is/will have a lower msrv than future diagnostic attributes. So lints will be emitted when I or others compile it on a lower msrv.

At this time, there are two options to silence these lints:

-  `#[allow(unknown_or_malformed_diagnostic_attributes)]` - this risks diagnostic regressions if I (or others) mess up using the attribute, or if the attribute's syntax ever changes.
- write a build script to detect the compiler version and emit cfgs, and then conditionally enable the attribute:
    ```rust
    #[cfg_attr(rust_version_99, diagnostic::new_attr_in_rust_99(thing = ..))]`
    struct Foo;
    ```
    or conditionally `allow`  the lint:
    ```rust
   // lib.rs
   #![cfg_attr(not(current_rust), allow(unknown_or_malformed_diagnostic_attributes))]
   ```

I like to avoid using build scripts if I can, so the following works much better for me. That is what this PR will let me do in the future:
```rust
    #[allow(unknown_diagnostic_attribute, reason = "attribute came out in rust 1.99 but msrv is 1.70")]
    #[diagnostic::new_attr_in_rust_99(thing = ..)]`
    struct Foo;
-rw-r--r--compiler/rustc_lint/src/lib.rs8
-rw-r--r--compiler/rustc_lint_defs/src/builtin.rs101
-rw-r--r--compiler/rustc_passes/src/check_attr.rs11
-rw-r--r--compiler/rustc_resolve/src/macros.rs4
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs16
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented_format.rs8
-rw-r--r--src/tools/lint-docs/src/groups.rs4
-rw-r--r--tests/ui/attributes/malformed-attrs.stderr2
-rw-r--r--tests/ui/diagnostic_namespace/deny_malformed_attribute.stderr1
-rw-r--r--tests/ui/diagnostic_namespace/do_not_recommend/does_not_acccept_args.current.stderr2
-rw-r--r--tests/ui/diagnostic_namespace/do_not_recommend/does_not_acccept_args.next.stderr2
-rw-r--r--tests/ui/diagnostic_namespace/do_not_recommend/incorrect-locations.current.stderr2
-rw-r--r--tests/ui/diagnostic_namespace/do_not_recommend/incorrect-locations.next.stderr2
-rw-r--r--tests/ui/diagnostic_namespace/multiline_spans.rs16
-rw-r--r--tests/ui/diagnostic_namespace/multiline_spans.stderr1
-rw-r--r--tests/ui/diagnostic_namespace/non_existing_attributes_accepted.stderr2
-rw-r--r--tests/ui/diagnostic_namespace/on_unimplemented/broken_format.rs12
-rw-r--r--tests/ui/diagnostic_namespace/on_unimplemented/broken_format.stderr2
-rw-r--r--tests/ui/diagnostic_namespace/on_unimplemented/do_not_accept_options_of_the_internal_rustc_attribute.stderr4
-rw-r--r--tests/ui/diagnostic_namespace/on_unimplemented/do_not_fail_parsing_on_invalid_options_1.stderr4
-rw-r--r--tests/ui/diagnostic_namespace/on_unimplemented/ignore_unsupported_options_and_continue_to_use_fallback.stderr2
-rw-r--r--tests/ui/diagnostic_namespace/on_unimplemented/on_impl_trait.stderr2
-rw-r--r--tests/ui/diagnostic_namespace/on_unimplemented/report_warning_on_duplicated_options.stderr2
-rw-r--r--tests/ui/diagnostic_namespace/suggest_typos.rs2
-rw-r--r--tests/ui/diagnostic_namespace/suggest_typos.stderr4
25 files changed, 157 insertions, 59 deletions
diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs
index 48982bda0a0..419124d5144 100644
--- a/compiler/rustc_lint/src/lib.rs
+++ b/compiler/rustc_lint/src/lib.rs
@@ -339,6 +339,14 @@ fn register_builtins(store: &mut LintStore) {
 
     add_lint_group!("deprecated_safe", DEPRECATED_SAFE_2024);
 
+    add_lint_group!(
+        "unknown_or_malformed_diagnostic_attributes",
+        MALFORMED_DIAGNOSTIC_ATTRIBUTES,
+        MALFORMED_DIAGNOSTIC_FORMAT_LITERALS,
+        MISPLACED_DIAGNOSTIC_ATTRIBUTES,
+        UNKNOWN_DIAGNOSTIC_ATTRIBUTES
+    );
+
     // Register renamed and removed lints.
     store.register_renamed("single_use_lifetime", "single_use_lifetimes");
     store.register_renamed("elided_lifetime_in_path", "elided_lifetimes_in_paths");
diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs
index 15f0bad226d..a08d68e2d15 100644
--- a/compiler/rustc_lint_defs/src/builtin.rs
+++ b/compiler/rustc_lint_defs/src/builtin.rs
@@ -63,7 +63,10 @@ declare_lint_pass! {
         LOSSY_PROVENANCE_CASTS,
         MACRO_EXPANDED_MACRO_EXPORTS_ACCESSED_BY_ABSOLUTE_PATHS,
         MACRO_USE_EXTERN_CRATE,
+        MALFORMED_DIAGNOSTIC_ATTRIBUTES,
+        MALFORMED_DIAGNOSTIC_FORMAT_LITERALS,
         META_VARIABLE_MISUSE,
+        MISPLACED_DIAGNOSTIC_ATTRIBUTES,
         MISSING_ABI,
         MISSING_UNSAFE_ON_EXTERN,
         MUST_NOT_SUSPEND,
@@ -112,8 +115,8 @@ declare_lint_pass! {
         UNFULFILLED_LINT_EXPECTATIONS,
         UNINHABITED_STATIC,
         UNKNOWN_CRATE_TYPES,
+        UNKNOWN_DIAGNOSTIC_ATTRIBUTES,
         UNKNOWN_LINTS,
-        UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
         UNNAMEABLE_TEST_ITEMS,
         UNNAMEABLE_TYPES,
         UNREACHABLE_CODE,
@@ -4284,32 +4287,106 @@ declare_lint! {
 }
 
 declare_lint! {
-    /// The `unknown_or_malformed_diagnostic_attributes` lint detects unrecognized or otherwise malformed
-    /// diagnostic attributes.
+    /// The `malformed_diagnostic_attributes` lint detects malformed diagnostic attributes.
     ///
     /// ### Example
     ///
     /// ```rust
-    /// #![feature(diagnostic_namespace)]
-    /// #[diagnostic::does_not_exist]
-    /// struct Foo;
+    /// #[diagnostic::do_not_recommend(message = "message")]
+    /// trait Trait {}
     /// ```
     ///
     /// {{produces}}
     ///
+    /// ### Explanation
+    ///
+    /// It is usually a mistake to use options or syntax that is not supported. Check the spelling,
+    /// and check the diagnostic attribute listing for the correct name and syntax. Also consider if
+    /// you are using an old version of the compiler; perhaps the option or syntax is only available
+    /// in a newer version. See the [reference] for a list of diagnostic attributes and the syntax
+    /// of each.
+    ///
+    /// [reference]: https://doc.rust-lang.org/nightly/reference/attributes/diagnostics.html#the-diagnostic-tool-attribute-namespace
+    pub MALFORMED_DIAGNOSTIC_ATTRIBUTES,
+    Warn,
+    "detects malformed diagnostic attributes",
+}
+
+declare_lint! {
+    /// The `misplaced_diagnostic_attributes` lint detects wrongly placed diagnostic attributes.
+    ///
+    /// ### Example
+    ///
+    /// ```rust
+    /// #[diagnostic::do_not_recommend]
+    /// struct NotUserFacing;
+    /// ```
+    ///
+    /// {{produces}}
     ///
     /// ### Explanation
     ///
-    /// It is usually a mistake to specify a diagnostic attribute that does not exist. Check
-    /// the spelling, and check the diagnostic attribute listing for the correct name. Also
-    /// consider if you are using an old version of the compiler, and the attribute
-    /// is only available in a newer version.
-    pub UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
+    /// It is usually a mistake to specify a diagnostic attribute on an item it is not meant for.
+    /// For example, `#[diagnostic::do_not_recommend]` can only be placed on trait implementations,
+    /// and does nothing if placed elsewhere. See the [reference] for a list of diagnostic
+    /// attributes and their correct positions.
+    ///
+    /// [reference]: https://doc.rust-lang.org/nightly/reference/attributes/diagnostics.html#the-diagnostic-tool-attribute-namespace
+    pub MISPLACED_DIAGNOSTIC_ATTRIBUTES,
     Warn,
-    "unrecognized or malformed diagnostic attribute",
+    "detects diagnostic attributes that are placed on the wrong item",
 }
 
 declare_lint! {
+    /// The `unknown_diagnostic_attributes` lint detects unknown diagnostic attributes.
+    ///
+    /// ### Example
+    ///
+    /// ```rust
+    /// #[diagnostic::does_not_exist]
+    /// struct Thing;
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// It is usually a mistake to specify a diagnostic attribute that does not exist. Check the
+    /// spelling, and check the diagnostic attribute listing for the correct name. Also consider if
+    /// you are using an old version of the compiler and the attribute is only available in a newer
+    /// version. See the [reference] for the list of diagnostic attributes.
+    ///
+    /// [reference]: https://doc.rust-lang.org/nightly/reference/attributes/diagnostics.html#the-diagnostic-tool-attribute-namespace
+    pub UNKNOWN_DIAGNOSTIC_ATTRIBUTES,
+    Warn,
+    "detects unknown diagnostic attributes",
+}
+
+declare_lint! {
+    /// The `malformed_diagnostic_format_literals` lint detects malformed diagnostic format
+    /// literals.
+    ///
+    /// ### Example
+    ///
+    /// ```rust
+    /// #[diagnostic::on_unimplemented(message = "{Self}} does not implement `Trait`")]
+    /// trait Trait {}
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// The `#[diagnostic::on_unimplemented]` attribute accepts string literal values that are
+    /// similar to `format!`'s string literal. See the [reference] for details on what is permitted
+    /// in this string literal.
+    ///
+    /// [reference]: https://doc.rust-lang.org/nightly/reference/attributes/diagnostics.html#the-diagnostic-tool-attribute-namespace
+    pub MALFORMED_DIAGNOSTIC_FORMAT_LITERALS,
+    Warn,
+    "detects diagnostic attribute with malformed diagnostic format literals",
+}
+declare_lint! {
     /// The `ambiguous_glob_imports` lint detects glob imports that should report ambiguity
     /// errors, but previously didn't do that due to rustc bugs.
     ///
diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs
index 2c95fead9e8..86f10f33589 100644
--- a/compiler/rustc_passes/src/check_attr.rs
+++ b/compiler/rustc_passes/src/check_attr.rs
@@ -33,7 +33,7 @@ use rustc_session::config::CrateType;
 use rustc_session::lint;
 use rustc_session::lint::builtin::{
     CONFLICTING_REPR_HINTS, INVALID_DOC_ATTRIBUTES, INVALID_MACRO_EXPORT_ARGUMENTS,
-    UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, UNUSED_ATTRIBUTES,
+    MALFORMED_DIAGNOSTIC_ATTRIBUTES, MISPLACED_DIAGNOSTIC_ATTRIBUTES, UNUSED_ATTRIBUTES,
 };
 use rustc_session::parse::feature_err;
 use rustc_span::edition::Edition;
@@ -460,7 +460,8 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
         );
     }
 
-    /// Checks if `#[diagnostic::do_not_recommend]` is applied on a trait impl.
+    /// Checks if `#[diagnostic::do_not_recommend]` is applied on a trait impl and that it has no
+    /// arguments.
     fn check_do_not_recommend(
         &self,
         attr_span: Span,
@@ -477,7 +478,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
             )
         {
             self.tcx.emit_node_span_lint(
-                UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
+                MISPLACED_DIAGNOSTIC_ATTRIBUTES,
                 hir_id,
                 attr_span,
                 errors::IncorrectDoNotRecommendLocation,
@@ -485,7 +486,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
         }
         if !attr.is_word() {
             self.tcx.emit_node_span_lint(
-                UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
+                MALFORMED_DIAGNOSTIC_ATTRIBUTES,
                 hir_id,
                 attr_span,
                 errors::DoNotRecommendDoesNotExpectArgs,
@@ -497,7 +498,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
     fn check_diagnostic_on_unimplemented(&self, attr_span: Span, hir_id: HirId, target: Target) {
         if !matches!(target, Target::Trait) {
             self.tcx.emit_node_span_lint(
-                UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
+                MISPLACED_DIAGNOSTIC_ATTRIBUTES,
                 hir_id,
                 attr_span,
                 DiagnosticOnUnimplementedOnlyForTraits,
diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs
index 89bbe8dad98..5864b035f73 100644
--- a/compiler/rustc_resolve/src/macros.rs
+++ b/compiler/rustc_resolve/src/macros.rs
@@ -24,7 +24,7 @@ use rustc_middle::middle::stability;
 use rustc_middle::ty::{RegisteredTools, TyCtxt, Visibility};
 use rustc_session::lint::BuiltinLintDiag;
 use rustc_session::lint::builtin::{
-    LEGACY_DERIVE_HELPERS, OUT_OF_SCOPE_MACRO_CALLS, UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
+    LEGACY_DERIVE_HELPERS, OUT_OF_SCOPE_MACRO_CALLS, UNKNOWN_DIAGNOSTIC_ATTRIBUTES,
     UNUSED_MACRO_RULES, UNUSED_MACROS,
 };
 use rustc_session::parse::feature_err;
@@ -689,7 +689,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
             );
 
             self.tcx.sess.psess.buffer_lint(
-                UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
+                UNKNOWN_DIAGNOSTIC_ATTRIBUTES,
                 attribute.span(),
                 node_id,
                 BuiltinLintDiag::UnknownDiagnosticAttribute { span: attribute.span(), typo_name },
diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs
index a52dbedfe1e..2344bc79f21 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs
@@ -11,7 +11,9 @@ use rustc_macros::LintDiagnostic;
 use rustc_middle::bug;
 use rustc_middle::ty::print::PrintTraitRefExt;
 use rustc_middle::ty::{self, GenericArgsRef, GenericParamDef, GenericParamDefKind, TyCtxt};
-use rustc_session::lint::builtin::UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES;
+use rustc_session::lint::builtin::{
+    MALFORMED_DIAGNOSTIC_ATTRIBUTES, MALFORMED_DIAGNOSTIC_FORMAT_LITERALS,
+};
 use rustc_span::{Span, Symbol, sym};
 use tracing::{debug, info};
 
@@ -382,7 +384,7 @@ impl IgnoredDiagnosticOption {
         if let (Some(new_item), Some(old_item)) = (new, old) {
             if let Some(item_def_id) = item_def_id.as_local() {
                 tcx.emit_node_span_lint(
-                    UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
+                    MALFORMED_DIAGNOSTIC_ATTRIBUTES,
                     tcx.local_def_id_to_hir_id(item_def_id),
                     new_item,
                     IgnoredDiagnosticOption { span: new_item, prev_span: old_item, option_name },
@@ -533,7 +535,7 @@ impl<'tcx> OnUnimplementedDirective {
             if is_diagnostic_namespace_variant {
                 if let Some(def_id) = item_def_id.as_local() {
                     tcx.emit_node_span_lint(
-                        UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
+                        MALFORMED_DIAGNOSTIC_ATTRIBUTES,
                         tcx.local_def_id_to_hir_id(def_id),
                         vec![item.span()],
                         MalformedOnUnimplementedAttrLint::new(item.span()),
@@ -689,7 +691,7 @@ impl<'tcx> OnUnimplementedDirective {
 
                 if let Some(item_def_id) = item_def_id.as_local() {
                     tcx.emit_node_span_lint(
-                        UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
+                        MALFORMED_DIAGNOSTIC_ATTRIBUTES,
                         tcx.local_def_id_to_hir_id(item_def_id),
                         report_span,
                         MalformedOnUnimplementedAttrLint::new(report_span),
@@ -702,7 +704,7 @@ impl<'tcx> OnUnimplementedDirective {
                 Attribute::Unparsed(p) if !matches!(p.args, AttrArgs::Empty) => {
                     if let Some(item_def_id) = item_def_id.as_local() {
                         tcx.emit_node_span_lint(
-                            UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
+                            MALFORMED_DIAGNOSTIC_ATTRIBUTES,
                             tcx.local_def_id_to_hir_id(item_def_id),
                             attr.span(),
                             MalformedOnUnimplementedAttrLint::new(attr.span()),
@@ -712,7 +714,7 @@ impl<'tcx> OnUnimplementedDirective {
                 _ => {
                     if let Some(item_def_id) = item_def_id.as_local() {
                         tcx.emit_node_span_lint(
-                            UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
+                            MALFORMED_DIAGNOSTIC_ATTRIBUTES,
                             tcx.local_def_id_to_hir_id(item_def_id),
                             attr.span(),
                             MissingOptionsForOnUnimplementedAttr,
@@ -859,7 +861,7 @@ impl<'tcx> OnUnimplementedFormatString {
                 if self.is_diagnostic_namespace_variant {
                     if let Some(trait_def_id) = trait_def_id.as_local() {
                         tcx.emit_node_span_lint(
-                            UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
+                            MALFORMED_DIAGNOSTIC_FORMAT_LITERALS,
                             tcx.local_def_id_to_hir_id(trait_def_id),
                             self.span,
                             WrappedParserError { description: e.description, label: e.label },
diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented_format.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented_format.rs
index 3e8b906fa93..1954f8a1f63 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented_format.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented_format.rs
@@ -7,7 +7,7 @@ use rustc_middle::ty::{GenericParamDefKind, TyCtxt};
 use rustc_parse_format::{
     Argument, FormatSpec, ParseError, ParseMode, Parser, Piece as RpfPiece, Position,
 };
-use rustc_session::lint::builtin::UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES;
+use rustc_session::lint::builtin::MALFORMED_DIAGNOSTIC_FORMAT_LITERALS;
 use rustc_span::def_id::DefId;
 use rustc_span::{InnerSpan, Span, Symbol, kw, sym};
 
@@ -69,7 +69,7 @@ impl FormatWarning {
                 let this = tcx.item_ident(item_def_id);
                 if let Some(item_def_id) = item_def_id.as_local() {
                     tcx.emit_node_span_lint(
-                        UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
+                        MALFORMED_DIAGNOSTIC_FORMAT_LITERALS,
                         tcx.local_def_id_to_hir_id(item_def_id),
                         span,
                         UnknownFormatParameterForOnUnimplementedAttr {
@@ -82,7 +82,7 @@ impl FormatWarning {
             FormatWarning::PositionalArgument { span, .. } => {
                 if let Some(item_def_id) = item_def_id.as_local() {
                     tcx.emit_node_span_lint(
-                        UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
+                        MALFORMED_DIAGNOSTIC_FORMAT_LITERALS,
                         tcx.local_def_id_to_hir_id(item_def_id),
                         span,
                         DisallowedPositionalArgument,
@@ -92,7 +92,7 @@ impl FormatWarning {
             FormatWarning::InvalidSpecifier { span, .. } => {
                 if let Some(item_def_id) = item_def_id.as_local() {
                     tcx.emit_node_span_lint(
-                        UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
+                        MALFORMED_DIAGNOSTIC_FORMAT_LITERALS,
                         tcx.local_def_id_to_hir_id(item_def_id),
                         span,
                         InvalidFormatSpecifier,
diff --git a/src/tools/lint-docs/src/groups.rs b/src/tools/lint-docs/src/groups.rs
index 78d4f87ed0d..a24fbbc0cea 100644
--- a/src/tools/lint-docs/src/groups.rs
+++ b/src/tools/lint-docs/src/groups.rs
@@ -26,6 +26,10 @@ static GROUP_DESCRIPTIONS: &[(&str, &str)] = &[
         "Lints that detect identifiers which will be come keywords in later editions",
     ),
     ("deprecated-safe", "Lints for functions which were erroneously marked as safe in the past"),
+    (
+        "unknown-or-malformed-diagnostic-attributes",
+        "detects unknown or malformed diagnostic attributes",
+    ),
 ];
 
 type LintGroups = BTreeMap<String, BTreeSet<String>>;
diff --git a/tests/ui/attributes/malformed-attrs.stderr b/tests/ui/attributes/malformed-attrs.stderr
index be529df3a49..f16ba706485 100644
--- a/tests/ui/attributes/malformed-attrs.stderr
+++ b/tests/ui/attributes/malformed-attrs.stderr
@@ -594,7 +594,7 @@ warning: `#[diagnostic::do_not_recommend]` does not expect any arguments
 LL | #[diagnostic::do_not_recommend()]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-   = note: `#[warn(unknown_or_malformed_diagnostic_attributes)]` on by default
+   = note: `#[warn(malformed_diagnostic_attributes)]` on by default
 
 warning: missing options for `on_unimplemented` attribute
   --> $DIR/malformed-attrs.rs:138:1
diff --git a/tests/ui/diagnostic_namespace/deny_malformed_attribute.stderr b/tests/ui/diagnostic_namespace/deny_malformed_attribute.stderr
index 32be9db5317..5c0a437aad8 100644
--- a/tests/ui/diagnostic_namespace/deny_malformed_attribute.stderr
+++ b/tests/ui/diagnostic_namespace/deny_malformed_attribute.stderr
@@ -9,6 +9,7 @@ note: the lint level is defined here
    |
 LL | #![deny(unknown_or_malformed_diagnostic_attributes)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   = note: `#[deny(unknown_diagnostic_attributes)]` implied by `#[deny(unknown_or_malformed_diagnostic_attributes)]`
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/diagnostic_namespace/do_not_recommend/does_not_acccept_args.current.stderr b/tests/ui/diagnostic_namespace/do_not_recommend/does_not_acccept_args.current.stderr
index 8a478a5c733..9d1556ee0c1 100644
--- a/tests/ui/diagnostic_namespace/do_not_recommend/does_not_acccept_args.current.stderr
+++ b/tests/ui/diagnostic_namespace/do_not_recommend/does_not_acccept_args.current.stderr
@@ -4,7 +4,7 @@ warning: `#[diagnostic::do_not_recommend]` does not expect any arguments
 LL | #[diagnostic::do_not_recommend(not_accepted)]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-   = note: `#[warn(unknown_or_malformed_diagnostic_attributes)]` on by default
+   = note: `#[warn(malformed_diagnostic_attributes)]` on by default
 
 warning: `#[diagnostic::do_not_recommend]` does not expect any arguments
   --> $DIR/does_not_acccept_args.rs:15:1
diff --git a/tests/ui/diagnostic_namespace/do_not_recommend/does_not_acccept_args.next.stderr b/tests/ui/diagnostic_namespace/do_not_recommend/does_not_acccept_args.next.stderr
index 8a478a5c733..9d1556ee0c1 100644
--- a/tests/ui/diagnostic_namespace/do_not_recommend/does_not_acccept_args.next.stderr
+++ b/tests/ui/diagnostic_namespace/do_not_recommend/does_not_acccept_args.next.stderr
@@ -4,7 +4,7 @@ warning: `#[diagnostic::do_not_recommend]` does not expect any arguments
 LL | #[diagnostic::do_not_recommend(not_accepted)]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-   = note: `#[warn(unknown_or_malformed_diagnostic_attributes)]` on by default
+   = note: `#[warn(malformed_diagnostic_attributes)]` on by default
 
 warning: `#[diagnostic::do_not_recommend]` does not expect any arguments
   --> $DIR/does_not_acccept_args.rs:15:1
diff --git a/tests/ui/diagnostic_namespace/do_not_recommend/incorrect-locations.current.stderr b/tests/ui/diagnostic_namespace/do_not_recommend/incorrect-locations.current.stderr
index e348f0c8902..29ffbb5bf18 100644
--- a/tests/ui/diagnostic_namespace/do_not_recommend/incorrect-locations.current.stderr
+++ b/tests/ui/diagnostic_namespace/do_not_recommend/incorrect-locations.current.stderr
@@ -4,7 +4,7 @@ warning: `#[diagnostic::do_not_recommend]` can only be placed on trait implement
 LL | #[diagnostic::do_not_recommend]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-   = note: `#[warn(unknown_or_malformed_diagnostic_attributes)]` on by default
+   = note: `#[warn(misplaced_diagnostic_attributes)]` on by default
 
 warning: `#[diagnostic::do_not_recommend]` can only be placed on trait implementations
   --> $DIR/incorrect-locations.rs:11:1
diff --git a/tests/ui/diagnostic_namespace/do_not_recommend/incorrect-locations.next.stderr b/tests/ui/diagnostic_namespace/do_not_recommend/incorrect-locations.next.stderr
index e348f0c8902..29ffbb5bf18 100644
--- a/tests/ui/diagnostic_namespace/do_not_recommend/incorrect-locations.next.stderr
+++ b/tests/ui/diagnostic_namespace/do_not_recommend/incorrect-locations.next.stderr
@@ -4,7 +4,7 @@ warning: `#[diagnostic::do_not_recommend]` can only be placed on trait implement
 LL | #[diagnostic::do_not_recommend]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-   = note: `#[warn(unknown_or_malformed_diagnostic_attributes)]` on by default
+   = note: `#[warn(misplaced_diagnostic_attributes)]` on by default
 
 warning: `#[diagnostic::do_not_recommend]` can only be placed on trait implementations
   --> $DIR/incorrect-locations.rs:11:1
diff --git a/tests/ui/diagnostic_namespace/multiline_spans.rs b/tests/ui/diagnostic_namespace/multiline_spans.rs
index 994dd9fd011..e0876b19df0 100644
--- a/tests/ui/diagnostic_namespace/multiline_spans.rs
+++ b/tests/ui/diagnostic_namespace/multiline_spans.rs
@@ -5,17 +5,17 @@
 #[diagnostic::on_unimplemented(message = "here is a big \
                                          multiline string \
                                          {unknown}")]
-//~^ ERROR there is no parameter `unknown` on trait `MultiLine` [unknown_or_malformed_diagnostic_attributes]
+//~^ ERROR there is no parameter `unknown` on trait `MultiLine` [malformed_diagnostic_format_literals]
 pub trait MultiLine {}
 
 #[diagnostic::on_unimplemented(message = "here is a big \
                                          multiline string {unknown}")]
-//~^ ERROR there is no parameter `unknown` on trait `MultiLine2` [unknown_or_malformed_diagnostic_attributes]
+//~^ ERROR there is no parameter `unknown` on trait `MultiLine2` [malformed_diagnostic_format_literals]
 pub trait MultiLine2 {}
 
 #[diagnostic::on_unimplemented(message = "here is a big \
     multiline string {unknown}")]
-//~^ ERROR there is no parameter `unknown` on trait `MultiLine3` [unknown_or_malformed_diagnostic_attributes]
+//~^ ERROR there is no parameter `unknown` on trait `MultiLine3` [malformed_diagnostic_format_literals]
 pub trait MultiLine3 {}
 
 
@@ -25,23 +25,23 @@ pub trait MultiLine3 {}
                                 \
                                                 \
     multiline string {unknown}")]
-//~^ ERROR there is no parameter `unknown` on trait `MultiLine4` [unknown_or_malformed_diagnostic_attributes]
+//~^ ERROR there is no parameter `unknown` on trait `MultiLine4` [malformed_diagnostic_format_literals]
 pub trait MultiLine4 {}
 
 #[diagnostic::on_unimplemented(message = "here is a big \
                                          multiline string \
                                          {Self:+}")]
-//~^ ERROR invalid format specifier [unknown_or_malformed_diagnostic_attributes]
+//~^ ERROR invalid format specifier [malformed_diagnostic_format_literals]
 pub trait MultiLineFmt {}
 
 #[diagnostic::on_unimplemented(message = "here is a big \
                                          multiline string {Self:X}")]
-//~^ ERROR invalid format specifier [unknown_or_malformed_diagnostic_attributes]
+//~^ ERROR invalid format specifier [malformed_diagnostic_format_literals]
 pub trait MultiLineFmt2 {}
 
 #[diagnostic::on_unimplemented(message = "here is a big \
     multiline string {Self:#}")]
-//~^ ERROR invalid format specifier [unknown_or_malformed_diagnostic_attributes]
+//~^ ERROR invalid format specifier [malformed_diagnostic_format_literals]
 pub trait MultiLineFmt3 {}
 
 
@@ -51,5 +51,5 @@ pub trait MultiLineFmt3 {}
                                 \
                                                 \
     multiline string {Self:?}")]
-//~^ ERROR invalid format specifier [unknown_or_malformed_diagnostic_attributes]
+//~^ ERROR invalid format specifier [malformed_diagnostic_format_literals]
 pub trait MultiLineFmt4 {}
diff --git a/tests/ui/diagnostic_namespace/multiline_spans.stderr b/tests/ui/diagnostic_namespace/multiline_spans.stderr
index 894bfe3d90a..0e4bb864b96 100644
--- a/tests/ui/diagnostic_namespace/multiline_spans.stderr
+++ b/tests/ui/diagnostic_namespace/multiline_spans.stderr
@@ -10,6 +10,7 @@ note: the lint level is defined here
    |
 LL | #![deny(unknown_or_malformed_diagnostic_attributes)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   = note: `#[deny(malformed_diagnostic_format_literals)]` implied by `#[deny(unknown_or_malformed_diagnostic_attributes)]`
 
 error: there is no parameter `unknown` on trait `MultiLine2`
   --> $DIR/multiline_spans.rs:12:60
diff --git a/tests/ui/diagnostic_namespace/non_existing_attributes_accepted.stderr b/tests/ui/diagnostic_namespace/non_existing_attributes_accepted.stderr
index 753077b365e..4f9b7ba2bcf 100644
--- a/tests/ui/diagnostic_namespace/non_existing_attributes_accepted.stderr
+++ b/tests/ui/diagnostic_namespace/non_existing_attributes_accepted.stderr
@@ -4,7 +4,7 @@ warning: unknown diagnostic attribute
 LL | #[diagnostic::non_existing_attribute]
    |               ^^^^^^^^^^^^^^^^^^^^^^
    |
-   = note: `#[warn(unknown_or_malformed_diagnostic_attributes)]` on by default
+   = note: `#[warn(unknown_diagnostic_attributes)]` on by default
 
 warning: unknown diagnostic attribute
   --> $DIR/non_existing_attributes_accepted.rs:8:15
diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/broken_format.rs b/tests/ui/diagnostic_namespace/on_unimplemented/broken_format.rs
index 4762d9e793f..82c2db7e26d 100644
--- a/tests/ui/diagnostic_namespace/on_unimplemented/broken_format.rs
+++ b/tests/ui/diagnostic_namespace/on_unimplemented/broken_format.rs
@@ -12,8 +12,8 @@ trait ImportantTrait2 {}
 #[diagnostic::on_unimplemented(message = "Test {1:}")]
 //~^WARN positional format arguments are not allowed here
 //~|WARN positional format arguments are not allowed here
-//~|WARN invalid format specifier [unknown_or_malformed_diagnostic_attributes]
-//~|WARN invalid format specifier [unknown_or_malformed_diagnostic_attributes]
+//~|WARN invalid format specifier [malformed_diagnostic_format_literals]
+//~|WARN invalid format specifier [malformed_diagnostic_format_literals]
 trait ImportantTrait3 {}
 
 #[diagnostic::on_unimplemented(message = "Test {Self:123}")]
@@ -22,13 +22,13 @@ trait ImportantTrait3 {}
 trait ImportantTrait4 {}
 
 #[diagnostic::on_unimplemented(message = "Test {Self:!}")]
-//~^WARN invalid format specifier [unknown_or_malformed_diagnostic_attributes]
-//~|WARN invalid format specifier [unknown_or_malformed_diagnostic_attributes]
+//~^WARN invalid format specifier [malformed_diagnostic_format_literals]
+//~|WARN invalid format specifier [malformed_diagnostic_format_literals]
 trait ImportantTrait5 {}
 
 #[diagnostic::on_unimplemented(message = "Test {Self:}")]
-//~^WARN invalid format specifier [unknown_or_malformed_diagnostic_attributes]
-//~|WARN invalid format specifier [unknown_or_malformed_diagnostic_attributes]
+//~^WARN invalid format specifier [malformed_diagnostic_format_literals]
+//~|WARN invalid format specifier [malformed_diagnostic_format_literals]
 trait ImportantTrait6 {}
 
 
diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/broken_format.stderr b/tests/ui/diagnostic_namespace/on_unimplemented/broken_format.stderr
index 2670d0630f7..5002122f8b7 100644
--- a/tests/ui/diagnostic_namespace/on_unimplemented/broken_format.stderr
+++ b/tests/ui/diagnostic_namespace/on_unimplemented/broken_format.stderr
@@ -4,7 +4,7 @@ warning: unmatched `}` found
 LL | #[diagnostic::on_unimplemented(message = "{{Test } thing")]
    |                                          ^^^^^^^^^^^^^^^^
    |
-   = note: `#[warn(unknown_or_malformed_diagnostic_attributes)]` on by default
+   = note: `#[warn(malformed_diagnostic_format_literals)]` on by default
 
 warning: positional format arguments are not allowed here
   --> $DIR/broken_format.rs:7:49
diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/do_not_accept_options_of_the_internal_rustc_attribute.stderr b/tests/ui/diagnostic_namespace/on_unimplemented/do_not_accept_options_of_the_internal_rustc_attribute.stderr
index 8dace7d9052..42f4bc0d8b0 100644
--- a/tests/ui/diagnostic_namespace/on_unimplemented/do_not_accept_options_of_the_internal_rustc_attribute.stderr
+++ b/tests/ui/diagnostic_namespace/on_unimplemented/do_not_accept_options_of_the_internal_rustc_attribute.stderr
@@ -4,7 +4,7 @@ warning: `#[diagnostic::on_unimplemented]` can only be applied to trait definiti
 LL | #[diagnostic::on_unimplemented(message = "Not allowed to apply it on a impl")]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-   = note: `#[warn(unknown_or_malformed_diagnostic_attributes)]` on by default
+   = note: `#[warn(misplaced_diagnostic_attributes)]` on by default
 
 warning: malformed `on_unimplemented` attribute
   --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:6:5
@@ -13,6 +13,7 @@ LL |     on(Self = "&str"),
    |     ^^^^^^^^^^^^^^^^^ invalid option found here
    |
    = help: only `message`, `note` and `label` are allowed as options
+   = note: `#[warn(malformed_diagnostic_attributes)]` on by default
 
 warning: malformed `on_unimplemented` attribute
   --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:12:5
@@ -45,6 +46,7 @@ LL |     message = "{from_desugaring}{direct}{cause}{integral}{integer}",
    |                 ^^^^^^^^^^^^^^^
    |
    = help: expect either a generic argument name or `{Self}` as format argument
+   = note: `#[warn(malformed_diagnostic_format_literals)]` on by default
 
 warning: there is no parameter `direct` on trait `Baz`
   --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:33:34
diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/do_not_fail_parsing_on_invalid_options_1.stderr b/tests/ui/diagnostic_namespace/on_unimplemented/do_not_fail_parsing_on_invalid_options_1.stderr
index 80790dc3f79..85d74fb8955 100644
--- a/tests/ui/diagnostic_namespace/on_unimplemented/do_not_fail_parsing_on_invalid_options_1.stderr
+++ b/tests/ui/diagnostic_namespace/on_unimplemented/do_not_fail_parsing_on_invalid_options_1.stderr
@@ -4,7 +4,7 @@ warning: `#[diagnostic::on_unimplemented]` can only be applied to trait definiti
 LL | #[diagnostic::on_unimplemented(message = "Baz")]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-   = note: `#[warn(unknown_or_malformed_diagnostic_attributes)]` on by default
+   = note: `#[warn(misplaced_diagnostic_attributes)]` on by default
 
 warning: malformed `on_unimplemented` attribute
   --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:3:32
@@ -13,6 +13,7 @@ LL | #[diagnostic::on_unimplemented(unsupported = "foo")]
    |                                ^^^^^^^^^^^^^^^^^^^ invalid option found here
    |
    = help: only `message`, `note` and `label` are allowed as options
+   = note: `#[warn(malformed_diagnostic_attributes)]` on by default
 
 warning: malformed `on_unimplemented` attribute
   --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:12:50
@@ -61,6 +62,7 @@ LL | #[diagnostic::on_unimplemented(message = "{DoesNotExist}")]
    |                                            ^^^^^^^^^^^^
    |
    = help: expect either a generic argument name or `{Self}` as format argument
+   = note: `#[warn(malformed_diagnostic_format_literals)]` on by default
 
 warning: malformed `on_unimplemented` attribute
   --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:3:32
diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/ignore_unsupported_options_and_continue_to_use_fallback.stderr b/tests/ui/diagnostic_namespace/on_unimplemented/ignore_unsupported_options_and_continue_to_use_fallback.stderr
index e00846da77b..86fe75a62de 100644
--- a/tests/ui/diagnostic_namespace/on_unimplemented/ignore_unsupported_options_and_continue_to_use_fallback.stderr
+++ b/tests/ui/diagnostic_namespace/on_unimplemented/ignore_unsupported_options_and_continue_to_use_fallback.stderr
@@ -5,7 +5,7 @@ LL |     if(Self = "()"),
    |     ^^^^^^^^^^^^^^^ invalid option found here
    |
    = help: only `message`, `note` and `label` are allowed as options
-   = note: `#[warn(unknown_or_malformed_diagnostic_attributes)]` on by default
+   = note: `#[warn(malformed_diagnostic_attributes)]` on by default
 
 warning: `message` is ignored due to previous definition of `message`
   --> $DIR/ignore_unsupported_options_and_continue_to_use_fallback.rs:10:32
diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/on_impl_trait.stderr b/tests/ui/diagnostic_namespace/on_unimplemented/on_impl_trait.stderr
index 5eee6478922..69433f91543 100644
--- a/tests/ui/diagnostic_namespace/on_unimplemented/on_impl_trait.stderr
+++ b/tests/ui/diagnostic_namespace/on_unimplemented/on_impl_trait.stderr
@@ -4,7 +4,7 @@ warning: `#[diagnostic::on_unimplemented]` can only be applied to trait definiti
 LL | #[diagnostic::on_unimplemented(message = "blah", label = "blah", note = "blah")]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-   = note: `#[warn(unknown_or_malformed_diagnostic_attributes)]` on by default
+   = note: `#[warn(misplaced_diagnostic_attributes)]` on by default
 
 error[E0277]: the trait bound `{integer}: Alias` is not satisfied
   --> $DIR/on_impl_trait.rs:16:9
diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/report_warning_on_duplicated_options.stderr b/tests/ui/diagnostic_namespace/on_unimplemented/report_warning_on_duplicated_options.stderr
index feafe2cee76..d2e121b61a6 100644
--- a/tests/ui/diagnostic_namespace/on_unimplemented/report_warning_on_duplicated_options.stderr
+++ b/tests/ui/diagnostic_namespace/on_unimplemented/report_warning_on_duplicated_options.stderr
@@ -7,7 +7,7 @@ LL |     message = "first message",
 LL |     message = "second message",
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^ `message` is already declared here
    |
-   = note: `#[warn(unknown_or_malformed_diagnostic_attributes)]` on by default
+   = note: `#[warn(malformed_diagnostic_attributes)]` on by default
 
 warning: `label` is ignored due to previous definition of `label`
   --> $DIR/report_warning_on_duplicated_options.rs:11:5
diff --git a/tests/ui/diagnostic_namespace/suggest_typos.rs b/tests/ui/diagnostic_namespace/suggest_typos.rs
index 8d1dc6f59da..37a1c79bb08 100644
--- a/tests/ui/diagnostic_namespace/suggest_typos.rs
+++ b/tests/ui/diagnostic_namespace/suggest_typos.rs
@@ -1,5 +1,5 @@
 //@ reference: attributes.diagnostic.namespace.unknown-invalid-syntax
-#![deny(unknown_or_malformed_diagnostic_attributes)]
+#![deny(unknown_diagnostic_attributes)]
 
 #[diagnostic::onunimplemented]
 //~^ERROR unknown diagnostic attribute
diff --git a/tests/ui/diagnostic_namespace/suggest_typos.stderr b/tests/ui/diagnostic_namespace/suggest_typos.stderr
index 1f19fd4bbcf..c1177945ea6 100644
--- a/tests/ui/diagnostic_namespace/suggest_typos.stderr
+++ b/tests/ui/diagnostic_namespace/suggest_typos.stderr
@@ -7,8 +7,8 @@ LL | #[diagnostic::onunimplemented]
 note: the lint level is defined here
   --> $DIR/suggest_typos.rs:2:9
    |
-LL | #![deny(unknown_or_malformed_diagnostic_attributes)]
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | #![deny(unknown_diagnostic_attributes)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 help: an attribute with a similar name exists
    |
 LL | #[diagnostic::on_unimplemented]