Allow only a single accepter per attribute

This commit is contained in:
Jonathan Brouwer 2026-02-07 14:28:12 +01:00
parent 06cafcbe08
commit fd7ff90f70
No known key found for this signature in database
GPG key ID: 13619B051B673C52
2 changed files with 40 additions and 36 deletions

View file

@ -1,5 +1,6 @@
use std::cell::RefCell;
use std::collections::BTreeMap;
use std::collections::btree_map::Entry;
use std::ops::{Deref, DerefMut};
use std::sync::LazyLock;
@ -61,7 +62,7 @@ use crate::target_checking::AllowedTargets;
type GroupType<S> = LazyLock<GroupTypeInner<S>>;
pub(super) struct GroupTypeInner<S: Stage> {
pub(super) accepters: BTreeMap<&'static [Symbol], Vec<GroupTypeInnerAccept<S>>>,
pub(super) accepters: BTreeMap<&'static [Symbol], GroupTypeInnerAccept<S>>,
}
pub(super) struct GroupTypeInnerAccept<S: Stage> {
@ -101,7 +102,7 @@ macro_rules! attribute_parsers {
@[$stage: ty] pub(crate) static $name: ident = [$($names: ty),* $(,)?];
) => {
pub(crate) static $name: GroupType<$stage> = LazyLock::new(|| {
let mut accepters = BTreeMap::<_, Vec<GroupTypeInnerAccept<$stage>>>::new();
let mut accepters = BTreeMap::<_, GroupTypeInnerAccept<$stage>>::new();
$(
{
thread_local! {
@ -109,19 +110,24 @@ macro_rules! attribute_parsers {
};
for (path, template, accept_fn) in <$names>::ATTRIBUTES {
accepters.entry(*path).or_default().push(GroupTypeInnerAccept {
template: *template,
accept_fn: Box::new(|cx, args| {
STATE_OBJECT.with_borrow_mut(|s| {
accept_fn(s, cx, args)
})
}),
allowed_targets: <$names as crate::attributes::AttributeParser<$stage>>::ALLOWED_TARGETS,
finalizer: Box::new(|cx| {
let state = STATE_OBJECT.take();
state.finalize(cx)
}),
});
match accepters.entry(*path) {
Entry::Vacant(e) => {
e.insert(GroupTypeInnerAccept {
template: *template,
accept_fn: Box::new(|cx, args| {
STATE_OBJECT.with_borrow_mut(|s| {
accept_fn(s, cx, args)
})
}),
allowed_targets: <$names as crate::attributes::AttributeParser<$stage>>::ALLOWED_TARGETS,
finalizer: Box::new(|cx| {
let state = STATE_OBJECT.take();
state.finalize(cx)
})
});
}
Entry::Occupied(_) => panic!("Attribute {path:?} has multiple accepters"),
}
}
}
)*

View file

@ -327,7 +327,7 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
let parts =
n.item.path.segments.iter().map(|seg| seg.ident.name).collect::<Vec<_>>();
if let Some(accepts) = S::parsers().accepters.get(parts.as_slice()) {
if let Some(accept) = S::parsers().accepters.get(parts.as_slice()) {
let Some(args) = ArgParser::from_attr_args(
args,
&parts,
@ -368,28 +368,26 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
continue;
}
for accept in accepts {
let mut cx: AcceptContext<'_, 'sess, S> = AcceptContext {
shared: SharedContext {
cx: self,
target_span,
target,
emit_lint: &mut emit_lint,
},
attr_span,
inner_span: lower_span(n.item.span()),
attr_style: attr.style,
parsed_description: ParsedDescription::Attribute,
template: &accept.template,
attr_path: attr_path.clone(),
};
let mut cx: AcceptContext<'_, 'sess, S> = AcceptContext {
shared: SharedContext {
cx: self,
target_span,
target,
emit_lint: &mut emit_lint,
},
attr_span,
inner_span: lower_span(n.item.span()),
attr_style: attr.style,
parsed_description: ParsedDescription::Attribute,
template: &accept.template,
attr_path: attr_path.clone(),
};
(accept.accept_fn)(&mut cx, &args);
finalizers.push(&accept.finalizer);
(accept.accept_fn)(&mut cx, &args);
finalizers.push(&accept.finalizer);
if !matches!(cx.stage.should_emit(), ShouldEmit::Nothing) {
Self::check_target(&accept.allowed_targets, target, &mut cx);
}
if !matches!(cx.stage.should_emit(), ShouldEmit::Nothing) {
Self::check_target(&accept.allowed_targets, target, &mut cx);
}
} else {
// If we're here, we must be compiling a tool attribute... Or someone