diff options
| author | bors <bors@rust-lang.org> | 2025-07-13 01:11:56 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2025-07-13 01:11:56 +0000 |
| commit | b1d2f2c64c6254b2a935d6a2d29b68f4511cf500 (patch) | |
| tree | 6892fe2762a0422917f6bfde0db6bdc97767b5a3 /compiler | |
| parent | 288e94c4ba406d612a556520442683d0f1ef5dbb (diff) | |
| parent | a7bf5c4fa2d83669511e88657c194edaec82a872 (diff) | |
| download | rust-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;
Diffstat (limited to 'compiler')
6 files changed, 118 insertions, 30 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, |
