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