rustc_attr_parsing/attributes/mod.rs
1//! This module defines traits for attribute parsers, little state machines that recognize and parse
2//! attributes out of a longer list of attributes. The main trait is called [`AttributeParser`].
3//! You can find more docs about [`AttributeParser`]s on the trait itself.
4//! However, for many types of attributes, implementing [`AttributeParser`] is not necessary.
5//! It allows for a lot of flexibility you might not want.
6//!
7//! Specifically, you might not care about managing the state of your [`AttributeParser`]
8//! state machine yourself. In this case you can choose to implement:
9//!
10//! - [`SingleAttributeParser`]: makes it easy to implement an attribute which should error if it
11//! appears more than once in a list of attributes
12//! - [`CombineAttributeParser`]: makes it easy to implement an attribute which should combine the
13//! contents of attributes, if an attribute appear multiple times in a list
14//!
15//! Attributes should be added to [`ATTRIBUTE_MAPPING`](crate::context::ATTRIBUTE_MAPPING) to be parsed.
16
17use std::marker::PhantomData;
18
19use rustc_attr_data_structures::AttributeKind;
20use rustc_span::Span;
21use thin_vec::ThinVec;
22
23use crate::context::{AcceptContext, FinalizeContext};
24use crate::parser::ArgParser;
25
26pub(crate) mod allow_unstable;
27pub(crate) mod cfg;
28pub(crate) mod confusables;
29pub(crate) mod deprecation;
30pub(crate) mod repr;
31pub(crate) mod rustc;
32pub(crate) mod stability;
33pub(crate) mod transparency;
34pub(crate) mod util;
35
36type AcceptFn<T> = fn(&mut T, &AcceptContext<'_>, &ArgParser<'_>);
37type AcceptMapping<T> = &'static [(&'static [rustc_span::Symbol], AcceptFn<T>)];
38
39/// An [`AttributeParser`] is a type which searches for syntactic attributes.
40///
41/// Parsers are often tiny state machines that gets to see all syntactical attributes on an item.
42/// [`Default::default`] creates a fresh instance that sits in some kind of initial state, usually that the
43/// attribute it is looking for was not yet seen.
44///
45/// Then, it defines what paths this group will accept in [`AttributeParser::ATTRIBUTES`].
46/// These are listed as pairs, of symbols and function pointers. The function pointer will
47/// be called when that attribute is found on an item, which can influence the state of the little
48/// state machine.
49///
50/// Finally, after all attributes on an item have been seen, and possibly been accepted,
51/// the [`finalize`](AttributeParser::finalize) functions for all attribute parsers are called. Each can then report
52/// whether it has seen the attribute it has been looking for.
53///
54/// The state machine is automatically reset to parse attributes on the next item.
55pub(crate) trait AttributeParser: Default + 'static {
56 /// The symbols for the attributes that this parser is interested in.
57 ///
58 /// If an attribute has this symbol, the `accept` function will be called on it.
59 const ATTRIBUTES: AcceptMapping<Self>;
60
61 /// The parser has gotten a chance to accept the attributes on an item,
62 /// here it can produce an attribute.
63 fn finalize(self, cx: &FinalizeContext<'_>) -> Option<AttributeKind>;
64}
65
66/// Alternative to [`AttributeParser`] that automatically handles state management.
67/// A slightly simpler and more restricted way to convert attributes.
68/// Assumes that an attribute can only appear a single time on an item,
69/// and errors when it sees more.
70///
71/// [`Single<T> where T: SingleAttributeParser`](Single) implements [`AttributeParser`].
72///
73/// [`SingleAttributeParser`] can only convert attributes one-to-one, and cannot combine multiple
74/// attributes together like is necessary for `#[stable()]` and `#[unstable()]` for example.
75pub(crate) trait SingleAttributeParser: 'static {
76 const PATH: &'static [rustc_span::Symbol];
77
78 /// Caled when a duplicate attribute is found.
79 ///
80 /// `first_span` is the span of the first occurrence of this attribute.
81 // FIXME(jdonszelmann): default error
82 fn on_duplicate(cx: &AcceptContext<'_>, first_span: Span);
83
84 /// Converts a single syntactical attribute to a single semantic attribute, or [`AttributeKind`]
85 fn convert(cx: &AcceptContext<'_>, args: &ArgParser<'_>) -> Option<AttributeKind>;
86}
87
88pub(crate) struct Single<T: SingleAttributeParser>(PhantomData<T>, Option<(AttributeKind, Span)>);
89
90impl<T: SingleAttributeParser> Default for Single<T> {
91 fn default() -> Self {
92 Self(Default::default(), Default::default())
93 }
94}
95
96impl<T: SingleAttributeParser> AttributeParser for Single<T> {
97 const ATTRIBUTES: AcceptMapping<Self> = &[(T::PATH, |group: &mut Single<T>, cx, args| {
98 if let Some((_, s)) = group.1 {
99 T::on_duplicate(cx, s);
100 return;
101 }
102
103 if let Some(pa) = T::convert(cx, args) {
104 group.1 = Some((pa, cx.attr_span));
105 }
106 })];
107
108 fn finalize(self, _cx: &FinalizeContext<'_>) -> Option<AttributeKind> {
109 Some(self.1?.0)
110 }
111}
112
113type ConvertFn<E> = fn(ThinVec<E>) -> AttributeKind;
114
115/// Alternative to [`AttributeParser`] that automatically handles state management.
116/// If multiple attributes appear on an element, combines the values of each into a
117/// [`ThinVec`].
118/// [`Combine<T> where T: CombineAttributeParser`](Combine) implements [`AttributeParser`].
119///
120/// [`CombineAttributeParser`] can only convert a single kind of attribute, and cannot combine multiple
121/// attributes together like is necessary for `#[stable()]` and `#[unstable()]` for example.
122pub(crate) trait CombineAttributeParser: 'static {
123 const PATH: &'static [rustc_span::Symbol];
124
125 type Item;
126 const CONVERT: ConvertFn<Self::Item>;
127
128 /// Converts a single syntactical attribute to a number of elements of the semantic attribute, or [`AttributeKind`]
129 fn extend<'a>(
130 cx: &'a AcceptContext<'a>,
131 args: &'a ArgParser<'a>,
132 ) -> impl IntoIterator<Item = Self::Item> + 'a;
133}
134
135pub(crate) struct Combine<T: CombineAttributeParser>(
136 PhantomData<T>,
137 ThinVec<<T as CombineAttributeParser>::Item>,
138);
139
140impl<T: CombineAttributeParser> Default for Combine<T> {
141 fn default() -> Self {
142 Self(Default::default(), Default::default())
143 }
144}
145
146impl<T: CombineAttributeParser> AttributeParser for Combine<T> {
147 const ATTRIBUTES: AcceptMapping<Self> =
148 &[(T::PATH, |group: &mut Combine<T>, cx, args| group.1.extend(T::extend(cx, args)))];
149
150 fn finalize(self, _cx: &FinalizeContext<'_>) -> Option<AttributeKind> {
151 if self.1.is_empty() { None } else { Some(T::CONVERT(self.1)) }
152 }
153}