1use super::*;
2use crate::token::{Brace, Bracket, Paren};
3use proc_macro2::TokenStream;
4#[cfg(feature = "parsing")]
5use proc_macro2::{Delimiter, Group, Span, TokenTree};
6
7#[cfg(feature = "parsing")]
8use crate::parse::{Parse, ParseStream, Parser, Result};
9
10ast_struct! {
11 /// A macro invocation: `println!("{}", mac)`.
12 ///
13 /// *This type is available only if Syn is built with the `"derive"` or `"full"`
14 /// feature.*
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 ///
27 /// *This type is available only if Syn is built with the `"derive"` or `"full"`
28 /// feature.*
29 #[cfg_attr(doc_cfg, doc(cfg(any(feature = "full", feature = "derive"))))]
30 pub enum MacroDelimiter {
31 Paren(Paren),
32 Brace(Brace),
33 Bracket(Bracket),
34 }
35}
36
37#[cfg(feature = "parsing")]
38fn delimiter_span_close(macro_delimiter: &MacroDelimiter) -> Span {
39 let delimiter: Delimiter = match macro_delimiter {
40 MacroDelimiter::Paren(_) => Delimiter::Parenthesis,
41 MacroDelimiter::Brace(_) => Delimiter::Brace,
42 MacroDelimiter::Bracket(_) => Delimiter::Bracket,
43 };
44 let mut group: Group = Group::new(delimiter, stream:TokenStream::new());
45 group.set_span(match macro_delimiter {
46 MacroDelimiter::Paren(token: &Paren) => token.span,
47 MacroDelimiter::Brace(token: &Brace) => token.span,
48 MacroDelimiter::Bracket(token: &Bracket) => token.span,
49 });
50 group.span_close()
51}
52
53impl 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(doc_cfg, 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(doc_cfg, doc(cfg(feature = "parsing")))]
146 pub fn parse_body_with<F: Parser>(&self, parser: F) -> Result<F::Output> {
147 let scope = delimiter_span_close(&self.delimiter);
148 crate::parse::parse_scoped(parser, scope, self.tokens.clone())
149 }
150}
151
152#[cfg(feature = "parsing")]
153pub fn parse_delimiter(input: ParseStream) -> Result<(MacroDelimiter, TokenStream)> {
154 input.step(|cursor: StepCursor<'_, '_>| {
155 if let Some((TokenTree::Group(g: Group), rest: Cursor<'_>)) = cursor.token_tree() {
156 let span: Span = g.span();
157 let delimiter: MacroDelimiter = 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(message:"expected delimiter"));
163 }
164 };
165 Ok(((delimiter, g.stream()), rest))
166 } else {
167 Err(cursor.error(message:"expected delimiter"))
168 }
169 })
170}
171
172#[cfg(feature = "parsing")]
173pub mod parsing {
174 use super::*;
175 use crate::parse::{Parse, ParseStream, Result};
176
177 #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))]
178 impl Parse for Macro {
179 fn parse(input: ParseStream) -> Result<Self> {
180 let tokens: TokenStream;
181 Ok(Macro {
182 path: input.call(function:Path::parse_mod_style)?,
183 bang_token: input.parse()?,
184 delimiter: {
185 let (delimiter: MacroDelimiter, content: TokenStream) = parse_delimiter(input)?;
186 tokens = content;
187 delimiter
188 },
189 tokens,
190 })
191 }
192 }
193}
194
195#[cfg(feature = "printing")]
196mod printing {
197 use super::*;
198 use proc_macro2::TokenStream;
199 use quote::ToTokens;
200
201 #[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))]
202 impl ToTokens for Macro {
203 fn to_tokens(&self, tokens: &mut TokenStream) {
204 self.path.to_tokens(tokens);
205 self.bang_token.to_tokens(tokens);
206 match &self.delimiter {
207 MacroDelimiter::Paren(paren: &Paren) => {
208 paren.surround(tokens, |tokens: &mut TokenStream| self.tokens.to_tokens(tokens));
209 }
210 MacroDelimiter::Brace(brace: &Brace) => {
211 brace.surround(tokens, |tokens: &mut TokenStream| self.tokens.to_tokens(tokens));
212 }
213 MacroDelimiter::Bracket(bracket: &Bracket) => {
214 bracket.surround(tokens, |tokens: &mut TokenStream| self.tokens.to_tokens(tokens));
215 }
216 }
217 }
218 }
219}
220