Skip to content

Split up the unknown_or_malformed_diagnostic_attributes lint #140717

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 13, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions compiler/rustc_lint/src/lib.rs
Original file line number Diff line number Diff line change
@@ -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");
101 changes: 89 additions & 12 deletions compiler/rustc_lint_defs/src/builtin.rs
Original file line number Diff line number Diff line change
@@ -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,31 +4287,105 @@ 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.
11 changes: 6 additions & 5 deletions compiler/rustc_passes/src/check_attr.rs
Original file line number Diff line number Diff line change
@@ -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;
@@ -442,7 +442,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,
@@ -459,15 +460,15 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
)
{
self.tcx.emit_node_span_lint(
UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
MISPLACED_DIAGNOSTIC_ATTRIBUTES,
hir_id,
attr_span,
errors::IncorrectDoNotRecommendLocation,
);
}
if !attr.is_word() {
self.tcx.emit_node_span_lint(
UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
MALFORMED_DIAGNOSTIC_ATTRIBUTES,
hir_id,
attr_span,
errors::DoNotRecommendDoesNotExpectArgs,
@@ -479,7 +480,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,
4 changes: 2 additions & 2 deletions compiler/rustc_resolve/src/macros.rs
Original file line number Diff line number Diff line change
@@ -25,7 +25,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;
@@ -690,7 +690,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 },
Original file line number Diff line number Diff line change
@@ -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 },
Original file line number Diff line number Diff line change
@@ -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,
4 changes: 4 additions & 0 deletions src/tools/lint-docs/src/groups.rs
Original file line number Diff line number Diff line change
@@ -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>>;
2 changes: 1 addition & 1 deletion tests/ui/attributes/malformed-attrs.stderr
Original file line number Diff line number Diff line change
@@ -576,7 +576,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
Original file line number Diff line number Diff line change
@@ -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

Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Loading
Oops, something went wrong.
Loading
Oops, something went wrong.