1 | use crate::iter::{self, Iter, IterImpl}; |
2 | use crate::{Define, Error, Export, ExportArgs, FakeCallSite, Input, Macro, Visibility}; |
3 | use proc_macro::Delimiter::{Brace, Bracket, Parenthesis}; |
4 | use proc_macro::{Delimiter, Ident, Span, TokenStream, TokenTree}; |
5 | |
6 | pub(crate) fn parse_input(tokens: Iter) -> Result<Input, Error> { |
7 | let attrs: TokenStream = parse_attributes(tokens)?; |
8 | let vis: Option = parse_visibility(tokens); |
9 | let kw: Ident = parse_ident(tokens)?; |
10 | if kw.to_string() == "use" { |
11 | parse_export(attrs, vis, tokens).map(op:Input::Export) |
12 | } else if kw.to_string() == "fn" { |
13 | parse_define(attrs, vis, kw.span(), tokens).map(op:Input::Define) |
14 | } else { |
15 | Err(Error::new( |
16 | kw.span(), |
17 | msg:"unexpected input to #[proc_macro_hack]" , |
18 | )) |
19 | } |
20 | } |
21 | |
22 | fn parse_export(attrs: TokenStream, vis: Visibility, tokens: Iter) -> Result<Export, Error> { |
23 | let _ = parse_punct(tokens, ':' ); |
24 | let _ = parse_punct(tokens, ':' ); |
25 | let from = parse_ident(tokens)?; |
26 | parse_punct(tokens, ':' )?; |
27 | parse_punct(tokens, ':' )?; |
28 | |
29 | let mut macros = Vec::new(); |
30 | match tokens.peek() { |
31 | Some(TokenTree::Group(group)) if group.delimiter() == Brace => { |
32 | let ref mut content = iter::new(group.stream()); |
33 | loop { |
34 | macros.push(parse_macro(content)?); |
35 | if content.peek().is_none() { |
36 | break; |
37 | } |
38 | parse_punct(content, ',' )?; |
39 | if content.peek().is_none() { |
40 | break; |
41 | } |
42 | } |
43 | tokens.next().unwrap(); |
44 | } |
45 | _ => macros.push(parse_macro(tokens)?), |
46 | } |
47 | |
48 | parse_punct(tokens, ';' )?; |
49 | Ok(Export { |
50 | attrs, |
51 | vis, |
52 | from, |
53 | macros, |
54 | }) |
55 | } |
56 | |
57 | fn parse_punct(tokens: Iter, ch: char) -> Result<(), Error> { |
58 | match tokens.peek() { |
59 | Some(TokenTree::Punct(punct: &Punct)) if punct.as_char() == ch => { |
60 | tokens.next().unwrap(); |
61 | Ok(()) |
62 | } |
63 | tt: Option<&TokenTree> => Err(Error::new( |
64 | span:tt.map_or_else(Span::call_site, TokenTree::span), |
65 | msg:format!("expected ` {}`" , ch), |
66 | )), |
67 | } |
68 | } |
69 | |
70 | fn parse_define( |
71 | attrs: TokenStream, |
72 | vis: Visibility, |
73 | fn_token: Span, |
74 | tokens: Iter, |
75 | ) -> Result<Define, Error> { |
76 | if vis.is_none() { |
77 | return Err(Error::new( |
78 | span:fn_token, |
79 | msg:"functions tagged with `#[proc_macro_hack]` must be `pub`" , |
80 | )); |
81 | } |
82 | let name: Ident = parse_ident(tokens)?; |
83 | let body: TokenStream = tokens.collect(); |
84 | Ok(Define { attrs, name, body }) |
85 | } |
86 | |
87 | fn parse_macro(tokens: Iter) -> Result<Macro, Error> { |
88 | let name: Ident = parse_ident(tokens)?; |
89 | let export_as: Ident = match tokens.peek() { |
90 | Some(TokenTree::Ident(ident: &Ident)) if ident.to_string() == "as" => { |
91 | tokens.next().unwrap(); |
92 | parse_ident(tokens)? |
93 | } |
94 | _ => name.clone(), |
95 | }; |
96 | Ok(Macro { name, export_as }) |
97 | } |
98 | |
99 | fn parse_ident(tokens: Iter) -> Result<Ident, Error> { |
100 | match tokens.next() { |
101 | Some(TokenTree::Ident(ident: Ident)) => Ok(ident), |
102 | tt: Option => Err(Error::new( |
103 | span:tt.as_ref().map_or_else(Span::call_site, TokenTree::span), |
104 | msg:"expected identifier" , |
105 | )), |
106 | } |
107 | } |
108 | |
109 | fn parse_keyword(tokens: Iter, kw: &'static str) -> Result<(), Error> { |
110 | match &tokens.next() { |
111 | Some(TokenTree::Ident(ident: &Ident)) if ident.to_string() == kw => Ok(()), |
112 | tt: &Option => Err(Error::new( |
113 | span:tt.as_ref().map_or_else(Span::call_site, TokenTree::span), |
114 | msg:format!("expected ` {}`" , kw), |
115 | )), |
116 | } |
117 | } |
118 | |
119 | fn parse_int(tokens: Iter) -> Result<u16, Span> { |
120 | match tokens.next() { |
121 | Some(TokenTree::Literal(lit: Literal)) => lit.to_string().parse().map_err(|_| lit.span()), |
122 | Some(tt: TokenTree) => Err(tt.span()), |
123 | None => Err(Span::call_site()), |
124 | } |
125 | } |
126 | |
127 | fn parse_group(tokens: Iter, delimiter: Delimiter) -> Result<IterImpl, Error> { |
128 | match &tokens.next() { |
129 | Some(TokenTree::Group(group: &Group)) if group.delimiter() == delimiter => { |
130 | Ok(iter::new(tokens:group.stream())) |
131 | } |
132 | tt: &Option => Err(Error::new( |
133 | span:tt.as_ref().map_or_else(Span::call_site, TokenTree::span), |
134 | msg:"expected delimiter" , |
135 | )), |
136 | } |
137 | } |
138 | |
139 | fn parse_visibility(tokens: Iter) -> Visibility { |
140 | if let Some(TokenTree::Ident(ident: &Ident)) = tokens.peek() { |
141 | if ident.to_string() == "pub" { |
142 | match tokens.next().unwrap() { |
143 | TokenTree::Ident(vis: Ident) => return Some(vis), |
144 | _ => unreachable!(), |
145 | } |
146 | } |
147 | } |
148 | None |
149 | } |
150 | |
151 | fn parse_attributes(tokens: Iter) -> Result<TokenStream, Error> { |
152 | let mut attrs: TokenStream = TokenStream::new(); |
153 | while let Some(TokenTree::Punct(punct: &Punct)) = tokens.peek() { |
154 | if punct.as_char() != '#' { |
155 | break; |
156 | } |
157 | let span: Span = punct.span(); |
158 | attrs.extend(iter:tokens.next()); |
159 | match tokens.peek() { |
160 | Some(TokenTree::Group(group: &Group)) if group.delimiter() == Bracket => { |
161 | attrs.extend(iter:tokens.next()); |
162 | } |
163 | _ => return Err(Error::new(span, msg:"unexpected input" )), |
164 | } |
165 | } |
166 | Ok(attrs) |
167 | } |
168 | |
169 | pub(crate) fn parse_export_args(tokens: Iter) -> Result<ExportArgs, Error> { |
170 | let mut args = ExportArgs { |
171 | support_nested: false, |
172 | internal_macro_calls: 0, |
173 | fake_call_site: false, |
174 | only_hack_old_rustc: false, |
175 | }; |
176 | |
177 | while let Some(tt) = tokens.next() { |
178 | match &tt { |
179 | TokenTree::Ident(ident) if ident.to_string() == "support_nested" => { |
180 | args.support_nested = true; |
181 | } |
182 | TokenTree::Ident(ident) if ident.to_string() == "internal_macro_calls" => { |
183 | parse_punct(tokens, '=' )?; |
184 | let calls = parse_int(tokens).map_err(|span| { |
185 | Error::new(span, "expected integer value for internal_macro_calls" ) |
186 | })?; |
187 | args.internal_macro_calls = calls; |
188 | } |
189 | TokenTree::Ident(ident) if ident.to_string() == "fake_call_site" => { |
190 | args.fake_call_site = true; |
191 | } |
192 | TokenTree::Ident(ident) if ident.to_string() == "only_hack_old_rustc" => { |
193 | args.only_hack_old_rustc = true; |
194 | } |
195 | _ => { |
196 | return Err(Error::new( |
197 | tt.span(), |
198 | "expected one of: `support_nested`, `internal_macro_calls`, `fake_call_site`, `only_hack_old_rustc`" , |
199 | )); |
200 | } |
201 | } |
202 | if tokens.peek().is_none() { |
203 | break; |
204 | } |
205 | parse_punct(tokens, ',' )?; |
206 | } |
207 | |
208 | Ok(args) |
209 | } |
210 | |
211 | pub(crate) fn parse_define_args(tokens: Iter) -> Result<(), Error> { |
212 | match tokens.peek() { |
213 | None => Ok(()), |
214 | Some(token: &TokenTree) => Err(Error::new( |
215 | token.span(), |
216 | msg:"unexpected argument to proc_macro_hack macro implementation; args are only accepted on the macro declaration (the `pub use`)" , |
217 | )), |
218 | } |
219 | } |
220 | |
221 | pub(crate) fn parse_enum_hack(tokens: Iter) -> Result<TokenStream, Error> { |
222 | parse_keyword(tokens, kw:"enum" )?; |
223 | parse_ident(tokens)?; |
224 | |
225 | let ref mut braces: &mut IterImpl = parse_group(tokens, delimiter:Brace)?; |
226 | parse_ident(tokens:braces)?; |
227 | parse_punct(tokens:braces, ch:'=' )?; |
228 | |
229 | let ref mut parens: &mut IterImpl = parse_group(tokens:braces, delimiter:Parenthesis)?; |
230 | parse_ident(tokens:parens)?; |
231 | parse_punct(tokens:parens, ch:'!' )?; |
232 | |
233 | let ref mut inner: &mut IterImpl = parse_group(tokens:parens, delimiter:Brace)?; |
234 | let token_stream: TokenStream = inner.collect(); |
235 | |
236 | parse_punct(tokens:parens, ch:',' )?; |
237 | let _ = parens.next(); |
238 | parse_punct(tokens:braces, ch:'.' )?; |
239 | let _ = braces.next(); |
240 | parse_punct(tokens:braces, ch:',' )?; |
241 | |
242 | Ok(token_stream) |
243 | } |
244 | |
245 | pub(crate) fn parse_fake_call_site(tokens: Iter) -> Result<FakeCallSite, Error> { |
246 | parse_punct(tokens, ch:'#' )?; |
247 | let ref mut attr: &mut IterImpl = parse_group(tokens, delimiter:Bracket)?; |
248 | parse_keyword(tokens:attr, kw:"derive" )?; |
249 | let ref mut path: &mut IterImpl = parse_group(tokens:attr, delimiter:Parenthesis)?; |
250 | Ok(FakeCallSite { |
251 | derive: parse_ident(tokens:path)?, |
252 | rest: tokens.collect(), |
253 | }) |
254 | } |
255 | |