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