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 { 38 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")))] 47 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")))] 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")))] 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")] 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 { 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 { 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 { 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