diff options
| author | Samuel Tardieu <sam@rfc1149.net> | 2025-08-26 23:25:02 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-08-26 23:25:02 +0200 |
| commit | fbf247dd31fd2610fc9d565d937a5d3b03d7312e (patch) | |
| tree | c52a48a3a90c75be7acce1f1e55bf30052e80e13 /compiler/rustc_attr_parsing | |
| parent | 87ab2c8355cbed4973dc16a6c89de4567fc5e47f (diff) | |
| parent | a8e9ca195e8345fc574420418d2fd3aed258f703 (diff) | |
| download | rust-fbf247dd31fd2610fc9d565d937a5d3b03d7312e.tar.gz rust-fbf247dd31fd2610fc9d565d937a5d3b03d7312e.zip | |
Rollup merge of #145792 - scrabsha:push-umpytyxunpxq, r=jdonszelmann
Use attribute name in message for "outer attr used as inner attr" errors
Diffstat (limited to 'compiler/rustc_attr_parsing')
| -rw-r--r-- | compiler/rustc_attr_parsing/messages.ftl | 6 | ||||
| -rw-r--r-- | compiler/rustc_attr_parsing/src/attributes/crate_level.rs | 3 | ||||
| -rw-r--r-- | compiler/rustc_attr_parsing/src/attributes/mod.rs | 17 | ||||
| -rw-r--r-- | compiler/rustc_attr_parsing/src/attributes/traits.rs | 3 | ||||
| -rw-r--r-- | compiler/rustc_attr_parsing/src/context.rs | 18 | ||||
| -rw-r--r-- | compiler/rustc_attr_parsing/src/interface.rs | 4 | ||||
| -rw-r--r-- | compiler/rustc_attr_parsing/src/lints.rs | 14 | ||||
| -rw-r--r-- | compiler/rustc_attr_parsing/src/session_diagnostics.rs | 12 | ||||
| -rw-r--r-- | compiler/rustc_attr_parsing/src/target_checking.rs | 33 |
9 files changed, 100 insertions, 10 deletions
diff --git a/compiler/rustc_attr_parsing/messages.ftl b/compiler/rustc_attr_parsing/messages.ftl index 40e9d597530..8f24b51f1d9 100644 --- a/compiler/rustc_attr_parsing/messages.ftl +++ b/compiler/rustc_attr_parsing/messages.ftl @@ -86,6 +86,12 @@ attr_parsing_invalid_repr_hint_no_value = attr_parsing_invalid_since = 'since' must be a Rust version number, such as "1.31.0" +attr_parsing_invalid_style = {$is_used_as_inner -> + [false] crate-level attribute should be an inner attribute: add an exclamation mark: `#![{$name}]` + *[other] the `#![{$name}]` attribute can only be used at the crate root + } + .note = This attribute does not have an `!`, which means it is applied to this {$target} + attr_parsing_link_ordinal_out_of_range = ordinal value in `link_ordinal` is too large: `{$ordinal}` .note = the value may not exceed `u16::MAX` diff --git a/compiler/rustc_attr_parsing/src/attributes/crate_level.rs b/compiler/rustc_attr_parsing/src/attributes/crate_level.rs index 9175d7479e1..2fed09b85e8 100644 --- a/compiler/rustc_attr_parsing/src/attributes/crate_level.rs +++ b/compiler/rustc_attr_parsing/src/attributes/crate_level.rs @@ -1,3 +1,5 @@ +use rustc_feature::AttributeType; + use super::prelude::*; pub(crate) struct CrateNameParser; @@ -7,6 +9,7 @@ impl<S: Stage> SingleAttributeParser<S> for CrateNameParser { const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost; const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError; const TEMPLATE: AttributeTemplate = template!(NameValueStr: "name"); + const TYPE: AttributeType = AttributeType::CrateLevel; // FIXME: crate name is allowed on all targets and ignored, // even though it should only be valid on crates of course diff --git a/compiler/rustc_attr_parsing/src/attributes/mod.rs b/compiler/rustc_attr_parsing/src/attributes/mod.rs index 9dad9c893f0..043bc925eac 100644 --- a/compiler/rustc_attr_parsing/src/attributes/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/mod.rs @@ -12,11 +12,15 @@ //! - [`CombineAttributeParser`](crate::attributes::CombineAttributeParser): makes it easy to implement an attribute which should combine the //! contents of attributes, if an attribute appear multiple times in a list //! +//! By default, attributes are allowed anywhere. When adding an attribute that should only be used +//! at the crate root, consider setting the `TYPE` in the parser trait to +//! [`AttributeType::CrateLevel`](rustc_feature::AttributeType::CrateLevel). +//! //! Attributes should be added to `crate::context::ATTRIBUTE_PARSERS` to be parsed. use std::marker::PhantomData; -use rustc_feature::{AttributeTemplate, template}; +use rustc_feature::{AttributeTemplate, AttributeType, template}; use rustc_hir::attrs::AttributeKind; use rustc_span::{Span, Symbol}; use thin_vec::ThinVec; @@ -88,6 +92,8 @@ pub(crate) trait AttributeParser<S: Stage>: Default + 'static { const ALLOWED_TARGETS: AllowedTargets; + const TYPE: AttributeType = AttributeType::Normal; + /// The parser has gotten a chance to accept the attributes on an item, /// here it can produce an attribute. /// @@ -129,6 +135,8 @@ pub(crate) trait SingleAttributeParser<S: Stage>: 'static { /// The template this attribute parser should implement. Used for diagnostics. const TEMPLATE: AttributeTemplate; + const TYPE: AttributeType = AttributeType::Normal; + /// Converts a single syntactical attribute to a single semantic attribute, or [`AttributeKind`] fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind>; } @@ -175,6 +183,8 @@ impl<T: SingleAttributeParser<S>, S: Stage> AttributeParser<S> for Single<T, S> )]; const ALLOWED_TARGETS: AllowedTargets = T::ALLOWED_TARGETS; + const TYPE: AttributeType = T::TYPE; + fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> { Some(self.1?.0) } @@ -259,6 +269,7 @@ pub(crate) trait NoArgsAttributeParser<S: Stage>: 'static { const PATH: &[Symbol]; const ON_DUPLICATE: OnDuplicate<S>; const ALLOWED_TARGETS: AllowedTargets; + const TYPE: AttributeType = AttributeType::Normal; /// Create the [`AttributeKind`] given attribute's [`Span`]. const CREATE: fn(Span) -> AttributeKind; @@ -278,6 +289,7 @@ impl<T: NoArgsAttributeParser<S>, S: Stage> SingleAttributeParser<S> for Without const ON_DUPLICATE: OnDuplicate<S> = T::ON_DUPLICATE; const ALLOWED_TARGETS: AllowedTargets = T::ALLOWED_TARGETS; const TEMPLATE: AttributeTemplate = template!(Word); + const TYPE: AttributeType = T::TYPE; fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> { if let Err(span) = args.no_args() { @@ -311,6 +323,8 @@ pub(crate) trait CombineAttributeParser<S: Stage>: 'static { /// The template this attribute parser should implement. Used for diagnostics. const TEMPLATE: AttributeTemplate; + const TYPE: AttributeType = AttributeType::Normal; + /// Converts a single syntactical attribute to a number of elements of the semantic attribute, or [`AttributeKind`] fn extend<'c>( cx: &'c mut AcceptContext<'_, '_, S>, @@ -346,6 +360,7 @@ impl<T: CombineAttributeParser<S>, S: Stage> AttributeParser<S> for Combine<T, S group.items.extend(T::extend(cx, args)) })]; const ALLOWED_TARGETS: AllowedTargets = T::ALLOWED_TARGETS; + const TYPE: AttributeType = T::TYPE; fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> { if let Some(first_span) = self.first_span { diff --git a/compiler/rustc_attr_parsing/src/attributes/traits.rs b/compiler/rustc_attr_parsing/src/attributes/traits.rs index 89ac1b07d16..fbd9a480fbb 100644 --- a/compiler/rustc_attr_parsing/src/attributes/traits.rs +++ b/compiler/rustc_attr_parsing/src/attributes/traits.rs @@ -1,5 +1,7 @@ use std::mem; +use rustc_feature::AttributeType; + use super::prelude::*; use crate::attributes::{ AttributeOrder, NoArgsAttributeParser, OnDuplicate, SingleAttributeParser, @@ -154,6 +156,7 @@ impl<S: Stage> NoArgsAttributeParser<S> for CoherenceIsCoreParser { const PATH: &[Symbol] = &[sym::rustc_coherence_is_core]; const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error; const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]); + const TYPE: AttributeType = AttributeType::CrateLevel; const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::CoherenceIsCore; } diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index d4b9cfe00ad..b16ef7edd64 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -4,12 +4,12 @@ use std::ops::{Deref, DerefMut}; use std::sync::LazyLock; use private::Sealed; -use rustc_ast::{AttrStyle, MetaItemLit, NodeId}; +use rustc_ast::{AttrStyle, CRATE_NODE_ID, MetaItemLit, NodeId}; use rustc_errors::{Diag, Diagnostic, Level}; -use rustc_feature::AttributeTemplate; +use rustc_feature::{AttributeTemplate, AttributeType}; use rustc_hir::attrs::AttributeKind; use rustc_hir::lints::{AttributeLint, AttributeLintKind}; -use rustc_hir::{AttrPath, HirId}; +use rustc_hir::{AttrPath, CRATE_HIR_ID, HirId}; use rustc_session::Session; use rustc_span::{ErrorGuaranteed, Span, Symbol}; @@ -80,6 +80,7 @@ pub(super) struct GroupTypeInnerAccept<S: Stage> { pub(super) template: AttributeTemplate, pub(super) accept_fn: AcceptFn<S>, pub(super) allowed_targets: AllowedTargets, + pub(super) attribute_type: AttributeType, } type AcceptFn<S> = @@ -129,6 +130,7 @@ macro_rules! attribute_parsers { }) }), allowed_targets: <$names as crate::attributes::AttributeParser<$stage>>::ALLOWED_TARGETS, + attribute_type: <$names as crate::attributes::AttributeParser<$stage>>::TYPE, }); } @@ -250,6 +252,8 @@ pub trait Stage: Sized + 'static + Sealed { ) -> ErrorGuaranteed; fn should_emit(&self) -> ShouldEmit; + + fn id_is_crate_root(id: Self::Id) -> bool; } // allow because it's a sealed trait @@ -271,6 +275,10 @@ impl Stage for Early { fn should_emit(&self) -> ShouldEmit { self.emit_errors } + + fn id_is_crate_root(id: Self::Id) -> bool { + id == CRATE_NODE_ID + } } // allow because it's a sealed trait @@ -292,6 +300,10 @@ impl Stage for Late { fn should_emit(&self) -> ShouldEmit { ShouldEmit::ErrorsAndLints } + + fn id_is_crate_root(id: Self::Id) -> bool { + id == CRATE_HIR_ID + } } /// used when parsing attributes for miscellaneous things *before* ast lowering diff --git a/compiler/rustc_attr_parsing/src/interface.rs b/compiler/rustc_attr_parsing/src/interface.rs index 60523c2877c..a3558850ef3 100644 --- a/compiler/rustc_attr_parsing/src/interface.rs +++ b/compiler/rustc_attr_parsing/src/interface.rs @@ -271,8 +271,8 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { }; (accept.accept_fn)(&mut cx, args); - - if !matches!(self.stage.should_emit(), ShouldEmit::Nothing) { + if !matches!(cx.stage.should_emit(), ShouldEmit::Nothing) { + Self::check_type(accept.attribute_type, target, &mut cx); self.check_target( path.get_attribute_path(), attr.span, diff --git a/compiler/rustc_attr_parsing/src/lints.rs b/compiler/rustc_attr_parsing/src/lints.rs index 84ae19c4fc6..b1a971eec32 100644 --- a/compiler/rustc_attr_parsing/src/lints.rs +++ b/compiler/rustc_attr_parsing/src/lints.rs @@ -66,5 +66,19 @@ pub fn emit_attribute_lint<L: LintEmitter>(lint: &AttributeLint<L::Id>, lint_emi attr_span: *span, }, ), + + &AttributeLintKind::InvalidStyle { ref name, is_used_as_inner, target, target_span } => { + lint_emitter.emit_node_span_lint( + rustc_session::lint::builtin::UNUSED_ATTRIBUTES, + *id, + *span, + session_diagnostics::InvalidAttrStyle { + name: name.clone(), + is_used_as_inner, + target_span: (!is_used_as_inner).then_some(target_span), + target, + }, + ) + } } } diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs index 72bee0ddfbf..a639b55e81f 100644 --- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs @@ -6,7 +6,7 @@ use rustc_errors::{ Applicability, Diag, DiagArgValue, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level, }; use rustc_feature::AttributeTemplate; -use rustc_hir::AttrPath; +use rustc_hir::{AttrPath, Target}; use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; use rustc_span::{Span, Symbol}; @@ -826,3 +826,13 @@ pub(crate) struct SuffixedLiteralInAttribute { #[primary_span] pub span: Span, } + +#[derive(LintDiagnostic)] +#[diag(attr_parsing_invalid_style)] +pub(crate) struct InvalidAttrStyle { + pub name: AttrPath, + pub is_used_as_inner: bool, + #[note] + pub target_span: Option<Span>, + pub target: Target, +} diff --git a/compiler/rustc_attr_parsing/src/target_checking.rs b/compiler/rustc_attr_parsing/src/target_checking.rs index 9568b791b3f..6f4dd76fa81 100644 --- a/compiler/rustc_attr_parsing/src/target_checking.rs +++ b/compiler/rustc_attr_parsing/src/target_checking.rs @@ -1,13 +1,14 @@ use std::borrow::Cow; +use rustc_ast::AttrStyle; use rustc_errors::DiagArgValue; -use rustc_feature::Features; +use rustc_feature::{AttributeType, Features}; use rustc_hir::lints::{AttributeLint, AttributeLintKind}; use rustc_hir::{AttrPath, MethodKind, Target}; use rustc_span::Span; use crate::AttributeParser; -use crate::context::Stage; +use crate::context::{AcceptContext, Stage}; use crate::session_diagnostics::InvalidTarget; #[derive(Debug)] @@ -68,7 +69,7 @@ pub(crate) enum Policy { Error(Target), } -impl<S: Stage> AttributeParser<'_, S> { +impl<'sess, S: Stage> AttributeParser<'sess, S> { pub(crate) fn check_target( &self, attr_name: AttrPath, @@ -111,6 +112,32 @@ impl<S: Stage> AttributeParser<'_, S> { } } } + + pub(crate) fn check_type( + attribute_type: AttributeType, + target: Target, + cx: &mut AcceptContext<'_, 'sess, S>, + ) { + let is_crate_root = S::id_is_crate_root(cx.target_id); + + if is_crate_root { + return; + } + + if attribute_type != AttributeType::CrateLevel { + return; + } + + let lint = AttributeLintKind::InvalidStyle { + name: cx.attr_path.clone(), + is_used_as_inner: cx.attr_style == AttrStyle::Inner, + target, + target_span: cx.target_span, + }; + let attr_span = cx.attr_span; + + cx.emit_lint(lint, attr_span); + } } /// Takes a list of `allowed_targets` for an attribute, and the `target` the attribute was applied to. |
