]> git.lizzy.rs Git - rust.git/blob - src/libsyntax/ext/expand.rs
auto merge of #12957 : lkuper/rust/docsprint, r=alexcrichton
[rust.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::{P, Block, Crate, DeclLocal, ExprMac};
12 use ast::{Local, Ident, MacInvocTT};
13 use ast::{ItemMac, Mrk, Stmt, StmtDecl, StmtMac, StmtExpr, StmtSemi};
14 use ast::TokenTree;
15 use ast;
16 use ext::mtwt;
17 use ext::build::AstBuilder;
18 use attr;
19 use attr::AttrMetaMethods;
20 use codemap;
21 use codemap::{Span, Spanned, ExpnInfo, NameAndSpan, MacroBang, MacroAttribute};
22 use crateid::CrateId;
23 use ext::base::*;
24 use fold::*;
25 use parse;
26 use parse::token::{fresh_mark, fresh_name, intern};
27 use parse::token;
28 use visit;
29 use visit::Visitor;
30 use util::small_vector::SmallVector;
31
32 use std::cast;
33 use std::unstable::dynamic_lib::DynamicLibrary;
34 use std::os;
35 use std::vec_ng::Vec;
36
37 pub fn expand_expr(e: @ast::Expr, fld: &mut MacroExpander) -> @ast::Expr {
38     match e.node {
39         // expr_mac should really be expr_ext or something; it's the
40         // entry-point for all syntax extensions.
41         ExprMac(ref mac) => {
42             match (*mac).node {
43                 // it would almost certainly be cleaner to pass the whole
44                 // macro invocation in, rather than pulling it apart and
45                 // marking the tts and the ctxt separately. This also goes
46                 // for the other three macro invocation chunks of code
47                 // in this file.
48                 // Token-tree macros:
49                 MacInvocTT(ref pth, ref tts, _) => {
50                     if pth.segments.len() > 1u {
51                         fld.cx.span_err(
52                             pth.span,
53                             format!("expected macro name without module \
54                                   separators"));
55                         // let compilation continue
56                         return MacResult::raw_dummy_expr(e.span);
57                     }
58                     let extname = pth.segments.get(0).identifier;
59                     let extnamestr = token::get_ident(extname);
60                     // leaving explicit deref here to highlight unbox op:
61                     let marked_after = match fld.extsbox.find(&extname.name) {
62                         None => {
63                             fld.cx.span_err(
64                                 pth.span,
65                                 format!("macro undefined: '{}'",
66                                         extnamestr.get()));
67
68                             // let compilation continue
69                             return MacResult::raw_dummy_expr(e.span);
70                         }
71                         Some(&NormalTT(ref expandfun, exp_span)) => {
72                             fld.cx.bt_push(ExpnInfo {
73                                 call_site: e.span,
74                                 callee: NameAndSpan {
75                                     name: extnamestr.get().to_str(),
76                                     format: MacroBang,
77                                     span: exp_span,
78                                 },
79                             });
80                             let fm = fresh_mark();
81                             // mark before:
82                             let marked_before = mark_tts(tts.as_slice(), fm);
83
84                             // The span that we pass to the expanders we want to
85                             // be the root of the call stack. That's the most
86                             // relevant span and it's the actual invocation of
87                             // the macro.
88                             let mac_span = original_span(fld.cx);
89
90                             let expanded = match expandfun.expand(fld.cx,
91                                                    mac_span.call_site,
92                                                    marked_before.as_slice()) {
93                                 MRExpr(e) => e,
94                                 MRAny(any_macro) => any_macro.make_expr(),
95                                 _ => {
96                                     fld.cx.span_err(
97                                         pth.span,
98                                         format!(
99                                             "non-expr macro in expr pos: {}",
100                                             extnamestr.get()
101                                         )
102                                     );
103                                     return MacResult::raw_dummy_expr(e.span);
104                                 }
105                             };
106
107                             // mark after:
108                             mark_expr(expanded,fm)
109                         }
110                         _ => {
111                             fld.cx.span_err(
112                                 pth.span,
113                                 format!("'{}' is not a tt-style macro",
114                                         extnamestr.get())
115                             );
116                             return MacResult::raw_dummy_expr(e.span);
117                         }
118                     };
119
120                     // Keep going, outside-in.
121                     //
122                     // FIXME(pcwalton): Is it necessary to clone the
123                     // node here?
124                     let fully_expanded =
125                         fld.fold_expr(marked_after).node.clone();
126                     fld.cx.bt_pop();
127
128                     @ast::Expr {
129                         id: ast::DUMMY_NODE_ID,
130                         node: fully_expanded,
131                         span: e.span,
132                     }
133                 }
134             }
135         }
136
137         // Desugar expr_for_loop
138         // From: `['<ident>:] for <src_pat> in <src_expr> <src_loop_block>`
139         // FIXME #6993: change type of opt_ident to Option<Name>
140         ast::ExprForLoop(src_pat, src_expr, src_loop_block, opt_ident) => {
141             // Expand any interior macros etc.
142             // NB: we don't fold pats yet. Curious.
143             let src_expr = fld.fold_expr(src_expr).clone();
144             let (src_loop_block, opt_ident) = expand_loop_block(src_loop_block, opt_ident, fld);
145
146             let span = e.span;
147
148             // to:
149             //
150             //   match &mut <src_expr> {
151             //     i => {
152             //       ['<ident>:] loop {
153             //         match i.next() {
154             //           None => break,
155             //           Some(<src_pat>) => <src_loop_block>
156             //         }
157             //       }
158             //     }
159             //   }
160
161             let local_ident = token::gensym_ident("i");
162             let next_ident = fld.cx.ident_of("next");
163             let none_ident = fld.cx.ident_of("None");
164
165             let local_path = fld.cx.path_ident(span, local_ident);
166             let some_path = fld.cx.path_ident(span, fld.cx.ident_of("Some"));
167
168             // `None => break ['<ident>];`
169             let none_arm = {
170                 let break_expr = fld.cx.expr(span, ast::ExprBreak(opt_ident));
171                 let none_pat = fld.cx.pat_ident(span, none_ident);
172                 fld.cx.arm(span, vec!(none_pat), break_expr)
173             };
174
175             // `Some(<src_pat>) => <src_loop_block>`
176             let some_arm =
177                 fld.cx.arm(span,
178                            vec!(fld.cx.pat_enum(span, some_path, vec!(src_pat))),
179                            fld.cx.expr_block(src_loop_block));
180
181             // `match i.next() { ... }`
182             let match_expr = {
183                 let next_call_expr =
184                     fld.cx.expr_method_call(span,
185                                             fld.cx.expr_path(local_path),
186                                             next_ident,
187                                             Vec::new());
188
189                 fld.cx.expr_match(span, next_call_expr, vec!(none_arm, some_arm))
190             };
191
192             // ['ident:] loop { ... }
193             let loop_expr = fld.cx.expr(span,
194                                         ast::ExprLoop(fld.cx.block_expr(match_expr),
195                                                       opt_ident));
196
197             // `i => loop { ... }`
198
199             // `match &mut <src_expr> { i => loop { ... } }`
200             let discrim = fld.cx.expr_mut_addr_of(span, src_expr);
201             let i_pattern = fld.cx.pat_ident(span, local_ident);
202             let arm = fld.cx.arm(span, vec!(i_pattern), loop_expr);
203             fld.cx.expr_match(span, discrim, vec!(arm))
204         }
205
206         ast::ExprLoop(loop_block, opt_ident) => {
207             let (loop_block, opt_ident) = expand_loop_block(loop_block, opt_ident, fld);
208             fld.cx.expr(e.span, ast::ExprLoop(loop_block, opt_ident))
209         }
210
211         _ => noop_fold_expr(e, fld)
212     }
213 }
214
215 // Rename loop label and expand its loop body
216 //
217 // The renaming procedure for loop is different in the sense that the loop
218 // body is in a block enclosed by loop head so the renaming of loop label
219 // must be propagated to the enclosed context.
220 fn expand_loop_block(loop_block: P<Block>,
221                      opt_ident: Option<Ident>,
222                      fld: &mut MacroExpander) -> (P<Block>, Option<Ident>) {
223     match opt_ident {
224         Some(label) => {
225             let new_label = fresh_name(&label);
226             let rename = (label, new_label);
227
228             // The rename *must not* be added to the pending list of current
229             // syntax context otherwise an unrelated `break` or `continue` in
230             // the same context will pick that up in the deferred renaming pass
231             // and be renamed incorrectly.
232             let mut rename_list = vec!(rename);
233             let mut rename_fld = renames_to_fold(&mut rename_list);
234             let renamed_ident = rename_fld.fold_ident(label);
235
236             // The rename *must* be added to the enclosed syntax context for
237             // `break` or `continue` to pick up because by definition they are
238             // in a block enclosed by loop head.
239             fld.extsbox.push_frame();
240             fld.extsbox.info().pending_renames.push(rename);
241             let expanded_block = expand_block_elts(loop_block, fld);
242             fld.extsbox.pop_frame();
243
244             (expanded_block, Some(renamed_ident))
245         }
246         None => (fld.fold_block(loop_block), opt_ident)
247     }
248 }
249
250 // eval $e with a new exts frame:
251 macro_rules! with_exts_frame (
252     ($extsboxexpr:expr,$macros_escape:expr,$e:expr) =>
253     ({$extsboxexpr.push_frame();
254       $extsboxexpr.info().macros_escape = $macros_escape;
255       let result = $e;
256       $extsboxexpr.pop_frame();
257       result
258      })
259 )
260
261 // When we enter a module, record it, for the sake of `module!`
262 pub fn expand_item(it: @ast::Item, fld: &mut MacroExpander)
263                    -> SmallVector<@ast::Item> {
264     let it = expand_item_modifiers(it, fld);
265
266     let mut decorator_items = SmallVector::zero();
267     for attr in it.attrs.rev_iter() {
268         let mname = attr.name();
269
270         match fld.extsbox.find(&intern(mname.get())) {
271             Some(&ItemDecorator(dec_fn)) => {
272                 fld.cx.bt_push(ExpnInfo {
273                     call_site: attr.span,
274                     callee: NameAndSpan {
275                         name: mname.get().to_str(),
276                         format: MacroAttribute,
277                         span: None
278                     }
279                 });
280
281                 // we'd ideally decorator_items.push_all(expand_item(item, fld)),
282                 // but that double-mut-borrows fld
283                 let mut items: SmallVector<@ast::Item> = SmallVector::zero();
284                 dec_fn(fld.cx, attr.span, attr.node.value, it,
285                        |item| items.push(item));
286                 decorator_items.extend(&mut items.move_iter()
287                     .flat_map(|item| expand_item(item, fld).move_iter()));
288
289                 fld.cx.bt_pop();
290             }
291             _ => {}
292         }
293     }
294
295     let mut new_items = match it.node {
296         ast::ItemMac(..) => expand_item_mac(it, fld),
297         ast::ItemMod(_) | ast::ItemForeignMod(_) => {
298             fld.cx.mod_push(it.ident);
299             let macro_escape = contains_macro_escape(it.attrs.as_slice());
300             let result = with_exts_frame!(fld.extsbox,
301                                           macro_escape,
302                                           noop_fold_item(it, fld));
303             fld.cx.mod_pop();
304             result
305         },
306         _ => noop_fold_item(it, fld)
307     };
308
309     new_items.push_all(decorator_items);
310     new_items
311 }
312
313 fn expand_item_modifiers(mut it: @ast::Item, fld: &mut MacroExpander)
314                          -> @ast::Item {
315     let (modifiers, attrs) = it.attrs.partitioned(|attr| {
316         match fld.extsbox.find(&intern(attr.name().get())) {
317             Some(&ItemModifier(_)) => true,
318             _ => false
319         }
320     });
321
322     it = @ast::Item {
323         attrs: attrs,
324         ..(*it).clone()
325     };
326
327     if modifiers.is_empty() {
328         return it;
329     }
330
331     for attr in modifiers.iter() {
332         let mname = attr.name();
333
334         match fld.extsbox.find(&intern(mname.get())) {
335             Some(&ItemModifier(dec_fn)) => {
336                 fld.cx.bt_push(ExpnInfo {
337                     call_site: attr.span,
338                     callee: NameAndSpan {
339                         name: mname.get().to_str(),
340                         format: MacroAttribute,
341                         span: None,
342                     }
343                 });
344                 it = dec_fn(fld.cx, attr.span, attr.node.value, it);
345                 fld.cx.bt_pop();
346             }
347             _ => unreachable!()
348         }
349     }
350
351     // expansion may have added new ItemModifiers
352     expand_item_modifiers(it, fld)
353 }
354
355 // does this attribute list contain "macro_escape" ?
356 pub fn contains_macro_escape(attrs: &[ast::Attribute]) -> bool {
357     attr::contains_name(attrs, "macro_escape")
358 }
359
360 // Support for item-position macro invocations, exactly the same
361 // logic as for expression-position macro invocations.
362 pub fn expand_item_mac(it: @ast::Item, fld: &mut MacroExpander)
363                        -> SmallVector<@ast::Item> {
364     let (pth, tts) = match it.node {
365         ItemMac(codemap::Spanned {
366             node: MacInvocTT(ref pth, ref tts, _),
367             ..
368         }) => {
369             (pth, (*tts).clone())
370         }
371         _ => fld.cx.span_bug(it.span, "invalid item macro invocation")
372     };
373
374     let extname = pth.segments.get(0).identifier;
375     let extnamestr = token::get_ident(extname);
376     let fm = fresh_mark();
377     let expanded = match fld.extsbox.find(&extname.name) {
378         None => {
379             fld.cx.span_err(pth.span,
380                             format!("macro undefined: '{}!'",
381                                     extnamestr));
382             // let compilation continue
383             return SmallVector::zero();
384         }
385
386         Some(&NormalTT(ref expander, span)) => {
387             if it.ident.name != parse::token::special_idents::invalid.name {
388                 fld.cx.span_err(pth.span,
389                                 format!("macro {}! expects no ident argument, \
390                                         given '{}'",
391                                         extnamestr,
392                                         token::get_ident(it.ident)));
393                 return SmallVector::zero();
394             }
395             fld.cx.bt_push(ExpnInfo {
396                 call_site: it.span,
397                 callee: NameAndSpan {
398                     name: extnamestr.get().to_str(),
399                     format: MacroBang,
400                     span: span
401                 }
402             });
403             // mark before expansion:
404             let marked_before = mark_tts(tts.as_slice(), fm);
405             expander.expand(fld.cx, it.span, marked_before.as_slice())
406         }
407         Some(&IdentTT(ref expander, span)) => {
408             if it.ident.name == parse::token::special_idents::invalid.name {
409                 fld.cx.span_err(pth.span,
410                                 format!("macro {}! expects an ident argument",
411                                         extnamestr.get()));
412                 return SmallVector::zero();
413             }
414             fld.cx.bt_push(ExpnInfo {
415                 call_site: it.span,
416                 callee: NameAndSpan {
417                     name: extnamestr.get().to_str(),
418                     format: MacroBang,
419                     span: span
420                 }
421             });
422             // mark before expansion:
423             let marked_tts = mark_tts(tts.as_slice(), fm);
424             expander.expand(fld.cx, it.span, it.ident, marked_tts)
425         }
426         _ => {
427             fld.cx.span_err(it.span,
428                             format!("{}! is not legal in item position",
429                                     extnamestr.get()));
430             return SmallVector::zero();
431         }
432     };
433
434     let items = match expanded {
435         MRItem(it) => {
436             mark_item(it,fm).move_iter()
437                 .flat_map(|i| fld.fold_item(i).move_iter())
438                 .collect()
439         }
440         MRExpr(_) => {
441             fld.cx.span_err(pth.span,
442                             format!("expr macro in item position: {}",
443                                     extnamestr.get()));
444             return SmallVector::zero();
445         }
446         MRAny(any_macro) => {
447             any_macro.make_items().move_iter()
448                     .flat_map(|i| mark_item(i, fm).move_iter())
449                     .flat_map(|i| fld.fold_item(i).move_iter())
450                     .collect()
451         }
452         MRDef(MacroDef { name, ext }) => {
453             // yikes... no idea how to apply the mark to this. I'm afraid
454             // we're going to have to wait-and-see on this one.
455             fld.extsbox.insert(intern(name), ext);
456             if attr::contains_name(it.attrs.as_slice(), "macro_export") {
457                 SmallVector::one(it)
458             } else {
459                 SmallVector::zero()
460             }
461         }
462     };
463     fld.cx.bt_pop();
464     return items;
465 }
466
467 // load macros from syntax-phase crates
468 pub fn expand_view_item(vi: &ast::ViewItem,
469                         fld: &mut MacroExpander)
470                         -> ast::ViewItem {
471     match vi.node {
472         ast::ViewItemExternCrate(..) => {
473             let should_load = vi.attrs.iter().any(|attr| {
474                 attr.name().get() == "phase" &&
475                     attr.meta_item_list().map_or(false, |phases| {
476                         attr::contains_name(phases, "syntax")
477                     })
478             });
479
480             if should_load {
481                 load_extern_macros(vi, fld);
482             }
483         }
484         ast::ViewItemUse(_) => {}
485     }
486
487     noop_fold_view_item(vi, fld)
488 }
489
490 fn load_extern_macros(krate: &ast::ViewItem, fld: &mut MacroExpander) {
491     let MacroCrate { lib, cnum } = fld.cx.ecfg.loader.load_crate(krate);
492
493     let crate_name = match krate.node {
494         ast::ViewItemExternCrate(name, _, _) => name,
495         _ => unreachable!()
496     };
497     let name = format!("<{} macros>", token::get_ident(crate_name));
498
499     let exported_macros = fld.cx.ecfg.loader.get_exported_macros(cnum);
500     for source in exported_macros.iter() {
501         let item = parse::parse_item_from_source_str(name.clone(),
502                                                      (*source).clone(),
503                                                      fld.cx.cfg(),
504                                                      fld.cx.parse_sess())
505                 .expect("expected a serialized item");
506         expand_item_mac(item, fld);
507     }
508
509     let path = match lib {
510         Some(path) => path,
511         None => return
512     };
513     // Make sure the path contains a / or the linker will search for it.
514     let path = os::make_absolute(&path);
515
516     let registrar = match fld.cx.ecfg.loader.get_registrar_symbol(cnum) {
517         Some(registrar) => registrar,
518         None => return
519     };
520
521     let lib = match DynamicLibrary::open(Some(&path)) {
522         Ok(lib) => lib,
523         // this is fatal: there are almost certainly macros we need
524         // inside this crate, so continue would spew "macro undefined"
525         // errors
526         Err(err) => fld.cx.span_fatal(krate.span, err)
527     };
528
529     unsafe {
530         let registrar: MacroCrateRegistrationFun = match lib.symbol(registrar) {
531             Ok(registrar) => registrar,
532             // again fatal if we can't register macros
533             Err(err) => fld.cx.span_fatal(krate.span, err)
534         };
535         registrar(|name, extension| {
536             let extension = match extension {
537                 NormalTT(ext, _) => NormalTT(ext, Some(krate.span)),
538                 IdentTT(ext, _) => IdentTT(ext, Some(krate.span)),
539                 ItemDecorator(ext) => ItemDecorator(ext),
540                 ItemModifier(ext) => ItemModifier(ext),
541             };
542             fld.extsbox.insert(name, extension);
543         });
544
545         // Intentionally leak the dynamic library. We can't ever unload it
546         // since the library can do things that will outlive the expansion
547         // phase (e.g. make an @-box cycle or launch a task).
548         cast::forget(lib);
549     }
550 }
551
552 // expand a stmt
553 pub fn expand_stmt(s: &Stmt, fld: &mut MacroExpander) -> SmallVector<@Stmt> {
554     // why the copying here and not in expand_expr?
555     // looks like classic changed-in-only-one-place
556     let (pth, tts, semi) = match s.node {
557         StmtMac(ref mac, semi) => {
558             match mac.node {
559                 MacInvocTT(ref pth, ref tts, _) => {
560                     (pth, (*tts).clone(), semi)
561                 }
562             }
563         }
564         _ => return expand_non_macro_stmt(s, fld)
565     };
566     if pth.segments.len() > 1u {
567         fld.cx.span_err(pth.span, "expected macro name without module separators");
568         return SmallVector::zero();
569     }
570     let extname = pth.segments.get(0).identifier;
571     let extnamestr = token::get_ident(extname);
572     let marked_after = match fld.extsbox.find(&extname.name) {
573         None => {
574             fld.cx.span_err(pth.span, format!("macro undefined: '{}'", extnamestr));
575             return SmallVector::zero();
576         }
577
578         Some(&NormalTT(ref expandfun, exp_span)) => {
579             fld.cx.bt_push(ExpnInfo {
580                 call_site: s.span,
581                 callee: NameAndSpan {
582                     name: extnamestr.get().to_str(),
583                     format: MacroBang,
584                     span: exp_span,
585                 }
586             });
587             let fm = fresh_mark();
588             // mark before expansion:
589             let marked_tts = mark_tts(tts.as_slice(), fm);
590
591             // See the comment in expand_expr for why we want the original span,
592             // not the current mac.span.
593             let mac_span = original_span(fld.cx);
594
595             let expanded = match expandfun.expand(fld.cx,
596                                                   mac_span.call_site,
597                                                   marked_tts.as_slice()) {
598                 MRExpr(e) => {
599                     @codemap::Spanned {
600                         node: StmtExpr(e, ast::DUMMY_NODE_ID),
601                         span: e.span,
602                     }
603                 }
604                 MRAny(any_macro) => any_macro.make_stmt(),
605                 _ => {
606                     fld.cx.span_err(pth.span,
607                                     format!("non-stmt macro in stmt pos: {}",
608                                             extnamestr));
609                     return SmallVector::zero();
610                 }
611             };
612
613             mark_stmt(expanded,fm)
614         }
615
616         _ => {
617             fld.cx.span_err(pth.span, format!("'{}' is not a tt-style macro",
618                                               extnamestr));
619             return SmallVector::zero();
620         }
621     };
622
623     // Keep going, outside-in.
624     let fully_expanded = fld.fold_stmt(marked_after);
625     if fully_expanded.is_empty() {
626         fld.cx.span_err(pth.span, "macro didn't expand to a statement");
627         return SmallVector::zero();
628     }
629     fld.cx.bt_pop();
630     let fully_expanded: SmallVector<@Stmt> = fully_expanded.move_iter()
631             .map(|s| @Spanned { span: s.span, node: s.node.clone() })
632             .collect();
633
634     fully_expanded.move_iter().map(|s| {
635         match s.node {
636             StmtExpr(e, stmt_id) if semi => {
637                 @Spanned {
638                     span: s.span,
639                     node: StmtSemi(e, stmt_id)
640                 }
641             }
642             _ => s /* might already have a semi */
643         }
644     }).collect()
645 }
646
647 // expand a non-macro stmt. this is essentially the fallthrough for
648 // expand_stmt, above.
649 fn expand_non_macro_stmt(s: &Stmt, fld: &mut MacroExpander)
650                          -> SmallVector<@Stmt> {
651     // is it a let?
652     match s.node {
653         StmtDecl(decl, node_id) => {
654             match *decl {
655                 Spanned {
656                     node: DeclLocal(ref local),
657                     span: stmt_span
658                 } => {
659                     // take it apart:
660                     let Local {
661                         ty: _,
662                         pat: pat,
663                         init: init,
664                         id: id,
665                         span: span
666                     } = **local;
667                     // expand the pat (it might contain exprs... #:(o)>
668                     let expanded_pat = fld.fold_pat(pat);
669                     // find the pat_idents in the pattern:
670                     // oh dear heaven... this is going to include the enum
671                     // names, as well... but that should be okay, as long as
672                     // the new names are gensyms for the old ones.
673                     let mut name_finder = new_name_finder(Vec::new());
674                     name_finder.visit_pat(expanded_pat,());
675                     // generate fresh names, push them to a new pending list
676                     let mut new_pending_renames = Vec::new();
677                     for ident in name_finder.ident_accumulator.iter() {
678                         let new_name = fresh_name(ident);
679                         new_pending_renames.push((*ident,new_name));
680                     }
681                     let rewritten_pat = {
682                         let mut rename_fld =
683                             renames_to_fold(&mut new_pending_renames);
684                         // rewrite the pattern using the new names (the old
685                         // ones have already been applied):
686                         rename_fld.fold_pat(expanded_pat)
687                     };
688                     // add them to the existing pending renames:
689                     fld.extsbox.info().pending_renames.push_all_move(new_pending_renames);
690                     // also, don't forget to expand the init:
691                     let new_init_opt = init.map(|e| fld.fold_expr(e));
692                     let rewritten_local =
693                         @Local {
694                             ty: local.ty,
695                             pat: rewritten_pat,
696                             init: new_init_opt,
697                             id: id,
698                             span: span,
699                         };
700                     SmallVector::one(@Spanned {
701                         node: StmtDecl(@Spanned {
702                                 node: DeclLocal(rewritten_local),
703                                 span: stmt_span
704                             },
705                             node_id),
706                         span: span
707                     })
708                 }
709                 _ => noop_fold_stmt(s, fld),
710             }
711         },
712         _ => noop_fold_stmt(s, fld),
713     }
714 }
715
716 // a visitor that extracts the pat_ident paths
717 // from a given thingy and puts them in a mutable
718 // array (passed in to the traversal)
719 #[deriving(Clone)]
720 pub struct NewNameFinderContext {
721     ident_accumulator: Vec<ast::Ident> ,
722 }
723
724 impl Visitor<()> for NewNameFinderContext {
725     fn visit_pat(&mut self, pattern: &ast::Pat, _: ()) {
726         match *pattern {
727             // we found a pat_ident!
728             ast::Pat {
729                 id: _,
730                 node: ast::PatIdent(_, ref path, ref inner),
731                 span: _
732             } => {
733                 match path {
734                     // a path of length one:
735                     &ast::Path {
736                         global: false,
737                         span: _,
738                         segments: ref segments
739                     } if segments.len() == 1 => {
740                         self.ident_accumulator.push(segments.get(0)
741                                                             .identifier)
742                     }
743                     // I believe these must be enums...
744                     _ => ()
745                 }
746                 // visit optional subpattern of pat_ident:
747                 for subpat in inner.iter() {
748                     self.visit_pat(*subpat, ())
749                 }
750             }
751             // use the default traversal for non-pat_idents
752             _ => visit::walk_pat(self, pattern, ())
753         }
754     }
755
756     fn visit_ty(&mut self, typ: &ast::Ty, _: ()) {
757         visit::walk_ty(self, typ, ())
758     }
759
760 }
761
762 // return a visitor that extracts the pat_ident paths
763 // from a given thingy and puts them in a mutable
764 // array (passed in to the traversal)
765 pub fn new_name_finder(idents: Vec<ast::Ident> ) -> NewNameFinderContext {
766     NewNameFinderContext {
767         ident_accumulator: idents,
768     }
769 }
770
771 // expand a block. pushes a new exts_frame, then calls expand_block_elts
772 pub fn expand_block(blk: &Block, fld: &mut MacroExpander) -> P<Block> {
773     // see note below about treatment of exts table
774     with_exts_frame!(fld.extsbox,false,
775                      expand_block_elts(blk, fld))
776 }
777
778 // expand the elements of a block.
779 pub fn expand_block_elts(b: &Block, fld: &mut MacroExpander) -> P<Block> {
780     let new_view_items = b.view_items.map(|x| fld.fold_view_item(x));
781     let new_stmts =
782         b.stmts.iter().flat_map(|x| {
783             let renamed_stmt = {
784                 let pending_renames = &mut fld.extsbox.info().pending_renames;
785                 let mut rename_fld = renames_to_fold(pending_renames);
786                 rename_fld.fold_stmt(*x).expect_one("rename_fold didn't return one value")
787             };
788             fld.fold_stmt(renamed_stmt).move_iter()
789         }).collect();
790     let new_expr = b.expr.map(|x| {
791         let expr = {
792             let pending_renames = &mut fld.extsbox.info().pending_renames;
793             let mut rename_fld = renames_to_fold(pending_renames);
794             rename_fld.fold_expr(x)
795         };
796         fld.fold_expr(expr)
797     });
798     P(Block {
799         view_items: new_view_items,
800         stmts: new_stmts,
801         expr: new_expr,
802         id: fld.new_id(b.id),
803         rules: b.rules,
804         span: b.span,
805     })
806 }
807
808 pub struct IdentRenamer<'a> {
809     renames: &'a mut RenameList,
810 }
811
812 impl<'a> Folder for IdentRenamer<'a> {
813     fn fold_ident(&mut self, id: Ident) -> Ident {
814         let new_ctxt = self.renames.iter().fold(id.ctxt, |ctxt, &(from, to)| {
815             mtwt::new_rename(from, to, ctxt)
816         });
817         Ident {
818             name: id.name,
819             ctxt: new_ctxt,
820         }
821     }
822 }
823
824 // given a mutable list of renames, return a tree-folder that applies those
825 // renames.
826 pub fn renames_to_fold<'a>(renames: &'a mut RenameList) -> IdentRenamer<'a> {
827     IdentRenamer {
828         renames: renames,
829     }
830 }
831
832 pub fn new_span(cx: &ExtCtxt, sp: Span) -> Span {
833     /* this discards information in the case of macro-defining macros */
834     Span {
835         lo: sp.lo,
836         hi: sp.hi,
837         expn_info: cx.backtrace(),
838     }
839 }
840
841 pub struct MacroExpander<'a, 'b> {
842     extsbox: SyntaxEnv,
843     cx: &'a mut ExtCtxt<'b>,
844 }
845
846 impl<'a, 'b> Folder for MacroExpander<'a, 'b> {
847     fn fold_expr(&mut self, expr: @ast::Expr) -> @ast::Expr {
848         expand_expr(expr, self)
849     }
850
851     fn fold_item(&mut self, item: @ast::Item) -> SmallVector<@ast::Item> {
852         expand_item(item, self)
853     }
854
855     fn fold_view_item(&mut self, vi: &ast::ViewItem) -> ast::ViewItem {
856         expand_view_item(vi, self)
857     }
858
859     fn fold_stmt(&mut self, stmt: &ast::Stmt) -> SmallVector<@ast::Stmt> {
860         expand_stmt(stmt, self)
861     }
862
863     fn fold_block(&mut self, block: P<Block>) -> P<Block> {
864         expand_block(block, self)
865     }
866
867     fn new_span(&mut self, span: Span) -> Span {
868         new_span(self.cx, span)
869     }
870 }
871
872 pub struct ExpansionConfig<'a> {
873     loader: &'a mut CrateLoader,
874     deriving_hash_type_parameter: bool,
875     crate_id: CrateId,
876 }
877
878 pub fn expand_crate(parse_sess: &parse::ParseSess,
879                     cfg: ExpansionConfig,
880                     c: Crate) -> Crate {
881     let mut cx = ExtCtxt::new(parse_sess, c.config.clone(), cfg);
882     let mut expander = MacroExpander {
883         extsbox: syntax_expander_table(),
884         cx: &mut cx,
885     };
886
887     let ret = expander.fold_crate(c);
888     parse_sess.span_diagnostic.handler().abort_if_errors();
889     return ret;
890 }
891
892 // HYGIENIC CONTEXT EXTENSION:
893 // all of these functions are for walking over
894 // ASTs and making some change to the context of every
895 // element that has one. a CtxtFn is a trait-ified
896 // version of a closure in (SyntaxContext -> SyntaxContext).
897 // the ones defined here include:
898 // Marker - add a mark to a context
899
900 // A Marker adds the given mark to the syntax context
901 struct Marker { mark: Mrk }
902
903 impl Folder for Marker {
904     fn fold_ident(&mut self, id: Ident) -> Ident {
905         ast::Ident {
906             name: id.name,
907             ctxt: mtwt::new_mark(self.mark, id.ctxt)
908         }
909     }
910     fn fold_mac(&mut self, m: &ast::Mac) -> ast::Mac {
911         let macro = match m.node {
912             MacInvocTT(ref path, ref tts, ctxt) => {
913                 MacInvocTT(self.fold_path(path),
914                            fold_tts(tts.as_slice(), self),
915                            mtwt::new_mark(self.mark, ctxt))
916             }
917         };
918         Spanned {
919             node: macro,
920             span: m.span,
921         }
922     }
923 }
924
925 // just a convenience:
926 fn new_mark_folder(m: Mrk) -> Marker {
927     Marker {mark: m}
928 }
929
930 // apply a given mark to the given token trees. Used prior to expansion of a macro.
931 fn mark_tts(tts: &[TokenTree], m: Mrk) -> Vec<TokenTree> {
932     fold_tts(tts, &mut new_mark_folder(m))
933 }
934
935 // apply a given mark to the given expr. Used following the expansion of a macro.
936 fn mark_expr(expr: @ast::Expr, m: Mrk) -> @ast::Expr {
937     new_mark_folder(m).fold_expr(expr)
938 }
939
940 // apply a given mark to the given stmt. Used following the expansion of a macro.
941 fn mark_stmt(expr: &ast::Stmt, m: Mrk) -> @ast::Stmt {
942     new_mark_folder(m).fold_stmt(expr)
943             .expect_one("marking a stmt didn't return a stmt")
944 }
945
946 // apply a given mark to the given item. Used following the expansion of a macro.
947 fn mark_item(expr: @ast::Item, m: Mrk) -> SmallVector<@ast::Item> {
948     new_mark_folder(m).fold_item(expr)
949 }
950
951 fn original_span(cx: &ExtCtxt) -> @codemap::ExpnInfo {
952     let mut relevant_info = cx.backtrace();
953     let mut einfo = relevant_info.unwrap();
954     loop {
955         match relevant_info {
956             None => { break }
957             Some(e) => {
958                 einfo = e;
959                 relevant_info = einfo.call_site.expn_info;
960             }
961         }
962     }
963     return einfo;
964 }
965
966 #[cfg(test)]
967 mod test {
968     use super::*;
969     use ast;
970     use ast::{Attribute_, AttrOuter, MetaWord};
971     use codemap;
972     use codemap::Spanned;
973     use ext::base::{CrateLoader, MacroCrate};
974     use ext::mtwt;
975     use parse;
976     use parse::token;
977     use util::parser_testing::{string_to_parser};
978     use util::parser_testing::{string_to_pat, strs_to_idents};
979     use visit;
980     use visit::Visitor;
981
982     use std::vec_ng::Vec;
983
984     // a visitor that extracts the paths
985     // from a given thingy and puts them in a mutable
986     // array (passed in to the traversal)
987     #[deriving(Clone)]
988     struct NewPathExprFinderContext {
989         path_accumulator: Vec<ast::Path> ,
990     }
991
992     impl Visitor<()> for NewPathExprFinderContext {
993
994         fn visit_expr(&mut self, expr: &ast::Expr, _: ()) {
995             match *expr {
996                 ast::Expr{id:_,span:_,node:ast::ExprPath(ref p)} => {
997                     self.path_accumulator.push(p.clone());
998                     // not calling visit_path, should be fine.
999                 }
1000                 _ => visit::walk_expr(self,expr,())
1001             }
1002         }
1003
1004         fn visit_ty(&mut self, typ: &ast::Ty, _: ()) {
1005             visit::walk_ty(self, typ, ())
1006         }
1007
1008     }
1009
1010     // return a visitor that extracts the paths
1011     // from a given pattern and puts them in a mutable
1012     // array (passed in to the traversal)
1013     pub fn new_path_finder(paths: Vec<ast::Path> ) -> NewPathExprFinderContext {
1014         NewPathExprFinderContext {
1015             path_accumulator: paths
1016         }
1017     }
1018
1019     struct ErrLoader;
1020
1021     impl CrateLoader for ErrLoader {
1022         fn load_crate(&mut self, _: &ast::ViewItem) -> MacroCrate {
1023             fail!("lolwut")
1024         }
1025
1026         fn get_exported_macros(&mut self, _: ast::CrateNum) -> Vec<~str> {
1027             fail!("lolwut")
1028         }
1029
1030         fn get_registrar_symbol(&mut self, _: ast::CrateNum) -> Option<~str> {
1031             fail!("lolwut")
1032         }
1033     }
1034
1035     // these following tests are quite fragile, in that they don't test what
1036     // *kind* of failure occurs.
1037
1038     // make sure that macros can leave scope
1039     #[should_fail]
1040     #[test] fn macros_cant_escape_fns_test () {
1041         let src = ~"fn bogus() {macro_rules! z (() => (3+4))}\
1042                     fn inty() -> int { z!() }";
1043         let sess = parse::new_parse_sess();
1044         let crate_ast = parse::parse_crate_from_source_str(
1045             ~"<test>",
1046             src,
1047             Vec::new(), &sess);
1048         // should fail:
1049         let mut loader = ErrLoader;
1050         let cfg = ::syntax::ext::expand::ExpansionConfig {
1051             loader: &mut loader,
1052             deriving_hash_type_parameter: false,
1053             crate_id: from_str("test").unwrap(),
1054         };
1055         expand_crate(&sess,cfg,crate_ast);
1056     }
1057
1058     // make sure that macros can leave scope for modules
1059     #[should_fail]
1060     #[test] fn macros_cant_escape_mods_test () {
1061         let src = ~"mod foo {macro_rules! z (() => (3+4))}\
1062                     fn inty() -> int { z!() }";
1063         let sess = parse::new_parse_sess();
1064         let crate_ast = parse::parse_crate_from_source_str(
1065             ~"<test>",
1066             src,
1067             Vec::new(), &sess);
1068         // should fail:
1069         let mut loader = ErrLoader;
1070         let cfg = ::syntax::ext::expand::ExpansionConfig {
1071             loader: &mut loader,
1072             deriving_hash_type_parameter: false,
1073             crate_id: from_str("test").unwrap(),
1074         };
1075         expand_crate(&sess,cfg,crate_ast);
1076     }
1077
1078     // macro_escape modules shouldn't cause macros to leave scope
1079     #[test] fn macros_can_escape_flattened_mods_test () {
1080         let src = ~"#[macro_escape] mod foo {macro_rules! z (() => (3+4))}\
1081                     fn inty() -> int { z!() }";
1082         let sess = parse::new_parse_sess();
1083         let crate_ast = parse::parse_crate_from_source_str(
1084             ~"<test>",
1085             src,
1086             Vec::new(), &sess);
1087         // should fail:
1088         let mut loader = ErrLoader;
1089         let cfg = ::syntax::ext::expand::ExpansionConfig {
1090             loader: &mut loader,
1091             deriving_hash_type_parameter: false,
1092             crate_id: from_str("test").unwrap(),
1093         };
1094         expand_crate(&sess, cfg, crate_ast);
1095     }
1096
1097     #[test] fn test_contains_flatten (){
1098         let attr1 = make_dummy_attr ("foo");
1099         let attr2 = make_dummy_attr ("bar");
1100         let escape_attr = make_dummy_attr ("macro_escape");
1101         let attrs1 = vec!(attr1, escape_attr, attr2);
1102         assert_eq!(contains_macro_escape(attrs1.as_slice()),true);
1103         let attrs2 = vec!(attr1,attr2);
1104         assert_eq!(contains_macro_escape(attrs2.as_slice()),false);
1105     }
1106
1107     // make a MetaWord outer attribute with the given name
1108     fn make_dummy_attr(s: &str) -> ast::Attribute {
1109         Spanned {
1110             span:codemap::DUMMY_SP,
1111             node: Attribute_ {
1112                 style: AttrOuter,
1113                 value: @Spanned {
1114                     node: MetaWord(token::intern_and_get_ident(s)),
1115                     span: codemap::DUMMY_SP,
1116                 },
1117                 is_sugared_doc: false,
1118             }
1119         }
1120     }
1121
1122     //fn fake_print_crate(krate: &ast::Crate) {
1123     //    let mut out = ~std::io::stderr() as ~std::io::Writer;
1124     //    let mut s = pprust::rust_printer(out, get_ident_interner());
1125     //    pprust::print_crate_(&mut s, krate);
1126     //}
1127
1128     fn expand_crate_str(crate_str: ~str) -> ast::Crate {
1129         let ps = parse::new_parse_sess();
1130         let crate_ast = string_to_parser(&ps, crate_str).parse_crate_mod();
1131         // the cfg argument actually does matter, here...
1132         let mut loader = ErrLoader;
1133         let cfg = ::syntax::ext::expand::ExpansionConfig {
1134             loader: &mut loader,
1135             deriving_hash_type_parameter: false,
1136             crate_id: from_str("test").unwrap(),
1137         };
1138         expand_crate(&ps,cfg,crate_ast)
1139     }
1140
1141     //fn expand_and_resolve(crate_str: @str) -> ast::crate {
1142         //let expanded_ast = expand_crate_str(crate_str);
1143         // println!("expanded: {:?}\n",expanded_ast);
1144         //mtwt_resolve_crate(expanded_ast)
1145     //}
1146     //fn expand_and_resolve_and_pretty_print (crate_str: @str) -> ~str {
1147         //let resolved_ast = expand_and_resolve(crate_str);
1148         //pprust::to_str(&resolved_ast,fake_print_crate,get_ident_interner())
1149     //}
1150
1151     #[test] fn macro_tokens_should_match(){
1152         expand_crate_str(~"macro_rules! m((a)=>(13)) fn main(){m!(a);}");
1153     }
1154
1155     // renaming tests expand a crate and then check that the bindings match
1156     // the right varrefs. The specification of the test case includes the
1157     // text of the crate, and also an array of arrays.  Each element in the
1158     // outer array corresponds to a binding in the traversal of the AST
1159     // induced by visit.  Each of these arrays contains a list of indexes,
1160     // interpreted as the varrefs in the varref traversal that this binding
1161     // should match.  So, for instance, in a program with two bindings and
1162     // three varrefs, the array ~[~[1,2],~[0]] would indicate that the first
1163     // binding should match the second two varrefs, and the second binding
1164     // should match the first varref.
1165     //
1166     // The comparisons are done post-mtwt-resolve, so we're comparing renamed
1167     // names; differences in marks don't matter any more.
1168     //
1169     // oog... I also want tests that check "binding-identifier-=?". That is,
1170     // not just "do these have the same name", but "do they have the same
1171     // name *and* the same marks"? Understanding this is really pretty painful.
1172     // in principle, you might want to control this boolean on a per-varref basis,
1173     // but that would make things even harder to understand, and might not be
1174     // necessary for thorough testing.
1175     type RenamingTest = (&'static str, Vec<Vec<uint>>, bool);
1176
1177     #[test]
1178     fn automatic_renaming () {
1179         let tests: Vec<RenamingTest> =
1180             vec!(// b & c should get new names throughout, in the expr too:
1181                 ("fn a() -> int { let b = 13; let c = b; b+c }",
1182                  vec!(vec!(0,1),vec!(2)), false),
1183                 // both x's should be renamed (how is this causing a bug?)
1184                 ("fn main () {let x: int = 13;x;}",
1185                  vec!(vec!(0)), false),
1186                 // the use of b after the + should be renamed, the other one not:
1187                 ("macro_rules! f (($x:ident) => (b + $x)) fn a() -> int { let b = 13; f!(b)}",
1188                  vec!(vec!(1)), false),
1189                 // the b before the plus should not be renamed (requires marks)
1190                 ("macro_rules! f (($x:ident) => ({let b=9; ($x + b)})) fn a() -> int { f!(b)}",
1191                  vec!(vec!(1)), false),
1192                 // the marks going in and out of letty should cancel, allowing that $x to
1193                 // capture the one following the semicolon.
1194                 // this was an awesome test case, and caught a *lot* of bugs.
1195                 ("macro_rules! letty(($x:ident) => (let $x = 15;))
1196                   macro_rules! user(($x:ident) => ({letty!($x); $x}))
1197                   fn main() -> int {user!(z)}",
1198                  vec!(vec!(0)), false));
1199         for (idx,s) in tests.iter().enumerate() {
1200             run_renaming_test(s,idx);
1201         }
1202     }
1203
1204     // run one of the renaming tests
1205     fn run_renaming_test(t: &RenamingTest, test_idx: uint) {
1206         let invalid_name = token::special_idents::invalid.name;
1207         let (teststr, bound_connections, bound_ident_check) = match *t {
1208             (ref str,ref conns, bic) => (str.to_owned(), conns.clone(), bic)
1209         };
1210         let cr = expand_crate_str(teststr.to_owned());
1211         // find the bindings:
1212         let mut name_finder = new_name_finder(Vec::new());
1213         visit::walk_crate(&mut name_finder,&cr,());
1214         let bindings = name_finder.ident_accumulator;
1215
1216         // find the varrefs:
1217         let mut path_finder = new_path_finder(Vec::new());
1218         visit::walk_crate(&mut path_finder,&cr,());
1219         let varrefs = path_finder.path_accumulator;
1220
1221         // must be one check clause for each binding:
1222         assert_eq!(bindings.len(),bound_connections.len());
1223         for (binding_idx,shouldmatch) in bound_connections.iter().enumerate() {
1224             let binding_name = mtwt::resolve(*bindings.get(binding_idx));
1225             let binding_marks = mtwt::marksof(bindings.get(binding_idx).ctxt, invalid_name);
1226             // shouldmatch can't name varrefs that don't exist:
1227             assert!((shouldmatch.len() == 0) ||
1228                     (varrefs.len() > *shouldmatch.iter().max().unwrap()));
1229             for (idx,varref) in varrefs.iter().enumerate() {
1230                 if shouldmatch.contains(&idx) {
1231                     // it should be a path of length 1, and it should
1232                     // be free-identifier=? or bound-identifier=? to the given binding
1233                     assert_eq!(varref.segments.len(),1);
1234                     let varref_name = mtwt::resolve(varref.segments
1235                                                           .get(0)
1236                                                           .identifier);
1237                     let varref_marks = mtwt::marksof(varref.segments
1238                                                            .get(0)
1239                                                            .identifier
1240                                                            .ctxt,
1241                                                      invalid_name);
1242                     if !(varref_name==binding_name) {
1243                         println!("uh oh, should match but doesn't:");
1244                         println!("varref: {:?}",varref);
1245                         println!("binding: {:?}", *bindings.get(binding_idx));
1246                         mtwt::with_sctable(|x| mtwt::display_sctable(x));
1247                     }
1248                     assert_eq!(varref_name,binding_name);
1249                     if bound_ident_check {
1250                         // we're checking bound-identifier=?, and the marks
1251                         // should be the same, too:
1252                         assert_eq!(varref_marks,binding_marks.clone());
1253                     }
1254                 } else {
1255                     let fail = (varref.segments.len() == 1)
1256                         && (mtwt::resolve(varref.segments.get(0).identifier)
1257                             == binding_name);
1258                     // temp debugging:
1259                     if fail {
1260                         println!("failure on test {}",test_idx);
1261                         println!("text of test case: \"{}\"", teststr);
1262                         println!("");
1263                         println!("uh oh, matches but shouldn't:");
1264                         println!("varref: {:?}",varref);
1265                         // good lord, you can't make a path with 0 segments, can you?
1266                         let string = token::get_ident(varref.segments
1267                                                             .get(0)
1268                                                             .identifier);
1269                         println!("varref's first segment's uint: {}, and string: \"{}\"",
1270                                  varref.segments.get(0).identifier.name,
1271                                  string.get());
1272                         println!("binding: {:?}", *bindings.get(binding_idx));
1273                         mtwt::with_sctable(|x| mtwt::display_sctable(x));
1274                     }
1275                     assert!(!fail);
1276                 }
1277             }
1278         }
1279     }
1280
1281     #[test] fn fmt_in_macro_used_inside_module_macro() {
1282         let crate_str = ~"macro_rules! fmt_wrap(($b:expr)=>($b.to_str()))
1283 macro_rules! foo_module (() => (mod generated { fn a() { let xx = 147; fmt_wrap!(xx);}}))
1284 foo_module!()
1285 ";
1286         let cr = expand_crate_str(crate_str);
1287         // find the xx binding
1288         let mut name_finder = new_name_finder(Vec::new());
1289         visit::walk_crate(&mut name_finder, &cr, ());
1290         let bindings = name_finder.ident_accumulator;
1291
1292         let cxbinds: Vec<&ast::Ident> =
1293             bindings.iter().filter(|b| {
1294                 let ident = token::get_ident(**b);
1295                 let string = ident.get();
1296                 "xx" == string
1297             }).collect();
1298         let cxbinds: &[&ast::Ident] = cxbinds.as_slice();
1299         let cxbind = match cxbinds {
1300             [b] => b,
1301             _ => fail!("expected just one binding for ext_cx")
1302         };
1303         let resolved_binding = mtwt::resolve(*cxbind);
1304         // find all the xx varrefs:
1305         let mut path_finder = new_path_finder(Vec::new());
1306         visit::walk_crate(&mut path_finder, &cr, ());
1307         let varrefs = path_finder.path_accumulator;
1308
1309         // the xx binding should bind all of the xx varrefs:
1310         for (idx,v) in varrefs.iter().filter(|p| {
1311             p.segments.len() == 1
1312             && "xx" == token::get_ident(p.segments.get(0).identifier).get()
1313         }).enumerate() {
1314             if mtwt::resolve(v.segments.get(0).identifier) != resolved_binding {
1315                 println!("uh oh, xx binding didn't match xx varref:");
1316                 println!("this is xx varref \\# {:?}",idx);
1317                 println!("binding: {:?}",cxbind);
1318                 println!("resolves to: {:?}",resolved_binding);
1319                 println!("varref: {:?}",v.segments.get(0).identifier);
1320                 println!("resolves to: {:?}",
1321                          mtwt::resolve(v.segments.get(0).identifier));
1322                 mtwt::with_sctable(|x| mtwt::display_sctable(x));
1323             }
1324             assert_eq!(mtwt::resolve(v.segments.get(0).identifier),
1325                        resolved_binding);
1326         };
1327     }
1328
1329     #[test]
1330     fn pat_idents(){
1331         let pat = string_to_pat(~"(a,Foo{x:c @ (b,9),y:Bar(4,d)})");
1332         let mut pat_idents = new_name_finder(Vec::new());
1333         pat_idents.visit_pat(pat, ());
1334         assert_eq!(pat_idents.ident_accumulator,
1335                    strs_to_idents(vec!("a","c","b","d")));
1336     }
1337
1338 }