syn/
mac.rs

1use super::*;
2use crate::token::{Brace, Bracket, Paren};
3use proc_macro2::TokenStream;
4#[cfg(feature = "parsing")]
5use proc_macro2::{Delimiter, Group, Span, TokenTree};
6
7#[cfg(feature = "parsing")]
8use crate::parse::{Parse, ParseStream, Parser, Result};
9
10ast_struct! {
11    /// A macro invocation: `println!("{}", mac)`.
12    ///
13    /// *This type is available only if Syn is built with the `"derive"` or `"full"`
14    /// feature.*
15    #[cfg_attr(doc_cfg, doc(cfg(any(feature = "full", feature = "derive"))))]
16    pub struct Macro {
17        pub path: Path,
18        pub bang_token: Token![!],
19        pub delimiter: MacroDelimiter,
20        pub tokens: TokenStream,
21    }
22}
23
24ast_enum! {
25    /// A grouping token that surrounds a macro body: `m!(...)` or `m!{...}` or `m![...]`.
26    ///
27    /// *This type is available only if Syn is built with the `"derive"` or `"full"`
28    /// feature.*
29    #[cfg_attr(doc_cfg, doc(cfg(any(feature = "full", feature = "derive"))))]
30    pub enum MacroDelimiter {
31        Paren(Paren),
32        Brace(Brace),
33        Bracket(Bracket),
34    }
35}
36
37#[cfg(feature = "parsing")]
38fn delimiter_span_close(macro_delimiter: &MacroDelimiter) -> Span {
39    let delimiter = match macro_delimiter {
40        MacroDelimiter::Paren(_) => Delimiter::Parenthesis,
41        MacroDelimiter::Brace(_) => Delimiter::Brace,
42        MacroDelimiter::Bracket(_) => Delimiter::Bracket,
43    };
44    let mut group = Group::new(delimiter, TokenStream::new());
45    group.set_span(match macro_delimiter {
46        MacroDelimiter::Paren(token) => token.span,
47        MacroDelimiter::Brace(token) => token.span,
48        MacroDelimiter::Bracket(token) => token.span,
49    });
50    group.span_close()
51}
52
53impl Macro {
54    /// Parse the tokens within the macro invocation's delimiters into a syntax
55    /// tree.
56    ///
57    /// This is equivalent to `syn::parse2::<T>(mac.tokens)` except that it
58    /// produces a more useful span when `tokens` is empty.
59    ///
60    /// # Example
61    ///
62    /// ```
63    /// use syn::{parse_quote, Expr, ExprLit, Ident, Lit, LitStr, Macro, Token};
64    /// use syn::ext::IdentExt;
65    /// use syn::parse::{Error, Parse, ParseStream, Result};
66    /// use syn::punctuated::Punctuated;
67    ///
68    /// // The arguments expected by libcore's format_args macro, and as a
69    /// // result most other formatting and printing macros like println.
70    /// //
71    /// //     println!("{} is {number:.prec$}", "x", prec=5, number=0.01)
72    /// struct FormatArgs {
73    ///     format_string: Expr,
74    ///     positional_args: Vec<Expr>,
75    ///     named_args: Vec<(Ident, Expr)>,
76    /// }
77    ///
78    /// impl Parse for FormatArgs {
79    ///     fn parse(input: ParseStream) -> Result<Self> {
80    ///         let format_string: Expr;
81    ///         let mut positional_args = Vec::new();
82    ///         let mut named_args = Vec::new();
83    ///
84    ///         format_string = input.parse()?;
85    ///         while !input.is_empty() {
86    ///             input.parse::<Token![,]>()?;
87    ///             if input.is_empty() {
88    ///                 break;
89    ///             }
90    ///             if input.peek(Ident::peek_any) && input.peek2(Token![=]) {
91    ///                 while !input.is_empty() {
92    ///                     let name: Ident = input.call(Ident::parse_any)?;
93    ///                     input.parse::<Token![=]>()?;
94    ///                     let value: Expr = input.parse()?;
95    ///                     named_args.push((name, value));
96    ///                     if input.is_empty() {
97    ///                         break;
98    ///                     }
99    ///                     input.parse::<Token![,]>()?;
100    ///                 }
101    ///                 break;
102    ///             }
103    ///             positional_args.push(input.parse()?);
104    ///         }
105    ///
106    ///         Ok(FormatArgs {
107    ///             format_string,
108    ///             positional_args,
109    ///             named_args,
110    ///         })
111    ///     }
112    /// }
113    ///
114    /// // Extract the first argument, the format string literal, from an
115    /// // invocation of a formatting or printing macro.
116    /// fn get_format_string(m: &Macro) -> Result<LitStr> {
117    ///     let args: FormatArgs = m.parse_body()?;
118    ///     match args.format_string {
119    ///         Expr::Lit(ExprLit { lit: Lit::Str(lit), .. }) => Ok(lit),
120    ///         other => {
121    ///             // First argument was not a string literal expression.
122    ///             // Maybe something like: println!(concat!(...), ...)
123    ///             Err(Error::new_spanned(other, "format string must be a string literal"))
124    ///         }
125    ///     }
126    /// }
127    ///
128    /// fn main() {
129    ///     let invocation = parse_quote! {
130    ///         println!("{:?}", Instant::now())
131    ///     };
132    ///     let lit = get_format_string(&invocation).unwrap();
133    ///     assert_eq!(lit.value(), "{:?}");
134    /// }
135    /// ```
136    #[cfg(feature = "parsing")]
137    #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))]
138    pub fn parse_body<T: Parse>(&self) -> Result<T> {
139        self.parse_body_with(T::parse)
140    }
141
142    /// Parse the tokens within the macro invocation's delimiters using the
143    /// given parser.
144    #[cfg(feature = "parsing")]
145    #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))]
146    pub fn parse_body_with<F: Parser>(&self, parser: F) -> Result<F::Output> {
147        let scope = delimiter_span_close(&self.delimiter);
148        crate::parse::parse_scoped(parser, scope, self.tokens.clone())
149    }
150}
151
152#[cfg(feature = "parsing")]
153pub fn parse_delimiter(input: ParseStream) -> Result<(MacroDelimiter, TokenStream)> {
154    input.step(|cursor| {
155        if let Some((TokenTree::Group(g), rest)) = cursor.token_tree() {
156            let span = g.span();
157            let delimiter = match g.delimiter() {
158                Delimiter::Parenthesis => MacroDelimiter::Paren(Paren(span)),
159                Delimiter::Brace => MacroDelimiter::Brace(Brace(span)),
160                Delimiter::Bracket => MacroDelimiter::Bracket(Bracket(span)),
161                Delimiter::None => {
162                    return Err(cursor.error("expected delimiter"));
163                }
164            };
165            Ok(((delimiter, g.stream()), rest))
166        } else {
167            Err(cursor.error("expected delimiter"))
168        }
169    })
170}
171
172#[cfg(feature = "parsing")]
173pub mod parsing {
174    use super::*;
175    use crate::parse::{Parse, ParseStream, Result};
176
177    #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))]
178    impl Parse for Macro {
179        fn parse(input: ParseStream) -> Result<Self> {
180            let tokens;
181            Ok(Macro {
182                path: input.call(Path::parse_mod_style)?,
183                bang_token: input.parse()?,
184                delimiter: {
185                    let (delimiter, content) = parse_delimiter(input)?;
186                    tokens = content;
187                    delimiter
188                },
189                tokens,
190            })
191        }
192    }
193}
194
195#[cfg(feature = "printing")]
196mod printing {
197    use super::*;
198    use proc_macro2::TokenStream;
199    use quote::ToTokens;
200
201    #[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))]
202    impl ToTokens for Macro {
203        fn to_tokens(&self, tokens: &mut TokenStream) {
204            self.path.to_tokens(tokens);
205            self.bang_token.to_tokens(tokens);
206            match &self.delimiter {
207                MacroDelimiter::Paren(paren) => {
208                    paren.surround(tokens, |tokens| self.tokens.to_tokens(tokens));
209                }
210                MacroDelimiter::Brace(brace) => {
211                    brace.surround(tokens, |tokens| self.tokens.to_tokens(tokens));
212                }
213                MacroDelimiter::Bracket(bracket) => {
214                    bracket.surround(tokens, |tokens| self.tokens.to_tokens(tokens));
215                }
216            }
217        }
218    }
219}