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