xref: /linux/rust/syn/mac.rs (revision 808c999fc9e7c366fd47da564e69d579c1dc8279)
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(docsrs, 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(docsrs, 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 {
36     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     #[cfg(all(feature = "full", any(feature = "parsing", feature = "printing")))]
45     pub(crate) fn is_brace(&self) -> bool {
46         match self {
47             MacroDelimiter::Brace(_) => true,
48             MacroDelimiter::Paren(_) | MacroDelimiter::Bracket(_) => false,
49         }
50     }
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(docsrs, doc(cfg(feature = "parsing")))]
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(docsrs, doc(cfg(feature = "parsing")))]
146     pub fn parse_body_with<F: Parser>(&self, parser: F) -> Result<F::Output> {
147         let scope = self.delimiter.span().close();
148         crate::parse::parse_scoped(parser, scope, self.tokens.clone())
149     }
150 }
151 
152 #[cfg(feature = "parsing")]
153 pub(crate) 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.delim_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(crate) mod parsing {
174     use crate::error::Result;
175     use crate::mac::{parse_delimiter, Macro};
176     use crate::parse::{Parse, ParseStream};
177     use crate::path::Path;
178 
179     #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
180     impl Parse for Macro {
181         fn parse(input: ParseStream) -> Result<Self> {
182             let tokens;
183             Ok(Macro {
184                 path: input.call(Path::parse_mod_style)?,
185                 bang_token: input.parse()?,
186                 delimiter: {
187                     let (delimiter, content) = parse_delimiter(input)?;
188                     tokens = content;
189                     delimiter
190                 },
191                 tokens,
192             })
193         }
194     }
195 }
196 
197 #[cfg(feature = "printing")]
198 mod printing {
199     use crate::mac::{Macro, MacroDelimiter};
200     use crate::path;
201     use crate::path::printing::PathStyle;
202     use crate::token;
203     use proc_macro2::{Delimiter, TokenStream};
204     use quote::ToTokens;
205 
206     impl MacroDelimiter {
207         pub(crate) fn surround(&self, tokens: &mut TokenStream, inner: TokenStream) {
208             let (delim, span) = match self {
209                 MacroDelimiter::Paren(paren) => (Delimiter::Parenthesis, paren.span),
210                 MacroDelimiter::Brace(brace) => (Delimiter::Brace, brace.span),
211                 MacroDelimiter::Bracket(bracket) => (Delimiter::Bracket, bracket.span),
212             };
213             token::printing::delim(delim, span.join(), tokens, inner);
214         }
215     }
216 
217     #[cfg_attr(docsrs, doc(cfg(feature = "printing")))]
218     impl ToTokens for Macro {
219         fn to_tokens(&self, tokens: &mut TokenStream) {
220             path::printing::print_path(tokens, &self.path, PathStyle::Mod);
221             self.bang_token.to_tokens(tokens);
222             self.delimiter.surround(tokens, self.tokens.clone());
223         }
224     }
225 }
226