rustc_attr_parsing/
context.rs

1use std::cell::RefCell;
2use std::collections::BTreeMap;
3use std::marker::PhantomData;
4use std::ops::{Deref, DerefMut};
5use std::sync::LazyLock;
6
7use private::Sealed;
8use rustc_ast::{self as ast, MetaItemLit, NodeId};
9use rustc_attr_data_structures::AttributeKind;
10use rustc_attr_data_structures::lints::{AttributeLint, AttributeLintKind};
11use rustc_errors::{DiagCtxtHandle, Diagnostic};
12use rustc_feature::{AttributeTemplate, Features};
13use rustc_hir::{AttrArgs, AttrItem, AttrPath, Attribute, HashIgnoredAttrId, HirId};
14use rustc_session::Session;
15use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, Symbol, sym};
16
17use crate::attributes::allow_unstable::{AllowConstFnUnstableParser, AllowInternalUnstableParser};
18use crate::attributes::codegen_attrs::{ColdParser, NoMangleParser, OptimizeParser};
19use crate::attributes::confusables::ConfusablesParser;
20use crate::attributes::deprecation::DeprecationParser;
21use crate::attributes::inline::{InlineParser, RustcForceInlineParser};
22use crate::attributes::lint_helpers::{AsPtrParser, PubTransparentParser};
23use crate::attributes::must_use::MustUseParser;
24use crate::attributes::repr::{AlignParser, ReprParser};
25use crate::attributes::semantics::MayDangleParser;
26use crate::attributes::stability::{
27    BodyStabilityParser, ConstStabilityIndirectParser, ConstStabilityParser, StabilityParser,
28};
29use crate::attributes::transparency::TransparencyParser;
30use crate::attributes::{AttributeParser as _, Combine, Single};
31use crate::parser::{ArgParser, MetaItemParser};
32use crate::session_diagnostics::{AttributeParseError, AttributeParseErrorReason, UnknownMetaItem};
33
34macro_rules! group_type {
35    ($stage: ty) => {
36         LazyLock<(
37            BTreeMap<&'static [Symbol], Vec<(AttributeTemplate, Box<dyn for<'sess, 'a> Fn(&mut AcceptContext<'_, 'sess, $stage>, &ArgParser<'a>) + Send + Sync>)>>,
38            Vec<Box<dyn Send + Sync + Fn(&mut FinalizeContext<'_, '_, $stage>) -> Option<AttributeKind>>>
39        )>
40    };
41}
42
43macro_rules! attribute_parsers {
44    (
45        pub(crate) static $name: ident = [$($names: ty),* $(,)?];
46    ) => {
47        mod early {
48            use super::*;
49            type Combine<T> = super::Combine<T, Early>;
50            type Single<T> = super::Single<T, Early>;
51
52            attribute_parsers!(@[Early] pub(crate) static $name = [$($names),*];);
53        }
54        mod late {
55            use super::*;
56            type Combine<T> = super::Combine<T, Late>;
57            type Single<T> = super::Single<T, Late>;
58
59            attribute_parsers!(@[Late] pub(crate) static $name = [$($names),*];);
60        }
61    };
62    (
63        @[$ty: ty] pub(crate) static $name: ident = [$($names: ty),* $(,)?];
64    ) => {
65        pub(crate) static $name: group_type!($ty) = LazyLock::new(|| {
66            let mut accepts = BTreeMap::<_, Vec<(AttributeTemplate, Box<dyn for<'sess, 'a> Fn(&mut AcceptContext<'_, 'sess, $ty>, &ArgParser<'a>) + Send + Sync>)>>::new();
67            let mut finalizes = Vec::<Box<dyn Send + Sync + Fn(&mut FinalizeContext<'_, '_, $ty>) -> Option<AttributeKind>>>::new();
68            $(
69                {
70                    thread_local! {
71                        static STATE_OBJECT: RefCell<$names> = RefCell::new(<$names>::default());
72                    };
73
74                    for (path, template, accept_fn) in <$names>::ATTRIBUTES {
75                        accepts.entry(*path).or_default().push((*template, Box::new(|cx, args| {
76                            STATE_OBJECT.with_borrow_mut(|s| {
77                                accept_fn(s, cx, args)
78                            })
79                        })));
80                    }
81
82                    finalizes.push(Box::new(|cx| {
83                        let state = STATE_OBJECT.take();
84                        state.finalize(cx)
85                    }));
86                }
87            )*
88
89            (accepts, finalizes)
90        });
91    };
92}
93attribute_parsers!(
94    pub(crate) static ATTRIBUTE_PARSERS = [
95        // tidy-alphabetical-start
96        AlignParser,
97        BodyStabilityParser,
98        ConfusablesParser,
99        ConstStabilityParser,
100        StabilityParser,
101        // tidy-alphabetical-end
102
103        // tidy-alphabetical-start
104        Combine<AllowConstFnUnstableParser>,
105        Combine<AllowInternalUnstableParser>,
106        Combine<ReprParser>,
107        // tidy-alphabetical-end
108
109        // tidy-alphabetical-start
110        Single<AsPtrParser>,
111        Single<ColdParser>,
112        Single<ConstStabilityIndirectParser>,
113        Single<DeprecationParser>,
114        Single<InlineParser>,
115        Single<MayDangleParser>,
116        Single<MustUseParser>,
117        Single<NoMangleParser>,
118        Single<OptimizeParser>,
119        Single<PubTransparentParser>,
120        Single<RustcForceInlineParser>,
121        Single<TransparencyParser>,
122        // tidy-alphabetical-end
123    ];
124);
125
126mod private {
127    pub trait Sealed {}
128    impl Sealed for super::Early {}
129    impl Sealed for super::Late {}
130}
131
132// allow because it's a sealed trait
133#[allow(private_interfaces)]
134pub trait Stage: Sized + 'static + Sealed {
135    type Id: Copy;
136
137    fn parsers() -> &'static group_type!(Self);
138
139    fn emit_err<'sess>(sess: &'sess Session, diag: impl for<'x> Diagnostic<'x>) -> ErrorGuaranteed;
140}
141
142// allow because it's a sealed trait
143#[allow(private_interfaces)]
144impl Stage for Early {
145    type Id = NodeId;
146
147    fn parsers() -> &'static group_type!(Self) {
148        &early::ATTRIBUTE_PARSERS
149    }
150    fn emit_err<'sess>(sess: &'sess Session, diag: impl for<'x> Diagnostic<'x>) -> ErrorGuaranteed {
151        sess.dcx().create_err(diag).delay_as_bug()
152    }
153}
154
155// allow because it's a sealed trait
156#[allow(private_interfaces)]
157impl Stage for Late {
158    type Id = HirId;
159
160    fn parsers() -> &'static group_type!(Self) {
161        &late::ATTRIBUTE_PARSERS
162    }
163    fn emit_err<'sess>(tcx: &'sess Session, diag: impl for<'x> Diagnostic<'x>) -> ErrorGuaranteed {
164        tcx.dcx().emit_err(diag)
165    }
166}
167
168/// used when parsing attributes for miscelaneous things *before* ast lowering
169pub struct Early;
170/// used when parsing attributes during ast lowering
171pub struct Late;
172
173/// Context given to every attribute parser when accepting
174///
175/// Gives [`AttributeParser`]s enough information to create errors, for example.
176pub(crate) struct AcceptContext<'f, 'sess, S: Stage> {
177    pub(crate) finalize_cx: FinalizeContext<'f, 'sess, S>,
178    /// The span of the attribute currently being parsed
179    pub(crate) attr_span: Span,
180
181    /// The expected structure of the attribute.
182    ///
183    /// Used in reporting errors to give a hint to users what the attribute *should* look like.
184    pub(crate) template: &'f AttributeTemplate,
185
186    /// The name of the attribute we're currently accepting.
187    pub(crate) attr_path: AttrPath,
188}
189
190impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
191    pub(crate) fn emit_err(&self, diag: impl for<'x> Diagnostic<'x>) -> ErrorGuaranteed {
192        S::emit_err(&self.sess, diag)
193    }
194
195    /// Emit a lint. This method is somewhat special, since lints emitted during attribute parsing
196    /// must be delayed until after HIR is built. This method will take care of the details of
197    /// that.
198    pub(crate) fn emit_lint(&mut self, lint: AttributeLintKind, span: Span) {
199        let id = self.target_id;
200        (self.emit_lint)(AttributeLint { id, span, kind: lint });
201    }
202
203    pub(crate) fn unknown_key(
204        &self,
205        span: Span,
206        found: String,
207        options: &'static [&'static str],
208    ) -> ErrorGuaranteed {
209        self.emit_err(UnknownMetaItem { span, item: found, expected: options })
210    }
211
212    /// error that a string literal was expected.
213    /// You can optionally give the literal you did find (which you found not to be a string literal)
214    /// which can make better errors. For example, if the literal was a byte string it will suggest
215    /// removing the `b` prefix.
216    pub(crate) fn expected_string_literal(
217        &self,
218        span: Span,
219        actual_literal: Option<&MetaItemLit>,
220    ) -> ErrorGuaranteed {
221        self.emit_err(AttributeParseError {
222            span,
223            attr_span: self.attr_span,
224            template: self.template.clone(),
225            attribute: self.attr_path.clone(),
226            reason: AttributeParseErrorReason::ExpectedStringLiteral {
227                byte_string: actual_literal.and_then(|i| {
228                    i.kind.is_bytestr().then(|| self.sess().source_map().start_point(i.span))
229                }),
230            },
231        })
232    }
233
234    pub(crate) fn expected_list(&self, span: Span) -> ErrorGuaranteed {
235        self.emit_err(AttributeParseError {
236            span,
237            attr_span: self.attr_span,
238            template: self.template.clone(),
239            attribute: self.attr_path.clone(),
240            reason: AttributeParseErrorReason::ExpectedList,
241        })
242    }
243
244    pub(crate) fn expected_no_args(&self, args_span: Span) -> ErrorGuaranteed {
245        self.emit_err(AttributeParseError {
246            span: args_span,
247            attr_span: self.attr_span,
248            template: self.template.clone(),
249            attribute: self.attr_path.clone(),
250            reason: AttributeParseErrorReason::ExpectedNoArgs,
251        })
252    }
253
254    /// emit an error that a `name = value` pair was expected at this span. The symbol can be given for
255    /// a nicer error message talking about the specific name that was found lacking a value.
256    pub(crate) fn expected_name_value(&self, span: Span, name: Option<Symbol>) -> ErrorGuaranteed {
257        self.emit_err(AttributeParseError {
258            span,
259            attr_span: self.attr_span,
260            template: self.template.clone(),
261            attribute: self.attr_path.clone(),
262            reason: AttributeParseErrorReason::ExpectedNameValue(name),
263        })
264    }
265
266    /// emit an error that a `name = value` pair was found where that name was already seen.
267    pub(crate) fn duplicate_key(&self, span: Span, key: Symbol) -> ErrorGuaranteed {
268        self.emit_err(AttributeParseError {
269            span,
270            attr_span: self.attr_span,
271            template: self.template.clone(),
272            attribute: self.attr_path.clone(),
273            reason: AttributeParseErrorReason::DuplicateKey(key),
274        })
275    }
276
277    /// an error that should be emitted when a [`MetaItemOrLitParser`](crate::parser::MetaItemOrLitParser)
278    /// was expected *not* to be a literal, but instead a meta item.
279    pub(crate) fn unexpected_literal(&self, span: Span) -> ErrorGuaranteed {
280        self.emit_err(AttributeParseError {
281            span,
282            attr_span: self.attr_span,
283            template: self.template.clone(),
284            attribute: self.attr_path.clone(),
285            reason: AttributeParseErrorReason::UnexpectedLiteral,
286        })
287    }
288
289    pub(crate) fn expected_single_argument(&self, span: Span) -> ErrorGuaranteed {
290        self.emit_err(AttributeParseError {
291            span,
292            attr_span: self.attr_span,
293            template: self.template.clone(),
294            attribute: self.attr_path.clone(),
295            reason: AttributeParseErrorReason::ExpectedSingleArgument,
296        })
297    }
298
299    pub(crate) fn expected_specific_argument(
300        &self,
301        span: Span,
302        possibilities: Vec<&'static str>,
303    ) -> ErrorGuaranteed {
304        self.emit_err(AttributeParseError {
305            span,
306            attr_span: self.attr_span,
307            template: self.template.clone(),
308            attribute: self.attr_path.clone(),
309            reason: AttributeParseErrorReason::ExpectedSpecificArgument {
310                possibilities,
311                strings: false,
312            },
313        })
314    }
315
316    pub(crate) fn expected_specific_argument_strings(
317        &self,
318        span: Span,
319        possibilities: Vec<&'static str>,
320    ) -> ErrorGuaranteed {
321        self.emit_err(AttributeParseError {
322            span,
323            attr_span: self.attr_span,
324            template: self.template.clone(),
325            attribute: self.attr_path.clone(),
326            reason: AttributeParseErrorReason::ExpectedSpecificArgument {
327                possibilities,
328                strings: true,
329            },
330        })
331    }
332}
333
334impl<'f, 'sess, S: Stage> Deref for AcceptContext<'f, 'sess, S> {
335    type Target = FinalizeContext<'f, 'sess, S>;
336
337    fn deref(&self) -> &Self::Target {
338        &self.finalize_cx
339    }
340}
341
342impl<'f, 'sess, S: Stage> DerefMut for AcceptContext<'f, 'sess, S> {
343    fn deref_mut(&mut self) -> &mut Self::Target {
344        &mut self.finalize_cx
345    }
346}
347
348/// Context given to every attribute parser during finalization.
349///
350/// Gives [`AttributeParser`](crate::attributes::AttributeParser)s enough information to create
351/// errors, for example.
352pub(crate) struct FinalizeContext<'p, 'sess, S: Stage> {
353    /// The parse context, gives access to the session and the
354    /// diagnostics context.
355    pub(crate) cx: &'p mut AttributeParser<'sess, S>,
356    /// The span of the syntactical component this attribute was applied to
357    pub(crate) target_span: Span,
358    /// The id ([`NodeId`] if `S` is `Early`, [`HirId`] if `S` is `Late`) of the syntactical component this attribute was applied to
359    pub(crate) target_id: S::Id,
360
361    pub(crate) emit_lint: &'p mut dyn FnMut(AttributeLint<S::Id>),
362}
363
364impl<'p, 'sess: 'p, S: Stage> Deref for FinalizeContext<'p, 'sess, S> {
365    type Target = AttributeParser<'sess, S>;
366
367    fn deref(&self) -> &Self::Target {
368        self.cx
369    }
370}
371
372impl<'p, 'sess: 'p, S: Stage> DerefMut for FinalizeContext<'p, 'sess, S> {
373    fn deref_mut(&mut self) -> &mut Self::Target {
374        self.cx
375    }
376}
377
378#[derive(PartialEq, Clone, Copy, Debug)]
379pub enum OmitDoc {
380    Lower,
381    Skip,
382}
383
384/// Context created once, for example as part of the ast lowering
385/// context, through which all attributes can be lowered.
386pub struct AttributeParser<'sess, S: Stage = Late> {
387    #[expect(dead_code)] // FIXME(jdonszelmann): needed later to verify we parsed all attributes
388    tools: Vec<Symbol>,
389    features: Option<&'sess Features>,
390    sess: &'sess Session,
391    stage: PhantomData<S>,
392
393    /// *Only* parse attributes with this symbol.
394    ///
395    /// Used in cases where we want the lowering infrastructure for parse just a single attribute.
396    parse_only: Option<Symbol>,
397}
398
399impl<'sess> AttributeParser<'sess, Early> {
400    /// This method allows you to parse attributes *before* you have access to features or tools.
401    /// One example where this is necessary, is to parse `feature` attributes themselves for
402    /// example.
403    ///
404    /// Try to use this as little as possible. Attributes *should* be lowered during
405    /// `rustc_ast_lowering`. Some attributes require access to features to parse, which would
406    /// crash if you tried to do so through [`parse_limited`](Self::parse_limited).
407    ///
408    /// To make sure use is limited, supply a `Symbol` you'd like to parse. Only attributes with
409    /// that symbol are picked out of the list of instructions and parsed. Those are returned.
410    ///
411    /// No diagnostics will be emitted when parsing limited. Lints are not emitted at all, while
412    /// errors will be emitted as a delayed bugs. in other words, we *expect* attributes parsed
413    /// with `parse_limited` to be reparsed later during ast lowering where we *do* emit the errors
414    pub fn parse_limited(
415        sess: &'sess Session,
416        attrs: &[ast::Attribute],
417        sym: Symbol,
418        target_span: Span,
419        target_node_id: NodeId,
420    ) -> Option<Attribute> {
421        let mut p = Self {
422            features: None,
423            tools: Vec::new(),
424            parse_only: Some(sym),
425            sess,
426            stage: PhantomData,
427        };
428        let mut parsed = p.parse_attribute_list(
429            attrs,
430            target_span,
431            target_node_id,
432            OmitDoc::Skip,
433            std::convert::identity,
434            |_lint| {
435                panic!("can't emit lints here for now (nothing uses this atm)");
436            },
437        );
438        assert!(parsed.len() <= 1);
439
440        parsed.pop()
441    }
442}
443
444impl<'sess, S: Stage> AttributeParser<'sess, S> {
445    pub fn new(sess: &'sess Session, features: &'sess Features, tools: Vec<Symbol>) -> Self {
446        Self { features: Some(features), tools, parse_only: None, sess, stage: PhantomData }
447    }
448
449    pub(crate) fn sess(&self) -> &'sess Session {
450        &self.sess
451    }
452
453    pub(crate) fn features(&self) -> &'sess Features {
454        self.features.expect("features not available at this point in the compiler")
455    }
456
457    pub(crate) fn dcx(&self) -> DiagCtxtHandle<'sess> {
458        self.sess().dcx()
459    }
460
461    /// Parse a list of attributes.
462    ///
463    /// `target_span` is the span of the thing this list of attributes is applied to,
464    /// and when `omit_doc` is set, doc attributes are filtered out.
465    pub fn parse_attribute_list(
466        &mut self,
467        attrs: &[ast::Attribute],
468        target_span: Span,
469        target_id: S::Id,
470        omit_doc: OmitDoc,
471
472        lower_span: impl Copy + Fn(Span) -> Span,
473        mut emit_lint: impl FnMut(AttributeLint<S::Id>),
474    ) -> Vec<Attribute> {
475        let mut attributes = Vec::new();
476
477        for attr in attrs {
478            // If we're only looking for a single attribute, skip all the ones we don't care about.
479            if let Some(expected) = self.parse_only {
480                if !attr.has_name(expected) {
481                    continue;
482                }
483            }
484
485            // Sometimes, for example for `#![doc = include_str!("readme.md")]`,
486            // doc still contains a non-literal. You might say, when we're lowering attributes
487            // that's expanded right? But no, sometimes, when parsing attributes on macros,
488            // we already use the lowering logic and these are still there. So, when `omit_doc`
489            // is set we *also* want to ignore these.
490            if omit_doc == OmitDoc::Skip && attr.has_name(sym::doc) {
491                continue;
492            }
493
494            match &attr.kind {
495                ast::AttrKind::DocComment(comment_kind, symbol) => {
496                    if omit_doc == OmitDoc::Skip {
497                        continue;
498                    }
499
500                    attributes.push(Attribute::Parsed(AttributeKind::DocComment {
501                        style: attr.style,
502                        kind: *comment_kind,
503                        span: lower_span(attr.span),
504                        comment: *symbol,
505                    }))
506                }
507                // // FIXME: make doc attributes go through a proper attribute parser
508                // ast::AttrKind::Normal(n) if n.has_name(sym::doc) => {
509                //     let p = GenericMetaItemParser::from_attr(&n, self.dcx());
510                //
511                //     attributes.push(Attribute::Parsed(AttributeKind::DocComment {
512                //         style: attr.style,
513                //         kind: CommentKind::Line,
514                //         span: attr.span,
515                //         comment: p.args().name_value(),
516                //     }))
517                // }
518                ast::AttrKind::Normal(n) => {
519                    let parser = MetaItemParser::from_attr(n, self.dcx());
520                    let path = parser.path();
521                    let args = parser.args();
522                    let parts = path.segments().map(|i| i.name).collect::<Vec<_>>();
523
524                    if let Some(accepts) = S::parsers().0.get(parts.as_slice()) {
525                        for (template, accept) in accepts {
526                            let mut cx: AcceptContext<'_, 'sess, S> = AcceptContext {
527                                finalize_cx: FinalizeContext {
528                                    cx: self,
529                                    target_span,
530                                    target_id,
531                                    emit_lint: &mut emit_lint,
532                                },
533                                attr_span: lower_span(attr.span),
534                                template,
535                                attr_path: path.get_attribute_path(),
536                            };
537
538                            accept(&mut cx, args)
539                        }
540                    } else {
541                        // If we're here, we must be compiling a tool attribute... Or someone
542                        // forgot to parse their fancy new attribute. Let's warn them in any case.
543                        // If you are that person, and you really think your attribute should
544                        // remain unparsed, carefully read the documentation in this module and if
545                        // you still think so you can add an exception to this assertion.
546
547                        // FIXME(jdonszelmann): convert other attributes, and check with this that
548                        // we caught em all
549                        // const FIXME_TEMPORARY_ATTR_ALLOWLIST: &[Symbol] = &[sym::cfg];
550                        // assert!(
551                        //     self.tools.contains(&parts[0]) || true,
552                        //     // || FIXME_TEMPORARY_ATTR_ALLOWLIST.contains(&parts[0]),
553                        //     "attribute {path} wasn't parsed and isn't a know tool attribute",
554                        // );
555
556                        attributes.push(Attribute::Unparsed(Box::new(AttrItem {
557                            path: AttrPath::from_ast(&n.item.path),
558                            args: self.lower_attr_args(&n.item.args, lower_span),
559                            id: HashIgnoredAttrId { attr_id: attr.id },
560                            style: attr.style,
561                            span: lower_span(attr.span),
562                        })));
563                    }
564                }
565            }
566        }
567
568        let mut parsed_attributes = Vec::new();
569        for f in &S::parsers().1 {
570            if let Some(attr) = f(&mut FinalizeContext {
571                cx: self,
572                target_span,
573                target_id,
574                emit_lint: &mut emit_lint,
575            }) {
576                parsed_attributes.push(Attribute::Parsed(attr));
577            }
578        }
579
580        attributes.extend(parsed_attributes);
581
582        attributes
583    }
584
585    fn lower_attr_args(&self, args: &ast::AttrArgs, lower_span: impl Fn(Span) -> Span) -> AttrArgs {
586        match args {
587            ast::AttrArgs::Empty => AttrArgs::Empty,
588            ast::AttrArgs::Delimited(args) => AttrArgs::Delimited(args.clone()),
589            // This is an inert key-value attribute - it will never be visible to macros
590            // after it gets lowered to HIR. Therefore, we can extract literals to handle
591            // nonterminals in `#[doc]` (e.g. `#[doc = $e]`).
592            ast::AttrArgs::Eq { eq_span, expr } => {
593                // In valid code the value always ends up as a single literal. Otherwise, a dummy
594                // literal suffices because the error is handled elsewhere.
595                let lit = if let ast::ExprKind::Lit(token_lit) = expr.kind
596                    && let Ok(lit) =
597                        ast::MetaItemLit::from_token_lit(token_lit, lower_span(expr.span))
598                {
599                    lit
600                } else {
601                    let guar = self.dcx().span_delayed_bug(
602                        args.span().unwrap_or(DUMMY_SP),
603                        "expr in place where literal is expected (builtin attr parsing)",
604                    );
605                    ast::MetaItemLit {
606                        symbol: sym::dummy,
607                        suffix: None,
608                        kind: ast::LitKind::Err(guar),
609                        span: DUMMY_SP,
610                    }
611                };
612                AttrArgs::Eq { eq_span: lower_span(*eq_span), expr: lit }
613            }
614        }
615    }
616}