1 use super::*;
2 use proc_macro2::TokenStream;
3 #[cfg(feature = "parsing")]
4 use proc_macro2::{Delimiter, Span, TokenTree};
5 use token::{Brace, Bracket, Paren};
6 
7 #[cfg(feature = "parsing")]
8 use parse::{Parse, ParseStream, Parser, Result};
9 #[cfg(feature = "extra-traits")]
10 use std::hash::{Hash, Hasher};
11 #[cfg(feature = "extra-traits")]
12 use tt::TokenStreamHelper;
13 
14 ast_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 tts: TokenStream,
24     }
25 }
26 
27 ast_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")]
40 impl Eq for Macro {}
41 
42 #[cfg(feature = "extra-traits")]
43 impl PartialEq for Macro {
eq(&self, other: &Self) -> bool44     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.tts) == TokenStreamHelper(&other.tts)
49     }
50 }
51 
52 #[cfg(feature = "extra-traits")]
53 impl Hash for Macro {
hash<H>(&self, state: &mut H) where H: Hasher,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.tts).hash(state);
62     }
63 }
64 
65 #[cfg(feature = "parsing")]
delimiter_span(delimiter: &MacroDelimiter) -> Span66 fn delimiter_span(delimiter: &MacroDelimiter) -> Span {
67     match *delimiter {
68         MacroDelimiter::Paren(ref token) => token.span,
69         MacroDelimiter::Brace(ref token) => token.span,
70         MacroDelimiter::Bracket(ref token) => token.span,
71     }
72 }
73 
74 impl 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.tts)` except that it
79     /// produces a more useful span when `tts` is empty.
80     ///
81     /// # Example
82     ///
83     /// ```edition2018
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")]
parse_body<T: Parse>(&self) -> Result<T>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")]
parse_body_with<F: Parser>(&self, parser: F) -> Result<F::Output>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         private::parse_scoped(parser, scope, self.tts.clone())
170     }
171 }
172 
173 #[cfg(feature = "parsing")]
parse_delimiter(input: ParseStream) -> Result<(MacroDelimiter, TokenStream)>174 pub 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().clone()), rest))
187         } else {
188             Err(cursor.error("expected delimiter"))
189         }
190     })
191 }
192 
193 #[cfg(feature = "parsing")]
194 pub mod parsing {
195     use super::*;
196 
197     use parse::{Parse, ParseStream, Result};
198 
199     impl Parse for Macro {
parse(input: ParseStream) -> Result<Self>200         fn parse(input: ParseStream) -> Result<Self> {
201             let tts;
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                     tts = content;
208                     delimiter
209                 },
210                 tts: tts,
211             })
212         }
213     }
214 }
215 
216 #[cfg(feature = "printing")]
217 mod printing {
218     use super::*;
219     use proc_macro2::TokenStream;
220     use quote::ToTokens;
221 
222     impl ToTokens for Macro {
to_tokens(&self, tokens: &mut TokenStream)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(ref paren) => {
228                     paren.surround(tokens, |tokens| self.tts.to_tokens(tokens));
229                 }
230                 MacroDelimiter::Brace(ref brace) => {
231                     brace.surround(tokens, |tokens| self.tts.to_tokens(tokens));
232                 }
233                 MacroDelimiter::Bracket(ref bracket) => {
234                     bracket.surround(tokens, |tokens| self.tts.to_tokens(tokens));
235                 }
236             }
237         }
238     }
239 }
240