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) => &token.span, 38 MacroDelimiter::Brace(token) => &token.span, 39 MacroDelimiter::Bracket(token) => &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| { 146 if let Some((TokenTree::Group(g), rest)) = cursor.token_tree() { 147 let span = g.delim_span(); 148 let delimiter = 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("expected delimiter")); 154 } 155 }; 156 Ok(((delimiter, g.stream()), rest)) 157 } else { 158 Err(cursor.error("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; 172 Ok(Macro { 173 path: input.call(Path::parse_mod_style)?, 174 bang_token: input.parse()?, 175 delimiter: { 176 let (delimiter, content) = 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