attribute_derive/parsing.rs
1//! This module defines the traits defining how parsing works.
2//!
3//! `attribute-derive` reuses the same traits for nested values and the root
4//! attribute, this is why all traits in this module are named `Attribute*`.
5#![doc = include_str!("../docs/traits.html")]
6
7use std::ops::Range;
8
9use manyhow::{span_range, SpanRanged};
10use proc_macro2::{Ident, Span};
11use quote::ToTokens;
12use syn::ext::IdentExt;
13use syn::parse::discouraged::Speculative;
14#[cfg(doc)]
15use syn::parse::ParseBuffer;
16use syn::parse::{Parse, ParseStream};
17use syn::token::Paren;
18use syn::{parenthesized, Result, Token};
19
20use crate::from_partial::FromPartial;
21use crate::FromAttr;
22
23/// Values that can be parsed named, e.g. `<name>(<value>)`, `<name> = <value>`,
24/// `<name>` (as flag).
25///
26/// This is the default parsing mode used for fields in derived [`FromAttr`]
27/// implementations.
28pub trait AttributeNamed: AttributeBase {
29 /// What open delimiter to use when providing error messages.
30 ///
31 /// For `<name> = <value>`, this is `" = "`, for `<function>(<like>)`, it is
32 /// `"("`.
33 ///
34 /// As named attributes can allow both `<name> = <value>` and
35 /// `name(<value>)`, this might not be the only way this attribute can be
36 /// used.
37 const PREFERRED_OPEN_DELIMITER: &'static str = " = ";
38 /// What close delimiter to use when providing error messages.
39 ///
40 /// For `<name> = <value>`, this is `""`, for `<function>(<like>)`, it is
41 /// `")"`.
42 ///
43 /// As named attributes can allow both `<name> = <value>` and
44 /// `<name>(<value>)`, this might not be the only way this attribute can be
45 /// used.
46 const PREFERRED_CLOSE_DELIMITER: &'static str = "";
47
48 /// Parses an attribute containing `Self` called `name`.
49 ///
50 /// While this function can be implemented freely, the provided
51 /// implementations support `<name> = <value>`, `<function>(<like>)` and
52 /// `<flag>`.
53 ///
54 /// **Note:** This needs to stop parsing at the end of the value, before a
55 /// possible following `,` and further arguments.
56 fn parse_named(
57 name: &'static str,
58 input: ParseStream,
59 ) -> Result<Option<Named<SpannedValue<Self::Partial>>>>;
60}
61
62/// Values that can be parsed positionally, i.e., without a name, e.g.
63/// `"literal"`, `a + b`, `true`.
64///
65/// When deriving [`FromAttr`] this behavior is enabled via putting
66/// `#[attr(positional)]` on the field.
67///
68/// The trait is implemented for each [`AttributeValue`] that implements the
69/// marker trait [`PositionalValue`].
70pub trait AttributePositional: AttributeBase {
71 /// Parses `Self`, positionally.
72 ///
73 /// **Note:** This needs to stop parsing at the end of the value, before a
74 /// possible following `,` and further arguments.
75 fn parse_positional(input: ParseStream) -> Result<Option<SpannedValue<Self::Partial>>>;
76}
77
78/// Any values that can be parsed in an attribute input.
79///
80/// This is probably the trait you want to implement when you created a custom
81/// type for field inside [`#[derive(FromAttr)]`](FromAttr), as it will provide
82/// implementations for [`FromAttr`], [`AttributeNamed`] and, if you implement
83/// the marker trait [`PositionalValue`], [`AttributePositional`] as well.
84///
85/// For named attributes by default it will support both `<name> = <value>` and
86/// `<function>(<like>)`, though this can be tweaked in the implementation.
87pub trait AttributeValue: AttributeBase {
88 /// Printed when not encountering a `(` or `=` respectively while trying to
89 /// parse a [`AttributeNamed`].
90 const EXPECTED: &'static str = "expected `=` or `(`";
91 /// What open delimiter to use when providing error messages.
92 ///
93 /// For `<name> = <value>`, this is `" = "`, for `<function>(<like>)`, it is
94 /// `"("`.
95 ///
96 /// As named attributes can allow both `<name> = <value>` and
97 /// `name(<value>)`, this might not be the only way this attribute can be
98 /// used.
99 const PREFERRED_OPEN_DELIMITER: &'static str = " = ";
100 /// What close delimiter to use when providing error messages.
101 ///
102 /// For `<name> = <value>`, this is `""`, for `<function>(<like>)`, it is
103 /// `")"`.
104 ///
105 /// As named attributes can allow both `<name> = <value>` and
106 /// `<name>(<value>)`, this might not be the only way this attribute can be
107 /// used.
108 const PREFERRED_CLOSE_DELIMITER: &'static str = "";
109 /// Parses the attribute value when parentheses (`(`) were peeked.
110 ///
111 /// Note: this is the input with the parentheses, and potentially following
112 /// arguments.
113 ///
114 /// ```text
115 /// attribute(value), ...
116 /// ^^^^^^^^^^^^
117 /// ```
118 ///
119 /// In the default implementation this calls through to
120 /// [`parse_value`](Self::parse_value) after removing the parentheses.
121 fn parse_value_meta(input: ParseStream) -> Result<SpannedValue<Self::Partial>> {
122 let content;
123 let paren = parenthesized!(content in input);
124 Self::parse_value(&content)
125 .map(SpannedValue::value)
126 .map(SpannedValue::with(paren.span.join()))
127 }
128
129 /// Parses the attribute value when an equals (`=`) was peeked.
130 ///
131 /// Note: this is the input with the equals, and potentially following
132 /// arguments.
133 ///
134 /// ```text
135 /// attribute = value, ...
136 /// ^^^^^^^^^^^^
137 /// ```
138 ///
139 /// In the default implementation this calls through to
140 /// [`parse_value`](Self::parse_value) after removing the `=`.
141 fn parse_value_eq(input: ParseStream) -> Result<SpannedValue<Self::Partial>> {
142 <Token![=]>::parse(input)?;
143 Self::parse_value(input)
144 }
145
146 /// Parses the plain attribute value without leading `=` or enclosing
147 /// parenthesis.
148 ///
149 /// **Note:** this input includes potentially a trailing `,` and following
150 /// arguments.
151 ///
152 /// ```text
153 /// attribute = value, ...
154 /// ^^^^^^^^^^
155 /// ```
156 ///
157 /// For simple syntax this is the only function needed to implement, as the
158 /// default implementations for [`parse_value_meta`](Self::parse_value_meta)
159 /// and [`parse_value_eq`](Self::parse_value_eq).
160 fn parse_value(input: ParseStream) -> Result<SpannedValue<Self::Partial>>;
161}
162
163impl<T: AttributeValue> FromAttr for T {
164 fn parse_partial(input: ParseStream) -> Result<Self::Partial> {
165 Self::parse_value(input).map(SpannedValue::value)
166 }
167}
168
169impl<T: AttributeValue> AttributeNamed for T {
170 const PREFERRED_CLOSE_DELIMITER: &'static str = Self::PREFERRED_CLOSE_DELIMITER;
171 const PREFERRED_OPEN_DELIMITER: &'static str = Self::PREFERRED_OPEN_DELIMITER;
172
173 fn parse_named(
174 name: &'static str,
175 input: ParseStream,
176 ) -> Result<Option<Named<SpannedValue<Self::Partial>>>> {
177 let Some(name) = parse_name(input, name) else {
178 return Ok(None);
179 };
180 let value = if input.peek(Token![=]) {
181 Self::parse_value_eq(input)?
182 } else if input.peek(Paren) {
183 Self::parse_value_meta(input)?
184 } else {
185 return Err(input.error(Self::EXPECTED));
186 };
187 Ok(Some(Named { name, value }))
188 }
189}
190
191/// Marker trait that enables the blanket implementation of
192/// [`AttributePositional`] for [`AttributeValue`].
193pub trait PositionalValue {}
194
195impl<T: AttributeValue + PositionalValue> AttributePositional for T {
196 fn parse_positional(input: ParseStream) -> Result<Option<SpannedValue<Self::Partial>>> {
197 Self::parse_value(input).map(Some)
198 }
199}
200
201/// Trait implementing parsing for `<function>(<like>)` attributes.
202///
203/// This is the trait defining the parsing of both top level attributes deriving
204/// [`FromAttr`] and sub attributes.
205/// ```
206/// # quote::quote!(
207/// #[attribute(sub_attribute("hello", "world"))]
208/// # );
209/// ```
210pub trait AttributeMeta: AttributeBase {
211 /// Parses the content of the parenthesis:
212 ///
213 /// ```text
214 /// attribute(value)
215 /// ^^^^^
216 /// ```
217 fn parse_inner(input: ParseStream) -> Result<Self::Partial>;
218}
219
220impl<T: AttributeMeta> AttributeValue for T {
221 const EXPECTED: &'static str = "expected `(`";
222 const PREFERRED_CLOSE_DELIMITER: &'static str = ")";
223 const PREFERRED_OPEN_DELIMITER: &'static str = "(";
224
225 fn parse_value_eq(input: ParseStream) -> Result<SpannedValue<Self::Partial>> {
226 Err(input.error(Self::EXPECTED))
227 }
228
229 fn parse_value(input: ParseStream) -> Result<SpannedValue<Self::Partial>> {
230 Self::parse_inner(input).map(SpannedValue::call_site)
231 }
232}
233
234/// Trait implemented for attributes that can be parsed optionally as a
235/// positional argument, and the requirement for `Option<T>` to implement
236/// [`AttributePositional`].
237pub trait AttributePeekable {
238 /// Used to decide whether to parse optional positional values.
239 ///
240 /// While most implementations should not mutate `input`, it might be good
241 /// to call this on a [`fork`](ParseBuffer::fork) of the original
242 /// [`ParseStream`] to ensure no mutation is persisted.
243 ///
244 /// # Implementation notes
245 /// This should not try to parse `input`, if you cannot decide if `input`
246 /// matches using [`ParseBuffer::peek`] ([peek2](ParseBuffer::peek2),
247 /// [peek3](ParseBuffer::peek3)), consider not implementing
248 /// [`AttributePeekable`].
249 ///
250 /// `attribute-derive` will always [`fork`](ParseBuffer::fork) before
251 /// calling this function to allow `peek` to modify `input` without
252 /// effecting further parsing.
253 fn peek(input: ParseStream) -> bool;
254}
255
256#[derive(Debug)]
257/// Helper struct to hold a value and the ident of its property.
258pub struct Named<T> {
259 /// The value.
260 pub value: T,
261 /// The argument name.
262 pub name: Ident,
263}
264
265impl<T> Named<T> {
266 #[doc(hidden)]
267 pub fn error_span(&self) -> Span {
268 self.name.span()
269 }
270}
271
272impl<T> Named<SpannedValue<T>> {
273 /// The value.
274 pub fn value(self) -> T {
275 self.value.value
276 }
277}
278
279/// Parses the name, if it matches returns `Some(name)` and removes the `name`
280/// from input, if `None` it does not modify the input.
281pub fn parse_name(input: ParseStream, name: &str) -> Option<Ident> {
282 let fork = &input.fork();
283 let ident: Ident = Ident::parse_any(fork).ok()?;
284 if ident == name {
285 input.advance_to(fork);
286 Some(ident)
287 } else {
288 None
289 }
290}
291
292/// Utility crate holding `Self::Partial` used in most attribute traits, i.e.,
293/// [`FromAttr`], [`AttributeValue`], [`AttributePositional`], ...
294pub trait AttributeBase: FromPartial<Self::Partial> {
295 /// Partial type for this attribute. In most cases this can be `Self`,
296 /// unless the attribute can be parsed in multiple on-its-own-incomplete
297 /// parts or needs special handling on the conversion.
298 type Partial;
299}
300
301/// Helper struct to hold a value and the corresponding range.
302#[derive(Debug)]
303pub struct SpannedValue<T> {
304 /// The value.
305 pub value: T,
306 /// The value's span.
307 pub span: Range<Span>,
308}
309
310impl<T: Default> Default for SpannedValue<T> {
311 fn default() -> Self {
312 Self::call_site(Default::default())
313 }
314}
315
316impl<T> SpannedValue<T> {
317 /// The value.
318 pub fn value(self) -> T {
319 self.value
320 }
321
322 /// The value's span.
323 pub fn span(&self) -> Range<Span> {
324 self.span.clone()
325 }
326
327 /// Map the value to a new type, keeping the span.
328 pub fn map_value<I>(self, map: impl FnOnce(T) -> I) -> SpannedValue<I> {
329 SpannedValue {
330 span: self.span(),
331 value: map(self.value()),
332 }
333 }
334
335 pub(crate) fn with(span: impl SpanRanged) -> impl Fn(T) -> Self {
336 move |value| Self::new(value, span.span_range())
337 }
338
339 /// Creates a new `SpannedValue` from a `value` implementing [`ToTokens`].
340 pub fn from_to_tokens(value: T) -> Self
341 where
342 T: ToTokens,
343 {
344 Self {
345 span: span_range!(value),
346 value,
347 }
348 }
349
350 /// Creates a new `SpannedValue` from a `value` and a [`span`](SpanRanged).
351 pub fn new(value: T, span: impl SpanRanged) -> SpannedValue<T> {
352 Self {
353 value,
354 span: span.span_range(),
355 }
356 }
357
358 /// Creates a new `SpannedValue` with the span [`Span::call_site()`].
359 pub fn call_site(value: T) -> SpannedValue<T> {
360 Self::new(value, Span::call_site())
361 }
362
363 #[doc(hidden)]
364 pub fn error_span(&self) -> Span {
365 self.span()
366 .span_joined()
367 .unwrap_or_else(|| self.span().start)
368 }
369}
370
371impl<T> SpanRanged for SpannedValue<T> {
372 fn span_range(&self) -> Range<Span> {
373 self.span.clone()
374 }
375}