xref: /linux/rust/syn/meta.rs (revision 784faa8eca8270671e0ed6d9d21f04bbb80fc5f7)
1*69942c0aSMiguel Ojeda // SPDX-License-Identifier: Apache-2.0 OR MIT
2*69942c0aSMiguel Ojeda 
3808c999fSMiguel Ojeda //! Facility for interpreting structured content inside of an `Attribute`.
4808c999fSMiguel Ojeda 
5808c999fSMiguel Ojeda use crate::error::{Error, Result};
6808c999fSMiguel Ojeda use crate::ext::IdentExt as _;
7808c999fSMiguel Ojeda use crate::lit::Lit;
8808c999fSMiguel Ojeda use crate::parse::{ParseStream, Parser};
9808c999fSMiguel Ojeda use crate::path::{Path, PathSegment};
10808c999fSMiguel Ojeda use crate::punctuated::Punctuated;
11808c999fSMiguel Ojeda use proc_macro2::Ident;
12808c999fSMiguel Ojeda use std::fmt::Display;
13808c999fSMiguel Ojeda 
14808c999fSMiguel Ojeda /// Make a parser that is usable with `parse_macro_input!` in a
15808c999fSMiguel Ojeda /// `#[proc_macro_attribute]` macro.
16808c999fSMiguel Ojeda ///
17808c999fSMiguel Ojeda /// *Warning:* When parsing attribute args **other than** the
18808c999fSMiguel Ojeda /// `proc_macro::TokenStream` input of a `proc_macro_attribute`, you do **not**
19808c999fSMiguel Ojeda /// need this function. In several cases your callers will get worse error
20808c999fSMiguel Ojeda /// messages if you use this function, because the surrounding delimiter's span
21808c999fSMiguel Ojeda /// is concealed from attribute macros by rustc. Use
22808c999fSMiguel Ojeda /// [`Attribute::parse_nested_meta`] instead.
23808c999fSMiguel Ojeda ///
24808c999fSMiguel Ojeda /// [`Attribute::parse_nested_meta`]: crate::Attribute::parse_nested_meta
25808c999fSMiguel Ojeda ///
26808c999fSMiguel Ojeda /// # Example
27808c999fSMiguel Ojeda ///
28808c999fSMiguel Ojeda /// This example implements an attribute macro whose invocations look like this:
29808c999fSMiguel Ojeda ///
30808c999fSMiguel Ojeda /// ```
31808c999fSMiguel Ojeda /// # const IGNORE: &str = stringify! {
32808c999fSMiguel Ojeda /// #[tea(kind = "EarlGrey", hot)]
33808c999fSMiguel Ojeda /// struct Picard {...}
34808c999fSMiguel Ojeda /// # };
35808c999fSMiguel Ojeda /// ```
36808c999fSMiguel Ojeda ///
37808c999fSMiguel Ojeda /// The "parameters" supported by the attribute are:
38808c999fSMiguel Ojeda ///
39808c999fSMiguel Ojeda /// - `kind = "..."`
40808c999fSMiguel Ojeda /// - `hot`
41808c999fSMiguel Ojeda /// - `with(sugar, milk, ...)`, a comma-separated list of ingredients
42808c999fSMiguel Ojeda ///
43808c999fSMiguel Ojeda /// ```
44808c999fSMiguel Ojeda /// # extern crate proc_macro;
45808c999fSMiguel Ojeda /// #
46808c999fSMiguel Ojeda /// use proc_macro::TokenStream;
47808c999fSMiguel Ojeda /// use syn::{parse_macro_input, LitStr, Path};
48808c999fSMiguel Ojeda ///
49808c999fSMiguel Ojeda /// # const IGNORE: &str = stringify! {
50808c999fSMiguel Ojeda /// #[proc_macro_attribute]
51808c999fSMiguel Ojeda /// # };
52808c999fSMiguel Ojeda /// pub fn tea(args: TokenStream, input: TokenStream) -> TokenStream {
53808c999fSMiguel Ojeda ///     let mut kind: Option<LitStr> = None;
54808c999fSMiguel Ojeda ///     let mut hot: bool = false;
55808c999fSMiguel Ojeda ///     let mut with: Vec<Path> = Vec::new();
56808c999fSMiguel Ojeda ///     let tea_parser = syn::meta::parser(|meta| {
57808c999fSMiguel Ojeda ///         if meta.path.is_ident("kind") {
58808c999fSMiguel Ojeda ///             kind = Some(meta.value()?.parse()?);
59808c999fSMiguel Ojeda ///             Ok(())
60808c999fSMiguel Ojeda ///         } else if meta.path.is_ident("hot") {
61808c999fSMiguel Ojeda ///             hot = true;
62808c999fSMiguel Ojeda ///             Ok(())
63808c999fSMiguel Ojeda ///         } else if meta.path.is_ident("with") {
64808c999fSMiguel Ojeda ///             meta.parse_nested_meta(|meta| {
65808c999fSMiguel Ojeda ///                 with.push(meta.path);
66808c999fSMiguel Ojeda ///                 Ok(())
67808c999fSMiguel Ojeda ///             })
68808c999fSMiguel Ojeda ///         } else {
69808c999fSMiguel Ojeda ///             Err(meta.error("unsupported tea property"))
70808c999fSMiguel Ojeda ///         }
71808c999fSMiguel Ojeda ///     });
72808c999fSMiguel Ojeda ///
73808c999fSMiguel Ojeda ///     parse_macro_input!(args with tea_parser);
74808c999fSMiguel Ojeda ///     eprintln!("kind={kind:?} hot={hot} with={with:?}");
75808c999fSMiguel Ojeda ///
76808c999fSMiguel Ojeda ///     /* ... */
77808c999fSMiguel Ojeda /// #   TokenStream::new()
78808c999fSMiguel Ojeda /// }
79808c999fSMiguel Ojeda /// ```
80808c999fSMiguel Ojeda ///
81808c999fSMiguel Ojeda /// The `syn::meta` library will take care of dealing with the commas including
82808c999fSMiguel Ojeda /// trailing commas, and producing sensible error messages on unexpected input.
83808c999fSMiguel Ojeda ///
84808c999fSMiguel Ojeda /// ```console
85808c999fSMiguel Ojeda /// error: expected `,`
86808c999fSMiguel Ojeda ///  --> src/main.rs:3:37
87808c999fSMiguel Ojeda ///   |
88808c999fSMiguel Ojeda /// 3 | #[tea(kind = "EarlGrey", with(sugar = "lol", milk))]
89808c999fSMiguel Ojeda ///   |                                     ^
90808c999fSMiguel Ojeda /// ```
91808c999fSMiguel Ojeda ///
92808c999fSMiguel Ojeda /// # Example
93808c999fSMiguel Ojeda ///
94808c999fSMiguel Ojeda /// Same as above but we factor out most of the logic into a separate function.
95808c999fSMiguel Ojeda ///
96808c999fSMiguel Ojeda /// ```
97808c999fSMiguel Ojeda /// # extern crate proc_macro;
98808c999fSMiguel Ojeda /// #
99808c999fSMiguel Ojeda /// use proc_macro::TokenStream;
100808c999fSMiguel Ojeda /// use syn::meta::ParseNestedMeta;
101808c999fSMiguel Ojeda /// use syn::parse::{Parser, Result};
102808c999fSMiguel Ojeda /// use syn::{parse_macro_input, LitStr, Path};
103808c999fSMiguel Ojeda ///
104808c999fSMiguel Ojeda /// # const IGNORE: &str = stringify! {
105808c999fSMiguel Ojeda /// #[proc_macro_attribute]
106808c999fSMiguel Ojeda /// # };
107808c999fSMiguel Ojeda /// pub fn tea(args: TokenStream, input: TokenStream) -> TokenStream {
108808c999fSMiguel Ojeda ///     let mut attrs = TeaAttributes::default();
109808c999fSMiguel Ojeda ///     let tea_parser = syn::meta::parser(|meta| attrs.parse(meta));
110808c999fSMiguel Ojeda ///     parse_macro_input!(args with tea_parser);
111808c999fSMiguel Ojeda ///
112808c999fSMiguel Ojeda ///     /* ... */
113808c999fSMiguel Ojeda /// #   TokenStream::new()
114808c999fSMiguel Ojeda /// }
115808c999fSMiguel Ojeda ///
116808c999fSMiguel Ojeda /// #[derive(Default)]
117808c999fSMiguel Ojeda /// struct TeaAttributes {
118808c999fSMiguel Ojeda ///     kind: Option<LitStr>,
119808c999fSMiguel Ojeda ///     hot: bool,
120808c999fSMiguel Ojeda ///     with: Vec<Path>,
121808c999fSMiguel Ojeda /// }
122808c999fSMiguel Ojeda ///
123808c999fSMiguel Ojeda /// impl TeaAttributes {
124808c999fSMiguel Ojeda ///     fn parse(&mut self, meta: ParseNestedMeta) -> Result<()> {
125808c999fSMiguel Ojeda ///         if meta.path.is_ident("kind") {
126808c999fSMiguel Ojeda ///             self.kind = Some(meta.value()?.parse()?);
127808c999fSMiguel Ojeda ///             Ok(())
128808c999fSMiguel Ojeda ///         } else /* just like in last example */
129808c999fSMiguel Ojeda /// #           { unimplemented!() }
130808c999fSMiguel Ojeda ///
131808c999fSMiguel Ojeda ///     }
132808c999fSMiguel Ojeda /// }
133808c999fSMiguel Ojeda /// ```
parser(logic: impl FnMut(ParseNestedMeta) -> Result<()>) -> impl Parser<Output = ()>134808c999fSMiguel Ojeda pub fn parser(logic: impl FnMut(ParseNestedMeta) -> Result<()>) -> impl Parser<Output = ()> {
135808c999fSMiguel Ojeda     |input: ParseStream| {
136808c999fSMiguel Ojeda         if input.is_empty() {
137808c999fSMiguel Ojeda             Ok(())
138808c999fSMiguel Ojeda         } else {
139808c999fSMiguel Ojeda             parse_nested_meta(input, logic)
140808c999fSMiguel Ojeda         }
141808c999fSMiguel Ojeda     }
142808c999fSMiguel Ojeda }
143808c999fSMiguel Ojeda 
144808c999fSMiguel Ojeda /// Context for parsing a single property in the conventional syntax for
145808c999fSMiguel Ojeda /// structured attributes.
146808c999fSMiguel Ojeda ///
147808c999fSMiguel Ojeda /// # Examples
148808c999fSMiguel Ojeda ///
149808c999fSMiguel Ojeda /// Refer to usage examples on the following two entry-points:
150808c999fSMiguel Ojeda ///
151808c999fSMiguel Ojeda /// - [`Attribute::parse_nested_meta`] if you have an entire `Attribute` to
152808c999fSMiguel Ojeda ///   parse. Always use this if possible. Generally this is able to produce
153808c999fSMiguel Ojeda ///   better error messages because `Attribute` holds span information for all
154808c999fSMiguel Ojeda ///   of the delimiters therein.
155808c999fSMiguel Ojeda ///
156808c999fSMiguel Ojeda /// - [`syn::meta::parser`] if you are implementing a `proc_macro_attribute`
157808c999fSMiguel Ojeda ///   macro and parsing the arguments to the attribute macro, i.e. the ones
158808c999fSMiguel Ojeda ///   written in the same attribute that dispatched the macro invocation. Rustc
159808c999fSMiguel Ojeda ///   does not pass span information for the surrounding delimiters into the
160808c999fSMiguel Ojeda ///   attribute macro invocation in this situation, so error messages might be
161808c999fSMiguel Ojeda ///   less precise.
162808c999fSMiguel Ojeda ///
163808c999fSMiguel Ojeda /// [`Attribute::parse_nested_meta`]: crate::Attribute::parse_nested_meta
164808c999fSMiguel Ojeda /// [`syn::meta::parser`]: crate::meta::parser
165808c999fSMiguel Ojeda #[non_exhaustive]
166808c999fSMiguel Ojeda pub struct ParseNestedMeta<'a> {
167808c999fSMiguel Ojeda     pub path: Path,
168808c999fSMiguel Ojeda     pub input: ParseStream<'a>,
169808c999fSMiguel Ojeda }
170808c999fSMiguel Ojeda 
171808c999fSMiguel Ojeda impl<'a> ParseNestedMeta<'a> {
172808c999fSMiguel Ojeda     /// Used when parsing `key = "value"` syntax.
173808c999fSMiguel Ojeda     ///
174808c999fSMiguel Ojeda     /// All it does is advance `meta.input` past the `=` sign in the input. You
175808c999fSMiguel Ojeda     /// could accomplish the same effect by writing
176808c999fSMiguel Ojeda     /// `meta.parse::<Token![=]>()?`, so at most it is a minor convenience to
177808c999fSMiguel Ojeda     /// use `meta.value()?`.
178808c999fSMiguel Ojeda     ///
179808c999fSMiguel Ojeda     /// # Example
180808c999fSMiguel Ojeda     ///
181808c999fSMiguel Ojeda     /// ```
182808c999fSMiguel Ojeda     /// use syn::{parse_quote, Attribute, LitStr};
183808c999fSMiguel Ojeda     ///
184808c999fSMiguel Ojeda     /// let attr: Attribute = parse_quote! {
185808c999fSMiguel Ojeda     ///     #[tea(kind = "EarlGrey")]
186808c999fSMiguel Ojeda     /// };
187808c999fSMiguel Ojeda     ///                                          // conceptually:
188808c999fSMiguel Ojeda     /// if attr.path().is_ident("tea") {         // this parses the `tea`
189808c999fSMiguel Ojeda     ///     attr.parse_nested_meta(|meta| {      // this parses the `(`
190808c999fSMiguel Ojeda     ///         if meta.path.is_ident("kind") {  // this parses the `kind`
191808c999fSMiguel Ojeda     ///             let value = meta.value()?;   // this parses the `=`
192808c999fSMiguel Ojeda     ///             let s: LitStr = value.parse()?;  // this parses `"EarlGrey"`
193808c999fSMiguel Ojeda     ///             if s.value() == "EarlGrey" {
194808c999fSMiguel Ojeda     ///                 // ...
195808c999fSMiguel Ojeda     ///             }
196808c999fSMiguel Ojeda     ///             Ok(())
197808c999fSMiguel Ojeda     ///         } else {
198808c999fSMiguel Ojeda     ///             Err(meta.error("unsupported attribute"))
199808c999fSMiguel Ojeda     ///         }
200808c999fSMiguel Ojeda     ///     })?;
201808c999fSMiguel Ojeda     /// }
202808c999fSMiguel Ojeda     /// # anyhow::Ok(())
203808c999fSMiguel Ojeda     /// ```
value(&self) -> Result<ParseStream<'a>>204808c999fSMiguel Ojeda     pub fn value(&self) -> Result<ParseStream<'a>> {
205808c999fSMiguel Ojeda         self.input.parse::<Token![=]>()?;
206808c999fSMiguel Ojeda         Ok(self.input)
207808c999fSMiguel Ojeda     }
208808c999fSMiguel Ojeda 
209808c999fSMiguel Ojeda     /// Used when parsing `list(...)` syntax **if** the content inside the
210808c999fSMiguel Ojeda     /// nested parentheses is also expected to conform to Rust's structured
211808c999fSMiguel Ojeda     /// attribute convention.
212808c999fSMiguel Ojeda     ///
213808c999fSMiguel Ojeda     /// # Example
214808c999fSMiguel Ojeda     ///
215808c999fSMiguel Ojeda     /// ```
216808c999fSMiguel Ojeda     /// use syn::{parse_quote, Attribute};
217808c999fSMiguel Ojeda     ///
218808c999fSMiguel Ojeda     /// let attr: Attribute = parse_quote! {
219808c999fSMiguel Ojeda     ///     #[tea(with(sugar, milk))]
220808c999fSMiguel Ojeda     /// };
221808c999fSMiguel Ojeda     ///
222808c999fSMiguel Ojeda     /// if attr.path().is_ident("tea") {
223808c999fSMiguel Ojeda     ///     attr.parse_nested_meta(|meta| {
224808c999fSMiguel Ojeda     ///         if meta.path.is_ident("with") {
225808c999fSMiguel Ojeda     ///             meta.parse_nested_meta(|meta| {  // <---
226808c999fSMiguel Ojeda     ///                 if meta.path.is_ident("sugar") {
227808c999fSMiguel Ojeda     ///                     // Here we can go even deeper if needed.
228808c999fSMiguel Ojeda     ///                     Ok(())
229808c999fSMiguel Ojeda     ///                 } else if meta.path.is_ident("milk") {
230808c999fSMiguel Ojeda     ///                     Ok(())
231808c999fSMiguel Ojeda     ///                 } else {
232808c999fSMiguel Ojeda     ///                     Err(meta.error("unsupported ingredient"))
233808c999fSMiguel Ojeda     ///                 }
234808c999fSMiguel Ojeda     ///             })
235808c999fSMiguel Ojeda     ///         } else {
236808c999fSMiguel Ojeda     ///             Err(meta.error("unsupported tea property"))
237808c999fSMiguel Ojeda     ///         }
238808c999fSMiguel Ojeda     ///     })?;
239808c999fSMiguel Ojeda     /// }
240808c999fSMiguel Ojeda     /// # anyhow::Ok(())
241808c999fSMiguel Ojeda     /// ```
242808c999fSMiguel Ojeda     ///
243808c999fSMiguel Ojeda     /// # Counterexample
244808c999fSMiguel Ojeda     ///
245808c999fSMiguel Ojeda     /// If you don't need `parse_nested_meta`'s help in parsing the content
246808c999fSMiguel Ojeda     /// written within the nested parentheses, keep in mind that you can always
247808c999fSMiguel Ojeda     /// just parse it yourself from the exposed ParseStream. Rust syntax permits
248808c999fSMiguel Ojeda     /// arbitrary tokens within those parentheses so for the crazier stuff,
249808c999fSMiguel Ojeda     /// `parse_nested_meta` is not what you want.
250808c999fSMiguel Ojeda     ///
251808c999fSMiguel Ojeda     /// ```
252808c999fSMiguel Ojeda     /// use syn::{parenthesized, parse_quote, Attribute, LitInt};
253808c999fSMiguel Ojeda     ///
254808c999fSMiguel Ojeda     /// let attr: Attribute = parse_quote! {
255808c999fSMiguel Ojeda     ///     #[repr(align(32))]
256808c999fSMiguel Ojeda     /// };
257808c999fSMiguel Ojeda     ///
258808c999fSMiguel Ojeda     /// let mut align: Option<LitInt> = None;
259808c999fSMiguel Ojeda     /// if attr.path().is_ident("repr") {
260808c999fSMiguel Ojeda     ///     attr.parse_nested_meta(|meta| {
261808c999fSMiguel Ojeda     ///         if meta.path.is_ident("align") {
262808c999fSMiguel Ojeda     ///             let content;
263808c999fSMiguel Ojeda     ///             parenthesized!(content in meta.input);
264808c999fSMiguel Ojeda     ///             align = Some(content.parse()?);
265808c999fSMiguel Ojeda     ///             Ok(())
266808c999fSMiguel Ojeda     ///         } else {
267808c999fSMiguel Ojeda     ///             Err(meta.error("unsupported repr"))
268808c999fSMiguel Ojeda     ///         }
269808c999fSMiguel Ojeda     ///     })?;
270808c999fSMiguel Ojeda     /// }
271808c999fSMiguel Ojeda     /// # anyhow::Ok(())
272808c999fSMiguel Ojeda     /// ```
parse_nested_meta( &self, logic: impl FnMut(ParseNestedMeta) -> Result<()>, ) -> Result<()>273808c999fSMiguel Ojeda     pub fn parse_nested_meta(
274808c999fSMiguel Ojeda         &self,
275808c999fSMiguel Ojeda         logic: impl FnMut(ParseNestedMeta) -> Result<()>,
276808c999fSMiguel Ojeda     ) -> Result<()> {
277808c999fSMiguel Ojeda         let content;
278808c999fSMiguel Ojeda         parenthesized!(content in self.input);
279808c999fSMiguel Ojeda         parse_nested_meta(&content, logic)
280808c999fSMiguel Ojeda     }
281808c999fSMiguel Ojeda 
282808c999fSMiguel Ojeda     /// Report that the attribute's content did not conform to expectations.
283808c999fSMiguel Ojeda     ///
284808c999fSMiguel Ojeda     /// The span of the resulting error will cover `meta.path` *and* everything
285808c999fSMiguel Ojeda     /// that has been parsed so far since it.
286808c999fSMiguel Ojeda     ///
287808c999fSMiguel Ojeda     /// There are 2 ways you might call this. First, if `meta.path` is not
288808c999fSMiguel Ojeda     /// something you recognize:
289808c999fSMiguel Ojeda     ///
290808c999fSMiguel Ojeda     /// ```
291808c999fSMiguel Ojeda     /// # use syn::Attribute;
292808c999fSMiguel Ojeda     /// #
293808c999fSMiguel Ojeda     /// # fn example(attr: &Attribute) -> syn::Result<()> {
294808c999fSMiguel Ojeda     /// attr.parse_nested_meta(|meta| {
295808c999fSMiguel Ojeda     ///     if meta.path.is_ident("kind") {
296808c999fSMiguel Ojeda     ///         // ...
297808c999fSMiguel Ojeda     ///         Ok(())
298808c999fSMiguel Ojeda     ///     } else {
299808c999fSMiguel Ojeda     ///         Err(meta.error("unsupported tea property"))
300808c999fSMiguel Ojeda     ///     }
301808c999fSMiguel Ojeda     /// })?;
302808c999fSMiguel Ojeda     /// # Ok(())
303808c999fSMiguel Ojeda     /// # }
304808c999fSMiguel Ojeda     /// ```
305808c999fSMiguel Ojeda     ///
306808c999fSMiguel Ojeda     /// In this case, it behaves exactly like
307808c999fSMiguel Ojeda     /// `syn::Error::new_spanned(&meta.path, "message...")`.
308808c999fSMiguel Ojeda     ///
309808c999fSMiguel Ojeda     /// ```console
310808c999fSMiguel Ojeda     /// error: unsupported tea property
311808c999fSMiguel Ojeda     ///  --> src/main.rs:3:26
312808c999fSMiguel Ojeda     ///   |
313808c999fSMiguel Ojeda     /// 3 | #[tea(kind = "EarlGrey", wat = "foo")]
314808c999fSMiguel Ojeda     ///   |                          ^^^
315808c999fSMiguel Ojeda     /// ```
316808c999fSMiguel Ojeda     ///
317808c999fSMiguel Ojeda     /// More usefully, the second place is if you've already parsed a value but
318808c999fSMiguel Ojeda     /// have decided not to accept the value:
319808c999fSMiguel Ojeda     ///
320808c999fSMiguel Ojeda     /// ```
321808c999fSMiguel Ojeda     /// # use syn::Attribute;
322808c999fSMiguel Ojeda     /// #
323808c999fSMiguel Ojeda     /// # fn example(attr: &Attribute) -> syn::Result<()> {
324808c999fSMiguel Ojeda     /// use syn::Expr;
325808c999fSMiguel Ojeda     ///
326808c999fSMiguel Ojeda     /// attr.parse_nested_meta(|meta| {
327808c999fSMiguel Ojeda     ///     if meta.path.is_ident("kind") {
328808c999fSMiguel Ojeda     ///         let expr: Expr = meta.value()?.parse()?;
329808c999fSMiguel Ojeda     ///         match expr {
330808c999fSMiguel Ojeda     ///             Expr::Lit(expr) => /* ... */
331808c999fSMiguel Ojeda     /// #               unimplemented!(),
332808c999fSMiguel Ojeda     ///             Expr::Path(expr) => /* ... */
333808c999fSMiguel Ojeda     /// #               unimplemented!(),
334808c999fSMiguel Ojeda     ///             Expr::Macro(expr) => /* ... */
335808c999fSMiguel Ojeda     /// #               unimplemented!(),
336808c999fSMiguel Ojeda     ///             _ => Err(meta.error("tea kind must be a string literal, path, or macro")),
337808c999fSMiguel Ojeda     ///         }
338808c999fSMiguel Ojeda     ///     } else /* as above */
339808c999fSMiguel Ojeda     /// #       { unimplemented!() }
340808c999fSMiguel Ojeda     ///
341808c999fSMiguel Ojeda     /// })?;
342808c999fSMiguel Ojeda     /// # Ok(())
343808c999fSMiguel Ojeda     /// # }
344808c999fSMiguel Ojeda     /// ```
345808c999fSMiguel Ojeda     ///
346808c999fSMiguel Ojeda     /// ```console
347808c999fSMiguel Ojeda     /// error: tea kind must be a string literal, path, or macro
348808c999fSMiguel Ojeda     ///  --> src/main.rs:3:7
349808c999fSMiguel Ojeda     ///   |
350808c999fSMiguel Ojeda     /// 3 | #[tea(kind = async { replicator.await })]
351808c999fSMiguel Ojeda     ///   |       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
352808c999fSMiguel Ojeda     /// ```
353808c999fSMiguel Ojeda     ///
354808c999fSMiguel Ojeda     /// Often you may want to use `syn::Error::new_spanned` even in this
355808c999fSMiguel Ojeda     /// situation. In the above code, that would be:
356808c999fSMiguel Ojeda     ///
357808c999fSMiguel Ojeda     /// ```
358808c999fSMiguel Ojeda     /// # use syn::{Error, Expr};
359808c999fSMiguel Ojeda     /// #
360808c999fSMiguel Ojeda     /// # fn example(expr: Expr) -> syn::Result<()> {
361808c999fSMiguel Ojeda     ///     match expr {
362808c999fSMiguel Ojeda     ///         Expr::Lit(expr) => /* ... */
363808c999fSMiguel Ojeda     /// #           unimplemented!(),
364808c999fSMiguel Ojeda     ///         Expr::Path(expr) => /* ... */
365808c999fSMiguel Ojeda     /// #           unimplemented!(),
366808c999fSMiguel Ojeda     ///         Expr::Macro(expr) => /* ... */
367808c999fSMiguel Ojeda     /// #           unimplemented!(),
368808c999fSMiguel Ojeda     ///         _ => Err(Error::new_spanned(expr, "unsupported expression type for `kind`")),
369808c999fSMiguel Ojeda     ///     }
370808c999fSMiguel Ojeda     /// # }
371808c999fSMiguel Ojeda     /// ```
372808c999fSMiguel Ojeda     ///
373808c999fSMiguel Ojeda     /// ```console
374808c999fSMiguel Ojeda     /// error: unsupported expression type for `kind`
375808c999fSMiguel Ojeda     ///  --> src/main.rs:3:14
376808c999fSMiguel Ojeda     ///   |
377808c999fSMiguel Ojeda     /// 3 | #[tea(kind = async { replicator.await })]
378808c999fSMiguel Ojeda     ///   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^
379808c999fSMiguel Ojeda     /// ```
error(&self, msg: impl Display) -> Error380808c999fSMiguel Ojeda     pub fn error(&self, msg: impl Display) -> Error {
381808c999fSMiguel Ojeda         let start_span = self.path.segments[0].ident.span();
382808c999fSMiguel Ojeda         let end_span = self.input.cursor().prev_span();
383808c999fSMiguel Ojeda         crate::error::new2(start_span, end_span, msg)
384808c999fSMiguel Ojeda     }
385808c999fSMiguel Ojeda }
386808c999fSMiguel Ojeda 
parse_nested_meta( input: ParseStream, mut logic: impl FnMut(ParseNestedMeta) -> Result<()>, ) -> Result<()>387808c999fSMiguel Ojeda pub(crate) fn parse_nested_meta(
388808c999fSMiguel Ojeda     input: ParseStream,
389808c999fSMiguel Ojeda     mut logic: impl FnMut(ParseNestedMeta) -> Result<()>,
390808c999fSMiguel Ojeda ) -> Result<()> {
391808c999fSMiguel Ojeda     loop {
392808c999fSMiguel Ojeda         let path = input.call(parse_meta_path)?;
393808c999fSMiguel Ojeda         logic(ParseNestedMeta { path, input })?;
394808c999fSMiguel Ojeda         if input.is_empty() {
395808c999fSMiguel Ojeda             return Ok(());
396808c999fSMiguel Ojeda         }
397808c999fSMiguel Ojeda         input.parse::<Token![,]>()?;
398808c999fSMiguel Ojeda         if input.is_empty() {
399808c999fSMiguel Ojeda             return Ok(());
400808c999fSMiguel Ojeda         }
401808c999fSMiguel Ojeda     }
402808c999fSMiguel Ojeda }
403808c999fSMiguel Ojeda 
404808c999fSMiguel Ojeda // Like Path::parse_mod_style, but accepts keywords in the path.
parse_meta_path(input: ParseStream) -> Result<Path>405808c999fSMiguel Ojeda fn parse_meta_path(input: ParseStream) -> Result<Path> {
406808c999fSMiguel Ojeda     Ok(Path {
407808c999fSMiguel Ojeda         leading_colon: input.parse()?,
408808c999fSMiguel Ojeda         segments: {
409808c999fSMiguel Ojeda             let mut segments = Punctuated::new();
410808c999fSMiguel Ojeda             if input.peek(Ident::peek_any) {
411808c999fSMiguel Ojeda                 let ident = Ident::parse_any(input)?;
412808c999fSMiguel Ojeda                 segments.push_value(PathSegment::from(ident));
413808c999fSMiguel Ojeda             } else if input.is_empty() {
414808c999fSMiguel Ojeda                 return Err(input.error("expected nested attribute"));
415808c999fSMiguel Ojeda             } else if input.peek(Lit) {
416808c999fSMiguel Ojeda                 return Err(input.error("unexpected literal in nested attribute, expected ident"));
417808c999fSMiguel Ojeda             } else {
418808c999fSMiguel Ojeda                 return Err(input.error("unexpected token in nested attribute, expected ident"));
419808c999fSMiguel Ojeda             }
420808c999fSMiguel Ojeda             while input.peek(Token![::]) {
421808c999fSMiguel Ojeda                 let punct = input.parse()?;
422808c999fSMiguel Ojeda                 segments.push_punct(punct);
423808c999fSMiguel Ojeda                 let ident = Ident::parse_any(input)?;
424808c999fSMiguel Ojeda                 segments.push_value(PathSegment::from(ident));
425808c999fSMiguel Ojeda             }
426808c999fSMiguel Ojeda             segments
427808c999fSMiguel Ojeda         },
428808c999fSMiguel Ojeda     })
429808c999fSMiguel Ojeda }
430