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