syn/mac.rs
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(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
25ast_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
35impl 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
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(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")]
153pub(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")]
173pub(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")]
198mod 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}