rustc_attr_parsing/
context.rs

1use std::cell::RefCell;
2use std::collections::BTreeMap;
3use std::ops::Deref;
4use std::sync::LazyLock;
5
6use rustc_ast as ast;
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::stability::{
19    BodyStabilityParser, ConstStabilityIndirectParser, ConstStabilityParser, StabilityParser,
20};
21use crate::attributes::transparency::TransparencyParser;
22use crate::attributes::{AttributeParser as _, Combine, Single};
23use crate::parser::{ArgParser, MetaItemParser};
24
25macro_rules! attribute_groups {
26    (
27        pub(crate) static $name: ident = [$($names: ty),* $(,)?];
28    ) => {
29        type Accepts = BTreeMap<
30            &'static [Symbol],
31            Box<dyn Send + Sync + Fn(&AcceptContext<'_>, &ArgParser<'_>)>
32        >;
33        type Finalizes = Vec<
34            Box<dyn Send + Sync + Fn(&FinalizeContext<'_>) -> Option<AttributeKind>>
35        >;
36        pub(crate) static $name: LazyLock<(Accepts, Finalizes)> = LazyLock::new(|| {
37            let mut accepts = Accepts::new();
38            let mut finalizes = Finalizes::new();
39            $(
40                {
41                    thread_local! {
42                        static STATE_OBJECT: RefCell<$names> = RefCell::new(<$names>::default());
43                    };
44
45                    for (k, v) in <$names>::ATTRIBUTES {
46                        let old = accepts.insert(*k, Box::new(|cx, args| {
47                            STATE_OBJECT.with_borrow_mut(|s| {
48                                v(s, cx, args)
49                            })
50                        }));
51                        assert!(old.is_none());
52                    }
53
54                    finalizes.push(Box::new(|cx| {
55                        let state = STATE_OBJECT.take();
56                        state.finalize(cx)
57                    }));
58                }
59            )*
60
61            (accepts, finalizes)
62        });
63    };
64}
65
66attribute_groups!(
67    pub(crate) static ATTRIBUTE_MAPPING = [
68        // tidy-alphabetical-start
69        BodyStabilityParser,
70        ConfusablesParser,
71        ConstStabilityParser,
72        StabilityParser,
73        // tidy-alphabetical-end
74
75        // tidy-alphabetical-start
76        Combine<AllowConstFnUnstableParser>,
77        Combine<AllowInternalUnstableParser>,
78        Combine<ReprParser>,
79        // tidy-alphabetical-end
80
81        // tidy-alphabetical-start
82        Single<ConstStabilityIndirectParser>,
83        Single<DeprecationParser>,
84        Single<TransparencyParser>,
85        // tidy-alphabetical-end
86    ];
87);
88
89/// Context given to every attribute parser when accepting
90///
91/// Gives [`AttributeParser`]s enough information to create errors, for example.
92pub(crate) struct AcceptContext<'a> {
93    pub(crate) group_cx: &'a FinalizeContext<'a>,
94    /// The span of the attribute currently being parsed
95    pub(crate) attr_span: Span,
96}
97
98impl<'a> AcceptContext<'a> {
99    pub(crate) fn emit_err(&self, diag: impl Diagnostic<'a>) -> ErrorGuaranteed {
100        if self.limit_diagnostics {
101            self.dcx().create_err(diag).delay_as_bug()
102        } else {
103            self.dcx().emit_err(diag)
104        }
105    }
106}
107
108impl<'a> Deref for AcceptContext<'a> {
109    type Target = FinalizeContext<'a>;
110
111    fn deref(&self) -> &Self::Target {
112        &self.group_cx
113    }
114}
115
116/// Context given to every attribute parser during finalization.
117///
118/// Gives [`AttributeParser`](crate::attributes::AttributeParser)s enough information to create
119/// errors, for example.
120pub(crate) struct FinalizeContext<'a> {
121    /// The parse context, gives access to the session and the
122    /// diagnostics context.
123    pub(crate) cx: &'a AttributeParser<'a>,
124    /// The span of the syntactical component this attribute was applied to
125    pub(crate) target_span: Span,
126}
127
128impl<'a> Deref for FinalizeContext<'a> {
129    type Target = AttributeParser<'a>;
130
131    fn deref(&self) -> &Self::Target {
132        &self.cx
133    }
134}
135
136#[derive(PartialEq, Clone, Copy, Debug)]
137pub enum OmitDoc {
138    Lower,
139    Skip,
140}
141
142/// Context created once, for example as part of the ast lowering
143/// context, through which all attributes can be lowered.
144pub struct AttributeParser<'sess> {
145    #[expect(dead_code)] // FIXME(jdonszelmann): needed later to verify we parsed all attributes
146    tools: Vec<Symbol>,
147    sess: &'sess Session,
148    features: Option<&'sess Features>,
149
150    /// *Only* parse attributes with this symbol.
151    ///
152    /// Used in cases where we want the lowering infrastructure for parse just a single attribute.
153    parse_only: Option<Symbol>,
154
155    /// Can be used to instruct parsers to reduce the number of diagnostics it emits.
156    /// Useful when using `parse_limited` and you know the attr will be reparsed later.
157    pub(crate) limit_diagnostics: bool,
158}
159
160impl<'sess> AttributeParser<'sess> {
161    /// This method allows you to parse attributes *before* you have access to features or tools.
162    /// One example where this is necessary, is to parse `feature` attributes themselves for
163    /// example.
164    ///
165    /// Try to use this as little as possible. Attributes *should* be lowered during
166    /// `rustc_ast_lowering`. Some attributes require access to features to parse, which would
167    /// crash if you tried to do so through [`parse_limited`](Self::parse_limited).
168    ///
169    /// To make sure use is limited, supply a `Symbol` you'd like to parse. Only attributes with
170    /// that symbol are picked out of the list of instructions and parsed. Those are returned.
171    pub fn parse_limited(
172        sess: &'sess Session,
173        attrs: &[ast::Attribute],
174        sym: Symbol,
175        target_span: Span,
176        limit_diagnostics: bool,
177    ) -> Option<Attribute> {
178        let mut parsed = Self {
179            sess,
180            features: None,
181            tools: Vec::new(),
182            parse_only: Some(sym),
183            limit_diagnostics,
184        }
185        .parse_attribute_list(attrs, target_span, OmitDoc::Skip, std::convert::identity);
186
187        assert!(parsed.len() <= 1);
188
189        parsed.pop()
190    }
191
192    pub fn new(sess: &'sess Session, features: &'sess Features, tools: Vec<Symbol>) -> Self {
193        Self { sess, features: Some(features), tools, parse_only: None, limit_diagnostics: false }
194    }
195
196    pub(crate) fn sess(&self) -> &'sess Session {
197        self.sess
198    }
199
200    pub(crate) fn features(&self) -> &'sess Features {
201        self.features.expect("features not available at this point in the compiler")
202    }
203
204    pub(crate) fn dcx(&self) -> DiagCtxtHandle<'sess> {
205        self.sess.dcx()
206    }
207
208    /// Parse a list of attributes.
209    ///
210    /// `target_span` is the span of the thing this list of attributes is applied to,
211    /// and when `omit_doc` is set, doc attributes are filtered out.
212    pub fn parse_attribute_list<'a>(
213        &'a self,
214        attrs: &'a [ast::Attribute],
215        target_span: Span,
216        omit_doc: OmitDoc,
217
218        lower_span: impl Copy + Fn(Span) -> Span,
219    ) -> Vec<Attribute> {
220        let mut attributes = Vec::new();
221
222        let group_cx = FinalizeContext { cx: self, target_span };
223
224        for attr in attrs {
225            // If we're only looking for a single attribute, skip all the ones we don't care about.
226            if let Some(expected) = self.parse_only {
227                if !attr.has_name(expected) {
228                    continue;
229                }
230            }
231
232            // Sometimes, for example for `#![doc = include_str!("readme.md")]`,
233            // doc still contains a non-literal. You might say, when we're lowering attributes
234            // that's expanded right? But no, sometimes, when parsing attributes on macros,
235            // we already use the lowering logic and these are still there. So, when `omit_doc`
236            // is set we *also* want to ignore these.
237            if omit_doc == OmitDoc::Skip && attr.has_name(sym::doc) {
238                continue;
239            }
240
241            match &attr.kind {
242                ast::AttrKind::DocComment(comment_kind, symbol) => {
243                    if omit_doc == OmitDoc::Skip {
244                        continue;
245                    }
246
247                    attributes.push(Attribute::Parsed(AttributeKind::DocComment {
248                        style: attr.style,
249                        kind: *comment_kind,
250                        span: lower_span(attr.span),
251                        comment: *symbol,
252                    }))
253                }
254                // // FIXME: make doc attributes go through a proper attribute parser
255                // ast::AttrKind::Normal(n) if n.has_name(sym::doc) => {
256                //     let p = GenericMetaItemParser::from_attr(&n, self.dcx());
257                //
258                //     attributes.push(Attribute::Parsed(AttributeKind::DocComment {
259                //         style: attr.style,
260                //         kind: CommentKind::Line,
261                //         span: attr.span,
262                //         comment: p.args().name_value(),
263                //     }))
264                // }
265                ast::AttrKind::Normal(n) => {
266                    let parser = MetaItemParser::from_attr(n, self.dcx());
267                    let (path, args) = parser.deconstruct();
268                    let parts = path.segments().map(|i| i.name).collect::<Vec<_>>();
269
270                    if let Some(accept) = ATTRIBUTE_MAPPING.0.get(parts.as_slice()) {
271                        let cx =
272                            AcceptContext { group_cx: &group_cx, attr_span: lower_span(attr.span) };
273
274                        accept(&cx, &args)
275                    } else {
276                        // If we're here, we must be compiling a tool attribute... Or someone
277                        // forgot to parse their fancy new attribute. Let's warn them in any case.
278                        // If you are that person, and you really think your attribute should
279                        // remain unparsed, carefully read the documentation in this module and if
280                        // you still think so you can add an exception to this assertion.
281
282                        // FIXME(jdonszelmann): convert other attributes, and check with this that
283                        // we caught em all
284                        // const FIXME_TEMPORARY_ATTR_ALLOWLIST: &[Symbol] = &[sym::cfg];
285                        // assert!(
286                        //     self.tools.contains(&parts[0]) || true,
287                        //     // || FIXME_TEMPORARY_ATTR_ALLOWLIST.contains(&parts[0]),
288                        //     "attribute {path} wasn't parsed and isn't a know tool attribute",
289                        // );
290
291                        attributes.push(Attribute::Unparsed(Box::new(AttrItem {
292                            path: AttrPath::from_ast(&n.item.path),
293                            args: self.lower_attr_args(&n.item.args, lower_span),
294                            id: HashIgnoredAttrId { attr_id: attr.id },
295                            style: attr.style,
296                            span: lower_span(attr.span),
297                        })));
298                    }
299                }
300            }
301        }
302
303        let mut parsed_attributes = Vec::new();
304        for f in &ATTRIBUTE_MAPPING.1 {
305            if let Some(attr) = f(&group_cx) {
306                parsed_attributes.push(Attribute::Parsed(attr));
307            }
308        }
309
310        attributes.extend(parsed_attributes);
311
312        attributes
313    }
314
315    fn lower_attr_args(&self, args: &ast::AttrArgs, lower_span: impl Fn(Span) -> Span) -> AttrArgs {
316        match args {
317            ast::AttrArgs::Empty => AttrArgs::Empty,
318            ast::AttrArgs::Delimited(args) => AttrArgs::Delimited(args.clone()),
319            // This is an inert key-value attribute - it will never be visible to macros
320            // after it gets lowered to HIR. Therefore, we can extract literals to handle
321            // nonterminals in `#[doc]` (e.g. `#[doc = $e]`).
322            ast::AttrArgs::Eq { eq_span, expr } => {
323                // In valid code the value always ends up as a single literal. Otherwise, a dummy
324                // literal suffices because the error is handled elsewhere.
325                let lit = if let ast::ExprKind::Lit(token_lit) = expr.kind
326                    && let Ok(lit) =
327                        ast::MetaItemLit::from_token_lit(token_lit, lower_span(expr.span))
328                {
329                    lit
330                } else {
331                    let guar = self.dcx().span_delayed_bug(
332                        args.span().unwrap_or(DUMMY_SP),
333                        "expr in place where literal is expected (builtin attr parsing)",
334                    );
335                    ast::MetaItemLit {
336                        symbol: sym::dummy,
337                        suffix: None,
338                        kind: ast::LitKind::Err(guar),
339                        span: DUMMY_SP,
340                    }
341                };
342                AttrArgs::Eq { eq_span: lower_span(*eq_span), expr: lit }
343            }
344        }
345    }
346}