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