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