blob: c4b16ba8438f7b85cca86714775879909d9759e9 [file] [log] [blame]
David Tolnayf4bbbd92016-09-23 14:41:55 -07001use super::*;
David Tolnayab919512017-12-30 23:31:51 -05002use proc_macro2::TokenStream;
David Tolnaya7d69fc2018-08-26 13:30:24 -04003#[cfg(feature = "parsing")]
David Tolnayd639f612019-06-09 03:36:01 -07004use proc_macro2::{Delimiter, Span, TokenTree};
David Tolnay61037c62018-01-05 16:21:03 -08005use token::{Brace, Bracket, Paren};
David Tolnay9c76bcb2017-12-26 23:14:59 -05006
David Tolnaya7d69fc2018-08-26 13:30:24 -04007#[cfg(feature = "parsing")]
David Tolnayd639f612019-06-09 03:36:01 -07008use parse::{Parse, ParseStream, Parser, Result};
David Tolnay9c76bcb2017-12-26 23:14:59 -05009#[cfg(feature = "extra-traits")]
10use std::hash::{Hash, Hasher};
David Tolnayc43b44e2017-12-30 23:55:54 -050011#[cfg(feature = "extra-traits")]
12use tt::TokenStreamHelper;
Alex Crichtonccbb45d2017-05-23 10:58:24 -070013
Alex Crichton62a0a592017-05-22 13:58:53 -070014ast_struct! {
David Tolnay05658502018-01-07 09:56:37 -080015 /// A macro invocation: `println!("{}", mac)`.
David Tolnay461d98e2018-01-07 11:07:19 -080016 ///
17 /// *This type is available if Syn is built with the `"derive"` or `"full"`
18 /// feature.*
David Tolnay9c76bcb2017-12-26 23:14:59 -050019 pub struct Macro #manual_extra_traits {
Alex Crichton62a0a592017-05-22 13:58:53 -070020 pub path: Path,
David Tolnayf8db7ba2017-11-11 22:52:16 -080021 pub bang_token: Token![!],
David Tolnayab919512017-12-30 23:31:51 -050022 pub delimiter: MacroDelimiter,
23 pub tts: TokenStream,
24 }
25}
26
27ast_enum! {
David Tolnay05658502018-01-07 09:56:37 -080028 /// A grouping token that surrounds a macro body: `m!(...)` or `m!{...}` or `m![...]`.
David Tolnay461d98e2018-01-07 11:07:19 -080029 ///
30 /// *This type is available if Syn is built with the `"derive"` or `"full"`
31 /// feature.*
David Tolnayab919512017-12-30 23:31:51 -050032 pub enum MacroDelimiter {
David Tolnayab919512017-12-30 23:31:51 -050033 Paren(Paren),
David Tolnayab919512017-12-30 23:31:51 -050034 Brace(Brace),
David Tolnayab919512017-12-30 23:31:51 -050035 Bracket(Bracket),
Alex Crichton62a0a592017-05-22 13:58:53 -070036 }
David Tolnayf4bbbd92016-09-23 14:41:55 -070037}
38
David Tolnay9c76bcb2017-12-26 23:14:59 -050039#[cfg(feature = "extra-traits")]
40impl Eq for Macro {}
41
42#[cfg(feature = "extra-traits")]
43impl PartialEq for Macro {
44 fn eq(&self, other: &Self) -> bool {
David Tolnay65fb5662018-05-20 20:02:28 -070045 self.path == other.path
46 && self.bang_token == other.bang_token
David Tolnayab919512017-12-30 23:31:51 -050047 && self.delimiter == other.delimiter
48 && TokenStreamHelper(&self.tts) == TokenStreamHelper(&other.tts)
David Tolnay9c76bcb2017-12-26 23:14:59 -050049 }
50}
51
52#[cfg(feature = "extra-traits")]
53impl Hash for Macro {
54 fn hash<H>(&self, state: &mut H)
David Tolnay51382052017-12-27 13:46:21 -050055 where
56 H: Hasher,
David Tolnay9c76bcb2017-12-26 23:14:59 -050057 {
58 self.path.hash(state);
59 self.bang_token.hash(state);
David Tolnayab919512017-12-30 23:31:51 -050060 self.delimiter.hash(state);
61 TokenStreamHelper(&self.tts).hash(state);
David Tolnay9c76bcb2017-12-26 23:14:59 -050062 }
63}
Alex Crichtonccbb45d2017-05-23 10:58:24 -070064
David Tolnay84aa0752016-10-02 23:01:13 -070065#[cfg(feature = "parsing")]
David Tolnayd639f612019-06-09 03:36:01 -070066fn 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
74impl 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")]
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")]
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")]
David Tolnaya7d69fc2018-08-26 13:30:24 -0400174pub fn parse_delimiter(input: ParseStream) -> Result<(MacroDelimiter, TokenStream)> {
David Tolnayb50c65a2018-08-30 21:14:57 -0700175 input.step(|cursor| {
David Tolnaya7d69fc2018-08-26 13:30:24 -0400176 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")]
David Tolnay84aa0752016-10-02 23:01:13 -0700194pub mod parsing {
195 use super::*;
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700196
David Tolnaya7d69fc2018-08-26 13:30:24 -0400197 use parse::{Parse, ParseStream, Result};
David Tolnay84aa0752016-10-02 23:01:13 -0700198
David Tolnaya7d69fc2018-08-26 13:30:24 -0400199 impl Parse for Macro {
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,
Michael Layzell92639a52017-06-01 00:07:44 -0400211 })
Sergio Benitez5680d6a2017-12-29 11:20:29 -0800212 }
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700213 }
David Tolnayf4bbbd92016-09-23 14:41:55 -0700214}
David Tolnayf8cf9632016-10-02 23:15:25 -0700215
216#[cfg(feature = "printing")]
217mod printing {
218 use super::*;
Alex Crichtona74a1c82018-05-16 10:20:44 -0700219 use proc_macro2::TokenStream;
David Tolnay65fb5662018-05-20 20:02:28 -0700220 use quote::ToTokens;
David Tolnayf8cf9632016-10-02 23:15:25 -0700221
David Tolnaydecf28d2017-11-11 11:56:45 -0800222 impl ToTokens for Macro {
Alex Crichtona74a1c82018-05-16 10:20:44 -0700223 fn to_tokens(&self, tokens: &mut TokenStream) {
David Tolnayf8cf9632016-10-02 23:15:25 -0700224 self.path.to_tokens(tokens);
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700225 self.bang_token.to_tokens(tokens);
David Tolnayab919512017-12-30 23:31:51 -0500226 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 }
David Tolnayf8cf9632016-10-02 23:15:25 -0700237 }
238 }
David Tolnayf8cf9632016-10-02 23:15:25 -0700239}