]>
Commit | Line | Data |
---|---|---|
1a4d82fc | 1 | // Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT |
223e47cc LB |
2 | // file at the top-level directory of this distribution and at |
3 | // http://rust-lang.org/COPYRIGHT. | |
4 | // | |
5 | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | |
6 | // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | |
7 | // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | |
8 | // option. This file may not be copied, modified, or distributed | |
9 | // except according to those terms. | |
10 | ||
1a4d82fc JJ |
11 | use ast::{Block, Crate, DeclLocal, ExprMac, PatMac}; |
12 | use ast::{Local, Ident, MacInvocTT}; | |
13 | use ast::{ItemMac, MacStmtWithSemicolon, Mrk, Stmt, StmtDecl, StmtMac}; | |
14 | use ast::{StmtExpr, StmtSemi}; | |
15 | use ast::TokenTree; | |
223e47cc | 16 | use ast; |
1a4d82fc JJ |
17 | use ext::mtwt; |
18 | use ext::build::AstBuilder; | |
223e47cc | 19 | use attr; |
1a4d82fc | 20 | use attr::AttrMetaMethods; |
223e47cc | 21 | use codemap; |
d9579d0f | 22 | use codemap::{Span, Spanned, ExpnInfo, NameAndSpan, MacroBang, MacroAttribute, CompilerExpansion}; |
223e47cc | 23 | use ext::base::*; |
c34b1796 | 24 | use feature_gate::{self, Features}; |
1a4d82fc | 25 | use fold; |
223e47cc LB |
26 | use fold::*; |
27 | use parse; | |
1a4d82fc JJ |
28 | use parse::token::{fresh_mark, fresh_name, intern}; |
29 | use parse::token; | |
30 | use ptr::P; | |
31 | use util::small_vector::SmallVector; | |
970d7e83 LB |
32 | use visit; |
33 | use visit::Visitor; | |
85aaf69f | 34 | use std_inject; |
223e47cc | 35 | |
1a4d82fc | 36 | pub fn expand_expr(e: P<ast::Expr>, fld: &mut MacroExpander) -> P<ast::Expr> { |
d9579d0f AL |
37 | fn push_compiler_expansion(fld: &mut MacroExpander, span: Span, expansion_desc: &str) { |
38 | fld.cx.bt_push(ExpnInfo { | |
39 | call_site: span, | |
40 | callee: NameAndSpan { | |
41 | name: expansion_desc.to_string(), | |
42 | format: CompilerExpansion, | |
43 | allow_internal_unstable: true, | |
44 | span: None, | |
45 | }, | |
46 | }); | |
47 | } | |
48 | ||
1a4d82fc | 49 | e.and_then(|ast::Expr {id, node, span}| match node { |
223e47cc LB |
50 | // expr_mac should really be expr_ext or something; it's the |
51 | // entry-point for all syntax extensions. | |
1a4d82fc JJ |
52 | ast::ExprMac(mac) => { |
53 | let expanded_expr = match expand_mac_invoc(mac, span, | |
54 | |r| r.make_expr(), | |
55 | mark_expr, fld) { | |
56 | Some(expr) => expr, | |
57 | None => { | |
58 | return DummyResult::raw_expr(span); | |
59 | } | |
60 | }; | |
61 | ||
62 | // Keep going, outside-in. | |
63 | // | |
64 | let fully_expanded = fld.fold_expr(expanded_expr); | |
65 | fld.cx.bt_pop(); | |
66 | ||
67 | fully_expanded.map(|e| ast::Expr { | |
68 | id: ast::DUMMY_NODE_ID, | |
69 | node: e.node, | |
9346a6ac | 70 | span: fld.new_span(span), |
1a4d82fc JJ |
71 | }) |
72 | } | |
73 | ||
74 | ast::ExprWhile(cond, body, opt_ident) => { | |
75 | let cond = fld.fold_expr(cond); | |
76 | let (body, opt_ident) = expand_loop_block(body, opt_ident, fld); | |
77 | fld.cx.expr(span, ast::ExprWhile(cond, body, opt_ident)) | |
78 | } | |
79 | ||
80 | // Desugar ExprWhileLet | |
81 | // From: `[opt_ident]: while let <pat> = <expr> <body>` | |
82 | ast::ExprWhileLet(pat, expr, body, opt_ident) => { | |
83 | // to: | |
84 | // | |
85 | // [opt_ident]: loop { | |
86 | // match <expr> { | |
87 | // <pat> => <body>, | |
88 | // _ => break | |
89 | // } | |
90 | // } | |
91 | ||
d9579d0f AL |
92 | push_compiler_expansion(fld, span, "while let expansion"); |
93 | ||
1a4d82fc JJ |
94 | // `<pat> => <body>` |
95 | let pat_arm = { | |
96 | let body_expr = fld.cx.expr_block(body); | |
97 | fld.cx.arm(pat.span, vec![pat], body_expr) | |
98 | }; | |
99 | ||
100 | // `_ => break` | |
101 | let break_arm = { | |
102 | let pat_under = fld.cx.pat_wild(span); | |
103 | let break_expr = fld.cx.expr_break(span); | |
104 | fld.cx.arm(span, vec![pat_under], break_expr) | |
105 | }; | |
106 | ||
107 | // `match <expr> { ... }` | |
108 | let arms = vec![pat_arm, break_arm]; | |
109 | let match_expr = fld.cx.expr(span, | |
110 | ast::ExprMatch(expr, arms, ast::MatchSource::WhileLetDesugar)); | |
111 | ||
112 | // `[opt_ident]: loop { ... }` | |
113 | let loop_block = fld.cx.block_expr(match_expr); | |
114 | let (loop_block, opt_ident) = expand_loop_block(loop_block, opt_ident, fld); | |
d9579d0f AL |
115 | let result = fld.cx.expr(span, ast::ExprLoop(loop_block, opt_ident)); |
116 | fld.cx.bt_pop(); | |
117 | result | |
1a4d82fc JJ |
118 | } |
119 | ||
120 | // Desugar ExprIfLet | |
121 | // From: `if let <pat> = <expr> <body> [<elseopt>]` | |
122 | ast::ExprIfLet(pat, expr, body, mut elseopt) => { | |
123 | // to: | |
124 | // | |
125 | // match <expr> { | |
126 | // <pat> => <body>, | |
127 | // [_ if <elseopt_if_cond> => <elseopt_if_body>,] | |
128 | // _ => [<elseopt> | ()] | |
129 | // } | |
130 | ||
d9579d0f AL |
131 | push_compiler_expansion(fld, span, "if let expansion"); |
132 | ||
1a4d82fc JJ |
133 | // `<pat> => <body>` |
134 | let pat_arm = { | |
135 | let body_expr = fld.cx.expr_block(body); | |
136 | fld.cx.arm(pat.span, vec![pat], body_expr) | |
137 | }; | |
138 | ||
139 | // `[_ if <elseopt_if_cond> => <elseopt_if_body>,]` | |
140 | let else_if_arms = { | |
141 | let mut arms = vec![]; | |
142 | loop { | |
143 | let elseopt_continue = elseopt | |
144 | .and_then(|els| els.and_then(|els| match els.node { | |
145 | // else if | |
146 | ast::ExprIf(cond, then, elseopt) => { | |
147 | let pat_under = fld.cx.pat_wild(span); | |
148 | arms.push(ast::Arm { | |
149 | attrs: vec![], | |
150 | pats: vec![pat_under], | |
151 | guard: Some(cond), | |
152 | body: fld.cx.expr_block(then) | |
153 | }); | |
154 | elseopt.map(|elseopt| (elseopt, true)) | |
155 | } | |
156 | _ => Some((P(els), false)) | |
157 | })); | |
158 | match elseopt_continue { | |
159 | Some((e, true)) => { | |
160 | elseopt = Some(e); | |
161 | } | |
162 | Some((e, false)) => { | |
163 | elseopt = Some(e); | |
164 | break; | |
165 | } | |
223e47cc | 166 | None => { |
1a4d82fc JJ |
167 | elseopt = None; |
168 | break; | |
223e47cc | 169 | } |
1a4d82fc JJ |
170 | } |
171 | } | |
172 | arms | |
173 | }; | |
174 | ||
175 | let contains_else_clause = elseopt.is_some(); | |
176 | ||
177 | // `_ => [<elseopt> | ()]` | |
178 | let else_arm = { | |
179 | let pat_under = fld.cx.pat_wild(span); | |
180 | let else_expr = elseopt.unwrap_or_else(|| fld.cx.expr_tuple(span, vec![])); | |
181 | fld.cx.arm(span, vec![pat_under], else_expr) | |
182 | }; | |
183 | ||
184 | let mut arms = Vec::with_capacity(else_if_arms.len() + 2); | |
185 | arms.push(pat_arm); | |
62682a34 | 186 | arms.extend(else_if_arms); |
1a4d82fc JJ |
187 | arms.push(else_arm); |
188 | ||
189 | let match_expr = fld.cx.expr(span, | |
190 | ast::ExprMatch(expr, arms, | |
191 | ast::MatchSource::IfLetDesugar { | |
192 | contains_else_clause: contains_else_clause, | |
193 | })); | |
d9579d0f AL |
194 | let result = fld.fold_expr(match_expr); |
195 | fld.cx.bt_pop(); | |
196 | result | |
1a4d82fc JJ |
197 | } |
198 | ||
199 | // Desugar support for ExprIfLet in the ExprIf else position | |
200 | ast::ExprIf(cond, blk, elseopt) => { | |
201 | let elseopt = elseopt.map(|els| els.and_then(|els| match els.node { | |
202 | ast::ExprIfLet(..) => { | |
d9579d0f | 203 | push_compiler_expansion(fld, span, "if let expansion"); |
1a4d82fc JJ |
204 | // wrap the if-let expr in a block |
205 | let span = els.span; | |
206 | let blk = P(ast::Block { | |
1a4d82fc JJ |
207 | stmts: vec![], |
208 | expr: Some(P(els)), | |
209 | id: ast::DUMMY_NODE_ID, | |
210 | rules: ast::DefaultBlock, | |
211 | span: span | |
212 | }); | |
d9579d0f AL |
213 | let result = fld.cx.expr_block(blk); |
214 | fld.cx.bt_pop(); | |
215 | result | |
1a4d82fc JJ |
216 | } |
217 | _ => P(els) | |
218 | })); | |
219 | let if_expr = fld.cx.expr(span, ast::ExprIf(cond, blk, elseopt)); | |
220 | if_expr.map(|e| noop_fold_expr(e, fld)) | |
221 | } | |
222 | ||
223 | ast::ExprLoop(loop_block, opt_ident) => { | |
224 | let (loop_block, opt_ident) = expand_loop_block(loop_block, opt_ident, fld); | |
225 | fld.cx.expr(span, ast::ExprLoop(loop_block, opt_ident)) | |
226 | } | |
227 | ||
85aaf69f SL |
228 | // Desugar ExprForLoop |
229 | // From: `[opt_ident]: for <pat> in <head> <body>` | |
1a4d82fc | 230 | ast::ExprForLoop(pat, head, body, opt_ident) => { |
85aaf69f SL |
231 | // to: |
232 | // | |
233 | // { | |
234 | // let result = match ::std::iter::IntoIterator::into_iter(<head>) { | |
235 | // mut iter => { | |
236 | // [opt_ident]: loop { | |
237 | // match ::std::iter::Iterator::next(&mut iter) { | |
238 | // ::std::option::Option::Some(<pat>) => <body>, | |
239 | // ::std::option::Option::None => break | |
240 | // } | |
241 | // } | |
242 | // } | |
243 | // }; | |
244 | // result | |
245 | // } | |
246 | ||
d9579d0f AL |
247 | push_compiler_expansion(fld, span, "for loop expansion"); |
248 | ||
249 | let span = fld.new_span(span); | |
250 | ||
85aaf69f | 251 | // expand <head> |
1a4d82fc | 252 | let head = fld.fold_expr(head); |
85aaf69f SL |
253 | |
254 | // create an hygienic ident | |
255 | let iter = { | |
256 | let ident = fld.cx.ident_of("iter"); | |
257 | let new_ident = fresh_name(&ident); | |
258 | let rename = (ident, new_ident); | |
259 | let mut rename_list = vec![rename]; | |
260 | let mut rename_fld = IdentRenamer{ renames: &mut rename_list }; | |
261 | ||
262 | rename_fld.fold_ident(ident) | |
263 | }; | |
264 | ||
d9579d0f AL |
265 | let pat_span = fld.new_span(pat.span); |
266 | // `::std::option::Option::Some(<pat>) => <body>` | |
85aaf69f SL |
267 | let pat_arm = { |
268 | let body_expr = fld.cx.expr_block(body); | |
d9579d0f | 269 | let pat = noop_fold_pat(pat, fld); |
85aaf69f SL |
270 | let some_pat = fld.cx.pat_some(pat_span, pat); |
271 | ||
272 | fld.cx.arm(pat_span, vec![some_pat], body_expr) | |
273 | }; | |
274 | ||
275 | // `::std::option::Option::None => break` | |
276 | let break_arm = { | |
277 | let break_expr = fld.cx.expr_break(span); | |
278 | ||
279 | fld.cx.arm(span, vec![fld.cx.pat_none(span)], break_expr) | |
280 | }; | |
281 | ||
282 | // `match ::std::iter::Iterator::next(&mut iter) { ... }` | |
283 | let match_expr = { | |
284 | let next_path = { | |
285 | let strs = vec![ | |
286 | fld.cx.ident_of_std("core"), | |
287 | fld.cx.ident_of("iter"), | |
288 | fld.cx.ident_of("Iterator"), | |
289 | fld.cx.ident_of("next"), | |
290 | ]; | |
291 | ||
292 | fld.cx.path_global(span, strs) | |
293 | }; | |
294 | let ref_mut_iter = fld.cx.expr_mut_addr_of(span, fld.cx.expr_ident(span, iter)); | |
295 | let next_expr = | |
296 | fld.cx.expr_call(span, fld.cx.expr_path(next_path), vec![ref_mut_iter]); | |
297 | let arms = vec![pat_arm, break_arm]; | |
298 | ||
299 | fld.cx.expr(pat_span, | |
300 | ast::ExprMatch(next_expr, arms, ast::MatchSource::ForLoopDesugar)) | |
301 | }; | |
302 | ||
303 | // `[opt_ident]: loop { ... }` | |
304 | let loop_block = fld.cx.block_expr(match_expr); | |
305 | let (loop_block, opt_ident) = expand_loop_block(loop_block, opt_ident, fld); | |
306 | let loop_expr = fld.cx.expr(span, ast::ExprLoop(loop_block, opt_ident)); | |
307 | ||
308 | // `mut iter => { ... }` | |
309 | let iter_arm = { | |
310 | let iter_pat = | |
311 | fld.cx.pat_ident_binding_mode(span, iter, ast::BindByValue(ast::MutMutable)); | |
312 | fld.cx.arm(span, vec![iter_pat], loop_expr) | |
313 | }; | |
314 | ||
315 | // `match ::std::iter::IntoIterator::into_iter(<head>) { ... }` | |
316 | let into_iter_expr = { | |
317 | let into_iter_path = { | |
318 | let strs = vec![ | |
319 | fld.cx.ident_of_std("core"), | |
320 | fld.cx.ident_of("iter"), | |
321 | fld.cx.ident_of("IntoIterator"), | |
322 | fld.cx.ident_of("into_iter"), | |
323 | ]; | |
324 | ||
325 | fld.cx.path_global(span, strs) | |
326 | }; | |
327 | ||
328 | fld.cx.expr_call(span, fld.cx.expr_path(into_iter_path), vec![head]) | |
329 | }; | |
330 | ||
331 | let match_expr = fld.cx.expr_match(span, into_iter_expr, vec![iter_arm]); | |
332 | ||
333 | // `{ let result = ...; result }` | |
334 | let result_ident = token::gensym_ident("result"); | |
d9579d0f | 335 | let result = fld.cx.expr_block( |
85aaf69f SL |
336 | fld.cx.block_all( |
337 | span, | |
338 | vec![fld.cx.stmt_let(span, false, result_ident, match_expr)], | |
d9579d0f AL |
339 | Some(fld.cx.expr_ident(span, result_ident)))); |
340 | fld.cx.bt_pop(); | |
341 | result | |
1a4d82fc JJ |
342 | } |
343 | ||
85aaf69f | 344 | ast::ExprClosure(capture_clause, fn_decl, block) => { |
d9579d0f | 345 | push_compiler_expansion(fld, span, "closure expansion"); |
1a4d82fc JJ |
346 | let (rewritten_fn_decl, rewritten_block) |
347 | = expand_and_rename_fn_decl_and_block(fn_decl, block, fld); | |
348 | let new_node = ast::ExprClosure(capture_clause, | |
1a4d82fc JJ |
349 | rewritten_fn_decl, |
350 | rewritten_block); | |
d9579d0f AL |
351 | let result = P(ast::Expr{id:id, node: new_node, span: fld.new_span(span)}); |
352 | fld.cx.bt_pop(); | |
353 | result | |
1a4d82fc JJ |
354 | } |
355 | ||
356 | _ => { | |
357 | P(noop_fold_expr(ast::Expr { | |
358 | id: id, | |
359 | node: node, | |
360 | span: span | |
361 | }, fld)) | |
362 | } | |
363 | }) | |
364 | } | |
365 | ||
366 | /// Expand a (not-ident-style) macro invocation. Returns the result | |
367 | /// of expansion and the mark which must be applied to the result. | |
368 | /// Our current interface doesn't allow us to apply the mark to the | |
369 | /// result until after calling make_expr, make_items, etc. | |
370 | fn expand_mac_invoc<T, F, G>(mac: ast::Mac, span: codemap::Span, | |
371 | parse_thunk: F, | |
372 | mark_thunk: G, | |
373 | fld: &mut MacroExpander) | |
374 | -> Option<T> where | |
85aaf69f | 375 | F: for<'a> FnOnce(Box<MacResult+'a>) -> Option<T>, |
1a4d82fc JJ |
376 | G: FnOnce(T, Mrk) -> T, |
377 | { | |
378 | match mac.node { | |
379 | // it would almost certainly be cleaner to pass the whole | |
380 | // macro invocation in, rather than pulling it apart and | |
381 | // marking the tts and the ctxt separately. This also goes | |
382 | // for the other three macro invocation chunks of code | |
383 | // in this file. | |
384 | // Token-tree macros: | |
385 | MacInvocTT(pth, tts, _) => { | |
85aaf69f | 386 | if pth.segments.len() > 1 { |
1a4d82fc JJ |
387 | fld.cx.span_err(pth.span, |
388 | "expected macro name without module \ | |
389 | separators"); | |
390 | // let compilation continue | |
391 | return None; | |
392 | } | |
393 | let extname = pth.segments[0].identifier; | |
394 | let extnamestr = token::get_ident(extname); | |
395 | match fld.cx.syntax_env.find(&extname.name) { | |
396 | None => { | |
397 | fld.cx.span_err( | |
398 | pth.span, | |
399 | &format!("macro undefined: '{}!'", | |
c34b1796 | 400 | &extnamestr)); |
1a4d82fc JJ |
401 | |
402 | // let compilation continue | |
403 | None | |
404 | } | |
405 | Some(rc) => match *rc { | |
c34b1796 | 406 | NormalTT(ref expandfun, exp_span, allow_internal_unstable) => { |
1a4d82fc JJ |
407 | fld.cx.bt_push(ExpnInfo { |
408 | call_site: span, | |
223e47cc | 409 | callee: NameAndSpan { |
85aaf69f | 410 | name: extnamestr.to_string(), |
1a4d82fc JJ |
411 | format: MacroBang, |
412 | span: exp_span, | |
c34b1796 | 413 | allow_internal_unstable: allow_internal_unstable, |
223e47cc | 414 | }, |
1a4d82fc JJ |
415 | }); |
416 | let fm = fresh_mark(); | |
85aaf69f | 417 | let marked_before = mark_tts(&tts[..], fm); |
1a4d82fc JJ |
418 | |
419 | // The span that we pass to the expanders we want to | |
420 | // be the root of the call stack. That's the most | |
421 | // relevant span and it's the actual invocation of | |
422 | // the macro. | |
423 | let mac_span = fld.cx.original_span(); | |
424 | ||
425 | let opt_parsed = { | |
426 | let expanded = expandfun.expand(fld.cx, | |
427 | mac_span, | |
85aaf69f | 428 | &marked_before[..]); |
1a4d82fc JJ |
429 | parse_thunk(expanded) |
430 | }; | |
431 | let parsed = match opt_parsed { | |
432 | Some(e) => e, | |
433 | None => { | |
434 | fld.cx.span_err( | |
435 | pth.span, | |
436 | &format!("non-expression macro in expression position: {}", | |
85aaf69f | 437 | &extnamestr[..] |
c34b1796 | 438 | )); |
1a4d82fc JJ |
439 | return None; |
440 | } | |
441 | }; | |
442 | Some(mark_thunk(parsed,fm)) | |
443 | } | |
444 | _ => { | |
445 | fld.cx.span_err( | |
446 | pth.span, | |
447 | &format!("'{}' is not a tt-style macro", | |
c34b1796 | 448 | &extnamestr)); |
1a4d82fc | 449 | None |
223e47cc LB |
450 | } |
451 | } | |
452 | } | |
453 | } | |
223e47cc LB |
454 | } |
455 | } | |
456 | ||
1a4d82fc JJ |
457 | /// Rename loop label and expand its loop body |
458 | /// | |
459 | /// The renaming procedure for loop is different in the sense that the loop | |
460 | /// body is in a block enclosed by loop head so the renaming of loop label | |
461 | /// must be propagated to the enclosed context. | |
462 | fn expand_loop_block(loop_block: P<Block>, | |
463 | opt_ident: Option<Ident>, | |
464 | fld: &mut MacroExpander) -> (P<Block>, Option<Ident>) { | |
465 | match opt_ident { | |
466 | Some(label) => { | |
467 | let new_label = fresh_name(&label); | |
468 | let rename = (label, new_label); | |
469 | ||
470 | // The rename *must not* be added to the pending list of current | |
471 | // syntax context otherwise an unrelated `break` or `continue` in | |
472 | // the same context will pick that up in the deferred renaming pass | |
473 | // and be renamed incorrectly. | |
474 | let mut rename_list = vec!(rename); | |
475 | let mut rename_fld = IdentRenamer{renames: &mut rename_list}; | |
476 | let renamed_ident = rename_fld.fold_ident(label); | |
477 | ||
478 | // The rename *must* be added to the enclosed syntax context for | |
479 | // `break` or `continue` to pick up because by definition they are | |
480 | // in a block enclosed by loop head. | |
481 | fld.cx.syntax_env.push_frame(); | |
482 | fld.cx.syntax_env.info().pending_renames.push(rename); | |
483 | let expanded_block = expand_block_elts(loop_block, fld); | |
484 | fld.cx.syntax_env.pop_frame(); | |
485 | ||
486 | (expanded_block, Some(renamed_ident)) | |
223e47cc | 487 | } |
1a4d82fc JJ |
488 | None => (fld.fold_block(loop_block), opt_ident) |
489 | } | |
223e47cc LB |
490 | } |
491 | ||
1a4d82fc JJ |
492 | // eval $e with a new exts frame. |
493 | // must be a macro so that $e isn't evaluated too early. | |
494 | macro_rules! with_exts_frame { | |
970d7e83 | 495 | ($extsboxexpr:expr,$macros_escape:expr,$e:expr) => |
1a4d82fc JJ |
496 | ({$extsboxexpr.push_frame(); |
497 | $extsboxexpr.info().macros_escape = $macros_escape; | |
223e47cc | 498 | let result = $e; |
1a4d82fc | 499 | $extsboxexpr.pop_frame(); |
223e47cc LB |
500 | result |
501 | }) | |
1a4d82fc | 502 | } |
970d7e83 | 503 | |
223e47cc | 504 | // When we enter a module, record it, for the sake of `module!` |
1a4d82fc JJ |
505 | pub fn expand_item(it: P<ast::Item>, fld: &mut MacroExpander) |
506 | -> SmallVector<P<ast::Item>> { | |
507 | let it = expand_item_modifiers(it, fld); | |
508 | ||
85aaf69f SL |
509 | expand_annotatable(Annotatable::Item(it), fld) |
510 | .into_iter().map(|i| i.expect_item()).collect() | |
1a4d82fc JJ |
511 | } |
512 | ||
1a4d82fc JJ |
513 | /// Expand item_underscore |
514 | fn expand_item_underscore(item: ast::Item_, fld: &mut MacroExpander) -> ast::Item_ { | |
515 | match item { | |
62682a34 | 516 | ast::ItemFn(decl, unsafety, constness, abi, generics, body) => { |
1a4d82fc JJ |
517 | let (rewritten_fn_decl, rewritten_body) |
518 | = expand_and_rename_fn_decl_and_block(decl, body, fld); | |
519 | let expanded_generics = fold::noop_fold_generics(generics,fld); | |
62682a34 SL |
520 | ast::ItemFn(rewritten_fn_decl, unsafety, constness, abi, |
521 | expanded_generics, rewritten_body) | |
223e47cc | 522 | } |
1a4d82fc JJ |
523 | _ => noop_fold_item_underscore(item, fld) |
524 | } | |
223e47cc LB |
525 | } |
526 | ||
1a4d82fc JJ |
527 | // does this attribute list contain "macro_use" ? |
528 | fn contains_macro_use(fld: &mut MacroExpander, attrs: &[ast::Attribute]) -> bool { | |
85aaf69f | 529 | for attr in attrs { |
1a4d82fc JJ |
530 | let mut is_use = attr.check_name("macro_use"); |
531 | if attr.check_name("macro_escape") { | |
532 | fld.cx.span_warn(attr.span, "macro_escape is a deprecated synonym for macro_use"); | |
533 | is_use = true; | |
534 | if let ast::AttrInner = attr.node.style { | |
c34b1796 | 535 | fld.cx.fileline_help(attr.span, "consider an outer attribute, \ |
1a4d82fc JJ |
536 | #[macro_use] mod ..."); |
537 | } | |
538 | }; | |
970d7e83 | 539 | |
1a4d82fc JJ |
540 | if is_use { |
541 | match attr.node.value.node { | |
542 | ast::MetaWord(..) => (), | |
543 | _ => fld.cx.span_err(attr.span, "arguments to macro_use are not allowed here"), | |
223e47cc | 544 | } |
1a4d82fc JJ |
545 | return true; |
546 | } | |
547 | } | |
548 | false | |
549 | } | |
550 | ||
551 | // Support for item-position macro invocations, exactly the same | |
552 | // logic as for expression-position macro invocations. | |
553 | pub fn expand_item_mac(it: P<ast::Item>, | |
554 | fld: &mut MacroExpander) -> SmallVector<P<ast::Item>> { | |
555 | let (extname, path_span, tts) = match it.node { | |
556 | ItemMac(codemap::Spanned { | |
557 | node: MacInvocTT(ref pth, ref tts, _), | |
558 | .. | |
559 | }) => { | |
560 | (pth.segments[0].identifier, pth.span, (*tts).clone()) | |
223e47cc | 561 | } |
1a4d82fc | 562 | _ => fld.cx.span_bug(it.span, "invalid item macro invocation") |
223e47cc | 563 | }; |
223e47cc | 564 | |
1a4d82fc JJ |
565 | let extnamestr = token::get_ident(extname); |
566 | let fm = fresh_mark(); | |
567 | let items = { | |
568 | let expanded = match fld.cx.syntax_env.find(&extname.name) { | |
569 | None => { | |
570 | fld.cx.span_err(path_span, | |
571 | &format!("macro undefined: '{}!'", | |
c34b1796 | 572 | extnamestr)); |
1a4d82fc JJ |
573 | // let compilation continue |
574 | return SmallVector::zero(); | |
575 | } | |
576 | ||
577 | Some(rc) => match *rc { | |
c34b1796 | 578 | NormalTT(ref expander, span, allow_internal_unstable) => { |
1a4d82fc JJ |
579 | if it.ident.name != parse::token::special_idents::invalid.name { |
580 | fld.cx | |
581 | .span_err(path_span, | |
c34b1796 AL |
582 | &format!("macro {}! expects no ident argument, given '{}'", |
583 | extnamestr, | |
584 | token::get_ident(it.ident))); | |
1a4d82fc JJ |
585 | return SmallVector::zero(); |
586 | } | |
587 | fld.cx.bt_push(ExpnInfo { | |
588 | call_site: it.span, | |
589 | callee: NameAndSpan { | |
85aaf69f | 590 | name: extnamestr.to_string(), |
1a4d82fc | 591 | format: MacroBang, |
c34b1796 AL |
592 | span: span, |
593 | allow_internal_unstable: allow_internal_unstable, | |
1a4d82fc JJ |
594 | } |
595 | }); | |
596 | // mark before expansion: | |
85aaf69f SL |
597 | let marked_before = mark_tts(&tts[..], fm); |
598 | expander.expand(fld.cx, it.span, &marked_before[..]) | |
970d7e83 | 599 | } |
c34b1796 | 600 | IdentTT(ref expander, span, allow_internal_unstable) => { |
1a4d82fc JJ |
601 | if it.ident.name == parse::token::special_idents::invalid.name { |
602 | fld.cx.span_err(path_span, | |
603 | &format!("macro {}! expects an ident argument", | |
c34b1796 | 604 | &extnamestr)); |
1a4d82fc JJ |
605 | return SmallVector::zero(); |
606 | } | |
607 | fld.cx.bt_push(ExpnInfo { | |
608 | call_site: it.span, | |
609 | callee: NameAndSpan { | |
85aaf69f | 610 | name: extnamestr.to_string(), |
1a4d82fc | 611 | format: MacroBang, |
c34b1796 AL |
612 | span: span, |
613 | allow_internal_unstable: allow_internal_unstable, | |
1a4d82fc JJ |
614 | } |
615 | }); | |
616 | // mark before expansion: | |
85aaf69f | 617 | let marked_tts = mark_tts(&tts[..], fm); |
1a4d82fc | 618 | expander.expand(fld.cx, it.span, it.ident, marked_tts) |
970d7e83 | 619 | } |
1a4d82fc JJ |
620 | MacroRulesTT => { |
621 | if it.ident.name == parse::token::special_idents::invalid.name { | |
622 | fld.cx.span_err(path_span, | |
623 | &format!("macro_rules! expects an ident argument") | |
c34b1796 | 624 | ); |
1a4d82fc JJ |
625 | return SmallVector::zero(); |
626 | } | |
c34b1796 | 627 | |
1a4d82fc JJ |
628 | fld.cx.bt_push(ExpnInfo { |
629 | call_site: it.span, | |
630 | callee: NameAndSpan { | |
85aaf69f | 631 | name: extnamestr.to_string(), |
1a4d82fc JJ |
632 | format: MacroBang, |
633 | span: None, | |
c34b1796 AL |
634 | // `macro_rules!` doesn't directly allow |
635 | // unstable (this is orthogonal to whether | |
636 | // the macro it creates allows it) | |
637 | allow_internal_unstable: false, | |
1a4d82fc JJ |
638 | } |
639 | }); | |
640 | // DON'T mark before expansion. | |
641 | ||
c34b1796 AL |
642 | let allow_internal_unstable = attr::contains_name(&it.attrs, |
643 | "allow_internal_unstable"); | |
644 | ||
645 | // ensure any #[allow_internal_unstable]s are | |
646 | // detected (including nested macro definitions | |
647 | // etc.) | |
648 | if allow_internal_unstable && !fld.cx.ecfg.enable_allow_internal_unstable() { | |
649 | feature_gate::emit_feature_err( | |
650 | &fld.cx.parse_sess.span_diagnostic, | |
651 | "allow_internal_unstable", | |
652 | it.span, | |
653 | feature_gate::EXPLAIN_ALLOW_INTERNAL_UNSTABLE) | |
654 | } | |
655 | ||
1a4d82fc JJ |
656 | let def = ast::MacroDef { |
657 | ident: it.ident, | |
658 | attrs: it.attrs.clone(), | |
659 | id: ast::DUMMY_NODE_ID, | |
660 | span: it.span, | |
661 | imported_from: None, | |
85aaf69f | 662 | export: attr::contains_name(&it.attrs, "macro_export"), |
1a4d82fc | 663 | use_locally: true, |
c34b1796 | 664 | allow_internal_unstable: allow_internal_unstable, |
1a4d82fc JJ |
665 | body: tts, |
666 | }; | |
667 | fld.cx.insert_macro(def); | |
223e47cc | 668 | |
1a4d82fc JJ |
669 | // macro_rules! has a side effect but expands to nothing. |
670 | fld.cx.bt_pop(); | |
671 | return SmallVector::zero(); | |
672 | } | |
673 | _ => { | |
674 | fld.cx.span_err(it.span, | |
675 | &format!("{}! is not legal in item position", | |
c34b1796 | 676 | &extnamestr)); |
1a4d82fc JJ |
677 | return SmallVector::zero(); |
678 | } | |
679 | } | |
680 | }; | |
681 | ||
682 | expanded.make_items() | |
683 | }; | |
684 | ||
685 | let items = match items { | |
686 | Some(items) => { | |
687 | items.into_iter() | |
688 | .map(|i| mark_item(i, fm)) | |
689 | .flat_map(|i| fld.fold_item(i).into_iter()) | |
690 | .collect() | |
691 | } | |
692 | None => { | |
693 | fld.cx.span_err(path_span, | |
694 | &format!("non-item macro in item position: {}", | |
c34b1796 | 695 | &extnamestr)); |
1a4d82fc | 696 | return SmallVector::zero(); |
223e47cc | 697 | } |
1a4d82fc | 698 | }; |
223e47cc | 699 | |
1a4d82fc JJ |
700 | fld.cx.bt_pop(); |
701 | items | |
702 | } | |
703 | ||
704 | /// Expand a stmt | |
9346a6ac AL |
705 | fn expand_stmt(stmt: P<Stmt>, fld: &mut MacroExpander) -> SmallVector<P<Stmt>> { |
706 | let stmt = stmt.and_then(|stmt| stmt); | |
707 | let (mac, style) = match stmt.node { | |
1a4d82fc | 708 | StmtMac(mac, style) => (mac, style), |
9346a6ac | 709 | _ => return expand_non_macro_stmt(stmt, fld) |
1a4d82fc | 710 | }; |
9346a6ac AL |
711 | |
712 | let maybe_new_items = | |
713 | expand_mac_invoc(mac.and_then(|m| m), stmt.span, | |
714 | |r| r.make_stmts(), | |
715 | |stmts, mark| stmts.move_map(|m| mark_stmt(m, mark)), | |
716 | fld); | |
717 | ||
718 | let mut fully_expanded = match maybe_new_items { | |
719 | Some(stmts) => { | |
720 | // Keep going, outside-in. | |
721 | let new_items = stmts.into_iter().flat_map(|s| { | |
722 | fld.fold_stmt(s).into_iter() | |
723 | }).collect(); | |
724 | fld.cx.bt_pop(); | |
725 | new_items | |
223e47cc | 726 | } |
9346a6ac | 727 | None => SmallVector::zero() |
223e47cc LB |
728 | }; |
729 | ||
9346a6ac AL |
730 | // If this is a macro invocation with a semicolon, then apply that |
731 | // semicolon to the final statement produced by expansion. | |
1a4d82fc | 732 | if style == MacStmtWithSemicolon { |
9346a6ac AL |
733 | if let Some(stmt) = fully_expanded.pop() { |
734 | let new_stmt = stmt.map(|Spanned {node, span}| { | |
735 | Spanned { | |
736 | node: match node { | |
737 | StmtExpr(e, stmt_id) => StmtSemi(e, stmt_id), | |
738 | _ => node /* might already have a semi */ | |
739 | }, | |
740 | span: span | |
741 | } | |
742 | }); | |
743 | fully_expanded.push(new_stmt); | |
744 | } | |
1a4d82fc | 745 | } |
9346a6ac AL |
746 | |
747 | fully_expanded | |
1a4d82fc JJ |
748 | } |
749 | ||
750 | // expand a non-macro stmt. this is essentially the fallthrough for | |
751 | // expand_stmt, above. | |
752 | fn expand_non_macro_stmt(Spanned {node, span: stmt_span}: Stmt, fld: &mut MacroExpander) | |
753 | -> SmallVector<P<Stmt>> { | |
754 | // is it a let? | |
755 | match node { | |
756 | StmtDecl(decl, node_id) => decl.and_then(|Spanned {node: decl, span}| match decl { | |
757 | DeclLocal(local) => { | |
758 | // take it apart: | |
759 | let rewritten_local = local.map(|Local {id, pat, ty, init, source, span}| { | |
760 | // expand the ty since TyFixedLengthVec contains an Expr | |
761 | // and thus may have a macro use | |
762 | let expanded_ty = ty.map(|t| fld.fold_ty(t)); | |
763 | // expand the pat (it might contain macro uses): | |
764 | let expanded_pat = fld.fold_pat(pat); | |
765 | // find the PatIdents in the pattern: | |
766 | // oh dear heaven... this is going to include the enum | |
767 | // names, as well... but that should be okay, as long as | |
768 | // the new names are gensyms for the old ones. | |
769 | // generate fresh names, push them to a new pending list | |
770 | let idents = pattern_bindings(&*expanded_pat); | |
771 | let mut new_pending_renames = | |
772 | idents.iter().map(|ident| (*ident, fresh_name(ident))).collect(); | |
773 | // rewrite the pattern using the new names (the old | |
774 | // ones have already been applied): | |
775 | let rewritten_pat = { | |
776 | // nested binding to allow borrow to expire: | |
777 | let mut rename_fld = IdentRenamer{renames: &mut new_pending_renames}; | |
778 | rename_fld.fold_pat(expanded_pat) | |
779 | }; | |
780 | // add them to the existing pending renames: | |
781 | fld.cx.syntax_env.info().pending_renames | |
62682a34 | 782 | .extend(new_pending_renames); |
1a4d82fc JJ |
783 | Local { |
784 | id: id, | |
785 | ty: expanded_ty, | |
786 | pat: rewritten_pat, | |
787 | // also, don't forget to expand the init: | |
788 | init: init.map(|e| fld.fold_expr(e)), | |
789 | source: source, | |
790 | span: span | |
970d7e83 | 791 | } |
1a4d82fc JJ |
792 | }); |
793 | SmallVector::one(P(Spanned { | |
794 | node: StmtDecl(P(Spanned { | |
795 | node: DeclLocal(rewritten_local), | |
796 | span: span | |
797 | }), | |
798 | node_id), | |
799 | span: stmt_span | |
800 | })) | |
970d7e83 | 801 | } |
1a4d82fc JJ |
802 | _ => { |
803 | noop_fold_stmt(Spanned { | |
804 | node: StmtDecl(P(Spanned { | |
805 | node: decl, | |
806 | span: span | |
807 | }), | |
808 | node_id), | |
809 | span: stmt_span | |
810 | }, fld) | |
811 | } | |
812 | }), | |
813 | _ => { | |
814 | noop_fold_stmt(Spanned { | |
815 | node: node, | |
816 | span: stmt_span | |
817 | }, fld) | |
818 | } | |
970d7e83 LB |
819 | } |
820 | } | |
223e47cc | 821 | |
1a4d82fc JJ |
822 | // expand the arm of a 'match', renaming for macro hygiene |
823 | fn expand_arm(arm: ast::Arm, fld: &mut MacroExpander) -> ast::Arm { | |
824 | // expand pats... they might contain macro uses: | |
825 | let expanded_pats = arm.pats.move_map(|pat| fld.fold_pat(pat)); | |
9346a6ac | 826 | if expanded_pats.is_empty() { |
1a4d82fc JJ |
827 | panic!("encountered match arm with 0 patterns"); |
828 | } | |
829 | // all of the pats must have the same set of bindings, so use the | |
830 | // first one to extract them and generate new names: | |
831 | let idents = pattern_bindings(&*expanded_pats[0]); | |
832 | let new_renames = idents.into_iter().map(|id| (id, fresh_name(&id))).collect(); | |
833 | // apply the renaming, but only to the PatIdents: | |
834 | let mut rename_pats_fld = PatIdentRenamer{renames:&new_renames}; | |
835 | let rewritten_pats = expanded_pats.move_map(|pat| rename_pats_fld.fold_pat(pat)); | |
836 | // apply renaming and then expansion to the guard and the body: | |
837 | let mut rename_fld = IdentRenamer{renames:&new_renames}; | |
838 | let rewritten_guard = | |
839 | arm.guard.map(|g| fld.fold_expr(rename_fld.fold_expr(g))); | |
840 | let rewritten_body = fld.fold_expr(rename_fld.fold_expr(arm.body)); | |
841 | ast::Arm { | |
85aaf69f | 842 | attrs: fold::fold_attrs(arm.attrs, fld), |
1a4d82fc JJ |
843 | pats: rewritten_pats, |
844 | guard: rewritten_guard, | |
845 | body: rewritten_body, | |
846 | } | |
970d7e83 LB |
847 | } |
848 | ||
1a4d82fc JJ |
849 | /// A visitor that extracts the PatIdent (binding) paths |
850 | /// from a given thingy and puts them in a mutable | |
851 | /// array | |
852 | #[derive(Clone)] | |
853 | struct PatIdentFinder { | |
854 | ident_accumulator: Vec<ast::Ident> | |
855 | } | |
970d7e83 | 856 | |
1a4d82fc JJ |
857 | impl<'v> Visitor<'v> for PatIdentFinder { |
858 | fn visit_pat(&mut self, pattern: &ast::Pat) { | |
859 | match *pattern { | |
860 | ast::Pat { id: _, node: ast::PatIdent(_, ref path1, ref inner), span: _ } => { | |
861 | self.ident_accumulator.push(path1.node); | |
862 | // visit optional subpattern of PatIdent: | |
85aaf69f | 863 | if let Some(ref subpat) = *inner { |
1a4d82fc JJ |
864 | self.visit_pat(&**subpat) |
865 | } | |
866 | } | |
867 | // use the default traversal for non-PatIdents | |
868 | _ => visit::walk_pat(self, pattern) | |
869 | } | |
970d7e83 LB |
870 | } |
871 | } | |
872 | ||
1a4d82fc JJ |
873 | /// find the PatIdent paths in a pattern |
874 | fn pattern_bindings(pat: &ast::Pat) -> Vec<ast::Ident> { | |
875 | let mut name_finder = PatIdentFinder{ident_accumulator:Vec::new()}; | |
876 | name_finder.visit_pat(pat); | |
877 | name_finder.ident_accumulator | |
970d7e83 LB |
878 | } |
879 | ||
1a4d82fc JJ |
880 | /// find the PatIdent paths in a |
881 | fn fn_decl_arg_bindings(fn_decl: &ast::FnDecl) -> Vec<ast::Ident> { | |
882 | let mut pat_idents = PatIdentFinder{ident_accumulator:Vec::new()}; | |
85aaf69f | 883 | for arg in &fn_decl.inputs { |
1a4d82fc | 884 | pat_idents.visit_pat(&*arg.pat); |
223e47cc | 885 | } |
1a4d82fc | 886 | pat_idents.ident_accumulator |
223e47cc LB |
887 | } |
888 | ||
1a4d82fc JJ |
889 | // expand a block. pushes a new exts_frame, then calls expand_block_elts |
890 | pub fn expand_block(blk: P<Block>, fld: &mut MacroExpander) -> P<Block> { | |
891 | // see note below about treatment of exts table | |
892 | with_exts_frame!(fld.cx.syntax_env,false, | |
893 | expand_block_elts(blk, fld)) | |
894 | } | |
970d7e83 | 895 | |
1a4d82fc JJ |
896 | // expand the elements of a block. |
897 | pub fn expand_block_elts(b: P<Block>, fld: &mut MacroExpander) -> P<Block> { | |
85aaf69f | 898 | b.map(|Block {id, stmts, expr, rules, span}| { |
1a4d82fc JJ |
899 | let new_stmts = stmts.into_iter().flat_map(|x| { |
900 | // perform all pending renames | |
901 | let renamed_stmt = { | |
902 | let pending_renames = &mut fld.cx.syntax_env.info().pending_renames; | |
903 | let mut rename_fld = IdentRenamer{renames:pending_renames}; | |
904 | rename_fld.fold_stmt(x).expect_one("rename_fold didn't return one value") | |
905 | }; | |
906 | // expand macros in the statement | |
907 | fld.fold_stmt(renamed_stmt).into_iter() | |
908 | }).collect(); | |
909 | let new_expr = expr.map(|x| { | |
910 | let expr = { | |
911 | let pending_renames = &mut fld.cx.syntax_env.info().pending_renames; | |
912 | let mut rename_fld = IdentRenamer{renames:pending_renames}; | |
913 | rename_fld.fold_expr(x) | |
914 | }; | |
915 | fld.fold_expr(expr) | |
916 | }); | |
917 | Block { | |
918 | id: fld.new_id(id), | |
1a4d82fc JJ |
919 | stmts: new_stmts, |
920 | expr: new_expr, | |
921 | rules: rules, | |
922 | span: span | |
223e47cc | 923 | } |
1a4d82fc JJ |
924 | }) |
925 | } | |
223e47cc | 926 | |
1a4d82fc JJ |
927 | fn expand_pat(p: P<ast::Pat>, fld: &mut MacroExpander) -> P<ast::Pat> { |
928 | match p.node { | |
929 | PatMac(_) => {} | |
930 | _ => return noop_fold_pat(p, fld) | |
931 | } | |
932 | p.map(|ast::Pat {node, span, ..}| { | |
933 | let (pth, tts) = match node { | |
934 | PatMac(mac) => match mac.node { | |
935 | MacInvocTT(pth, tts, _) => { | |
936 | (pth, tts) | |
970d7e83 | 937 | } |
1a4d82fc JJ |
938 | }, |
939 | _ => unreachable!() | |
940 | }; | |
85aaf69f | 941 | if pth.segments.len() > 1 { |
1a4d82fc JJ |
942 | fld.cx.span_err(pth.span, "expected macro name without module separators"); |
943 | return DummyResult::raw_pat(span); | |
944 | } | |
945 | let extname = pth.segments[0].identifier; | |
946 | let extnamestr = token::get_ident(extname); | |
947 | let marked_after = match fld.cx.syntax_env.find(&extname.name) { | |
948 | None => { | |
949 | fld.cx.span_err(pth.span, | |
950 | &format!("macro undefined: '{}!'", | |
c34b1796 | 951 | extnamestr)); |
1a4d82fc JJ |
952 | // let compilation continue |
953 | return DummyResult::raw_pat(span); | |
970d7e83 | 954 | } |
1a4d82fc JJ |
955 | |
956 | Some(rc) => match *rc { | |
c34b1796 | 957 | NormalTT(ref expander, tt_span, allow_internal_unstable) => { |
1a4d82fc JJ |
958 | fld.cx.bt_push(ExpnInfo { |
959 | call_site: span, | |
960 | callee: NameAndSpan { | |
85aaf69f | 961 | name: extnamestr.to_string(), |
1a4d82fc | 962 | format: MacroBang, |
c34b1796 AL |
963 | span: tt_span, |
964 | allow_internal_unstable: allow_internal_unstable, | |
1a4d82fc JJ |
965 | } |
966 | }); | |
967 | ||
968 | let fm = fresh_mark(); | |
85aaf69f | 969 | let marked_before = mark_tts(&tts[..], fm); |
1a4d82fc | 970 | let mac_span = fld.cx.original_span(); |
bd371182 AL |
971 | let pat = expander.expand(fld.cx, |
972 | mac_span, | |
973 | &marked_before[..]).make_pat(); | |
974 | let expanded = match pat { | |
1a4d82fc JJ |
975 | Some(e) => e, |
976 | None => { | |
977 | fld.cx.span_err( | |
978 | pth.span, | |
979 | &format!( | |
980 | "non-pattern macro in pattern position: {}", | |
85aaf69f | 981 | &extnamestr |
c34b1796 | 982 | ) |
1a4d82fc JJ |
983 | ); |
984 | return DummyResult::raw_pat(span); | |
985 | } | |
986 | }; | |
987 | ||
988 | // mark after: | |
989 | mark_pat(expanded,fm) | |
970d7e83 | 990 | } |
1a4d82fc JJ |
991 | _ => { |
992 | fld.cx.span_err(span, | |
993 | &format!("{}! is not legal in pattern position", | |
c34b1796 | 994 | &extnamestr)); |
1a4d82fc | 995 | return DummyResult::raw_pat(span); |
970d7e83 LB |
996 | } |
997 | } | |
1a4d82fc JJ |
998 | }; |
999 | ||
1000 | let fully_expanded = | |
1001 | fld.fold_pat(marked_after).node.clone(); | |
1002 | fld.cx.bt_pop(); | |
223e47cc | 1003 | |
1a4d82fc JJ |
1004 | ast::Pat { |
1005 | id: ast::DUMMY_NODE_ID, | |
1006 | node: fully_expanded, | |
1007 | span: span | |
1008 | } | |
1009 | }) | |
1010 | } | |
223e47cc | 1011 | |
1a4d82fc JJ |
1012 | /// A tree-folder that applies every rename in its (mutable) list |
1013 | /// to every identifier, including both bindings and varrefs | |
1014 | /// (and lots of things that will turn out to be neither) | |
1015 | pub struct IdentRenamer<'a> { | |
1016 | renames: &'a mtwt::RenameList, | |
1017 | } | |
970d7e83 | 1018 | |
1a4d82fc JJ |
1019 | impl<'a> Folder for IdentRenamer<'a> { |
1020 | fn fold_ident(&mut self, id: Ident) -> Ident { | |
1021 | Ident { | |
1022 | name: id.name, | |
1023 | ctxt: mtwt::apply_renames(self.renames, id.ctxt), | |
1024 | } | |
1025 | } | |
1026 | fn fold_mac(&mut self, mac: ast::Mac) -> ast::Mac { | |
1027 | fold::noop_fold_mac(mac, self) | |
1028 | } | |
1029 | } | |
970d7e83 | 1030 | |
1a4d82fc JJ |
1031 | /// A tree-folder that applies every rename in its list to |
1032 | /// the idents that are in PatIdent patterns. This is more narrowly | |
1033 | /// focused than IdentRenamer, and is needed for FnDecl, | |
1034 | /// where we want to rename the args but not the fn name or the generics etc. | |
1035 | pub struct PatIdentRenamer<'a> { | |
1036 | renames: &'a mtwt::RenameList, | |
1037 | } | |
970d7e83 | 1038 | |
1a4d82fc JJ |
1039 | impl<'a> Folder for PatIdentRenamer<'a> { |
1040 | fn fold_pat(&mut self, pat: P<ast::Pat>) -> P<ast::Pat> { | |
1041 | match pat.node { | |
1042 | ast::PatIdent(..) => {}, | |
1043 | _ => return noop_fold_pat(pat, self) | |
1044 | } | |
223e47cc | 1045 | |
1a4d82fc JJ |
1046 | pat.map(|ast::Pat {id, node, span}| match node { |
1047 | ast::PatIdent(binding_mode, Spanned{span: sp, node: ident}, sub) => { | |
1048 | let new_ident = Ident{name: ident.name, | |
1049 | ctxt: mtwt::apply_renames(self.renames, ident.ctxt)}; | |
1050 | let new_node = | |
1051 | ast::PatIdent(binding_mode, | |
1052 | Spanned{span: self.new_span(sp), node: new_ident}, | |
1053 | sub.map(|p| self.fold_pat(p))); | |
1054 | ast::Pat { | |
1055 | id: id, | |
1056 | node: new_node, | |
1057 | span: self.new_span(span) | |
1058 | } | |
1059 | }, | |
1060 | _ => unreachable!() | |
1061 | }) | |
1062 | } | |
1063 | fn fold_mac(&mut self, mac: ast::Mac) -> ast::Mac { | |
1064 | fold::noop_fold_mac(mac, self) | |
1065 | } | |
1066 | } | |
223e47cc | 1067 | |
85aaf69f SL |
1068 | fn expand_annotatable(a: Annotatable, |
1069 | fld: &mut MacroExpander) | |
1070 | -> SmallVector<Annotatable> { | |
1071 | let a = expand_item_multi_modifier(a, fld); | |
1072 | ||
1073 | let mut decorator_items = SmallVector::zero(); | |
1074 | let mut new_attrs = Vec::new(); | |
d9579d0f | 1075 | expand_decorators(a.clone(), fld, &mut decorator_items, &mut new_attrs); |
85aaf69f SL |
1076 | |
1077 | let mut new_items: SmallVector<Annotatable> = match a { | |
1078 | Annotatable::Item(it) => match it.node { | |
1079 | ast::ItemMac(..) => { | |
1080 | expand_item_mac(it, fld).into_iter().map(|i| Annotatable::Item(i)).collect() | |
1081 | } | |
1082 | ast::ItemMod(_) | ast::ItemForeignMod(_) => { | |
1083 | let valid_ident = | |
1084 | it.ident.name != parse::token::special_idents::invalid.name; | |
1085 | ||
1086 | if valid_ident { | |
1087 | fld.cx.mod_push(it.ident); | |
1088 | } | |
1089 | let macro_use = contains_macro_use(fld, &new_attrs[..]); | |
1090 | let result = with_exts_frame!(fld.cx.syntax_env, | |
1091 | macro_use, | |
1092 | noop_fold_item(it, fld)); | |
1093 | if valid_ident { | |
1094 | fld.cx.mod_pop(); | |
1095 | } | |
1096 | result.into_iter().map(|i| Annotatable::Item(i)).collect() | |
1097 | }, | |
1098 | _ => { | |
1099 | let it = P(ast::Item { | |
1100 | attrs: new_attrs, | |
1101 | ..(*it).clone() | |
1102 | }); | |
1103 | noop_fold_item(it, fld).into_iter().map(|i| Annotatable::Item(i)).collect() | |
1104 | } | |
1105 | }, | |
c34b1796 AL |
1106 | |
1107 | Annotatable::TraitItem(it) => match it.node { | |
1108 | ast::MethodTraitItem(_, Some(_)) => SmallVector::one(it.map(|ti| ast::TraitItem { | |
1109 | id: ti.id, | |
1110 | ident: ti.ident, | |
1111 | attrs: ti.attrs, | |
1112 | node: match ti.node { | |
1113 | ast::MethodTraitItem(sig, Some(body)) => { | |
1114 | let (sig, body) = expand_and_rename_method(sig, body, fld); | |
1115 | ast::MethodTraitItem(sig, Some(body)) | |
1116 | } | |
1117 | _ => unreachable!() | |
1118 | }, | |
1119 | span: fld.new_span(ti.span) | |
1120 | })), | |
1121 | _ => fold::noop_fold_trait_item(it, fld) | |
1122 | }.into_iter().map(Annotatable::TraitItem).collect(), | |
1123 | ||
1124 | Annotatable::ImplItem(ii) => { | |
1125 | expand_impl_item(ii, fld).into_iter().map(Annotatable::ImplItem).collect() | |
85aaf69f SL |
1126 | } |
1127 | }; | |
1128 | ||
d9579d0f | 1129 | new_items.push_all(decorator_items); |
85aaf69f SL |
1130 | new_items |
1131 | } | |
1132 | ||
d9579d0f AL |
1133 | // Partition a set of attributes into one kind of attribute, and other kinds. |
1134 | macro_rules! partition { | |
1135 | ($fn_name: ident, $variant: ident) => { | |
1136 | #[allow(deprecated)] // The `allow` is needed because the `Modifier` variant might be used. | |
1137 | fn $fn_name(attrs: &[ast::Attribute], | |
1138 | fld: &MacroExpander) | |
1139 | -> (Vec<ast::Attribute>, Vec<ast::Attribute>) { | |
1140 | attrs.iter().cloned().partition(|attr| { | |
1141 | match fld.cx.syntax_env.find(&intern(&attr.name())) { | |
1142 | Some(rc) => match *rc { | |
1143 | $variant(..) => true, | |
1144 | _ => false | |
1145 | }, | |
1146 | _ => false | |
1147 | } | |
1148 | }) | |
85aaf69f | 1149 | } |
d9579d0f | 1150 | } |
85aaf69f SL |
1151 | } |
1152 | ||
d9579d0f AL |
1153 | partition!(modifiers, Modifier); |
1154 | partition!(multi_modifiers, MultiModifier); | |
1155 | ||
1156 | ||
1157 | #[allow(deprecated)] // The `allow` is needed because the `Decorator` variant is used. | |
1158 | fn expand_decorators(a: Annotatable, | |
1159 | fld: &mut MacroExpander, | |
1160 | decorator_items: &mut SmallVector<Annotatable>, | |
1161 | new_attrs: &mut Vec<ast::Attribute>) | |
1162 | { | |
1163 | for attr in a.attrs() { | |
1164 | let mname = attr.name(); | |
1165 | match fld.cx.syntax_env.find(&intern(&mname)) { | |
85aaf69f | 1166 | Some(rc) => match *rc { |
d9579d0f AL |
1167 | Decorator(ref dec) => { |
1168 | attr::mark_used(&attr); | |
1169 | ||
1170 | fld.cx.bt_push(ExpnInfo { | |
1171 | call_site: attr.span, | |
1172 | callee: NameAndSpan { | |
1173 | name: mname.to_string(), | |
1174 | format: MacroAttribute, | |
1175 | span: Some(attr.span), | |
1176 | // attributes can do whatever they like, | |
1177 | // for now. | |
1178 | allow_internal_unstable: true, | |
1179 | } | |
1180 | }); | |
1181 | ||
1182 | // we'd ideally decorator_items.push_all(expand_item(item, fld)), | |
1183 | // but that double-mut-borrows fld | |
1184 | let mut items: SmallVector<Annotatable> = SmallVector::zero(); | |
1185 | dec.expand(fld.cx, | |
1186 | attr.span, | |
1187 | &attr.node.value, | |
1188 | &a.clone().expect_item(), | |
1189 | &mut |item| items.push(Annotatable::Item(item))); | |
1190 | decorator_items.extend(items.into_iter() | |
1191 | .flat_map(|ann| expand_annotatable(ann, fld).into_iter())); | |
1192 | ||
1193 | fld.cx.bt_pop(); | |
1194 | } | |
1195 | MultiDecorator(ref dec) => { | |
1196 | attr::mark_used(&attr); | |
1197 | ||
1198 | fld.cx.bt_push(ExpnInfo { | |
1199 | call_site: attr.span, | |
1200 | callee: NameAndSpan { | |
1201 | name: mname.to_string(), | |
1202 | format: MacroAttribute, | |
1203 | span: Some(attr.span), | |
1204 | // attributes can do whatever they like, | |
1205 | // for now. | |
1206 | allow_internal_unstable: true, | |
1207 | } | |
1208 | }); | |
1209 | ||
1210 | // we'd ideally decorator_items.push_all(expand_annotatable(ann, fld)), | |
1211 | // but that double-mut-borrows fld | |
1212 | let mut items: SmallVector<Annotatable> = SmallVector::zero(); | |
1213 | dec.expand(fld.cx, | |
1214 | attr.span, | |
1215 | &attr.node.value, | |
62682a34 | 1216 | &a, |
d9579d0f AL |
1217 | &mut |ann| items.push(ann)); |
1218 | decorator_items.extend(items.into_iter() | |
1219 | .flat_map(|ann| expand_annotatable(ann, fld).into_iter())); | |
1220 | ||
1221 | fld.cx.bt_pop(); | |
1222 | } | |
1223 | _ => new_attrs.push((*attr).clone()), | |
85aaf69f | 1224 | }, |
d9579d0f | 1225 | _ => new_attrs.push((*attr).clone()), |
85aaf69f | 1226 | } |
d9579d0f | 1227 | } |
85aaf69f SL |
1228 | } |
1229 | ||
1230 | fn expand_item_multi_modifier(mut it: Annotatable, | |
1231 | fld: &mut MacroExpander) | |
1232 | -> Annotatable { | |
1233 | let (modifiers, other_attrs) = multi_modifiers(it.attrs(), fld); | |
1234 | ||
1235 | // Update the attrs, leave everything else alone. Is this mutation really a good idea? | |
1236 | it = it.fold_attrs(other_attrs); | |
1237 | ||
1238 | if modifiers.is_empty() { | |
1239 | return it | |
1240 | } | |
1241 | ||
1242 | for attr in &modifiers { | |
1243 | let mname = attr.name(); | |
1244 | ||
1245 | match fld.cx.syntax_env.find(&intern(&mname)) { | |
1246 | Some(rc) => match *rc { | |
1247 | MultiModifier(ref mac) => { | |
1248 | attr::mark_used(attr); | |
1249 | fld.cx.bt_push(ExpnInfo { | |
1250 | call_site: attr.span, | |
1251 | callee: NameAndSpan { | |
1252 | name: mname.to_string(), | |
1253 | format: MacroAttribute, | |
d9579d0f | 1254 | span: Some(attr.span), |
c34b1796 AL |
1255 | // attributes can do whatever they like, |
1256 | // for now | |
1257 | allow_internal_unstable: true, | |
85aaf69f SL |
1258 | } |
1259 | }); | |
1260 | it = mac.expand(fld.cx, attr.span, &*attr.node.value, it); | |
1261 | fld.cx.bt_pop(); | |
1262 | } | |
1263 | _ => unreachable!() | |
1264 | }, | |
1265 | _ => unreachable!() | |
1266 | } | |
1267 | } | |
1268 | ||
1269 | // Expansion may have added new ItemModifiers. | |
1270 | expand_item_multi_modifier(it, fld) | |
1271 | } | |
1272 | ||
d9579d0f AL |
1273 | #[allow(deprecated)] // This is needed because the `ItemModifier` trait is used |
1274 | fn expand_item_modifiers(mut it: P<ast::Item>, | |
1275 | fld: &mut MacroExpander) | |
1276 | -> P<ast::Item> { | |
1277 | // partition the attributes into ItemModifiers and others | |
1278 | let (modifiers, other_attrs) = modifiers(&it.attrs, fld); | |
1279 | ||
1280 | // update the attrs, leave everything else alone. Is this mutation really a good idea? | |
1281 | it = P(ast::Item { | |
1282 | attrs: other_attrs, | |
1283 | ..(*it).clone() | |
1284 | }); | |
1285 | ||
1286 | if modifiers.is_empty() { | |
1287 | let it = expand_item_multi_modifier(Annotatable::Item(it), fld); | |
1288 | return it.expect_item(); | |
1289 | } | |
1290 | ||
1291 | for attr in &modifiers { | |
1292 | let mname = attr.name(); | |
1293 | ||
1294 | match fld.cx.syntax_env.find(&intern(&mname)) { | |
1295 | Some(rc) => match *rc { | |
1296 | Modifier(ref mac) => { | |
1297 | attr::mark_used(attr); | |
1298 | fld.cx.bt_push(ExpnInfo { | |
1299 | call_site: attr.span, | |
1300 | callee: NameAndSpan { | |
1301 | name: mname.to_string(), | |
1302 | format: MacroAttribute, | |
1303 | span: Some(attr.span), | |
1304 | // attributes can do whatever they like, | |
1305 | // for now | |
1306 | allow_internal_unstable: true, | |
1307 | } | |
1308 | }); | |
1309 | it = mac.expand(fld.cx, attr.span, &*attr.node.value, it); | |
1310 | fld.cx.bt_pop(); | |
1311 | } | |
1312 | _ => unreachable!() | |
1313 | }, | |
1314 | _ => unreachable!() | |
1315 | } | |
1316 | } | |
1317 | ||
1318 | // Expansion may have added new ItemModifiers. | |
1319 | // It is possible, that an item modifier could expand to a multi-modifier or | |
1320 | // vice versa. In this case we will expand all modifiers before multi-modifiers, | |
1321 | // which might give an odd ordering. However, I think it is unlikely that the | |
1322 | // two kinds will be mixed, and old-style multi-modifiers are deprecated. | |
1323 | expand_item_modifiers(it, fld) | |
1324 | } | |
1325 | ||
c34b1796 AL |
1326 | fn expand_impl_item(ii: P<ast::ImplItem>, fld: &mut MacroExpander) |
1327 | -> SmallVector<P<ast::ImplItem>> { | |
1328 | match ii.node { | |
1329 | ast::MethodImplItem(..) => SmallVector::one(ii.map(|ii| ast::ImplItem { | |
1330 | id: ii.id, | |
1331 | ident: ii.ident, | |
1332 | attrs: ii.attrs, | |
1333 | vis: ii.vis, | |
1334 | node: match ii.node { | |
1335 | ast::MethodImplItem(sig, body) => { | |
1336 | let (sig, body) = expand_and_rename_method(sig, body, fld); | |
1337 | ast::MethodImplItem(sig, body) | |
1338 | } | |
1339 | _ => unreachable!() | |
1340 | }, | |
1341 | span: fld.new_span(ii.span) | |
1342 | })), | |
1343 | ast::MacImplItem(_) => { | |
1344 | let (span, mac) = ii.and_then(|ii| match ii.node { | |
1345 | ast::MacImplItem(mac) => (ii.span, mac), | |
1346 | _ => unreachable!() | |
1347 | }); | |
1348 | let maybe_new_items = | |
1349 | expand_mac_invoc(mac, span, | |
1350 | |r| r.make_impl_items(), | |
1351 | |meths, mark| meths.move_map(|m| mark_impl_item(m, mark)), | |
1a4d82fc JJ |
1352 | fld); |
1353 | ||
c34b1796 AL |
1354 | match maybe_new_items { |
1355 | Some(impl_items) => { | |
1a4d82fc | 1356 | // expand again if necessary |
c34b1796 AL |
1357 | let new_items = impl_items.into_iter().flat_map(|ii| { |
1358 | expand_impl_item(ii, fld).into_iter() | |
1359 | }).collect(); | |
1a4d82fc | 1360 | fld.cx.bt_pop(); |
c34b1796 | 1361 | new_items |
1a4d82fc JJ |
1362 | } |
1363 | None => SmallVector::zero() | |
223e47cc LB |
1364 | } |
1365 | } | |
c34b1796 AL |
1366 | _ => fold::noop_fold_impl_item(ii, fld) |
1367 | } | |
1a4d82fc | 1368 | } |
223e47cc | 1369 | |
1a4d82fc JJ |
1370 | /// Given a fn_decl and a block and a MacroExpander, expand the fn_decl, then use the |
1371 | /// PatIdents in its arguments to perform renaming in the FnDecl and | |
1372 | /// the block, returning both the new FnDecl and the new Block. | |
1373 | fn expand_and_rename_fn_decl_and_block(fn_decl: P<ast::FnDecl>, block: P<ast::Block>, | |
1374 | fld: &mut MacroExpander) | |
c34b1796 | 1375 | -> (P<ast::FnDecl>, P<ast::Block>) { |
1a4d82fc JJ |
1376 | let expanded_decl = fld.fold_fn_decl(fn_decl); |
1377 | let idents = fn_decl_arg_bindings(&*expanded_decl); | |
1378 | let renames = | |
1379 | idents.iter().map(|id : &ast::Ident| (*id,fresh_name(id))).collect(); | |
1380 | // first, a renamer for the PatIdents, for the fn_decl: | |
1381 | let mut rename_pat_fld = PatIdentRenamer{renames: &renames}; | |
1382 | let rewritten_fn_decl = rename_pat_fld.fold_fn_decl(expanded_decl); | |
1383 | // now, a renamer for *all* idents, for the body: | |
1384 | let mut rename_fld = IdentRenamer{renames: &renames}; | |
1385 | let rewritten_body = fld.fold_block(rename_fld.fold_block(block)); | |
1386 | (rewritten_fn_decl,rewritten_body) | |
1387 | } | |
1388 | ||
c34b1796 AL |
1389 | fn expand_and_rename_method(sig: ast::MethodSig, body: P<ast::Block>, |
1390 | fld: &mut MacroExpander) | |
1391 | -> (ast::MethodSig, P<ast::Block>) { | |
1392 | let (rewritten_fn_decl, rewritten_body) | |
1393 | = expand_and_rename_fn_decl_and_block(sig.decl, body, fld); | |
1394 | (ast::MethodSig { | |
1395 | generics: fld.fold_generics(sig.generics), | |
1396 | abi: sig.abi, | |
1397 | explicit_self: fld.fold_explicit_self(sig.explicit_self), | |
1398 | unsafety: sig.unsafety, | |
62682a34 | 1399 | constness: sig.constness, |
c34b1796 AL |
1400 | decl: rewritten_fn_decl |
1401 | }, rewritten_body) | |
1402 | } | |
1403 | ||
1a4d82fc JJ |
1404 | /// A tree-folder that performs macro expansion |
1405 | pub struct MacroExpander<'a, 'b:'a> { | |
1406 | pub cx: &'a mut ExtCtxt<'b>, | |
1a4d82fc JJ |
1407 | } |
1408 | ||
1409 | impl<'a, 'b> MacroExpander<'a, 'b> { | |
1410 | pub fn new(cx: &'a mut ExtCtxt<'b>) -> MacroExpander<'a, 'b> { | |
9346a6ac | 1411 | MacroExpander { cx: cx } |
1a4d82fc JJ |
1412 | } |
1413 | } | |
1414 | ||
1415 | impl<'a, 'b> Folder for MacroExpander<'a, 'b> { | |
1416 | fn fold_expr(&mut self, expr: P<ast::Expr>) -> P<ast::Expr> { | |
1417 | expand_expr(expr, self) | |
1418 | } | |
1419 | ||
1420 | fn fold_pat(&mut self, pat: P<ast::Pat>) -> P<ast::Pat> { | |
1421 | expand_pat(pat, self) | |
1422 | } | |
1423 | ||
1424 | fn fold_item(&mut self, item: P<ast::Item>) -> SmallVector<P<ast::Item>> { | |
9346a6ac | 1425 | expand_item(item, self) |
1a4d82fc JJ |
1426 | } |
1427 | ||
1428 | fn fold_item_underscore(&mut self, item: ast::Item_) -> ast::Item_ { | |
1429 | expand_item_underscore(item, self) | |
1430 | } | |
223e47cc | 1431 | |
1a4d82fc | 1432 | fn fold_stmt(&mut self, stmt: P<ast::Stmt>) -> SmallVector<P<ast::Stmt>> { |
9346a6ac | 1433 | expand_stmt(stmt, self) |
1a4d82fc JJ |
1434 | } |
1435 | ||
1436 | fn fold_block(&mut self, block: P<Block>) -> P<Block> { | |
1437 | expand_block(block, self) | |
1438 | } | |
1439 | ||
1440 | fn fold_arm(&mut self, arm: ast::Arm) -> ast::Arm { | |
1441 | expand_arm(arm, self) | |
1442 | } | |
1443 | ||
c34b1796 AL |
1444 | fn fold_trait_item(&mut self, i: P<ast::TraitItem>) -> SmallVector<P<ast::TraitItem>> { |
1445 | expand_annotatable(Annotatable::TraitItem(i), self) | |
1446 | .into_iter().map(|i| i.expect_trait_item()).collect() | |
85aaf69f SL |
1447 | } |
1448 | ||
c34b1796 AL |
1449 | fn fold_impl_item(&mut self, i: P<ast::ImplItem>) -> SmallVector<P<ast::ImplItem>> { |
1450 | expand_annotatable(Annotatable::ImplItem(i), self) | |
1451 | .into_iter().map(|i| i.expect_impl_item()).collect() | |
1a4d82fc JJ |
1452 | } |
1453 | ||
1a4d82fc JJ |
1454 | fn new_span(&mut self, span: Span) -> Span { |
1455 | new_span(self.cx, span) | |
1456 | } | |
970d7e83 LB |
1457 | } |
1458 | ||
1a4d82fc JJ |
1459 | fn new_span(cx: &ExtCtxt, sp: Span) -> Span { |
1460 | /* this discards information in the case of macro-defining macros */ | |
1461 | Span { | |
1462 | lo: sp.lo, | |
1463 | hi: sp.hi, | |
1464 | expn_id: cx.backtrace(), | |
1465 | } | |
223e47cc LB |
1466 | } |
1467 | ||
85aaf69f | 1468 | pub struct ExpansionConfig<'feat> { |
1a4d82fc | 1469 | pub crate_name: String, |
85aaf69f SL |
1470 | pub features: Option<&'feat Features>, |
1471 | pub recursion_limit: usize, | |
d9579d0f | 1472 | pub trace_mac: bool, |
1a4d82fc JJ |
1473 | } |
1474 | ||
c34b1796 AL |
1475 | macro_rules! feature_tests { |
1476 | ($( fn $getter:ident = $field:ident, )*) => { | |
1477 | $( | |
1478 | pub fn $getter(&self) -> bool { | |
1479 | match self.features { | |
1480 | Some(&Features { $field: true, .. }) => true, | |
1481 | _ => false, | |
1482 | } | |
1483 | } | |
1484 | )* | |
1485 | } | |
1486 | } | |
1487 | ||
85aaf69f SL |
1488 | impl<'feat> ExpansionConfig<'feat> { |
1489 | pub fn default(crate_name: String) -> ExpansionConfig<'static> { | |
1a4d82fc JJ |
1490 | ExpansionConfig { |
1491 | crate_name: crate_name, | |
85aaf69f | 1492 | features: None, |
1a4d82fc | 1493 | recursion_limit: 64, |
d9579d0f | 1494 | trace_mac: false, |
1a4d82fc | 1495 | } |
970d7e83 | 1496 | } |
85aaf69f | 1497 | |
c34b1796 AL |
1498 | feature_tests! { |
1499 | fn enable_quotes = allow_quote, | |
1500 | fn enable_asm = allow_asm, | |
1501 | fn enable_log_syntax = allow_log_syntax, | |
1502 | fn enable_concat_idents = allow_concat_idents, | |
1503 | fn enable_trace_macros = allow_trace_macros, | |
1504 | fn enable_allow_internal_unstable = allow_internal_unstable, | |
1505 | fn enable_custom_derive = allow_custom_derive, | |
85aaf69f | 1506 | } |
970d7e83 LB |
1507 | } |
1508 | ||
85aaf69f SL |
1509 | pub fn expand_crate<'feat>(parse_sess: &parse::ParseSess, |
1510 | cfg: ExpansionConfig<'feat>, | |
1511 | // these are the macros being imported to this crate: | |
1512 | imported_macros: Vec<ast::MacroDef>, | |
1513 | user_exts: Vec<NamedSyntaxExtension>, | |
1514 | c: Crate) -> Crate { | |
1a4d82fc | 1515 | let mut cx = ExtCtxt::new(parse_sess, c.config.clone(), cfg); |
85aaf69f SL |
1516 | cx.use_std = std_inject::use_std(&c); |
1517 | ||
1a4d82fc JJ |
1518 | let mut expander = MacroExpander::new(&mut cx); |
1519 | ||
85aaf69f | 1520 | for def in imported_macros { |
1a4d82fc JJ |
1521 | expander.cx.insert_macro(def); |
1522 | } | |
970d7e83 | 1523 | |
85aaf69f | 1524 | for (name, extension) in user_exts { |
1a4d82fc | 1525 | expander.cx.syntax_env.insert(name, extension); |
970d7e83 | 1526 | } |
1a4d82fc JJ |
1527 | |
1528 | let mut ret = expander.fold_crate(c); | |
1529 | ret.exported_macros = expander.cx.exported_macros.clone(); | |
1530 | parse_sess.span_diagnostic.handler().abort_if_errors(); | |
1531 | return ret; | |
1532 | } | |
1533 | ||
1534 | // HYGIENIC CONTEXT EXTENSION: | |
1535 | // all of these functions are for walking over | |
1536 | // ASTs and making some change to the context of every | |
1537 | // element that has one. a CtxtFn is a trait-ified | |
1538 | // version of a closure in (SyntaxContext -> SyntaxContext). | |
1539 | // the ones defined here include: | |
1540 | // Marker - add a mark to a context | |
1541 | ||
1542 | // A Marker adds the given mark to the syntax context | |
1543 | struct Marker { mark: Mrk } | |
1544 | ||
1545 | impl Folder for Marker { | |
1546 | fn fold_ident(&mut self, id: Ident) -> Ident { | |
1547 | ast::Ident { | |
1548 | name: id.name, | |
1549 | ctxt: mtwt::apply_mark(self.mark, id.ctxt) | |
1550 | } | |
1551 | } | |
1552 | fn fold_mac(&mut self, Spanned {node, span}: ast::Mac) -> ast::Mac { | |
1553 | Spanned { | |
1554 | node: match node { | |
1555 | MacInvocTT(path, tts, ctxt) => { | |
1556 | MacInvocTT(self.fold_path(path), | |
85aaf69f | 1557 | self.fold_tts(&tts[..]), |
1a4d82fc JJ |
1558 | mtwt::apply_mark(self.mark, ctxt)) |
1559 | } | |
1560 | }, | |
1561 | span: span, | |
1562 | } | |
1563 | } | |
1564 | } | |
1565 | ||
1566 | // apply a given mark to the given token trees. Used prior to expansion of a macro. | |
1567 | fn mark_tts(tts: &[TokenTree], m: Mrk) -> Vec<TokenTree> { | |
1568 | noop_fold_tts(tts, &mut Marker{mark:m}) | |
1569 | } | |
1570 | ||
1571 | // apply a given mark to the given expr. Used following the expansion of a macro. | |
1572 | fn mark_expr(expr: P<ast::Expr>, m: Mrk) -> P<ast::Expr> { | |
1573 | Marker{mark:m}.fold_expr(expr) | |
1574 | } | |
1575 | ||
1576 | // apply a given mark to the given pattern. Used following the expansion of a macro. | |
1577 | fn mark_pat(pat: P<ast::Pat>, m: Mrk) -> P<ast::Pat> { | |
1578 | Marker{mark:m}.fold_pat(pat) | |
970d7e83 LB |
1579 | } |
1580 | ||
1a4d82fc | 1581 | // apply a given mark to the given stmt. Used following the expansion of a macro. |
9346a6ac AL |
1582 | fn mark_stmt(stmt: P<ast::Stmt>, m: Mrk) -> P<ast::Stmt> { |
1583 | Marker{mark:m}.fold_stmt(stmt) | |
1a4d82fc JJ |
1584 | .expect_one("marking a stmt didn't return exactly one stmt") |
1585 | } | |
1586 | ||
1587 | // apply a given mark to the given item. Used following the expansion of a macro. | |
1588 | fn mark_item(expr: P<ast::Item>, m: Mrk) -> P<ast::Item> { | |
1589 | Marker{mark:m}.fold_item(expr) | |
1590 | .expect_one("marking an item didn't return exactly one item") | |
1591 | } | |
1592 | ||
1593 | // apply a given mark to the given item. Used following the expansion of a macro. | |
c34b1796 AL |
1594 | fn mark_impl_item(ii: P<ast::ImplItem>, m: Mrk) -> P<ast::ImplItem> { |
1595 | Marker{mark:m}.fold_impl_item(ii) | |
1596 | .expect_one("marking an impl item didn't return exactly one impl item") | |
1a4d82fc JJ |
1597 | } |
1598 | ||
1599 | /// Check that there are no macro invocations left in the AST: | |
1600 | pub fn check_for_macros(sess: &parse::ParseSess, krate: &ast::Crate) { | |
1601 | visit::walk_crate(&mut MacroExterminator{sess:sess}, krate); | |
1602 | } | |
1603 | ||
1604 | /// A visitor that ensures that no macro invocations remain in an AST. | |
1605 | struct MacroExterminator<'a>{ | |
1606 | sess: &'a parse::ParseSess | |
1607 | } | |
1608 | ||
1609 | impl<'a, 'v> Visitor<'v> for MacroExterminator<'a> { | |
1610 | fn visit_mac(&mut self, mac: &ast::Mac) { | |
1611 | self.sess.span_diagnostic.span_bug(mac.span, | |
1612 | "macro exterminator: expected AST \ | |
1613 | with no macro invocations"); | |
970d7e83 LB |
1614 | } |
1615 | } | |
1616 | ||
1617 | ||
223e47cc | 1618 | #[cfg(test)] |
d9579d0f | 1619 | mod tests { |
1a4d82fc JJ |
1620 | use super::{pattern_bindings, expand_crate}; |
1621 | use super::{PatIdentFinder, IdentRenamer, PatIdentRenamer, ExpansionConfig}; | |
223e47cc | 1622 | use ast; |
85aaf69f | 1623 | use ast::Name; |
223e47cc | 1624 | use codemap; |
1a4d82fc JJ |
1625 | use ext::mtwt; |
1626 | use fold::Folder; | |
223e47cc | 1627 | use parse; |
1a4d82fc | 1628 | use parse::token; |
1a4d82fc JJ |
1629 | use util::parser_testing::{string_to_parser}; |
1630 | use util::parser_testing::{string_to_pat, string_to_crate, strs_to_idents}; | |
1631 | use visit; | |
1632 | use visit::Visitor; | |
1633 | ||
1634 | // a visitor that extracts the paths | |
1635 | // from a given thingy and puts them in a mutable | |
1636 | // array (passed in to the traversal) | |
1637 | #[derive(Clone)] | |
1638 | struct PathExprFinderContext { | |
1639 | path_accumulator: Vec<ast::Path> , | |
1640 | } | |
1641 | ||
1642 | impl<'v> Visitor<'v> for PathExprFinderContext { | |
1643 | fn visit_expr(&mut self, expr: &ast::Expr) { | |
c34b1796 AL |
1644 | if let ast::ExprPath(None, ref p) = expr.node { |
1645 | self.path_accumulator.push(p.clone()); | |
1a4d82fc | 1646 | } |
c34b1796 | 1647 | visit::walk_expr(self, expr); |
1a4d82fc JJ |
1648 | } |
1649 | } | |
1650 | ||
1651 | // find the variable references in a crate | |
1652 | fn crate_varrefs(the_crate : &ast::Crate) -> Vec<ast::Path> { | |
1653 | let mut path_finder = PathExprFinderContext{path_accumulator:Vec::new()}; | |
1654 | visit::walk_crate(&mut path_finder, the_crate); | |
1655 | path_finder.path_accumulator | |
1656 | } | |
1657 | ||
1658 | /// A Visitor that extracts the identifiers from a thingy. | |
1659 | // as a side note, I'm starting to want to abstract over these.... | |
1660 | struct IdentFinder { | |
1661 | ident_accumulator: Vec<ast::Ident> | |
1662 | } | |
1663 | ||
1664 | impl<'v> Visitor<'v> for IdentFinder { | |
1665 | fn visit_ident(&mut self, _: codemap::Span, id: ast::Ident){ | |
1666 | self.ident_accumulator.push(id); | |
1667 | } | |
1668 | } | |
1669 | ||
1670 | /// Find the idents in a crate | |
1671 | fn crate_idents(the_crate: &ast::Crate) -> Vec<ast::Ident> { | |
1672 | let mut ident_finder = IdentFinder{ident_accumulator: Vec::new()}; | |
1673 | visit::walk_crate(&mut ident_finder, the_crate); | |
1674 | ident_finder.ident_accumulator | |
223e47cc LB |
1675 | } |
1676 | ||
1677 | // these following tests are quite fragile, in that they don't test what | |
1678 | // *kind* of failure occurs. | |
1679 | ||
85aaf69f | 1680 | fn test_ecfg() -> ExpansionConfig<'static> { |
1a4d82fc JJ |
1681 | ExpansionConfig::default("test".to_string()) |
1682 | } | |
1683 | ||
1684 | // make sure that macros can't escape fns | |
c34b1796 | 1685 | #[should_panic] |
223e47cc | 1686 | #[test] fn macros_cant_escape_fns_test () { |
1a4d82fc | 1687 | let src = "fn bogus() {macro_rules! z (() => (3+4));}\ |
85aaf69f | 1688 | fn inty() -> i32 { z!() }".to_string(); |
62682a34 | 1689 | let sess = parse::ParseSess::new(); |
223e47cc | 1690 | let crate_ast = parse::parse_crate_from_source_str( |
1a4d82fc | 1691 | "<test>".to_string(), |
970d7e83 | 1692 | src, |
1a4d82fc | 1693 | Vec::new(), &sess); |
223e47cc | 1694 | // should fail: |
1a4d82fc | 1695 | expand_crate(&sess,test_ecfg(),vec!(),vec!(),crate_ast); |
223e47cc LB |
1696 | } |
1697 | ||
1a4d82fc | 1698 | // make sure that macros can't escape modules |
c34b1796 | 1699 | #[should_panic] |
223e47cc | 1700 | #[test] fn macros_cant_escape_mods_test () { |
1a4d82fc | 1701 | let src = "mod foo {macro_rules! z (() => (3+4));}\ |
85aaf69f | 1702 | fn inty() -> i32 { z!() }".to_string(); |
62682a34 | 1703 | let sess = parse::ParseSess::new(); |
223e47cc | 1704 | let crate_ast = parse::parse_crate_from_source_str( |
1a4d82fc | 1705 | "<test>".to_string(), |
970d7e83 | 1706 | src, |
1a4d82fc JJ |
1707 | Vec::new(), &sess); |
1708 | expand_crate(&sess,test_ecfg(),vec!(),vec!(),crate_ast); | |
223e47cc LB |
1709 | } |
1710 | ||
1a4d82fc | 1711 | // macro_use modules should allow macros to escape |
223e47cc | 1712 | #[test] fn macros_can_escape_flattened_mods_test () { |
1a4d82fc | 1713 | let src = "#[macro_use] mod foo {macro_rules! z (() => (3+4));}\ |
85aaf69f | 1714 | fn inty() -> i32 { z!() }".to_string(); |
62682a34 | 1715 | let sess = parse::ParseSess::new(); |
223e47cc | 1716 | let crate_ast = parse::parse_crate_from_source_str( |
1a4d82fc | 1717 | "<test>".to_string(), |
970d7e83 | 1718 | src, |
1a4d82fc JJ |
1719 | Vec::new(), &sess); |
1720 | expand_crate(&sess, test_ecfg(), vec!(), vec!(), crate_ast); | |
223e47cc LB |
1721 | } |
1722 | ||
1a4d82fc | 1723 | fn expand_crate_str(crate_str: String) -> ast::Crate { |
62682a34 | 1724 | let ps = parse::ParseSess::new(); |
9346a6ac | 1725 | let crate_ast = panictry!(string_to_parser(&ps, crate_str).parse_crate_mod()); |
1a4d82fc JJ |
1726 | // the cfg argument actually does matter, here... |
1727 | expand_crate(&ps,test_ecfg(),vec!(),vec!(),crate_ast) | |
1728 | } | |
223e47cc | 1729 | |
1a4d82fc JJ |
1730 | // find the pat_ident paths in a crate |
1731 | fn crate_bindings(the_crate : &ast::Crate) -> Vec<ast::Ident> { | |
1732 | let mut name_finder = PatIdentFinder{ident_accumulator:Vec::new()}; | |
1733 | visit::walk_crate(&mut name_finder, the_crate); | |
1734 | name_finder.ident_accumulator | |
1735 | } | |
1736 | ||
1737 | #[test] fn macro_tokens_should_match(){ | |
1738 | expand_crate_str( | |
1739 | "macro_rules! m((a)=>(13)) ;fn main(){m!(a);}".to_string()); | |
1740 | } | |
1741 | ||
1742 | // should be able to use a bound identifier as a literal in a macro definition: | |
1743 | #[test] fn self_macro_parsing(){ | |
1744 | expand_crate_str( | |
85aaf69f SL |
1745 | "macro_rules! foo ((zz) => (287;)); |
1746 | fn f(zz: i32) {foo!(zz);}".to_string() | |
1a4d82fc JJ |
1747 | ); |
1748 | } | |
1749 | ||
1750 | // renaming tests expand a crate and then check that the bindings match | |
1751 | // the right varrefs. The specification of the test case includes the | |
1752 | // text of the crate, and also an array of arrays. Each element in the | |
1753 | // outer array corresponds to a binding in the traversal of the AST | |
1754 | // induced by visit. Each of these arrays contains a list of indexes, | |
1755 | // interpreted as the varrefs in the varref traversal that this binding | |
1756 | // should match. So, for instance, in a program with two bindings and | |
d9579d0f | 1757 | // three varrefs, the array [[1, 2], [0]] would indicate that the first |
1a4d82fc JJ |
1758 | // binding should match the second two varrefs, and the second binding |
1759 | // should match the first varref. | |
1760 | // | |
1761 | // Put differently; this is a sparse representation of a boolean matrix | |
1762 | // indicating which bindings capture which identifiers. | |
1763 | // | |
1764 | // Note also that this matrix is dependent on the implicit ordering of | |
1765 | // the bindings and the varrefs discovered by the name-finder and the path-finder. | |
1766 | // | |
1767 | // The comparisons are done post-mtwt-resolve, so we're comparing renamed | |
1768 | // names; differences in marks don't matter any more. | |
1769 | // | |
1770 | // oog... I also want tests that check "bound-identifier-=?". That is, | |
1771 | // not just "do these have the same name", but "do they have the same | |
1772 | // name *and* the same marks"? Understanding this is really pretty painful. | |
1773 | // in principle, you might want to control this boolean on a per-varref basis, | |
1774 | // but that would make things even harder to understand, and might not be | |
1775 | // necessary for thorough testing. | |
85aaf69f | 1776 | type RenamingTest = (&'static str, Vec<Vec<usize>>, bool); |
1a4d82fc JJ |
1777 | |
1778 | #[test] | |
1779 | fn automatic_renaming () { | |
1780 | let tests: Vec<RenamingTest> = | |
1781 | vec!(// b & c should get new names throughout, in the expr too: | |
85aaf69f | 1782 | ("fn a() -> i32 { let b = 13; let c = b; b+c }", |
1a4d82fc JJ |
1783 | vec!(vec!(0,1),vec!(2)), false), |
1784 | // both x's should be renamed (how is this causing a bug?) | |
85aaf69f | 1785 | ("fn main () {let x: i32 = 13;x;}", |
1a4d82fc JJ |
1786 | vec!(vec!(0)), false), |
1787 | // the use of b after the + should be renamed, the other one not: | |
85aaf69f | 1788 | ("macro_rules! f (($x:ident) => (b + $x)); fn a() -> i32 { let b = 13; f!(b)}", |
1a4d82fc JJ |
1789 | vec!(vec!(1)), false), |
1790 | // the b before the plus should not be renamed (requires marks) | |
85aaf69f | 1791 | ("macro_rules! f (($x:ident) => ({let b=9; ($x + b)})); fn a() -> i32 { f!(b)}", |
1a4d82fc JJ |
1792 | vec!(vec!(1)), false), |
1793 | // the marks going in and out of letty should cancel, allowing that $x to | |
1794 | // capture the one following the semicolon. | |
1795 | // this was an awesome test case, and caught a *lot* of bugs. | |
1796 | ("macro_rules! letty(($x:ident) => (let $x = 15;)); | |
1797 | macro_rules! user(($x:ident) => ({letty!($x); $x})); | |
85aaf69f | 1798 | fn main() -> i32 {user!(z)}", |
1a4d82fc JJ |
1799 | vec!(vec!(0)), false) |
1800 | ); | |
1801 | for (idx,s) in tests.iter().enumerate() { | |
1802 | run_renaming_test(s,idx); | |
1803 | } | |
1804 | } | |
1805 | ||
1806 | // no longer a fixme #8062: this test exposes a *potential* bug; our system does | |
1807 | // not behave exactly like MTWT, but a conversation with Matthew Flatt | |
1808 | // suggests that this can only occur in the presence of local-expand, which | |
1809 | // we have no plans to support. ... unless it's needed for item hygiene.... | |
1810 | #[ignore] | |
9346a6ac AL |
1811 | #[test] |
1812 | fn issue_8062(){ | |
1a4d82fc JJ |
1813 | run_renaming_test( |
1814 | &("fn main() {let hrcoo = 19; macro_rules! getx(()=>(hrcoo)); getx!();}", | |
1815 | vec!(vec!(0)), true), 0) | |
1816 | } | |
1817 | ||
1818 | // FIXME #6994: | |
1819 | // the z flows into and out of two macros (g & f) along one path, and one | |
1820 | // (just g) along the other, so the result of the whole thing should | |
1821 | // be "let z_123 = 3; z_123" | |
1822 | #[ignore] | |
9346a6ac AL |
1823 | #[test] |
1824 | fn issue_6994(){ | |
1a4d82fc JJ |
1825 | run_renaming_test( |
1826 | &("macro_rules! g (($x:ident) => | |
1827 | ({macro_rules! f(($y:ident)=>({let $y=3;$x}));f!($x)})); | |
1828 | fn a(){g!(z)}", | |
1829 | vec!(vec!(0)),false), | |
1830 | 0) | |
1831 | } | |
1832 | ||
1833 | // match variable hygiene. Should expand into | |
1834 | // fn z() {match 8 {x_1 => {match 9 {x_2 | x_2 if x_2 == x_1 => x_2 + x_1}}}} | |
9346a6ac AL |
1835 | #[test] |
1836 | fn issue_9384(){ | |
1a4d82fc JJ |
1837 | run_renaming_test( |
1838 | &("macro_rules! bad_macro (($ex:expr) => ({match 9 {x | x if x == $ex => x + $ex}})); | |
1839 | fn z() {match 8 {x => bad_macro!(x)}}", | |
1840 | // NB: the third "binding" is the repeat of the second one. | |
1841 | vec!(vec!(1,3),vec!(0,2),vec!(0,2)), | |
1842 | true), | |
1843 | 0) | |
1844 | } | |
1845 | ||
1846 | // interpolated nodes weren't getting labeled. | |
1847 | // should expand into | |
1848 | // fn main(){let g1_1 = 13; g1_1}} | |
9346a6ac AL |
1849 | #[test] |
1850 | fn pat_expand_issue_15221(){ | |
1a4d82fc JJ |
1851 | run_renaming_test( |
1852 | &("macro_rules! inner ( ($e:pat ) => ($e)); | |
1853 | macro_rules! outer ( ($e:pat ) => (inner!($e))); | |
1854 | fn main() { let outer!(g) = 13; g;}", | |
1855 | vec!(vec!(0)), | |
1856 | true), | |
1857 | 0) | |
1858 | } | |
1859 | ||
1860 | // create a really evil test case where a $x appears inside a binding of $x | |
1861 | // but *shouldn't* bind because it was inserted by a different macro.... | |
1862 | // can't write this test case until we have macro-generating macros. | |
1863 | ||
1864 | // method arg hygiene | |
85aaf69f | 1865 | // method expands to fn get_x(&self_0, x_1: i32) {self_0 + self_2 + x_3 + x_1} |
9346a6ac AL |
1866 | #[test] |
1867 | fn method_arg_hygiene(){ | |
1a4d82fc JJ |
1868 | run_renaming_test( |
1869 | &("macro_rules! inject_x (()=>(x)); | |
1870 | macro_rules! inject_self (()=>(self)); | |
1871 | struct A; | |
85aaf69f | 1872 | impl A{fn get_x(&self, x: i32) {self + inject_self!() + inject_x!() + x;} }", |
1a4d82fc JJ |
1873 | vec!(vec!(0),vec!(3)), |
1874 | true), | |
1875 | 0) | |
1876 | } | |
1877 | ||
1878 | // ooh, got another bite? | |
1879 | // expands to struct A; impl A {fn thingy(&self_1) {self_1;}} | |
9346a6ac AL |
1880 | #[test] |
1881 | fn method_arg_hygiene_2(){ | |
1a4d82fc JJ |
1882 | run_renaming_test( |
1883 | &("struct A; | |
1884 | macro_rules! add_method (($T:ty) => | |
1885 | (impl $T { fn thingy(&self) {self;} })); | |
1886 | add_method!(A);", | |
1887 | vec!(vec!(0)), | |
1888 | true), | |
1889 | 0) | |
1890 | } | |
1891 | ||
1892 | // item fn hygiene | |
85aaf69f | 1893 | // expands to fn q(x_1: i32){fn g(x_2: i32){x_2 + x_1};} |
9346a6ac AL |
1894 | #[test] |
1895 | fn issue_9383(){ | |
1a4d82fc | 1896 | run_renaming_test( |
85aaf69f SL |
1897 | &("macro_rules! bad_macro (($ex:expr) => (fn g(x: i32){ x + $ex })); |
1898 | fn q(x: i32) { bad_macro!(x); }", | |
1a4d82fc JJ |
1899 | vec!(vec!(1),vec!(0)),true), |
1900 | 0) | |
1901 | } | |
1902 | ||
1903 | // closure arg hygiene (ExprClosure) | |
85aaf69f | 1904 | // expands to fn f(){(|x_1 : i32| {(x_2 + x_1)})(3);} |
9346a6ac AL |
1905 | #[test] |
1906 | fn closure_arg_hygiene(){ | |
1a4d82fc JJ |
1907 | run_renaming_test( |
1908 | &("macro_rules! inject_x (()=>(x)); | |
85aaf69f | 1909 | fn f(){(|x : i32| {(inject_x!() + x)})(3);}", |
1a4d82fc JJ |
1910 | vec!(vec!(1)), |
1911 | true), | |
1912 | 0) | |
1913 | } | |
1914 | ||
1915 | // macro_rules in method position. Sadly, unimplemented. | |
9346a6ac AL |
1916 | #[test] |
1917 | fn macro_in_method_posn(){ | |
1a4d82fc | 1918 | expand_crate_str( |
85aaf69f | 1919 | "macro_rules! my_method (() => (fn thirteen(&self) -> i32 {13})); |
1a4d82fc JJ |
1920 | struct A; |
1921 | impl A{ my_method!(); } | |
1922 | fn f(){A.thirteen;}".to_string()); | |
1923 | } | |
1924 | ||
1925 | // another nested macro | |
1926 | // expands to impl Entries {fn size_hint(&self_1) {self_1;} | |
9346a6ac AL |
1927 | #[test] |
1928 | fn item_macro_workaround(){ | |
1a4d82fc JJ |
1929 | run_renaming_test( |
1930 | &("macro_rules! item { ($i:item) => {$i}} | |
1931 | struct Entries; | |
1932 | macro_rules! iterator_impl { | |
1933 | () => { item!( impl Entries { fn size_hint(&self) { self;}});}} | |
1934 | iterator_impl! { }", | |
1935 | vec!(vec!(0)), true), | |
1936 | 0) | |
1937 | } | |
1938 | ||
1939 | // run one of the renaming tests | |
85aaf69f | 1940 | fn run_renaming_test(t: &RenamingTest, test_idx: usize) { |
1a4d82fc JJ |
1941 | let invalid_name = token::special_idents::invalid.name; |
1942 | let (teststr, bound_connections, bound_ident_check) = match *t { | |
1943 | (ref str,ref conns, bic) => (str.to_string(), conns.clone(), bic) | |
1944 | }; | |
1945 | let cr = expand_crate_str(teststr.to_string()); | |
1946 | let bindings = crate_bindings(&cr); | |
1947 | let varrefs = crate_varrefs(&cr); | |
1948 | ||
1949 | // must be one check clause for each binding: | |
1950 | assert_eq!(bindings.len(),bound_connections.len()); | |
1951 | for (binding_idx,shouldmatch) in bound_connections.iter().enumerate() { | |
1952 | let binding_name = mtwt::resolve(bindings[binding_idx]); | |
1953 | let binding_marks = mtwt::marksof(bindings[binding_idx].ctxt, invalid_name); | |
1954 | // shouldmatch can't name varrefs that don't exist: | |
9346a6ac | 1955 | assert!((shouldmatch.is_empty()) || |
1a4d82fc JJ |
1956 | (varrefs.len() > *shouldmatch.iter().max().unwrap())); |
1957 | for (idx,varref) in varrefs.iter().enumerate() { | |
85aaf69f | 1958 | let print_hygiene_debug_info = || { |
1a4d82fc JJ |
1959 | // good lord, you can't make a path with 0 segments, can you? |
1960 | let final_varref_ident = match varref.segments.last() { | |
1961 | Some(pathsegment) => pathsegment.identifier, | |
1962 | None => panic!("varref with 0 path segments?") | |
1963 | }; | |
1964 | let varref_name = mtwt::resolve(final_varref_ident); | |
1965 | let varref_idents : Vec<ast::Ident> | |
1966 | = varref.segments.iter().map(|s| s.identifier) | |
1967 | .collect(); | |
1968 | println!("varref #{}: {:?}, resolves to {}",idx, varref_idents, varref_name); | |
1969 | let string = token::get_ident(final_varref_ident); | |
85aaf69f | 1970 | println!("varref's first segment's string: \"{}\"", &string[..]); |
1a4d82fc JJ |
1971 | println!("binding #{}: {}, resolves to {}", |
1972 | binding_idx, bindings[binding_idx], binding_name); | |
1973 | mtwt::with_sctable(|x| mtwt::display_sctable(x)); | |
1974 | }; | |
1975 | if shouldmatch.contains(&idx) { | |
1976 | // it should be a path of length 1, and it should | |
1977 | // be free-identifier=? or bound-identifier=? to the given binding | |
1978 | assert_eq!(varref.segments.len(),1); | |
1979 | let varref_name = mtwt::resolve(varref.segments[0].identifier); | |
1980 | let varref_marks = mtwt::marksof(varref.segments[0] | |
1981 | .identifier | |
1982 | .ctxt, | |
1983 | invalid_name); | |
1984 | if !(varref_name==binding_name) { | |
1985 | println!("uh oh, should match but doesn't:"); | |
1986 | print_hygiene_debug_info(); | |
1987 | } | |
1988 | assert_eq!(varref_name,binding_name); | |
1989 | if bound_ident_check { | |
1990 | // we're checking bound-identifier=?, and the marks | |
1991 | // should be the same, too: | |
1992 | assert_eq!(varref_marks,binding_marks.clone()); | |
1993 | } | |
1994 | } else { | |
1995 | let varref_name = mtwt::resolve(varref.segments[0].identifier); | |
1996 | let fail = (varref.segments.len() == 1) | |
1997 | && (varref_name == binding_name); | |
1998 | // temp debugging: | |
1999 | if fail { | |
2000 | println!("failure on test {}",test_idx); | |
2001 | println!("text of test case: \"{}\"", teststr); | |
2002 | println!(""); | |
2003 | println!("uh oh, matches but shouldn't:"); | |
2004 | print_hygiene_debug_info(); | |
2005 | } | |
2006 | assert!(!fail); | |
2007 | } | |
223e47cc LB |
2008 | } |
2009 | } | |
2010 | } | |
2011 | ||
9346a6ac AL |
2012 | #[test] |
2013 | fn fmt_in_macro_used_inside_module_macro() { | |
1a4d82fc JJ |
2014 | let crate_str = "macro_rules! fmt_wrap(($b:expr)=>($b.to_string())); |
2015 | macro_rules! foo_module (() => (mod generated { fn a() { let xx = 147; fmt_wrap!(xx);}})); | |
2016 | foo_module!(); | |
2017 | ".to_string(); | |
2018 | let cr = expand_crate_str(crate_str); | |
2019 | // find the xx binding | |
2020 | let bindings = crate_bindings(&cr); | |
2021 | let cxbinds: Vec<&ast::Ident> = | |
2022 | bindings.iter().filter(|b| { | |
2023 | let ident = token::get_ident(**b); | |
85aaf69f | 2024 | let string = &ident[..]; |
1a4d82fc JJ |
2025 | "xx" == string |
2026 | }).collect(); | |
85aaf69f | 2027 | let cxbinds: &[&ast::Ident] = &cxbinds[..]; |
d9579d0f AL |
2028 | let cxbind = match (cxbinds.len(), cxbinds.get(0)) { |
2029 | (1, Some(b)) => *b, | |
1a4d82fc JJ |
2030 | _ => panic!("expected just one binding for ext_cx") |
2031 | }; | |
2032 | let resolved_binding = mtwt::resolve(*cxbind); | |
2033 | let varrefs = crate_varrefs(&cr); | |
2034 | ||
2035 | // the xx binding should bind all of the xx varrefs: | |
2036 | for (idx,v) in varrefs.iter().filter(|p| { | |
2037 | p.segments.len() == 1 | |
c34b1796 | 2038 | && "xx" == &*token::get_ident(p.segments[0].identifier) |
1a4d82fc JJ |
2039 | }).enumerate() { |
2040 | if mtwt::resolve(v.segments[0].identifier) != resolved_binding { | |
2041 | println!("uh oh, xx binding didn't match xx varref:"); | |
2042 | println!("this is xx varref \\# {}", idx); | |
2043 | println!("binding: {}", cxbind); | |
2044 | println!("resolves to: {}", resolved_binding); | |
2045 | println!("varref: {}", v.segments[0].identifier); | |
2046 | println!("resolves to: {}", | |
2047 | mtwt::resolve(v.segments[0].identifier)); | |
2048 | mtwt::with_sctable(|x| mtwt::display_sctable(x)); | |
2049 | } | |
2050 | assert_eq!(mtwt::resolve(v.segments[0].identifier), | |
2051 | resolved_binding); | |
970d7e83 | 2052 | }; |
1a4d82fc | 2053 | } |
223e47cc | 2054 | |
1a4d82fc JJ |
2055 | #[test] |
2056 | fn pat_idents(){ | |
2057 | let pat = string_to_pat( | |
2058 | "(a,Foo{x:c @ (b,9),y:Bar(4,d)})".to_string()); | |
2059 | let idents = pattern_bindings(&*pat); | |
2060 | assert_eq!(idents, strs_to_idents(vec!("a","c","b","d"))); | |
2061 | } | |
970d7e83 | 2062 | |
1a4d82fc JJ |
2063 | // test the list of identifier patterns gathered by the visitor. Note that |
2064 | // 'None' is listed as an identifier pattern because we don't yet know that | |
2065 | // it's the name of a 0-ary variant, and that 'i' appears twice in succession. | |
2066 | #[test] | |
2067 | fn crate_bindings_test(){ | |
85aaf69f | 2068 | let the_crate = string_to_crate("fn main (a: i32) -> i32 {|b| { |
1a4d82fc JJ |
2069 | match 34 {None => 3, Some(i) | i => j, Foo{k:z,l:y} => \"banana\"}} }".to_string()); |
2070 | let idents = crate_bindings(&the_crate); | |
2071 | assert_eq!(idents, strs_to_idents(vec!("a","b","None","i","i","z","y"))); | |
970d7e83 LB |
2072 | } |
2073 | ||
1a4d82fc JJ |
2074 | // test the IdentRenamer directly |
2075 | #[test] | |
2076 | fn ident_renamer_test () { | |
85aaf69f | 2077 | let the_crate = string_to_crate("fn f(x: i32){let x = x; x}".to_string()); |
1a4d82fc JJ |
2078 | let f_ident = token::str_to_ident("f"); |
2079 | let x_ident = token::str_to_ident("x"); | |
85aaf69f | 2080 | let int_ident = token::str_to_ident("i32"); |
1a4d82fc JJ |
2081 | let renames = vec!((x_ident,Name(16))); |
2082 | let mut renamer = IdentRenamer{renames: &renames}; | |
2083 | let renamed_crate = renamer.fold_crate(the_crate); | |
2084 | let idents = crate_idents(&renamed_crate); | |
2085 | let resolved : Vec<ast::Name> = idents.iter().map(|id| mtwt::resolve(*id)).collect(); | |
c34b1796 | 2086 | assert_eq!(resolved, [f_ident.name,Name(16),int_ident.name,Name(16),Name(16),Name(16)]); |
1a4d82fc | 2087 | } |
970d7e83 | 2088 | |
1a4d82fc | 2089 | // test the PatIdentRenamer; only PatIdents get renamed |
970d7e83 | 2090 | #[test] |
1a4d82fc | 2091 | fn pat_ident_renamer_test () { |
85aaf69f | 2092 | let the_crate = string_to_crate("fn f(x: i32){let x = x; x}".to_string()); |
1a4d82fc JJ |
2093 | let f_ident = token::str_to_ident("f"); |
2094 | let x_ident = token::str_to_ident("x"); | |
85aaf69f | 2095 | let int_ident = token::str_to_ident("i32"); |
1a4d82fc JJ |
2096 | let renames = vec!((x_ident,Name(16))); |
2097 | let mut renamer = PatIdentRenamer{renames: &renames}; | |
2098 | let renamed_crate = renamer.fold_crate(the_crate); | |
2099 | let idents = crate_idents(&renamed_crate); | |
2100 | let resolved : Vec<ast::Name> = idents.iter().map(|id| mtwt::resolve(*id)).collect(); | |
2101 | let x_name = x_ident.name; | |
c34b1796 | 2102 | assert_eq!(resolved, [f_ident.name,Name(16),int_ident.name,Name(16),x_name,x_name]); |
970d7e83 LB |
2103 | } |
2104 | } |