• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use super::*;
2 use crate::token::{Brace, Bracket, Paren};
3 use proc_macro2::TokenStream;
4 #[cfg(feature = "parsing")]
5 use proc_macro2::{Delimiter, Group, Span, TokenTree};
6 
7 #[cfg(feature = "parsing")]
8 use crate::parse::{Parse, ParseStream, Parser, Result};
9 
10 ast_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 
24 ast_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")]
delimiter_span_close(macro_delimiter: &MacroDelimiter) -> Span38 fn 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 
53 impl 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")))]
parse_body<T: Parse>(&self) -> Result<T>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")))]
parse_body_with<F: Parser>(&self, parser: F) -> Result<F::Output>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")]
parse_delimiter(input: ParseStream) -> Result<(MacroDelimiter, TokenStream)>153 pub 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")]
173 pub 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 {
parse(input: ParseStream) -> Result<Self>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")]
196 mod 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 {
to_tokens(&self, tokens: &mut TokenStream)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 }
220