1 #[cfg(feature = "parsing")] 2 use crate::error::Result; 3 #[cfg(feature = "parsing")] 4 use crate::parse::{Parse, ParseStream, Parser}; 5 use crate::path::Path; 6 use crate::token::{Brace, Bracket, Paren}; 7 use proc_macro2::extra::DelimSpan; 8 #[cfg(feature = "parsing")] 9 use proc_macro2::Delimiter; 10 use proc_macro2::TokenStream; 11 #[cfg(feature = "parsing")] 12 use proc_macro2::TokenTree; 13 14 ast_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 25 ast_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 35 impl 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 53 impl 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")] 153 pub(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")] 173 pub(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")] 198 mod 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 } 226