1#[cfg(feature = "parsing")]
2use crate::error::Result;
3#[cfg(feature = "parsing")]
4use crate::parse::{Parse, ParseStream, Parser};
5use crate::path::Path;
6use crate::token::{Brace, Bracket, Paren};
7use proc_macro2::extra::DelimSpan;
8#[cfg(feature = "parsing")]
9use proc_macro2::Delimiter;
10use proc_macro2::TokenStream;
11#[cfg(feature = "parsing")]
12use proc_macro2::TokenTree;
13
14ast_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
25ast_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
35impl MacroDelimiter {
36 pub fn span(&self) -> &DelimSpan {
37 match self {
38 MacroDelimiter::Paren(token: &Paren) => &token.span,
39 MacroDelimiter::Brace(token: &Brace) => &token.span,
40 MacroDelimiter::Bracket(token: &Bracket) => &token.span,
41 }
42 }
43}
44
45impl 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")))]
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")))]
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")]
145pub(crate) fn parse_delimiter(input: ParseStream) -> Result<(MacroDelimiter, TokenStream)> {
146 input.step(|cursor: StepCursor<'_, '_>| {
147 if let Some((TokenTree::Group(g: Group), rest: Cursor<'_>)) = cursor.token_tree() {
148 let span: DelimSpan = g.delim_span();
149 let delimiter: MacroDelimiter = 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(message:"expected delimiter"));
155 }
156 };
157 Ok(((delimiter, g.stream()), rest))
158 } else {
159 Err(cursor.error(message:"expected delimiter"))
160 }
161 })
162}
163
164#[cfg(feature = "parsing")]
165pub(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 {
173 fn parse(input: ParseStream) -> Result<Self> {
174 let tokens: TokenStream;
175 Ok(Macro {
176 path: input.call(function:Path::parse_mod_style)?,
177 bang_token: input.parse()?,
178 delimiter: {
179 let (delimiter: MacroDelimiter, content: TokenStream) = parse_delimiter(input)?;
180 tokens = content;
181 delimiter
182 },
183 tokens,
184 })
185 }
186 }
187}
188
189#[cfg(feature = "printing")]
190mod 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 {
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 {
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