syn/
mac.rs

1#[cfg(feature = "parsing")]
2use crate::error::Result;
3#[cfg(feature = "parsing")]
4use crate::parse::{Parse, ParseStream, Parser};
5use crate::path::Path;
6use crate::token::{Brace, Bracket, Paren};
7use proc_macro2::extra::DelimSpan;
8#[cfg(feature = "parsing")]
9use proc_macro2::Delimiter;
10use proc_macro2::TokenStream;
11#[cfg(feature = "parsing")]
12use proc_macro2::TokenTree;
13
14ast_struct! {
15    /// A macro invocation: `println!("{}", mac)`.
16    #[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))]
17    pub struct Macro {
18        pub path: Path,
19        pub bang_token: Token![!],
20        pub delimiter: MacroDelimiter,
21        pub tokens: TokenStream,
22    }
23}
24
25ast_enum! {
26    /// A grouping token that surrounds a macro body: `m!(...)` or `m!{...}` or `m![...]`.
27    #[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))]
28    pub enum MacroDelimiter {
29        Paren(Paren),
30        Brace(Brace),
31        Bracket(Bracket),
32    }
33}
34
35impl MacroDelimiter {
36    pub fn span(&self) -> &DelimSpan {
37        match self {
38            MacroDelimiter::Paren(token) => &token.span,
39            MacroDelimiter::Brace(token) => &token.span,
40            MacroDelimiter::Bracket(token) => &token.span,
41        }
42    }
43
44    #[cfg(all(feature = "full", any(feature = "parsing", feature = "printing")))]
45    pub(crate) fn is_brace(&self) -> bool {
46        match self {
47            MacroDelimiter::Brace(_) => true,
48            MacroDelimiter::Paren(_) | MacroDelimiter::Bracket(_) => false,
49        }
50    }
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(docsrs, 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(docsrs, doc(cfg(feature = "parsing")))]
146    pub fn parse_body_with<F: Parser>(&self, parser: F) -> Result<F::Output> {
147        let scope = self.delimiter.span().close();
148        crate::parse::parse_scoped(parser, scope, self.tokens.clone())
149    }
150}
151
152#[cfg(feature = "parsing")]
153pub(crate) 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.delim_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(crate) mod parsing {
174    use crate::error::Result;
175    use crate::mac::{parse_delimiter, Macro};
176    use crate::parse::{Parse, ParseStream};
177    use crate::path::Path;
178
179    #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
180    impl Parse for Macro {
181        fn parse(input: ParseStream) -> Result<Self> {
182            let tokens;
183            Ok(Macro {
184                path: input.call(Path::parse_mod_style)?,
185                bang_token: input.parse()?,
186                delimiter: {
187                    let (delimiter, content) = parse_delimiter(input)?;
188                    tokens = content;
189                    delimiter
190                },
191                tokens,
192            })
193        }
194    }
195}
196
197#[cfg(feature = "printing")]
198mod printing {
199    use crate::mac::{Macro, MacroDelimiter};
200    use crate::path;
201    use crate::path::printing::PathStyle;
202    use crate::token;
203    use proc_macro2::{Delimiter, TokenStream};
204    use quote::ToTokens;
205
206    impl MacroDelimiter {
207        pub(crate) fn surround(&self, tokens: &mut TokenStream, inner: TokenStream) {
208            let (delim, span) = match self {
209                MacroDelimiter::Paren(paren) => (Delimiter::Parenthesis, paren.span),
210                MacroDelimiter::Brace(brace) => (Delimiter::Brace, brace.span),
211                MacroDelimiter::Bracket(bracket) => (Delimiter::Bracket, bracket.span),
212            };
213            token::printing::delim(delim, span.join(), tokens, inner);
214        }
215    }
216
217    #[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
218    impl ToTokens for Macro {
219        fn to_tokens(&self, tokens: &mut TokenStream) {
220            path::printing::print_path(tokens, &self.path, PathStyle::Mod);
221            self.bang_token.to_tokens(tokens);
222            self.delimiter.surround(tokens, self.tokens.clone());
223        }
224    }
225}