1use std::cell::RefCell;
2use std::collections::BTreeMap;
3use std::ops::Deref;
4use std::sync::LazyLock;
5
6use rustc_ast::{self as ast, DelimArgs};
7use rustc_attr_data_structures::AttributeKind;
8use rustc_errors::{DiagCtxtHandle, Diagnostic};
9use rustc_feature::Features;
10use rustc_hir::{AttrArgs, AttrItem, AttrPath, Attribute, HashIgnoredAttrId};
11use rustc_session::Session;
12use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, Symbol, sym};
13
14use crate::attributes::allow_unstable::{AllowConstFnUnstableParser, AllowInternalUnstableParser};
15use crate::attributes::confusables::ConfusablesParser;
16use crate::attributes::deprecation::DeprecationParser;
17use crate::attributes::repr::ReprParser;
18use crate::attributes::rustc::RustcMacroEdition2021Parser;
19use crate::attributes::stability::{
20 BodyStabilityParser, ConstStabilityIndirectParser, ConstStabilityParser, StabilityParser,
21};
22use crate::attributes::transparency::TransparencyParser;
23use crate::attributes::{AttributeParser as _, Combine, Single};
24use crate::parser::{ArgParser, MetaItemParser};
25
26macro_rules! attribute_groups {
27 (
28 pub(crate) static $name: ident = [$($names: ty),* $(,)?];
29 ) => {
30 pub(crate) static $name: LazyLock<(
31 BTreeMap<&'static [Symbol], Vec<Box<dyn Fn(&AcceptContext<'_>, &ArgParser<'_>) + Send + Sync>>>,
32 Vec<Box<dyn Send + Sync + Fn(&FinalizeContext<'_>) -> Option<AttributeKind>>>
33 )> = LazyLock::new(|| {
34 let mut accepts = BTreeMap::<_, Vec<Box<dyn Fn(&AcceptContext<'_>, &ArgParser<'_>) + Send + Sync>>>::new();
35 let mut finalizes = Vec::<Box<dyn Send + Sync + Fn(&FinalizeContext<'_>) -> Option<AttributeKind>>>::new();
36 $(
37 {
38 thread_local! {
39 static STATE_OBJECT: RefCell<$names> = RefCell::new(<$names>::default());
40 };
41
42 for (k, v) in <$names>::ATTRIBUTES {
43 accepts.entry(*k).or_default().push(Box::new(|cx, args| {
44 STATE_OBJECT.with_borrow_mut(|s| {
45 v(s, cx, args)
46 })
47 }));
48 }
49
50 finalizes.push(Box::new(|cx| {
51 let state = STATE_OBJECT.take();
52 state.finalize(cx)
53 }));
54 }
55 )*
56
57 (accepts, finalizes)
58 });
59 };
60}
61
62attribute_groups!(
63 pub(crate) static ATTRIBUTE_MAPPING = [
64 BodyStabilityParser,
66 ConfusablesParser,
67 ConstStabilityParser,
68 StabilityParser,
69 Combine<AllowConstFnUnstableParser>,
73 Combine<AllowInternalUnstableParser>,
74 Combine<ReprParser>,
75 Single<ConstStabilityIndirectParser>,
79 Single<DeprecationParser>,
80 Single<RustcMacroEdition2021Parser>,
81 Single<TransparencyParser>,
82 ];
84);
85
86pub(crate) struct AcceptContext<'a> {
90 pub(crate) group_cx: &'a FinalizeContext<'a>,
91 pub(crate) attr_span: Span,
93}
94
95impl<'a> AcceptContext<'a> {
96 pub(crate) fn emit_err(&self, diag: impl Diagnostic<'a>) -> ErrorGuaranteed {
97 if self.limit_diagnostics {
98 self.dcx().create_err(diag).delay_as_bug()
99 } else {
100 self.dcx().emit_err(diag)
101 }
102 }
103}
104
105impl<'a> Deref for AcceptContext<'a> {
106 type Target = FinalizeContext<'a>;
107
108 fn deref(&self) -> &Self::Target {
109 &self.group_cx
110 }
111}
112
113pub(crate) struct FinalizeContext<'a> {
117 pub(crate) cx: &'a AttributeParser<'a>,
120 pub(crate) target_span: Span,
122}
123
124impl<'a> Deref for FinalizeContext<'a> {
125 type Target = AttributeParser<'a>;
126
127 fn deref(&self) -> &Self::Target {
128 &self.cx
129 }
130}
131
132#[derive(PartialEq, Clone, Copy, Debug)]
133pub enum OmitDoc {
134 Lower,
135 Skip,
136}
137
138pub struct AttributeParser<'sess> {
141 #[expect(dead_code)] tools: Vec<Symbol>,
143 sess: &'sess Session,
144 features: Option<&'sess Features>,
145
146 parse_only: Option<Symbol>,
151
152 pub(crate) limit_diagnostics: bool,
155}
156
157impl<'sess> AttributeParser<'sess> {
158 pub fn parse_limited(
169 sess: &'sess Session,
170 attrs: &[ast::Attribute],
171 sym: Symbol,
172 target_span: Span,
173 limit_diagnostics: bool,
174 ) -> Option<Attribute> {
175 let mut parsed = Self {
176 sess,
177 features: None,
178 tools: Vec::new(),
179 parse_only: Some(sym),
180 limit_diagnostics,
181 }
182 .parse_attribute_list(attrs, target_span, OmitDoc::Skip, std::convert::identity);
183
184 assert!(parsed.len() <= 1);
185
186 parsed.pop()
187 }
188
189 pub fn new(sess: &'sess Session, features: &'sess Features, tools: Vec<Symbol>) -> Self {
190 Self { sess, features: Some(features), tools, parse_only: None, limit_diagnostics: false }
191 }
192
193 pub(crate) fn sess(&self) -> &'sess Session {
194 self.sess
195 }
196
197 pub(crate) fn features(&self) -> &'sess Features {
198 self.features.expect("features not available at this point in the compiler")
199 }
200
201 pub(crate) fn dcx(&self) -> DiagCtxtHandle<'sess> {
202 self.sess.dcx()
203 }
204
205 pub fn parse_attribute_list<'a>(
210 &'a self,
211 attrs: &'a [ast::Attribute],
212 target_span: Span,
213 omit_doc: OmitDoc,
214
215 lower_span: impl Copy + Fn(Span) -> Span,
216 ) -> Vec<Attribute> {
217 let mut attributes = Vec::new();
218
219 let group_cx = FinalizeContext { cx: self, target_span };
220
221 for attr in attrs {
222 if let Some(expected) = self.parse_only {
225 if attr.name_or_empty() != expected {
226 continue;
227 }
228 }
229
230 if omit_doc == OmitDoc::Skip && attr.name_or_empty() == sym::doc {
236 continue;
237 }
238
239 match &attr.kind {
240 ast::AttrKind::DocComment(comment_kind, symbol) => {
241 if omit_doc == OmitDoc::Skip {
242 continue;
243 }
244
245 attributes.push(Attribute::Parsed(AttributeKind::DocComment {
246 style: attr.style,
247 kind: *comment_kind,
248 span: lower_span(attr.span),
249 comment: *symbol,
250 }))
251 }
252 ast::AttrKind::Normal(n) => {
264 let parser = MetaItemParser::from_attr(n, self.dcx());
265 let (path, args) = parser.deconstruct();
266 let parts = path.segments().map(|i| i.name).collect::<Vec<_>>();
267
268 if let Some(accepts) = ATTRIBUTE_MAPPING.0.get(parts.as_slice()) {
269 for f in accepts {
270 let cx = AcceptContext {
271 group_cx: &group_cx,
272 attr_span: lower_span(attr.span),
273 };
274
275 f(&cx, &args)
276 }
277 } else {
278 attributes.push(Attribute::Unparsed(Box::new(AttrItem {
294 path: AttrPath::from_ast(&n.item.path),
295 args: self.lower_attr_args(&n.item.args, lower_span),
296 id: HashIgnoredAttrId { attr_id: attr.id },
297 style: attr.style,
298 span: lower_span(attr.span),
299 })));
300 }
301 }
302 }
303 }
304
305 let mut parsed_attributes = Vec::new();
306 for f in &ATTRIBUTE_MAPPING.1 {
307 if let Some(attr) = f(&group_cx) {
308 parsed_attributes.push(Attribute::Parsed(attr));
309 }
310 }
311
312 attributes.extend(parsed_attributes);
313
314 attributes
315 }
316
317 fn lower_attr_args(&self, args: &ast::AttrArgs, lower_span: impl Fn(Span) -> Span) -> AttrArgs {
318 match args {
319 ast::AttrArgs::Empty => AttrArgs::Empty,
320 ast::AttrArgs::Delimited(args) => AttrArgs::Delimited(DelimArgs {
321 dspan: args.dspan,
322 delim: args.delim,
323 tokens: args.tokens.clone(),
324 }),
325 ast::AttrArgs::Eq { eq_span, expr } => {
329 let lit = if let ast::ExprKind::Lit(token_lit) = expr.kind
332 && let Ok(lit) =
333 ast::MetaItemLit::from_token_lit(token_lit, lower_span(expr.span))
334 {
335 lit
336 } else {
337 let guar = self.dcx().span_delayed_bug(
338 args.span().unwrap_or(DUMMY_SP),
339 "expr in place where literal is expected (builtin attr parsing)",
340 );
341 ast::MetaItemLit {
342 symbol: sym::dummy,
343 suffix: None,
344 kind: ast::LitKind::Err(guar),
345 span: DUMMY_SP,
346 }
347 };
348 AttrArgs::Eq { eq_span: lower_span(*eq_span), expr: lit }
349 }
350 }
351 }
352}