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.
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.
11 use ast::{P, Block, Crate, DeclLocal, ExprMac, PatMac};
12 use ast::{Local, Ident, MacInvocTT};
13 use ast::{ItemMac, Mrk, Stmt, StmtDecl, StmtMac, StmtExpr, StmtSemi};
17 use ext::build::AstBuilder;
19 use attr::AttrMetaMethods;
21 use codemap::{Span, Spanned, ExpnInfo, NameAndSpan, MacroBang, MacroAttribute};
26 use parse::token::{fresh_mark, fresh_name, intern};
30 use util::small_vector::SmallVector;
32 use std::gc::{Gc, GC};
39 fn expand_expr(e: Gc<ast::Expr>, fld: &mut MacroExpander) -> Gc<ast::Expr> {
41 // expr_mac should really be expr_ext or something; it's the
42 // entry-point for all syntax extensions.
44 let expanded_expr = match expand_mac_invoc(mac,&e.span,
46 |expr,fm|{mark_expr(expr,fm)},
50 return DummyResult::raw_expr(e.span);
54 // Keep going, outside-in.
56 // FIXME(pcwalton): Is it necessary to clone the
59 fld.fold_expr(expanded_expr).node.clone();
63 id: ast::DUMMY_NODE_ID,
69 ast::ExprWhile(cond, body, opt_ident) => {
70 let cond = fld.fold_expr(cond);
71 let (body, opt_ident) = expand_loop_block(body, opt_ident, fld);
72 fld.cx.expr(e.span, ast::ExprWhile(cond, body, opt_ident))
75 ast::ExprLoop(loop_block, opt_ident) => {
76 let (loop_block, opt_ident) = expand_loop_block(loop_block, opt_ident, fld);
77 fld.cx.expr(e.span, ast::ExprLoop(loop_block, opt_ident))
80 ast::ExprForLoop(pat, head, body, opt_ident) => {
81 let pat = fld.fold_pat(pat);
82 let head = fld.fold_expr(head);
83 let (body, opt_ident) = expand_loop_block(body, opt_ident, fld);
84 fld.cx.expr(e.span, ast::ExprForLoop(pat, head, body, opt_ident))
87 ast::ExprFnBlock(capture_clause, fn_decl, block) => {
88 let (rewritten_fn_decl, rewritten_block)
89 = expand_and_rename_fn_decl_and_block(&*fn_decl, block, fld);
90 let new_node = ast::ExprFnBlock(capture_clause,
93 box(GC) ast::Expr{id:e.id, node: new_node, span: fld.new_span(e.span)}
96 ast::ExprProc(fn_decl, block) => {
97 let (rewritten_fn_decl, rewritten_block)
98 = expand_and_rename_fn_decl_and_block(&*fn_decl, block, fld);
99 let new_node = ast::ExprProc(rewritten_fn_decl, rewritten_block);
100 box(GC) ast::Expr{id:e.id, node: new_node, span: fld.new_span(e.span)}
103 _ => noop_fold_expr(e, fld)
107 /// Expand a (not-ident-style) macro invocation. Returns the result
108 /// of expansion and the mark which must be applied to the result.
109 /// Our current interface doesn't allow us to apply the mark to the
110 /// result until after calling make_expr, make_items, etc.
111 fn expand_mac_invoc<T>(mac: &ast::Mac, span: &codemap::Span,
112 parse_thunk: |Box<MacResult>|->Option<T>,
113 mark_thunk: |T,Mrk|->T,
114 fld: &mut MacroExpander)
118 // it would almost certainly be cleaner to pass the whole
119 // macro invocation in, rather than pulling it apart and
120 // marking the tts and the ctxt separately. This also goes
121 // for the other three macro invocation chunks of code
123 // Token-tree macros:
124 MacInvocTT(ref pth, ref tts, _) => {
125 if pth.segments.len() > 1u {
126 fld.cx.span_err(pth.span,
127 "expected macro name without module \
129 // let compilation continue
132 let extname = pth.segments.get(0).identifier;
133 let extnamestr = token::get_ident(extname);
134 match fld.cx.syntax_env.find(&extname.name) {
138 format!("macro undefined: '{}!'",
139 extnamestr.get()).as_slice());
141 // let compilation continue
144 Some(rc) => match *rc {
145 NormalTT(ref expandfun, exp_span) => {
146 fld.cx.bt_push(ExpnInfo {
148 callee: NameAndSpan {
149 name: extnamestr.get().to_string(),
154 let fm = fresh_mark();
155 let marked_before = mark_tts(tts.as_slice(), fm);
157 // The span that we pass to the expanders we want to
158 // be the root of the call stack. That's the most
159 // relevant span and it's the actual invocation of
161 let mac_span = original_span(fld.cx);
164 let expanded = expandfun.expand(fld.cx,
166 marked_before.as_slice());
167 parse_thunk(expanded)
169 let parsed = match opt_parsed {
174 format!("non-expression macro in expression position: {}",
175 extnamestr.get().as_slice()
180 Some(mark_thunk(parsed,fm))
185 format!("'{}' is not a tt-style macro",
186 extnamestr.get()).as_slice());
195 /// Rename loop label and expand its loop body
197 /// The renaming procedure for loop is different in the sense that the loop
198 /// body is in a block enclosed by loop head so the renaming of loop label
199 /// must be propagated to the enclosed context.
200 fn expand_loop_block(loop_block: P<Block>,
201 opt_ident: Option<Ident>,
202 fld: &mut MacroExpander) -> (P<Block>, Option<Ident>) {
205 let new_label = fresh_name(&label);
206 let rename = (label, new_label);
208 // The rename *must not* be added to the pending list of current
209 // syntax context otherwise an unrelated `break` or `continue` in
210 // the same context will pick that up in the deferred renaming pass
211 // and be renamed incorrectly.
212 let mut rename_list = vec!(rename);
213 let mut rename_fld = IdentRenamer{renames: &mut rename_list};
214 let renamed_ident = rename_fld.fold_ident(label);
216 // The rename *must* be added to the enclosed syntax context for
217 // `break` or `continue` to pick up because by definition they are
218 // in a block enclosed by loop head.
219 fld.cx.syntax_env.push_frame();
220 fld.cx.syntax_env.info().pending_renames.push(rename);
221 let expanded_block = expand_block_elts(&*loop_block, fld);
222 fld.cx.syntax_env.pop_frame();
224 (expanded_block, Some(renamed_ident))
226 None => (fld.fold_block(loop_block), opt_ident)
230 // eval $e with a new exts frame.
231 // must be a macro so that $e isn't evaluated too early.
232 macro_rules! with_exts_frame (
233 ($extsboxexpr:expr,$macros_escape:expr,$e:expr) =>
234 ({$extsboxexpr.push_frame();
235 $extsboxexpr.info().macros_escape = $macros_escape;
237 $extsboxexpr.pop_frame();
242 // When we enter a module, record it, for the sake of `module!`
243 fn expand_item(it: Gc<ast::Item>, fld: &mut MacroExpander)
244 -> SmallVector<Gc<ast::Item>> {
245 let it = expand_item_modifiers(it, fld);
247 let mut decorator_items = SmallVector::zero();
248 let mut new_attrs = Vec::new();
249 for attr in it.attrs.iter() {
250 let mname = attr.name();
252 match fld.cx.syntax_env.find(&intern(mname.get())) {
253 Some(rc) => match *rc {
254 ItemDecorator(ref dec) => {
255 attr::mark_used(attr);
257 fld.cx.bt_push(ExpnInfo {
258 call_site: attr.span,
259 callee: NameAndSpan {
260 name: mname.get().to_string(),
261 format: MacroAttribute,
266 // we'd ideally decorator_items.push_all(expand_item(item, fld)),
267 // but that double-mut-borrows fld
268 let mut items: SmallVector<Gc<ast::Item>> = SmallVector::zero();
269 dec.expand(fld.cx, attr.span, attr.node.value, it, |item| items.push(item));
270 decorator_items.extend(items.move_iter()
271 .flat_map(|item| expand_item(item, fld).move_iter()));
275 _ => new_attrs.push((*attr).clone()),
277 _ => new_attrs.push((*attr).clone()),
281 let mut new_items = match it.node {
282 ast::ItemMac(..) => expand_item_mac(it, fld),
283 ast::ItemMod(_) | ast::ItemForeignMod(_) => {
284 fld.cx.mod_push(it.ident);
285 let macro_escape = contains_macro_escape(new_attrs.as_slice());
286 let result = with_exts_frame!(fld.cx.syntax_env,
288 noop_fold_item(&*it, fld));
293 let it = box(GC) ast::Item {
298 noop_fold_item(&*it, fld)
302 new_items.push_all(decorator_items);
306 fn expand_item_modifiers(mut it: Gc<ast::Item>, fld: &mut MacroExpander)
308 // partition the attributes into ItemModifiers and others
309 let (modifiers, other_attrs) = it.attrs.partitioned(|attr| {
310 match fld.cx.syntax_env.find(&intern(attr.name().get())) {
311 Some(rc) => match *rc { ItemModifier(_) => true, _ => false },
315 // update the attrs, leave everything else alone. Is this mutation really a good idea?
316 it = box(GC) ast::Item {
321 if modifiers.is_empty() {
325 for attr in modifiers.iter() {
326 let mname = attr.name();
328 match fld.cx.syntax_env.find(&intern(mname.get())) {
329 Some(rc) => match *rc {
330 ItemModifier(ref mac) => {
331 attr::mark_used(attr);
332 fld.cx.bt_push(ExpnInfo {
333 call_site: attr.span,
334 callee: NameAndSpan {
335 name: mname.get().to_string(),
336 format: MacroAttribute,
340 it = mac.expand(fld.cx, attr.span, attr.node.value, it);
349 // expansion may have added new ItemModifiers
350 expand_item_modifiers(it, fld)
353 /// Expand item_underscore
354 fn expand_item_underscore(item: &ast::Item_, fld: &mut MacroExpander) -> ast::Item_ {
356 ast::ItemFn(decl, fn_style, abi, ref generics, body) => {
357 let (rewritten_fn_decl, rewritten_body)
358 = expand_and_rename_fn_decl_and_block(&*decl, body, fld);
359 let expanded_generics = fold::noop_fold_generics(generics,fld);
360 ast::ItemFn(rewritten_fn_decl, fn_style, abi, expanded_generics, rewritten_body)
362 _ => noop_fold_item_underscore(&*item, fld)
366 // does this attribute list contain "macro_escape" ?
367 fn contains_macro_escape(attrs: &[ast::Attribute]) -> bool {
368 attr::contains_name(attrs, "macro_escape")
371 // Support for item-position macro invocations, exactly the same
372 // logic as for expression-position macro invocations.
373 fn expand_item_mac(it: Gc<ast::Item>, fld: &mut MacroExpander)
374 -> SmallVector<Gc<ast::Item>>
376 let (pth, tts) = match it.node {
377 ItemMac(codemap::Spanned {
378 node: MacInvocTT(ref pth, ref tts, _),
381 (pth, (*tts).clone())
383 _ => fld.cx.span_bug(it.span, "invalid item macro invocation")
386 let extname = pth.segments.get(0).identifier;
387 let extnamestr = token::get_ident(extname);
388 let fm = fresh_mark();
390 let expanded = match fld.cx.syntax_env.find(&extname.name) {
392 fld.cx.span_err(pth.span,
393 format!("macro undefined: '{}!'",
394 extnamestr).as_slice());
395 // let compilation continue
396 return SmallVector::zero();
399 Some(rc) => match *rc {
400 NormalTT(ref expander, span) => {
401 if it.ident.name != parse::token::special_idents::invalid.name {
404 format!("macro {}! expects no ident argument, \
407 token::get_ident(it.ident)).as_slice());
408 return SmallVector::zero();
410 fld.cx.bt_push(ExpnInfo {
412 callee: NameAndSpan {
413 name: extnamestr.get().to_string(),
418 // mark before expansion:
419 let marked_before = mark_tts(tts.as_slice(), fm);
420 expander.expand(fld.cx, it.span, marked_before.as_slice())
422 IdentTT(ref expander, span) => {
423 if it.ident.name == parse::token::special_idents::invalid.name {
424 fld.cx.span_err(pth.span,
425 format!("macro {}! expects an ident argument",
426 extnamestr.get()).as_slice());
427 return SmallVector::zero();
429 fld.cx.bt_push(ExpnInfo {
431 callee: NameAndSpan {
432 name: extnamestr.get().to_string(),
437 // mark before expansion:
438 let marked_tts = mark_tts(tts.as_slice(), fm);
439 expander.expand(fld.cx, it.span, it.ident, marked_tts)
441 LetSyntaxTT(ref expander, span) => {
442 if it.ident.name == parse::token::special_idents::invalid.name {
443 fld.cx.span_err(pth.span,
444 format!("macro {}! expects an ident argument",
445 extnamestr.get()).as_slice());
446 return SmallVector::zero();
448 fld.cx.bt_push(ExpnInfo {
450 callee: NameAndSpan {
451 name: extnamestr.get().to_string(),
456 // DON'T mark before expansion:
457 expander.expand(fld.cx, it.span, it.ident, tts)
460 fld.cx.span_err(it.span,
461 format!("{}! is not legal in item position",
462 extnamestr.get()).as_slice());
463 return SmallVector::zero();
468 match expanded.make_def() {
469 Some(def) => Left(def),
470 None => Right(expanded.make_items())
474 let items = match def_or_items {
475 Left(MacroDef { name, ext }) => {
476 // hidden invariant: this should only be possible as the
477 // result of expanding a LetSyntaxTT, and thus doesn't
478 // need to be marked. Not that it could be marked anyway.
479 // create issue to recommend refactoring here?
480 fld.cx.syntax_env.insert(intern(name.as_slice()), ext);
481 if attr::contains_name(it.attrs.as_slice(), "macro_export") {
482 fld.cx.exported_macros.push(it);
486 Right(Some(items)) => {
488 .map(|i| mark_item(i, fm))
489 .flat_map(|i| fld.fold_item(i).move_iter())
493 fld.cx.span_err(pth.span,
494 format!("non-item macro in item position: {}",
495 extnamestr.get()).as_slice());
496 return SmallVector::zero();
506 // I don't understand why this returns a vector... it looks like we're
507 // half done adding machinery to allow macros to expand into multiple statements.
508 fn expand_stmt(s: &Stmt, fld: &mut MacroExpander) -> SmallVector<Gc<Stmt>> {
509 let (mac, semi) = match s.node {
510 StmtMac(ref mac, semi) => (mac, semi),
511 _ => return expand_non_macro_stmt(s, fld)
513 let expanded_stmt = match expand_mac_invoc(mac,&s.span,
521 return SmallVector::zero();
525 // Keep going, outside-in.
526 let fully_expanded = fld.fold_stmt(&*expanded_stmt);
528 let fully_expanded: SmallVector<Gc<Stmt>> = fully_expanded.move_iter()
529 .map(|s| box(GC) Spanned { span: s.span, node: s.node.clone() })
532 fully_expanded.move_iter().map(|s| {
534 StmtExpr(e, stmt_id) if semi => {
537 node: StmtSemi(e, stmt_id)
540 _ => s /* might already have a semi */
545 // expand a non-macro stmt. this is essentially the fallthrough for
546 // expand_stmt, above.
547 fn expand_non_macro_stmt(s: &Stmt, fld: &mut MacroExpander)
548 -> SmallVector<Gc<Stmt>> {
551 StmtDecl(decl, node_id) => {
554 node: DeclLocal(ref local),
566 // expand the ty since TyFixedLengthVec contains an Expr
567 // and thus may have a macro use
568 let expanded_ty = fld.fold_ty(ty);
569 // expand the pat (it might contain macro uses):
570 let expanded_pat = fld.fold_pat(pat);
571 // find the PatIdents in the pattern:
572 // oh dear heaven... this is going to include the enum
573 // names, as well... but that should be okay, as long as
574 // the new names are gensyms for the old ones.
575 // generate fresh names, push them to a new pending list
576 let idents = pattern_bindings(&*expanded_pat);
577 let mut new_pending_renames =
578 idents.iter().map(|ident| (*ident, fresh_name(ident))).collect();
579 // rewrite the pattern using the new names (the old
580 // ones have already been applied):
581 let rewritten_pat = {
582 // nested binding to allow borrow to expire:
583 let mut rename_fld = IdentRenamer{renames: &mut new_pending_renames};
584 rename_fld.fold_pat(expanded_pat)
586 // add them to the existing pending renames:
587 fld.cx.syntax_env.info().pending_renames.push_all_move(new_pending_renames);
588 // also, don't forget to expand the init:
589 let new_init_opt = init.map(|e| fld.fold_expr(e));
590 let rewritten_local =
599 SmallVector::one(box(GC) Spanned {
600 node: StmtDecl(box(GC) Spanned {
601 node: DeclLocal(rewritten_local),
608 _ => noop_fold_stmt(s, fld),
611 _ => noop_fold_stmt(s, fld),
615 // expand the arm of a 'match', renaming for macro hygiene
616 fn expand_arm(arm: &ast::Arm, fld: &mut MacroExpander) -> ast::Arm {
617 // expand pats... they might contain macro uses:
618 let expanded_pats : Vec<Gc<ast::Pat>> = arm.pats.iter().map(|pat| fld.fold_pat(*pat)).collect();
619 if expanded_pats.len() == 0 {
620 fail!("encountered match arm with 0 patterns");
622 // all of the pats must have the same set of bindings, so use the
623 // first one to extract them and generate new names:
624 let first_pat = expanded_pats.get(0);
625 let idents = pattern_bindings(&**first_pat);
627 idents.iter().map(|id| (*id,fresh_name(id))).collect();
628 // apply the renaming, but only to the PatIdents:
629 let mut rename_pats_fld = PatIdentRenamer{renames:&new_renames};
631 expanded_pats.iter().map(|pat| rename_pats_fld.fold_pat(*pat)).collect();
632 // apply renaming and then expansion to the guard and the body:
633 let mut rename_fld = IdentRenamer{renames:&new_renames};
634 let rewritten_guard =
635 arm.guard.map(|g| fld.fold_expr(rename_fld.fold_expr(g)));
636 let rewritten_body = fld.fold_expr(rename_fld.fold_expr(arm.body));
638 attrs: arm.attrs.iter().map(|x| fld.fold_attribute(*x)).collect(),
639 pats: rewritten_pats,
640 guard: rewritten_guard,
641 body: rewritten_body,
645 /// A visitor that extracts the PatIdent (binding) paths
646 /// from a given thingy and puts them in a mutable
649 struct PatIdentFinder {
650 ident_accumulator: Vec<ast::Ident> ,
653 impl Visitor<()> for PatIdentFinder {
654 fn visit_pat(&mut self, pattern: &ast::Pat, _: ()) {
656 ast::Pat { id: _, node: ast::PatIdent(_, ref path1, ref inner), span: _ } => {
657 self.ident_accumulator.push(path1.node);
658 // visit optional subpattern of PatIdent:
659 for subpat in inner.iter() {
660 self.visit_pat(&**subpat, ())
663 // use the default traversal for non-PatIdents
664 _ => visit::walk_pat(self, pattern, ())
669 /// find the PatIdent paths in a pattern
670 fn pattern_bindings(pat : &ast::Pat) -> Vec<ast::Ident> {
671 let mut name_finder = PatIdentFinder{ident_accumulator:Vec::new()};
672 name_finder.visit_pat(pat,());
673 name_finder.ident_accumulator
676 /// find the PatIdent paths in a
677 fn fn_decl_arg_bindings(fn_decl: &ast::FnDecl) -> Vec<ast::Ident> {
678 let mut pat_idents = PatIdentFinder{ident_accumulator:Vec::new()};
679 for arg in fn_decl.inputs.iter() {
680 pat_idents.visit_pat(&*arg.pat, ());
682 pat_idents.ident_accumulator
685 // expand a block. pushes a new exts_frame, then calls expand_block_elts
686 fn expand_block(blk: &Block, fld: &mut MacroExpander) -> P<Block> {
687 // see note below about treatment of exts table
688 with_exts_frame!(fld.cx.syntax_env,false,
689 expand_block_elts(blk, fld))
692 // expand the elements of a block.
693 fn expand_block_elts(b: &Block, fld: &mut MacroExpander) -> P<Block> {
694 let new_view_items = b.view_items.iter().map(|x| fld.fold_view_item(x)).collect();
696 b.stmts.iter().flat_map(|x| {
697 // perform all pending renames
699 let pending_renames = &mut fld.cx.syntax_env.info().pending_renames;
700 let mut rename_fld = IdentRenamer{renames:pending_renames};
701 rename_fld.fold_stmt(&**x).expect_one("rename_fold didn't return one value")
703 // expand macros in the statement
704 fld.fold_stmt(&*renamed_stmt).move_iter()
706 let new_expr = b.expr.map(|x| {
708 let pending_renames = &mut fld.cx.syntax_env.info().pending_renames;
709 let mut rename_fld = IdentRenamer{renames:pending_renames};
710 rename_fld.fold_expr(x)
715 view_items: new_view_items,
718 id: fld.new_id(b.id),
724 fn expand_pat(p: Gc<ast::Pat>, fld: &mut MacroExpander) -> Gc<ast::Pat> {
725 let (pth, tts) = match p.node {
728 MacInvocTT(ref pth, ref tts, _) => {
729 (pth, (*tts).clone())
733 _ => return noop_fold_pat(p, fld),
735 if pth.segments.len() > 1u {
736 fld.cx.span_err(pth.span, "expected macro name without module separators");
737 return DummyResult::raw_pat(p.span);
739 let extname = pth.segments.get(0).identifier;
740 let extnamestr = token::get_ident(extname);
741 let marked_after = match fld.cx.syntax_env.find(&extname.name) {
743 fld.cx.span_err(pth.span,
744 format!("macro undefined: '{}!'",
745 extnamestr).as_slice());
746 // let compilation continue
747 return DummyResult::raw_pat(p.span);
750 Some(rc) => match *rc {
751 NormalTT(ref expander, span) => {
752 fld.cx.bt_push(ExpnInfo {
754 callee: NameAndSpan {
755 name: extnamestr.get().to_string(),
761 let fm = fresh_mark();
762 let marked_before = mark_tts(tts.as_slice(), fm);
763 let mac_span = original_span(fld.cx);
764 let expanded = match expander.expand(fld.cx,
766 marked_before.as_slice()).make_pat() {
772 "non-pattern macro in pattern position: {}",
776 return DummyResult::raw_pat(p.span);
781 mark_pat(expanded,fm)
784 fld.cx.span_err(p.span,
785 format!("{}! is not legal in pattern position",
786 extnamestr.get()).as_slice());
787 return DummyResult::raw_pat(p.span);
793 fld.fold_pat(marked_after).node.clone();
797 id: ast::DUMMY_NODE_ID,
798 node: fully_expanded,
803 /// A tree-folder that applies every rename in its (mutable) list
804 /// to every identifier, including both bindings and varrefs
805 /// (and lots of things that will turn out to be neither)
806 pub struct IdentRenamer<'a> {
807 renames: &'a mtwt::RenameList,
810 impl<'a> Folder for IdentRenamer<'a> {
811 fn fold_ident(&mut self, id: Ident) -> Ident {
814 ctxt: mtwt::apply_renames(self.renames, id.ctxt),
817 fn fold_mac(&mut self, macro: &ast::Mac) -> ast::Mac {
818 fold::noop_fold_mac(macro, self)
822 /// A tree-folder that applies every rename in its list to
823 /// the idents that are in PatIdent patterns. This is more narrowly
824 /// focused than IdentRenamer, and is needed for FnDecl,
825 /// where we want to rename the args but not the fn name or the generics etc.
826 pub struct PatIdentRenamer<'a> {
827 renames: &'a mtwt::RenameList,
830 impl<'a> Folder for PatIdentRenamer<'a> {
831 fn fold_pat(&mut self, pat: Gc<ast::Pat>) -> Gc<ast::Pat> {
833 ast::PatIdent(binding_mode, Spanned{span: ref sp, node: id}, ref sub) => {
834 let new_ident = Ident{name: id.name,
835 ctxt: mtwt::apply_renames(self.renames, id.ctxt)};
837 ast::PatIdent(binding_mode,
838 Spanned{span: self.new_span(*sp), node: new_ident},
839 sub.map(|p| self.fold_pat(p)));
842 span: self.new_span(pat.span),
846 _ => noop_fold_pat(pat, self)
849 fn fold_mac(&mut self, macro: &ast::Mac) -> ast::Mac {
850 fold::noop_fold_mac(macro, self)
855 fn expand_method(m: &ast::Method, fld: &mut MacroExpander) -> SmallVector<Gc<ast::Method>> {
856 let id = fld.new_id(m.id);
866 let (rewritten_fn_decl, rewritten_body)
867 = expand_and_rename_fn_decl_and_block(&*decl,body,fld);
868 SmallVector::one(box(GC) ast::Method {
869 attrs: m.attrs.iter().map(|a| fld.fold_attribute(*a)).collect(),
871 span: fld.new_span(m.span),
872 node: ast::MethDecl(fld.fold_ident(ident),
873 noop_fold_generics(generics, fld),
875 fld.fold_explicit_self(explicit_self),
882 ast::MethMac(ref mac) => {
883 let maybe_new_methods =
884 expand_mac_invoc(mac, &m.span,
885 |r|{r.make_methods()},
887 meths.move_iter().map(|m|{mark_method(m,mark)})
891 let new_methods = match maybe_new_methods {
892 Some(methods) => methods,
893 None => SmallVector::zero()
896 // expand again if necessary
897 new_methods.move_iter().flat_map(|m| fld.fold_method(m).move_iter()).collect()
902 /// Given a fn_decl and a block and a MacroExpander, expand the fn_decl, then use the
903 /// PatIdents in its arguments to perform renaming in the FnDecl and
904 /// the block, returning both the new FnDecl and the new Block.
905 fn expand_and_rename_fn_decl_and_block(fn_decl: &ast::FnDecl, block: Gc<ast::Block>,
906 fld: &mut MacroExpander)
907 -> (Gc<ast::FnDecl>, Gc<ast::Block>) {
908 let expanded_decl = fld.fold_fn_decl(fn_decl);
909 let idents = fn_decl_arg_bindings(&*expanded_decl);
911 idents.iter().map(|id : &ast::Ident| (*id,fresh_name(id))).collect();
912 // first, a renamer for the PatIdents, for the fn_decl:
913 let mut rename_pat_fld = PatIdentRenamer{renames: &renames};
914 let rewritten_fn_decl = rename_pat_fld.fold_fn_decl(&*expanded_decl);
915 // now, a renamer for *all* idents, for the body:
916 let mut rename_fld = IdentRenamer{renames: &renames};
917 let rewritten_body = fld.fold_block(rename_fld.fold_block(block));
918 (rewritten_fn_decl,rewritten_body)
921 /// A tree-folder that performs macro expansion
922 pub struct MacroExpander<'a, 'b:'a> {
923 pub cx: &'a mut ExtCtxt<'b>,
926 impl<'a, 'b> Folder for MacroExpander<'a, 'b> {
927 fn fold_expr(&mut self, expr: Gc<ast::Expr>) -> Gc<ast::Expr> {
928 expand_expr(expr, self)
931 fn fold_pat(&mut self, pat: Gc<ast::Pat>) -> Gc<ast::Pat> {
932 expand_pat(pat, self)
935 fn fold_item(&mut self, item: Gc<ast::Item>) -> SmallVector<Gc<ast::Item>> {
936 expand_item(item, self)
939 fn fold_item_underscore(&mut self, item: &ast::Item_) -> ast::Item_ {
940 expand_item_underscore(item, self)
943 fn fold_stmt(&mut self, stmt: &ast::Stmt) -> SmallVector<Gc<ast::Stmt>> {
944 expand_stmt(stmt, self)
947 fn fold_block(&mut self, block: P<Block>) -> P<Block> {
948 expand_block(&*block, self)
951 fn fold_arm(&mut self, arm: &ast::Arm) -> ast::Arm {
952 expand_arm(arm, self)
955 fn fold_method(&mut self, method: Gc<ast::Method>) -> SmallVector<Gc<ast::Method>> {
956 expand_method(&*method, self)
959 fn new_span(&mut self, span: Span) -> Span {
960 new_span(self.cx, span)
964 fn new_span(cx: &ExtCtxt, sp: Span) -> Span {
965 /* this discards information in the case of macro-defining macros */
969 expn_info: cx.backtrace(),
973 pub struct ExpansionConfig {
974 pub deriving_hash_type_parameter: bool,
975 pub crate_name: String,
978 pub struct ExportedMacros {
979 pub crate_name: Ident,
980 pub macros: Vec<String>,
983 pub fn expand_crate(parse_sess: &parse::ParseSess,
984 cfg: ExpansionConfig,
985 // these are the macros being imported to this crate:
986 imported_macros: Vec<ExportedMacros>,
987 user_exts: Vec<NamedSyntaxExtension>,
989 let mut cx = ExtCtxt::new(parse_sess, c.config.clone(), cfg);
990 let mut expander = MacroExpander {
994 for ExportedMacros { crate_name, macros } in imported_macros.move_iter() {
995 let name = format!("<{} macros>", token::get_ident(crate_name))
998 for source in macros.move_iter() {
999 let item = parse::parse_item_from_source_str(name.clone(),
1002 expander.cx.parse_sess())
1003 .expect("expected a serialized item");
1004 expand_item_mac(item, &mut expander);
1008 for (name, extension) in user_exts.move_iter() {
1009 expander.cx.syntax_env.insert(name, extension);
1012 let mut ret = expander.fold_crate(c);
1013 ret.exported_macros = expander.cx.exported_macros.clone();
1014 parse_sess.span_diagnostic.handler().abort_if_errors();
1018 // HYGIENIC CONTEXT EXTENSION:
1019 // all of these functions are for walking over
1020 // ASTs and making some change to the context of every
1021 // element that has one. a CtxtFn is a trait-ified
1022 // version of a closure in (SyntaxContext -> SyntaxContext).
1023 // the ones defined here include:
1024 // Marker - add a mark to a context
1026 // A Marker adds the given mark to the syntax context
1027 struct Marker { mark: Mrk }
1029 impl Folder for Marker {
1030 fn fold_ident(&mut self, id: Ident) -> Ident {
1033 ctxt: mtwt::apply_mark(self.mark, id.ctxt)
1036 fn fold_mac(&mut self, m: &ast::Mac) -> ast::Mac {
1037 let macro = match m.node {
1038 MacInvocTT(ref path, ref tts, ctxt) => {
1039 MacInvocTT(self.fold_path(path),
1040 self.fold_tts(tts.as_slice()),
1041 mtwt::apply_mark(self.mark, ctxt))
1051 // apply a given mark to the given token trees. Used prior to expansion of a macro.
1052 fn mark_tts(tts: &[TokenTree], m: Mrk) -> Vec<TokenTree> {
1053 noop_fold_tts(tts, &mut Marker{mark:m})
1056 // apply a given mark to the given expr. Used following the expansion of a macro.
1057 fn mark_expr(expr: Gc<ast::Expr>, m: Mrk) -> Gc<ast::Expr> {
1058 Marker{mark:m}.fold_expr(expr)
1061 // apply a given mark to the given pattern. Used following the expansion of a macro.
1062 fn mark_pat(pat: Gc<ast::Pat>, m: Mrk) -> Gc<ast::Pat> {
1063 Marker{mark:m}.fold_pat(pat)
1066 // apply a given mark to the given stmt. Used following the expansion of a macro.
1067 fn mark_stmt(expr: &ast::Stmt, m: Mrk) -> Gc<ast::Stmt> {
1068 Marker{mark:m}.fold_stmt(expr)
1069 .expect_one("marking a stmt didn't return exactly one stmt")
1072 // apply a given mark to the given item. Used following the expansion of a macro.
1073 fn mark_item(expr: Gc<ast::Item>, m: Mrk) -> Gc<ast::Item> {
1074 Marker{mark:m}.fold_item(expr)
1075 .expect_one("marking an item didn't return exactly one item")
1078 // apply a given mark to the given item. Used following the expansion of a macro.
1079 fn mark_method(expr: Gc<ast::Method>, m: Mrk) -> Gc<ast::Method> {
1080 Marker{mark:m}.fold_method(expr)
1081 .expect_one("marking an item didn't return exactly one method")
1084 fn original_span(cx: &ExtCtxt) -> Gc<codemap::ExpnInfo> {
1085 let mut relevant_info = cx.backtrace();
1086 let mut einfo = relevant_info.unwrap();
1088 match relevant_info {
1092 relevant_info = einfo.call_site.expn_info;
1099 /// Check that there are no macro invocations left in the AST:
1100 pub fn check_for_macros(sess: &parse::ParseSess, krate: &ast::Crate) {
1101 visit::walk_crate(&mut MacroExterminator{sess:sess}, krate, ());
1104 /// A visitor that ensures that no macro invocations remain in an AST.
1105 struct MacroExterminator<'a>{
1106 sess: &'a parse::ParseSess
1109 impl<'a> visit::Visitor<()> for MacroExterminator<'a> {
1110 fn visit_mac(&mut self, macro: &ast::Mac, _:()) {
1111 self.sess.span_diagnostic.span_bug(macro.span,
1112 "macro exterminator: expected AST \
1113 with no macro invocations");
1120 use super::{pattern_bindings, expand_crate, contains_macro_escape};
1121 use super::{PatIdentFinder, IdentRenamer, PatIdentRenamer};
1123 use ast::{Attribute_, AttrOuter, MetaWord, Name};
1126 use codemap::Spanned;
1131 use util::parser_testing::{string_to_parser};
1132 use util::parser_testing::{string_to_pat, string_to_crate, strs_to_idents};
1138 // a visitor that extracts the paths
1139 // from a given thingy and puts them in a mutable
1140 // array (passed in to the traversal)
1142 struct PathExprFinderContext {
1143 path_accumulator: Vec<ast::Path> ,
1146 impl Visitor<()> for PathExprFinderContext {
1148 fn visit_expr(&mut self, expr: &ast::Expr, _: ()) {
1150 ast::Expr{id:_,span:_,node:ast::ExprPath(ref p)} => {
1151 self.path_accumulator.push(p.clone());
1152 // not calling visit_path, but it should be fine.
1154 _ => visit::walk_expr(self,expr,())
1159 // find the variable references in a crate
1160 fn crate_varrefs(the_crate : &ast::Crate) -> Vec<ast::Path> {
1161 let mut path_finder = PathExprFinderContext{path_accumulator:Vec::new()};
1162 visit::walk_crate(&mut path_finder, the_crate, ());
1163 path_finder.path_accumulator
1166 /// A Visitor that extracts the identifiers from a thingy.
1167 // as a side note, I'm starting to want to abstract over these....
1169 ident_accumulator: Vec<ast::Ident>
1172 impl Visitor<()> for IdentFinder {
1173 fn visit_ident(&mut self, _: codemap::Span, id: ast::Ident, _: ()){
1174 self.ident_accumulator.push(id);
1178 /// Find the idents in a crate
1179 fn crate_idents(the_crate: &ast::Crate) -> Vec<ast::Ident> {
1180 let mut ident_finder = IdentFinder{ident_accumulator: Vec::new()};
1181 visit::walk_crate(&mut ident_finder, the_crate, ());
1182 ident_finder.ident_accumulator
1185 // these following tests are quite fragile, in that they don't test what
1186 // *kind* of failure occurs.
1188 // make sure that macros can't escape fns
1190 #[test] fn macros_cant_escape_fns_test () {
1191 let src = "fn bogus() {macro_rules! z (() => (3+4))}\
1192 fn inty() -> int { z!() }".to_string();
1193 let sess = parse::new_parse_sess();
1194 let crate_ast = parse::parse_crate_from_source_str(
1195 "<test>".to_string(),
1199 let cfg = ::syntax::ext::expand::ExpansionConfig {
1200 deriving_hash_type_parameter: false,
1201 crate_name: "test".to_string(),
1203 expand_crate(&sess,cfg,vec!(),vec!(),crate_ast);
1206 // make sure that macros can't escape modules
1208 #[test] fn macros_cant_escape_mods_test () {
1209 let src = "mod foo {macro_rules! z (() => (3+4))}\
1210 fn inty() -> int { z!() }".to_string();
1211 let sess = parse::new_parse_sess();
1212 let crate_ast = parse::parse_crate_from_source_str(
1213 "<test>".to_string(),
1216 let cfg = ::syntax::ext::expand::ExpansionConfig {
1217 deriving_hash_type_parameter: false,
1218 crate_name: "test".to_string(),
1220 expand_crate(&sess,cfg,vec!(),vec!(),crate_ast);
1223 // macro_escape modules should allow macros to escape
1224 #[test] fn macros_can_escape_flattened_mods_test () {
1225 let src = "#[macro_escape] mod foo {macro_rules! z (() => (3+4))}\
1226 fn inty() -> int { z!() }".to_string();
1227 let sess = parse::new_parse_sess();
1228 let crate_ast = parse::parse_crate_from_source_str(
1229 "<test>".to_string(),
1232 let cfg = ::syntax::ext::expand::ExpansionConfig {
1233 deriving_hash_type_parameter: false,
1234 crate_name: "test".to_string(),
1236 expand_crate(&sess, cfg, vec!(), vec!(), crate_ast);
1239 #[test] fn test_contains_flatten (){
1240 let attr1 = make_dummy_attr ("foo");
1241 let attr2 = make_dummy_attr ("bar");
1242 let escape_attr = make_dummy_attr ("macro_escape");
1243 let attrs1 = vec!(attr1, escape_attr, attr2);
1244 assert_eq!(contains_macro_escape(attrs1.as_slice()),true);
1245 let attrs2 = vec!(attr1,attr2);
1246 assert_eq!(contains_macro_escape(attrs2.as_slice()),false);
1249 // make a MetaWord outer attribute with the given name
1250 fn make_dummy_attr(s: &str) -> ast::Attribute {
1252 span:codemap::DUMMY_SP,
1254 id: attr::mk_attr_id(),
1256 value: box(GC) Spanned {
1257 node: MetaWord(token::intern_and_get_ident(s)),
1258 span: codemap::DUMMY_SP,
1260 is_sugared_doc: false,
1265 fn expand_crate_str(crate_str: String) -> ast::Crate {
1266 let ps = parse::new_parse_sess();
1267 let crate_ast = string_to_parser(&ps, crate_str).parse_crate_mod();
1268 // the cfg argument actually does matter, here...
1269 let cfg = ::syntax::ext::expand::ExpansionConfig {
1270 deriving_hash_type_parameter: false,
1271 crate_name: "test".to_string(),
1273 expand_crate(&ps,cfg,vec!(),vec!(),crate_ast)
1276 // find the pat_ident paths in a crate
1277 fn crate_bindings(the_crate : &ast::Crate) -> Vec<ast::Ident> {
1278 let mut name_finder = PatIdentFinder{ident_accumulator:Vec::new()};
1279 visit::walk_crate(&mut name_finder, the_crate, ());
1280 name_finder.ident_accumulator
1283 #[test] fn macro_tokens_should_match(){
1285 "macro_rules! m((a)=>(13)) fn main(){m!(a);}".to_string());
1288 // should be able to use a bound identifier as a literal in a macro definition:
1289 #[test] fn self_macro_parsing(){
1291 "macro_rules! foo ((zz) => (287u;))
1292 fn f(zz : int) {foo!(zz);}".to_string()
1296 // renaming tests expand a crate and then check that the bindings match
1297 // the right varrefs. The specification of the test case includes the
1298 // text of the crate, and also an array of arrays. Each element in the
1299 // outer array corresponds to a binding in the traversal of the AST
1300 // induced by visit. Each of these arrays contains a list of indexes,
1301 // interpreted as the varrefs in the varref traversal that this binding
1302 // should match. So, for instance, in a program with two bindings and
1303 // three varrefs, the array ~[~[1,2],~[0]] would indicate that the first
1304 // binding should match the second two varrefs, and the second binding
1305 // should match the first varref.
1307 // Put differently; this is a sparse representation of a boolean matrix
1308 // indicating which bindings capture which identifiers.
1310 // Note also that this matrix is dependent on the implicit ordering of
1311 // the bindings and the varrefs discovered by the name-finder and the path-finder.
1313 // The comparisons are done post-mtwt-resolve, so we're comparing renamed
1314 // names; differences in marks don't matter any more.
1316 // oog... I also want tests that check "bound-identifier-=?". That is,
1317 // not just "do these have the same name", but "do they have the same
1318 // name *and* the same marks"? Understanding this is really pretty painful.
1319 // in principle, you might want to control this boolean on a per-varref basis,
1320 // but that would make things even harder to understand, and might not be
1321 // necessary for thorough testing.
1322 type RenamingTest = (&'static str, Vec<Vec<uint>>, bool);
1325 fn automatic_renaming () {
1326 let tests: Vec<RenamingTest> =
1327 vec!(// b & c should get new names throughout, in the expr too:
1328 ("fn a() -> int { let b = 13; let c = b; b+c }",
1329 vec!(vec!(0,1),vec!(2)), false),
1330 // both x's should be renamed (how is this causing a bug?)
1331 ("fn main () {let x: int = 13;x;}",
1332 vec!(vec!(0)), false),
1333 // the use of b after the + should be renamed, the other one not:
1334 ("macro_rules! f (($x:ident) => (b + $x)) fn a() -> int { let b = 13; f!(b)}",
1335 vec!(vec!(1)), false),
1336 // the b before the plus should not be renamed (requires marks)
1337 ("macro_rules! f (($x:ident) => ({let b=9; ($x + b)})) fn a() -> int { f!(b)}",
1338 vec!(vec!(1)), false),
1339 // the marks going in and out of letty should cancel, allowing that $x to
1340 // capture the one following the semicolon.
1341 // this was an awesome test case, and caught a *lot* of bugs.
1342 ("macro_rules! letty(($x:ident) => (let $x = 15;))
1343 macro_rules! user(($x:ident) => ({letty!($x); $x}))
1344 fn main() -> int {user!(z)}",
1345 vec!(vec!(0)), false)
1347 for (idx,s) in tests.iter().enumerate() {
1348 run_renaming_test(s,idx);
1352 // no longer a fixme #8062: this test exposes a *potential* bug; our system does
1353 // not behave exactly like MTWT, but a conversation with Matthew Flatt
1354 // suggests that this can only occur in the presence of local-expand, which
1355 // we have no plans to support. ... unless it's needed for item hygiene....
1357 #[test] fn issue_8062(){
1359 &("fn main() {let hrcoo = 19; macro_rules! getx(()=>(hrcoo)); getx!();}",
1360 vec!(vec!(0)), true), 0)
1364 // the z flows into and out of two macros (g & f) along one path, and one
1365 // (just g) along the other, so the result of the whole thing should
1366 // be "let z_123 = 3; z_123"
1368 #[test] fn issue_6994(){
1370 &("macro_rules! g (($x:ident) =>
1371 ({macro_rules! f(($y:ident)=>({let $y=3;$x}));f!($x)}))
1373 vec!(vec!(0)),false),
1377 // match variable hygiene. Should expand into
1378 // fn z() {match 8 {x_1 => {match 9 {x_2 | x_2 if x_2 == x_1 => x_2 + x_1}}}}
1379 #[test] fn issue_9384(){
1381 &("macro_rules! bad_macro (($ex:expr) => ({match 9 {x | x if x == $ex => x + $ex}}))
1382 fn z() {match 8 {x => bad_macro!(x)}}",
1383 // NB: the third "binding" is the repeat of the second one.
1384 vec!(vec!(1,3),vec!(0,2),vec!(0,2)),
1389 // interpolated nodes weren't getting labeled.
1390 // should expand into
1391 // fn main(){let g1_1 = 13; g1_1}}
1392 #[test] fn pat_expand_issue_15221(){
1394 &("macro_rules! inner ( ($e:pat ) => ($e))
1395 macro_rules! outer ( ($e:pat ) => (inner!($e)))
1396 fn main() { let outer!(g) = 13; g;}",
1402 // create a really evil test case where a $x appears inside a binding of $x
1403 // but *shouldn't* bind because it was inserted by a different macro....
1404 // can't write this test case until we have macro-generating macros.
1406 // method arg hygiene
1407 // method expands to fn get_x(&self_0, x_1:int) {self_0 + self_2 + x_3 + x_1}
1408 #[test] fn method_arg_hygiene(){
1410 &("macro_rules! inject_x (()=>(x))
1411 macro_rules! inject_self (()=>(self))
1413 impl A{fn get_x(&self, x: int) {self + inject_self!() + inject_x!() + x;} }",
1414 vec!(vec!(0),vec!(3)),
1419 // ooh, got another bite?
1420 // expands to struct A; impl A {fn thingy(&self_1) {self_1;}}
1421 #[test] fn method_arg_hygiene_2(){
1424 macro_rules! add_method (($T:ty) =>
1425 (impl $T { fn thingy(&self) {self;} }))
1433 // expands to fn q(x_1:int){fn g(x_2:int){x_2 + x_1};}
1434 #[test] fn issue_9383(){
1436 &("macro_rules! bad_macro (($ex:expr) => (fn g(x:int){ x + $ex }))
1437 fn q(x:int) { bad_macro!(x); }",
1438 vec!(vec!(1),vec!(0)),true),
1442 // closure arg hygiene (ExprFnBlock)
1443 // expands to fn f(){(|x_1 : int| {(x_2 + x_1)})(3);}
1444 #[test] fn closure_arg_hygiene(){
1446 &("macro_rules! inject_x (()=>(x))
1447 fn f(){(|x : int| {(inject_x!() + x)})(3);}",
1453 // closure arg hygiene (ExprProc)
1454 // expands to fn f(){(proc(x_1 : int) {(x_2 + x_1)})(3);}
1455 #[test] fn closure_arg_hygiene_2(){
1457 &("macro_rules! inject_x (()=>(x))
1458 fn f(){ (proc(x : int){(inject_x!() + x)})(3); }",
1464 // macro_rules in method position. Sadly, unimplemented.
1465 #[test] fn macro_in_method_posn(){
1467 "macro_rules! my_method (() => (fn thirteen(&self) -> int {13}))
1469 impl A{ my_method!()}
1470 fn f(){A.thirteen;}".to_string());
1473 // another nested macro
1474 // expands to impl Entries {fn size_hint(&self_1) {self_1;}
1475 #[test] fn item_macro_workaround(){
1477 &("macro_rules! item { ($i:item) => {$i}}
1479 macro_rules! iterator_impl {
1480 () => { item!( impl Entries { fn size_hint(&self) { self;}})}}
1481 iterator_impl! { }",
1482 vec!(vec!(0)), true),
1486 // run one of the renaming tests
1487 fn run_renaming_test(t: &RenamingTest, test_idx: uint) {
1488 let invalid_name = token::special_idents::invalid.name;
1489 let (teststr, bound_connections, bound_ident_check) = match *t {
1490 (ref str,ref conns, bic) => (str.to_string(), conns.clone(), bic)
1492 let cr = expand_crate_str(teststr.to_string());
1493 let bindings = crate_bindings(&cr);
1494 let varrefs = crate_varrefs(&cr);
1496 // must be one check clause for each binding:
1497 assert_eq!(bindings.len(),bound_connections.len());
1498 for (binding_idx,shouldmatch) in bound_connections.iter().enumerate() {
1499 let binding_name = mtwt::resolve(*bindings.get(binding_idx));
1500 let binding_marks = mtwt::marksof(bindings.get(binding_idx).ctxt, invalid_name);
1501 // shouldmatch can't name varrefs that don't exist:
1502 assert!((shouldmatch.len() == 0) ||
1503 (varrefs.len() > *shouldmatch.iter().max().unwrap()));
1504 for (idx,varref) in varrefs.iter().enumerate() {
1505 let print_hygiene_debug_info = || {
1506 // good lord, you can't make a path with 0 segments, can you?
1507 let final_varref_ident = match varref.segments.last() {
1508 Some(pathsegment) => pathsegment.identifier,
1509 None => fail!("varref with 0 path segments?")
1511 let varref_name = mtwt::resolve(final_varref_ident);
1512 let varref_idents : Vec<ast::Ident>
1513 = varref.segments.iter().map(|s| s.identifier)
1515 println!("varref #{}: {}, resolves to {}",idx, varref_idents, varref_name);
1516 let string = token::get_ident(final_varref_ident);
1517 println!("varref's first segment's string: \"{}\"", string.get());
1518 println!("binding #{}: {}, resolves to {}",
1519 binding_idx, *bindings.get(binding_idx), binding_name);
1520 mtwt::with_sctable(|x| mtwt::display_sctable(x));
1522 if shouldmatch.contains(&idx) {
1523 // it should be a path of length 1, and it should
1524 // be free-identifier=? or bound-identifier=? to the given binding
1525 assert_eq!(varref.segments.len(),1);
1526 let varref_name = mtwt::resolve(varref.segments.get(0).identifier);
1527 let varref_marks = mtwt::marksof(varref.segments
1532 if !(varref_name==binding_name) {
1533 println!("uh oh, should match but doesn't:");
1534 print_hygiene_debug_info();
1536 assert_eq!(varref_name,binding_name);
1537 if bound_ident_check {
1538 // we're checking bound-identifier=?, and the marks
1539 // should be the same, too:
1540 assert_eq!(varref_marks,binding_marks.clone());
1543 let varref_name = mtwt::resolve(varref.segments.get(0).identifier);
1544 let fail = (varref.segments.len() == 1)
1545 && (varref_name == binding_name);
1548 println!("failure on test {}",test_idx);
1549 println!("text of test case: \"{}\"", teststr);
1551 println!("uh oh, matches but shouldn't:");
1552 print_hygiene_debug_info();
1560 #[test] fn fmt_in_macro_used_inside_module_macro() {
1561 let crate_str = "macro_rules! fmt_wrap(($b:expr)=>($b.to_string()))
1562 macro_rules! foo_module (() => (mod generated { fn a() { let xx = 147; fmt_wrap!(xx);}}))
1565 let cr = expand_crate_str(crate_str);
1566 // find the xx binding
1567 let bindings = crate_bindings(&cr);
1568 let cxbinds: Vec<&ast::Ident> =
1569 bindings.iter().filter(|b| {
1570 let ident = token::get_ident(**b);
1571 let string = ident.get();
1574 let cxbinds: &[&ast::Ident] = cxbinds.as_slice();
1575 let cxbind = match cxbinds {
1577 _ => fail!("expected just one binding for ext_cx")
1579 let resolved_binding = mtwt::resolve(*cxbind);
1580 let varrefs = crate_varrefs(&cr);
1582 // the xx binding should bind all of the xx varrefs:
1583 for (idx,v) in varrefs.iter().filter(|p| {
1584 p.segments.len() == 1
1585 && "xx" == token::get_ident(p.segments.get(0).identifier).get()
1587 if mtwt::resolve(v.segments.get(0).identifier) != resolved_binding {
1588 println!("uh oh, xx binding didn't match xx varref:");
1589 println!("this is xx varref \\# {:?}",idx);
1590 println!("binding: {:?}",cxbind);
1591 println!("resolves to: {:?}",resolved_binding);
1592 println!("varref: {:?}",v.segments.get(0).identifier);
1593 println!("resolves to: {:?}",
1594 mtwt::resolve(v.segments.get(0).identifier));
1595 mtwt::with_sctable(|x| mtwt::display_sctable(x));
1597 assert_eq!(mtwt::resolve(v.segments.get(0).identifier),
1604 let pat = string_to_pat(
1605 "(a,Foo{x:c @ (b,9),y:Bar(4,d)})".to_string());
1606 let idents = pattern_bindings(&*pat);
1607 assert_eq!(idents, strs_to_idents(vec!("a","c","b","d")));
1610 // test the list of identifier patterns gathered by the visitor. Note that
1611 // 'None' is listed as an identifier pattern because we don't yet know that
1612 // it's the name of a 0-ary variant, and that 'i' appears twice in succession.
1614 fn crate_bindings_test(){
1615 let the_crate = string_to_crate("fn main (a : int) -> int {|b| {
1616 match 34 {None => 3, Some(i) | i => j, Foo{k:z,l:y} => \"banana\"}} }".to_string());
1617 let idents = crate_bindings(&the_crate);
1618 assert_eq!(idents, strs_to_idents(vec!("a","b","None","i","i","z","y")));
1621 // test the IdentRenamer directly
1623 fn ident_renamer_test () {
1624 let the_crate = string_to_crate("fn f(x : int){let x = x; x}".to_string());
1625 let f_ident = token::str_to_ident("f");
1626 let x_ident = token::str_to_ident("x");
1627 let int_ident = token::str_to_ident("int");
1628 let renames = vec!((x_ident,Name(16)));
1629 let mut renamer = IdentRenamer{renames: &renames};
1630 let renamed_crate = renamer.fold_crate(the_crate);
1631 let idents = crate_idents(&renamed_crate);
1632 let resolved : Vec<ast::Name> = idents.iter().map(|id| mtwt::resolve(*id)).collect();
1633 assert_eq!(resolved,vec!(f_ident.name,Name(16),int_ident.name,Name(16),Name(16),Name(16)));
1636 // test the PatIdentRenamer; only PatIdents get renamed
1638 fn pat_ident_renamer_test () {
1639 let the_crate = string_to_crate("fn f(x : int){let x = x; x}".to_string());
1640 let f_ident = token::str_to_ident("f");
1641 let x_ident = token::str_to_ident("x");
1642 let int_ident = token::str_to_ident("int");
1643 let renames = vec!((x_ident,Name(16)));
1644 let mut renamer = PatIdentRenamer{renames: &renames};
1645 let renamed_crate = renamer.fold_crate(the_crate);
1646 let idents = crate_idents(&renamed_crate);
1647 let resolved : Vec<ast::Name> = idents.iter().map(|id| mtwt::resolve(*id)).collect();
1648 let x_name = x_ident.name;
1649 assert_eq!(resolved,vec!(f_ident.name,Name(16),int_ident.name,Name(16),x_name,x_name));