1 use super::*;
2 use proc_macro2::TokenStream;
3 #[cfg(feature = "parsing")]
4 use proc_macro2::{Delimiter, Span, TokenTree};
5 use token::{Brace, Bracket, Paren};
6
7 #[cfg(feature = "parsing")]
8 use parse::{Parse, ParseStream, Parser, Result};
9 #[cfg(feature = "extra-traits")]
10 use std::hash::{Hash, Hasher};
11 #[cfg(feature = "extra-traits")]
12 use tt::TokenStreamHelper;
13
14 ast_struct! {
15 /// A macro invocation: `println!("{}", mac)`.
16 ///
17 /// *This type is available if Syn is built with the `"derive"` or `"full"`
18 /// feature.*
19 pub struct Macro #manual_extra_traits {
20 pub path: Path,
21 pub bang_token: Token![!],
22 pub delimiter: MacroDelimiter,
23 pub tts: TokenStream,
24 }
25 }
26
27 ast_enum! {
28 /// A grouping token that surrounds a macro body: `m!(...)` or `m!{...}` or `m![...]`.
29 ///
30 /// *This type is available if Syn is built with the `"derive"` or `"full"`
31 /// feature.*
32 pub enum MacroDelimiter {
33 Paren(Paren),
34 Brace(Brace),
35 Bracket(Bracket),
36 }
37 }
38
39 #[cfg(feature = "extra-traits")]
40 impl Eq for Macro {}
41
42 #[cfg(feature = "extra-traits")]
43 impl PartialEq for Macro {
eq(&self, other: &Self) -> bool44 fn eq(&self, other: &Self) -> bool {
45 self.path == other.path
46 && self.bang_token == other.bang_token
47 && self.delimiter == other.delimiter
48 && TokenStreamHelper(&self.tts) == TokenStreamHelper(&other.tts)
49 }
50 }
51
52 #[cfg(feature = "extra-traits")]
53 impl Hash for Macro {
hash<H>(&self, state: &mut H) where H: Hasher,54 fn hash<H>(&self, state: &mut H)
55 where
56 H: Hasher,
57 {
58 self.path.hash(state);
59 self.bang_token.hash(state);
60 self.delimiter.hash(state);
61 TokenStreamHelper(&self.tts).hash(state);
62 }
63 }
64
65 #[cfg(feature = "parsing")]
delimiter_span(delimiter: &MacroDelimiter) -> Span66 fn delimiter_span(delimiter: &MacroDelimiter) -> Span {
67 match *delimiter {
68 MacroDelimiter::Paren(ref token) => token.span,
69 MacroDelimiter::Brace(ref token) => token.span,
70 MacroDelimiter::Bracket(ref token) => token.span,
71 }
72 }
73
74 impl Macro {
75 /// Parse the tokens within the macro invocation's delimiters into a syntax
76 /// tree.
77 ///
78 /// This is equivalent to `syn::parse2::<T>(mac.tts)` except that it
79 /// produces a more useful span when `tts` is empty.
80 ///
81 /// # Example
82 ///
83 /// ```edition2018
84 /// use syn::{parse_quote, Expr, ExprLit, Ident, Lit, LitStr, Macro, Token};
85 /// use syn::ext::IdentExt;
86 /// use syn::parse::{Error, Parse, ParseStream, Result};
87 /// use syn::punctuated::Punctuated;
88 ///
89 /// // The arguments expected by libcore's format_args macro, and as a
90 /// // result most other formatting and printing macros like println.
91 /// //
92 /// // println!("{} is {number:.prec$}", "x", prec=5, number=0.01)
93 /// struct FormatArgs {
94 /// format_string: Expr,
95 /// positional_args: Vec<Expr>,
96 /// named_args: Vec<(Ident, Expr)>,
97 /// }
98 ///
99 /// impl Parse for FormatArgs {
100 /// fn parse(input: ParseStream) -> Result<Self> {
101 /// let format_string: Expr;
102 /// let mut positional_args = Vec::new();
103 /// let mut named_args = Vec::new();
104 ///
105 /// format_string = input.parse()?;
106 /// while !input.is_empty() {
107 /// input.parse::<Token![,]>()?;
108 /// if input.is_empty() {
109 /// break;
110 /// }
111 /// if input.peek(Ident::peek_any) && input.peek2(Token![=]) {
112 /// while !input.is_empty() {
113 /// let name: Ident = input.call(Ident::parse_any)?;
114 /// input.parse::<Token![=]>()?;
115 /// let value: Expr = input.parse()?;
116 /// named_args.push((name, value));
117 /// if input.is_empty() {
118 /// break;
119 /// }
120 /// input.parse::<Token![,]>()?;
121 /// }
122 /// break;
123 /// }
124 /// positional_args.push(input.parse()?);
125 /// }
126 ///
127 /// Ok(FormatArgs {
128 /// format_string,
129 /// positional_args,
130 /// named_args,
131 /// })
132 /// }
133 /// }
134 ///
135 /// // Extract the first argument, the format string literal, from an
136 /// // invocation of a formatting or printing macro.
137 /// fn get_format_string(m: &Macro) -> Result<LitStr> {
138 /// let args: FormatArgs = m.parse_body()?;
139 /// match args.format_string {
140 /// Expr::Lit(ExprLit { lit: Lit::Str(lit), .. }) => Ok(lit),
141 /// other => {
142 /// // First argument was not a string literal expression.
143 /// // Maybe something like: println!(concat!(...), ...)
144 /// Err(Error::new_spanned(other, "format string must be a string literal"))
145 /// }
146 /// }
147 /// }
148 ///
149 /// fn main() {
150 /// let invocation = parse_quote! {
151 /// println!("{:?}", Instant::now())
152 /// };
153 /// let lit = get_format_string(&invocation).unwrap();
154 /// assert_eq!(lit.value(), "{:?}");
155 /// }
156 /// ```
157 #[cfg(feature = "parsing")]
parse_body<T: Parse>(&self) -> Result<T>158 pub fn parse_body<T: Parse>(&self) -> Result<T> {
159 self.parse_body_with(T::parse)
160 }
161
162 /// Parse the tokens within the macro invocation's delimiters using the
163 /// given parser.
164 #[cfg(feature = "parsing")]
parse_body_with<F: Parser>(&self, parser: F) -> Result<F::Output>165 pub fn parse_body_with<F: Parser>(&self, parser: F) -> Result<F::Output> {
166 // TODO: see if we can get a group.span_close() span in here as the
167 // scope, rather than the span of the whole group.
168 let scope = delimiter_span(&self.delimiter);
169 private::parse_scoped(parser, scope, self.tts.clone())
170 }
171 }
172
173 #[cfg(feature = "parsing")]
parse_delimiter(input: ParseStream) -> Result<(MacroDelimiter, TokenStream)>174 pub fn parse_delimiter(input: ParseStream) -> Result<(MacroDelimiter, TokenStream)> {
175 input.step(|cursor| {
176 if let Some((TokenTree::Group(g), rest)) = cursor.token_tree() {
177 let span = g.span();
178 let delimiter = match g.delimiter() {
179 Delimiter::Parenthesis => MacroDelimiter::Paren(Paren(span)),
180 Delimiter::Brace => MacroDelimiter::Brace(Brace(span)),
181 Delimiter::Bracket => MacroDelimiter::Bracket(Bracket(span)),
182 Delimiter::None => {
183 return Err(cursor.error("expected delimiter"));
184 }
185 };
186 Ok(((delimiter, g.stream().clone()), rest))
187 } else {
188 Err(cursor.error("expected delimiter"))
189 }
190 })
191 }
192
193 #[cfg(feature = "parsing")]
194 pub mod parsing {
195 use super::*;
196
197 use parse::{Parse, ParseStream, Result};
198
199 impl Parse for Macro {
parse(input: ParseStream) -> Result<Self>200 fn parse(input: ParseStream) -> Result<Self> {
201 let tts;
202 Ok(Macro {
203 path: input.call(Path::parse_mod_style)?,
204 bang_token: input.parse()?,
205 delimiter: {
206 let (delimiter, content) = parse_delimiter(input)?;
207 tts = content;
208 delimiter
209 },
210 tts: tts,
211 })
212 }
213 }
214 }
215
216 #[cfg(feature = "printing")]
217 mod printing {
218 use super::*;
219 use proc_macro2::TokenStream;
220 use quote::ToTokens;
221
222 impl ToTokens for Macro {
to_tokens(&self, tokens: &mut TokenStream)223 fn to_tokens(&self, tokens: &mut TokenStream) {
224 self.path.to_tokens(tokens);
225 self.bang_token.to_tokens(tokens);
226 match self.delimiter {
227 MacroDelimiter::Paren(ref paren) => {
228 paren.surround(tokens, |tokens| self.tts.to_tokens(tokens));
229 }
230 MacroDelimiter::Brace(ref brace) => {
231 brace.surround(tokens, |tokens| self.tts.to_tokens(tokens));
232 }
233 MacroDelimiter::Bracket(ref bracket) => {
234 bracket.surround(tokens, |tokens| self.tts.to_tokens(tokens));
235 }
236 }
237 }
238 }
239 }
240