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