syn/
mac.rs

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