]> git.lizzy.rs Git - rust.git/blob - src/libsyntax/ext/expand.rs
rollup merge of #16832 : SebastianZaha/fix-inconsistent-version-numbering
[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, PatMac};
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 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::gc::{Gc, GC};
33
34 enum Either<L,R> {
35     Left(L),
36     Right(R)
37 }
38
39 fn expand_expr(e: Gc<ast::Expr>, fld: &mut MacroExpander) -> Gc<ast::Expr> {
40     match e.node {
41         // expr_mac should really be expr_ext or something; it's the
42         // entry-point for all syntax extensions.
43         ExprMac(ref mac) => {
44             let expanded_expr = match expand_mac_invoc(mac,&e.span,
45                                                        |r|{r.make_expr()},
46                                                        |expr,fm|{mark_expr(expr,fm)},
47                                                        fld) {
48                 Some(expr) => expr,
49                 None => {
50                     return DummyResult::raw_expr(e.span);
51                 }
52             };
53
54             // Keep going, outside-in.
55             //
56             // FIXME(pcwalton): Is it necessary to clone the
57             // node here?
58             let fully_expanded =
59                 fld.fold_expr(expanded_expr).node.clone();
60             fld.cx.bt_pop();
61
62             box(GC) ast::Expr {
63                 id: ast::DUMMY_NODE_ID,
64                 node: fully_expanded,
65                 span: e.span,
66             }
67         }
68
69         ast::ExprWhile(cond, body, opt_ident) => {
70             let cond = fld.fold_expr(cond);
71             let (body, opt_ident) = expand_loop_block(body, opt_ident, fld);
72             fld.cx.expr(e.span, ast::ExprWhile(cond, body, opt_ident))
73         }
74
75         ast::ExprLoop(loop_block, opt_ident) => {
76             let (loop_block, opt_ident) = expand_loop_block(loop_block, opt_ident, fld);
77             fld.cx.expr(e.span, ast::ExprLoop(loop_block, opt_ident))
78         }
79
80         ast::ExprForLoop(pat, head, body, opt_ident) => {
81             let pat = fld.fold_pat(pat);
82             let head = fld.fold_expr(head);
83             let (body, opt_ident) = expand_loop_block(body, opt_ident, fld);
84             fld.cx.expr(e.span, ast::ExprForLoop(pat, head, body, opt_ident))
85         }
86
87         ast::ExprFnBlock(capture_clause, fn_decl, block) => {
88             let (rewritten_fn_decl, rewritten_block)
89                 = expand_and_rename_fn_decl_and_block(&*fn_decl, block, fld);
90             let new_node = ast::ExprFnBlock(capture_clause,
91                                             rewritten_fn_decl,
92                                             rewritten_block);
93             box(GC) ast::Expr{id:e.id, node: new_node, span: fld.new_span(e.span)}
94         }
95
96         ast::ExprProc(fn_decl, block) => {
97             let (rewritten_fn_decl, rewritten_block)
98                 = expand_and_rename_fn_decl_and_block(&*fn_decl, block, fld);
99             let new_node = ast::ExprProc(rewritten_fn_decl, rewritten_block);
100             box(GC) ast::Expr{id:e.id, node: new_node, span: fld.new_span(e.span)}
101         }
102
103         _ => noop_fold_expr(e, fld)
104     }
105 }
106
107 /// Expand a (not-ident-style) macro invocation. Returns the result
108 /// of expansion and the mark which must be applied to the result.
109 /// Our current interface doesn't allow us to apply the mark to the
110 /// result until after calling make_expr, make_items, etc.
111 fn expand_mac_invoc<T>(mac: &ast::Mac, span: &codemap::Span,
112                        parse_thunk: |Box<MacResult>|->Option<T>,
113                        mark_thunk: |T,Mrk|->T,
114                        fld: &mut MacroExpander)
115                        -> Option<T>
116 {
117     match (*mac).node {
118         // it would almost certainly be cleaner to pass the whole
119         // macro invocation in, rather than pulling it apart and
120         // marking the tts and the ctxt separately. This also goes
121         // for the other three macro invocation chunks of code
122         // in this file.
123         // Token-tree macros:
124         MacInvocTT(ref pth, ref tts, _) => {
125             if pth.segments.len() > 1u {
126                 fld.cx.span_err(pth.span,
127                                 "expected macro name without module \
128                                 separators");
129                 // let compilation continue
130                 return None;
131             }
132             let extname = pth.segments.get(0).identifier;
133             let extnamestr = token::get_ident(extname);
134             match fld.cx.syntax_env.find(&extname.name) {
135                 None => {
136                     fld.cx.span_err(
137                         pth.span,
138                         format!("macro undefined: '{}!'",
139                                 extnamestr.get()).as_slice());
140
141                     // let compilation continue
142                     None
143                 }
144                 Some(rc) => match *rc {
145                     NormalTT(ref expandfun, exp_span) => {
146                         fld.cx.bt_push(ExpnInfo {
147                                 call_site: *span,
148                                 callee: NameAndSpan {
149                                     name: extnamestr.get().to_string(),
150                                     format: MacroBang,
151                                     span: exp_span,
152                                 },
153                             });
154                         let fm = fresh_mark();
155                         let marked_before = mark_tts(tts.as_slice(), fm);
156
157                         // The span that we pass to the expanders we want to
158                         // be the root of the call stack. That's the most
159                         // relevant span and it's the actual invocation of
160                         // the macro.
161                         let mac_span = original_span(fld.cx);
162
163                         let opt_parsed = {
164                             let expanded = expandfun.expand(fld.cx,
165                                                             mac_span.call_site,
166                                                             marked_before.as_slice());
167                             parse_thunk(expanded)
168                         };
169                         let parsed = match opt_parsed {
170                             Some(e) => e,
171                             None => {
172                                 fld.cx.span_err(
173                                     pth.span,
174                                     format!("non-expression macro in expression position: {}",
175                                             extnamestr.get().as_slice()
176                                             ).as_slice());
177                                 return None;
178                             }
179                         };
180                         Some(mark_thunk(parsed,fm))
181                     }
182                     _ => {
183                         fld.cx.span_err(
184                             pth.span,
185                             format!("'{}' is not a tt-style macro",
186                                     extnamestr.get()).as_slice());
187                         None
188                     }
189                 }
190             }
191         }
192     }
193 }
194
195 /// Rename loop label and expand its loop body
196 ///
197 /// The renaming procedure for loop is different in the sense that the loop
198 /// body is in a block enclosed by loop head so the renaming of loop label
199 /// must be propagated to the enclosed context.
200 fn expand_loop_block(loop_block: P<Block>,
201                      opt_ident: Option<Ident>,
202                      fld: &mut MacroExpander) -> (P<Block>, Option<Ident>) {
203     match opt_ident {
204         Some(label) => {
205             let new_label = fresh_name(&label);
206             let rename = (label, new_label);
207
208             // The rename *must not* be added to the pending list of current
209             // syntax context otherwise an unrelated `break` or `continue` in
210             // the same context will pick that up in the deferred renaming pass
211             // and be renamed incorrectly.
212             let mut rename_list = vec!(rename);
213             let mut rename_fld = IdentRenamer{renames: &mut rename_list};
214             let renamed_ident = rename_fld.fold_ident(label);
215
216             // The rename *must* be added to the enclosed syntax context for
217             // `break` or `continue` to pick up because by definition they are
218             // in a block enclosed by loop head.
219             fld.cx.syntax_env.push_frame();
220             fld.cx.syntax_env.info().pending_renames.push(rename);
221             let expanded_block = expand_block_elts(&*loop_block, fld);
222             fld.cx.syntax_env.pop_frame();
223
224             (expanded_block, Some(renamed_ident))
225         }
226         None => (fld.fold_block(loop_block), opt_ident)
227     }
228 }
229
230 // eval $e with a new exts frame.
231 // must be a macro so that $e isn't evaluated too early.
232 macro_rules! with_exts_frame (
233     ($extsboxexpr:expr,$macros_escape:expr,$e:expr) =>
234     ({$extsboxexpr.push_frame();
235       $extsboxexpr.info().macros_escape = $macros_escape;
236       let result = $e;
237       $extsboxexpr.pop_frame();
238       result
239      })
240 )
241
242 // When we enter a module, record it, for the sake of `module!`
243 fn expand_item(it: Gc<ast::Item>, fld: &mut MacroExpander)
244                    -> SmallVector<Gc<ast::Item>> {
245     let it = expand_item_modifiers(it, fld);
246
247     let mut decorator_items = SmallVector::zero();
248     let mut new_attrs = Vec::new();
249     for attr in it.attrs.iter() {
250         let mname = attr.name();
251
252         match fld.cx.syntax_env.find(&intern(mname.get())) {
253             Some(rc) => match *rc {
254                 ItemDecorator(dec_fn) => {
255                     attr::mark_used(attr);
256
257                     fld.cx.bt_push(ExpnInfo {
258                         call_site: attr.span,
259                         callee: NameAndSpan {
260                             name: mname.get().to_string(),
261                             format: MacroAttribute,
262                             span: None
263                         }
264                     });
265
266                     // we'd ideally decorator_items.push_all(expand_item(item, fld)),
267                     // but that double-mut-borrows fld
268                     let mut items: SmallVector<Gc<ast::Item>> = SmallVector::zero();
269                     dec_fn(fld.cx, attr.span, attr.node.value, it,
270                         |item| items.push(item));
271                     decorator_items.extend(items.move_iter()
272                         .flat_map(|item| expand_item(item, fld).move_iter()));
273
274                     fld.cx.bt_pop();
275                 }
276                 _ => new_attrs.push((*attr).clone()),
277             },
278             _ => new_attrs.push((*attr).clone()),
279         }
280     }
281
282     let mut new_items = match it.node {
283         ast::ItemMac(..) => expand_item_mac(it, fld),
284         ast::ItemMod(_) | ast::ItemForeignMod(_) => {
285             fld.cx.mod_push(it.ident);
286             let macro_escape = contains_macro_escape(new_attrs.as_slice());
287             let result = with_exts_frame!(fld.cx.syntax_env,
288                                           macro_escape,
289                                           noop_fold_item(&*it, fld));
290             fld.cx.mod_pop();
291             result
292         },
293         _ => {
294             let it = box(GC) ast::Item {
295                 attrs: new_attrs,
296                 ..(*it).clone()
297
298             };
299             noop_fold_item(&*it, fld)
300         }
301     };
302
303     new_items.push_all(decorator_items);
304     new_items
305 }
306
307 fn expand_item_modifiers(mut it: Gc<ast::Item>, fld: &mut MacroExpander)
308                          -> Gc<ast::Item> {
309     // partition the attributes into ItemModifiers and others
310     let (modifiers, other_attrs) = it.attrs.partitioned(|attr| {
311         match fld.cx.syntax_env.find(&intern(attr.name().get())) {
312             Some(rc) => match *rc { ItemModifier(_) => true, _ => false },
313             _ => false
314         }
315     });
316     // update the attrs, leave everything else alone. Is this mutation really a good idea?
317     it = box(GC) ast::Item {
318         attrs: other_attrs,
319         ..(*it).clone()
320     };
321
322     if modifiers.is_empty() {
323         return it;
324     }
325
326     for attr in modifiers.iter() {
327         let mname = attr.name();
328
329         match fld.cx.syntax_env.find(&intern(mname.get())) {
330             Some(rc) => match *rc {
331                 ItemModifier(dec_fn) => {
332                     attr::mark_used(attr);
333                     fld.cx.bt_push(ExpnInfo {
334                         call_site: attr.span,
335                         callee: NameAndSpan {
336                             name: mname.get().to_string(),
337                             format: MacroAttribute,
338                             span: None,
339                         }
340                     });
341                     it = dec_fn(fld.cx, attr.span, attr.node.value, it);
342                     fld.cx.bt_pop();
343                 }
344                 _ => unreachable!()
345             },
346             _ => unreachable!()
347         }
348     }
349
350     // expansion may have added new ItemModifiers
351     expand_item_modifiers(it, fld)
352 }
353
354 /// Expand item_underscore
355 fn expand_item_underscore(item: &ast::Item_, fld: &mut MacroExpander) -> ast::Item_ {
356     match *item {
357         ast::ItemFn(decl, fn_style, abi, ref generics, body) => {
358             let (rewritten_fn_decl, rewritten_body)
359                 = expand_and_rename_fn_decl_and_block(&*decl, body, fld);
360             let expanded_generics = fold::noop_fold_generics(generics,fld);
361             ast::ItemFn(rewritten_fn_decl, fn_style, abi, expanded_generics, rewritten_body)
362         }
363         _ => noop_fold_item_underscore(&*item, fld)
364     }
365 }
366
367 // does this attribute list contain "macro_escape" ?
368 fn contains_macro_escape(attrs: &[ast::Attribute]) -> bool {
369     attr::contains_name(attrs, "macro_escape")
370 }
371
372 // Support for item-position macro invocations, exactly the same
373 // logic as for expression-position macro invocations.
374 fn expand_item_mac(it: Gc<ast::Item>, fld: &mut MacroExpander)
375                    -> SmallVector<Gc<ast::Item>>
376 {
377     let (pth, tts) = match it.node {
378         ItemMac(codemap::Spanned {
379             node: MacInvocTT(ref pth, ref tts, _),
380             ..
381         }) => {
382             (pth, (*tts).clone())
383         }
384         _ => fld.cx.span_bug(it.span, "invalid item macro invocation")
385     };
386
387     let extname = pth.segments.get(0).identifier;
388     let extnamestr = token::get_ident(extname);
389     let fm = fresh_mark();
390     let def_or_items = {
391         let expanded = match fld.cx.syntax_env.find(&extname.name) {
392             None => {
393                 fld.cx.span_err(pth.span,
394                                 format!("macro undefined: '{}!'",
395                                         extnamestr).as_slice());
396                 // let compilation continue
397                 return SmallVector::zero();
398             }
399
400             Some(rc) => match *rc {
401                 NormalTT(ref expander, span) => {
402                     if it.ident.name != parse::token::special_idents::invalid.name {
403                         fld.cx
404                             .span_err(pth.span,
405                                       format!("macro {}! expects no ident argument, \
406                                         given '{}'",
407                                       extnamestr,
408                                       token::get_ident(it.ident)).as_slice());
409                         return SmallVector::zero();
410                     }
411                     fld.cx.bt_push(ExpnInfo {
412                         call_site: it.span,
413                         callee: NameAndSpan {
414                             name: extnamestr.get().to_string(),
415                             format: MacroBang,
416                             span: span
417                         }
418                     });
419                     // mark before expansion:
420                     let marked_before = mark_tts(tts.as_slice(), fm);
421                     expander.expand(fld.cx, it.span, marked_before.as_slice())
422                 }
423                 IdentTT(ref expander, span) => {
424                     if it.ident.name == parse::token::special_idents::invalid.name {
425                         fld.cx.span_err(pth.span,
426                                         format!("macro {}! expects an ident argument",
427                                                 extnamestr.get()).as_slice());
428                         return SmallVector::zero();
429                     }
430                     fld.cx.bt_push(ExpnInfo {
431                         call_site: it.span,
432                         callee: NameAndSpan {
433                             name: extnamestr.get().to_string(),
434                             format: MacroBang,
435                             span: span
436                         }
437                     });
438                     // mark before expansion:
439                     let marked_tts = mark_tts(tts.as_slice(), fm);
440                     expander.expand(fld.cx, it.span, it.ident, marked_tts)
441                 }
442                 LetSyntaxTT(ref expander, span) => {
443                     if it.ident.name == parse::token::special_idents::invalid.name {
444                         fld.cx.span_err(pth.span,
445                                         format!("macro {}! expects an ident argument",
446                                                 extnamestr.get()).as_slice());
447                         return SmallVector::zero();
448                     }
449                     fld.cx.bt_push(ExpnInfo {
450                         call_site: it.span,
451                         callee: NameAndSpan {
452                             name: extnamestr.get().to_string(),
453                             format: MacroBang,
454                             span: span
455                         }
456                     });
457                     // DON'T mark before expansion:
458                     expander.expand(fld.cx, it.span, it.ident, tts)
459                 }
460                 _ => {
461                     fld.cx.span_err(it.span,
462                                     format!("{}! is not legal in item position",
463                                             extnamestr.get()).as_slice());
464                     return SmallVector::zero();
465                 }
466             }
467         };
468
469         match expanded.make_def() {
470             Some(def) => Left(def),
471             None => Right(expanded.make_items())
472         }
473     };
474
475     let items = match def_or_items {
476         Left(MacroDef { name, ext }) => {
477             // hidden invariant: this should only be possible as the
478             // result of expanding a LetSyntaxTT, and thus doesn't
479             // need to be marked. Not that it could be marked anyway.
480             // create issue to recommend refactoring here?
481             fld.cx.syntax_env.insert(intern(name.as_slice()), ext);
482             if attr::contains_name(it.attrs.as_slice(), "macro_export") {
483                 fld.cx.exported_macros.push(it);
484             }
485             SmallVector::zero()
486         }
487         Right(Some(items)) => {
488             items.move_iter()
489                 .map(|i| mark_item(i, fm))
490                 .flat_map(|i| fld.fold_item(i).move_iter())
491                 .collect()
492         }
493         Right(None) => {
494             fld.cx.span_err(pth.span,
495                             format!("non-item macro in item position: {}",
496                                     extnamestr.get()).as_slice());
497             return SmallVector::zero();
498         }
499     };
500
501     fld.cx.bt_pop();
502     return items;
503 }
504
505 /// Expand a stmt
506 //
507 // I don't understand why this returns a vector... it looks like we're
508 // half done adding machinery to allow macros to expand into multiple statements.
509 fn expand_stmt(s: &Stmt, fld: &mut MacroExpander) -> SmallVector<Gc<Stmt>> {
510     let (mac, semi) = match s.node {
511         StmtMac(ref mac, semi) => (mac, semi),
512         _ => return expand_non_macro_stmt(s, fld)
513     };
514     let expanded_stmt = match expand_mac_invoc(mac,&s.span,
515                                                 |r|{r.make_stmt()},
516                                                 |sts,mrk| {
517                                                     mark_stmt(&*sts,mrk)
518                                                 },
519                                                 fld) {
520         Some(stmt) => stmt,
521         None => {
522             return SmallVector::zero();
523         }
524     };
525
526     // Keep going, outside-in.
527     let fully_expanded = fld.fold_stmt(&*expanded_stmt);
528     fld.cx.bt_pop();
529     let fully_expanded: SmallVector<Gc<Stmt>> = fully_expanded.move_iter()
530             .map(|s| box(GC) Spanned { span: s.span, node: s.node.clone() })
531             .collect();
532
533     fully_expanded.move_iter().map(|s| {
534         match s.node {
535             StmtExpr(e, stmt_id) if semi => {
536                 box(GC) Spanned {
537                     span: s.span,
538                     node: StmtSemi(e, stmt_id)
539                 }
540             }
541             _ => s /* might already have a semi */
542         }
543     }).collect()
544 }
545
546 // expand a non-macro stmt. this is essentially the fallthrough for
547 // expand_stmt, above.
548 fn expand_non_macro_stmt(s: &Stmt, fld: &mut MacroExpander)
549                          -> SmallVector<Gc<Stmt>> {
550     // is it a let?
551     match s.node {
552         StmtDecl(decl, node_id) => {
553             match *decl {
554                 Spanned {
555                     node: DeclLocal(ref local),
556                     span: stmt_span
557                 } => {
558                     // take it apart:
559                     let Local {
560                         ty: ty,
561                         pat: pat,
562                         init: init,
563                         id: id,
564                         span: span,
565                         source: source,
566                     } = **local;
567                     // expand the ty since TyFixedLengthVec contains an Expr
568                     // and thus may have a macro use
569                     let expanded_ty = fld.fold_ty(ty);
570                     // expand the pat (it might contain macro uses):
571                     let expanded_pat = fld.fold_pat(pat);
572                     // find the PatIdents in the pattern:
573                     // oh dear heaven... this is going to include the enum
574                     // names, as well... but that should be okay, as long as
575                     // the new names are gensyms for the old ones.
576                     // generate fresh names, push them to a new pending list
577                     let idents = pattern_bindings(&*expanded_pat);
578                     let mut new_pending_renames =
579                         idents.iter().map(|ident| (*ident, fresh_name(ident))).collect();
580                     // rewrite the pattern using the new names (the old
581                     // ones have already been applied):
582                     let rewritten_pat = {
583                         // nested binding to allow borrow to expire:
584                         let mut rename_fld = IdentRenamer{renames: &mut new_pending_renames};
585                         rename_fld.fold_pat(expanded_pat)
586                     };
587                     // add them to the existing pending renames:
588                     fld.cx.syntax_env.info().pending_renames.push_all_move(new_pending_renames);
589                     // also, don't forget to expand the init:
590                     let new_init_opt = init.map(|e| fld.fold_expr(e));
591                     let rewritten_local =
592                         box(GC) Local {
593                             ty: expanded_ty,
594                             pat: rewritten_pat,
595                             init: new_init_opt,
596                             id: id,
597                             span: span,
598                             source: source
599                         };
600                     SmallVector::one(box(GC) Spanned {
601                         node: StmtDecl(box(GC) Spanned {
602                                 node: DeclLocal(rewritten_local),
603                                 span: stmt_span
604                             },
605                             node_id),
606                         span: span
607                     })
608                 }
609                 _ => noop_fold_stmt(s, fld),
610             }
611         },
612         _ => noop_fold_stmt(s, fld),
613     }
614 }
615
616 // expand the arm of a 'match', renaming for macro hygiene
617 fn expand_arm(arm: &ast::Arm, fld: &mut MacroExpander) -> ast::Arm {
618     // expand pats... they might contain macro uses:
619     let expanded_pats : Vec<Gc<ast::Pat>> = arm.pats.iter().map(|pat| fld.fold_pat(*pat)).collect();
620     if expanded_pats.len() == 0 {
621         fail!("encountered match arm with 0 patterns");
622     }
623     // all of the pats must have the same set of bindings, so use the
624     // first one to extract them and generate new names:
625     let first_pat = expanded_pats.get(0);
626     let idents = pattern_bindings(&**first_pat);
627     let new_renames =
628         idents.iter().map(|id| (*id,fresh_name(id))).collect();
629     // apply the renaming, but only to the PatIdents:
630     let mut rename_pats_fld = PatIdentRenamer{renames:&new_renames};
631     let rewritten_pats =
632         expanded_pats.iter().map(|pat| rename_pats_fld.fold_pat(*pat)).collect();
633     // apply renaming and then expansion to the guard and the body:
634     let mut rename_fld = IdentRenamer{renames:&new_renames};
635     let rewritten_guard =
636         arm.guard.map(|g| fld.fold_expr(rename_fld.fold_expr(g)));
637     let rewritten_body = fld.fold_expr(rename_fld.fold_expr(arm.body));
638     ast::Arm {
639         attrs: arm.attrs.iter().map(|x| fld.fold_attribute(*x)).collect(),
640         pats: rewritten_pats,
641         guard: rewritten_guard,
642         body: rewritten_body,
643     }
644 }
645
646 /// A visitor that extracts the PatIdent (binding) paths
647 /// from a given thingy and puts them in a mutable
648 /// array
649 #[deriving(Clone)]
650 struct PatIdentFinder {
651     ident_accumulator: Vec<ast::Ident> ,
652 }
653
654 impl Visitor<()> for PatIdentFinder {
655     fn visit_pat(&mut self, pattern: &ast::Pat, _: ()) {
656         match *pattern {
657             ast::Pat { id: _, node: ast::PatIdent(_, ref path1, ref inner), span: _ } => {
658                 self.ident_accumulator.push(path1.node);
659                 // visit optional subpattern of PatIdent:
660                 for subpat in inner.iter() {
661                     self.visit_pat(&**subpat, ())
662                 }
663             }
664             // use the default traversal for non-PatIdents
665             _ => visit::walk_pat(self, pattern, ())
666         }
667     }
668 }
669
670 /// find the PatIdent paths in a pattern
671 fn pattern_bindings(pat : &ast::Pat) -> Vec<ast::Ident> {
672     let mut name_finder = PatIdentFinder{ident_accumulator:Vec::new()};
673     name_finder.visit_pat(pat,());
674     name_finder.ident_accumulator
675 }
676
677 /// find the PatIdent paths in a
678 fn fn_decl_arg_bindings(fn_decl: &ast::FnDecl) -> Vec<ast::Ident> {
679     let mut pat_idents = PatIdentFinder{ident_accumulator:Vec::new()};
680     for arg in fn_decl.inputs.iter() {
681         pat_idents.visit_pat(&*arg.pat, ());
682     }
683     pat_idents.ident_accumulator
684 }
685
686 // expand a block. pushes a new exts_frame, then calls expand_block_elts
687 fn expand_block(blk: &Block, fld: &mut MacroExpander) -> P<Block> {
688     // see note below about treatment of exts table
689     with_exts_frame!(fld.cx.syntax_env,false,
690                      expand_block_elts(blk, fld))
691 }
692
693 // expand the elements of a block.
694 fn expand_block_elts(b: &Block, fld: &mut MacroExpander) -> P<Block> {
695     let new_view_items = b.view_items.iter().map(|x| fld.fold_view_item(x)).collect();
696     let new_stmts =
697         b.stmts.iter().flat_map(|x| {
698             // perform all pending renames
699             let renamed_stmt = {
700                 let pending_renames = &mut fld.cx.syntax_env.info().pending_renames;
701                 let mut rename_fld = IdentRenamer{renames:pending_renames};
702                 rename_fld.fold_stmt(&**x).expect_one("rename_fold didn't return one value")
703             };
704             // expand macros in the statement
705             fld.fold_stmt(&*renamed_stmt).move_iter()
706         }).collect();
707     let new_expr = b.expr.map(|x| {
708         let expr = {
709             let pending_renames = &mut fld.cx.syntax_env.info().pending_renames;
710             let mut rename_fld = IdentRenamer{renames:pending_renames};
711             rename_fld.fold_expr(x)
712         };
713         fld.fold_expr(expr)
714     });
715     P(Block {
716         view_items: new_view_items,
717         stmts: new_stmts,
718         expr: new_expr,
719         id: fld.new_id(b.id),
720         rules: b.rules,
721         span: b.span,
722     })
723 }
724
725 fn expand_pat(p: Gc<ast::Pat>, fld: &mut MacroExpander) -> Gc<ast::Pat> {
726     let (pth, tts) = match p.node {
727         PatMac(ref mac) => {
728             match mac.node {
729                 MacInvocTT(ref pth, ref tts, _) => {
730                     (pth, (*tts).clone())
731                 }
732             }
733         }
734         _ => return noop_fold_pat(p, fld),
735     };
736     if pth.segments.len() > 1u {
737         fld.cx.span_err(pth.span, "expected macro name without module separators");
738         return DummyResult::raw_pat(p.span);
739     }
740     let extname = pth.segments.get(0).identifier;
741     let extnamestr = token::get_ident(extname);
742     let marked_after = match fld.cx.syntax_env.find(&extname.name) {
743         None => {
744             fld.cx.span_err(pth.span,
745                             format!("macro undefined: '{}!'",
746                                     extnamestr).as_slice());
747             // let compilation continue
748             return DummyResult::raw_pat(p.span);
749         }
750
751         Some(rc) => match *rc {
752             NormalTT(ref expander, span) => {
753                 fld.cx.bt_push(ExpnInfo {
754                     call_site: p.span,
755                     callee: NameAndSpan {
756                         name: extnamestr.get().to_string(),
757                         format: MacroBang,
758                         span: span
759                     }
760                 });
761
762                 let fm = fresh_mark();
763                 let marked_before = mark_tts(tts.as_slice(), fm);
764                 let mac_span = original_span(fld.cx);
765                 let expanded = match expander.expand(fld.cx,
766                                     mac_span.call_site,
767                                     marked_before.as_slice()).make_pat() {
768                     Some(e) => e,
769                     None => {
770                         fld.cx.span_err(
771                             pth.span,
772                             format!(
773                                 "non-pattern macro in pattern position: {}",
774                                 extnamestr.get()
775                             ).as_slice()
776                         );
777                         return DummyResult::raw_pat(p.span);
778                     }
779                 };
780
781                 // mark after:
782                 mark_pat(expanded,fm)
783             }
784             _ => {
785                 fld.cx.span_err(p.span,
786                                 format!("{}! is not legal in pattern position",
787                                         extnamestr.get()).as_slice());
788                 return DummyResult::raw_pat(p.span);
789             }
790         }
791     };
792
793     let fully_expanded =
794         fld.fold_pat(marked_after).node.clone();
795     fld.cx.bt_pop();
796
797     box(GC) ast::Pat {
798         id: ast::DUMMY_NODE_ID,
799         node: fully_expanded,
800         span: p.span,
801     }
802 }
803
804 /// A tree-folder that applies every rename in its (mutable) list
805 /// to every identifier, including both bindings and varrefs
806 /// (and lots of things that will turn out to be neither)
807 pub struct IdentRenamer<'a> {
808     renames: &'a mtwt::RenameList,
809 }
810
811 impl<'a> Folder for IdentRenamer<'a> {
812     fn fold_ident(&mut self, id: Ident) -> Ident {
813         Ident {
814             name: id.name,
815             ctxt: mtwt::apply_renames(self.renames, id.ctxt),
816         }
817     }
818     fn fold_mac(&mut self, macro: &ast::Mac) -> ast::Mac {
819         fold::noop_fold_mac(macro, self)
820     }
821 }
822
823 /// A tree-folder that applies every rename in its list to
824 /// the idents that are in PatIdent patterns. This is more narrowly
825 /// focused than IdentRenamer, and is needed for FnDecl,
826 /// where we want to rename the args but not the fn name or the generics etc.
827 pub struct PatIdentRenamer<'a> {
828     renames: &'a mtwt::RenameList,
829 }
830
831 impl<'a> Folder for PatIdentRenamer<'a> {
832     fn fold_pat(&mut self, pat: Gc<ast::Pat>) -> Gc<ast::Pat> {
833         match pat.node {
834             ast::PatIdent(binding_mode, Spanned{span: ref sp, node: id}, ref sub) => {
835                 let new_ident = Ident{name: id.name,
836                                       ctxt: mtwt::apply_renames(self.renames, id.ctxt)};
837                 let new_node =
838                     ast::PatIdent(binding_mode,
839                                   Spanned{span: self.new_span(*sp), node: new_ident},
840                                   sub.map(|p| self.fold_pat(p)));
841                 box(GC) ast::Pat {
842                     id: pat.id,
843                     span: self.new_span(pat.span),
844                     node: new_node,
845                 }
846             },
847             _ => noop_fold_pat(pat, self)
848         }
849     }
850     fn fold_mac(&mut self, macro: &ast::Mac) -> ast::Mac {
851         fold::noop_fold_mac(macro, self)
852     }
853 }
854
855 // expand a method
856 fn expand_method(m: &ast::Method, fld: &mut MacroExpander) -> SmallVector<Gc<ast::Method>> {
857     let id = fld.new_id(m.id);
858     match m.node {
859         ast::MethDecl(ident,
860                       ref generics,
861                       abi,
862                       ref explicit_self,
863                       fn_style,
864                       decl,
865                       body,
866                       vis) => {
867             let (rewritten_fn_decl, rewritten_body)
868                 = expand_and_rename_fn_decl_and_block(&*decl,body,fld);
869             SmallVector::one(box(GC) ast::Method {
870                     attrs: m.attrs.iter().map(|a| fld.fold_attribute(*a)).collect(),
871                     id: id,
872                     span: fld.new_span(m.span),
873                     node: ast::MethDecl(fld.fold_ident(ident),
874                                         noop_fold_generics(generics, fld),
875                                         abi,
876                                         fld.fold_explicit_self(explicit_self),
877                                         fn_style,
878                                         rewritten_fn_decl,
879                                         rewritten_body,
880                                         vis)
881                 })
882         },
883         ast::MethMac(ref mac) => {
884             let maybe_new_methods =
885                 expand_mac_invoc(mac, &m.span,
886                                  |r|{r.make_methods()},
887                                  |meths,mark|{
888                     meths.move_iter().map(|m|{mark_method(m,mark)})
889                         .collect()},
890                                  fld);
891
892             let new_methods = match maybe_new_methods {
893                 Some(methods) => methods,
894                 None => SmallVector::zero()
895             };
896
897             // expand again if necessary
898             new_methods.move_iter().flat_map(|m| fld.fold_method(m).move_iter()).collect()
899         }
900     }
901 }
902
903 /// Given a fn_decl and a block and a MacroExpander, expand the fn_decl, then use the
904 /// PatIdents in its arguments to perform renaming in the FnDecl and
905 /// the block, returning both the new FnDecl and the new Block.
906 fn expand_and_rename_fn_decl_and_block(fn_decl: &ast::FnDecl, block: Gc<ast::Block>,
907                                        fld: &mut MacroExpander)
908     -> (Gc<ast::FnDecl>, Gc<ast::Block>) {
909     let expanded_decl = fld.fold_fn_decl(fn_decl);
910     let idents = fn_decl_arg_bindings(&*expanded_decl);
911     let renames =
912         idents.iter().map(|id : &ast::Ident| (*id,fresh_name(id))).collect();
913     // first, a renamer for the PatIdents, for the fn_decl:
914     let mut rename_pat_fld = PatIdentRenamer{renames: &renames};
915     let rewritten_fn_decl = rename_pat_fld.fold_fn_decl(&*expanded_decl);
916     // now, a renamer for *all* idents, for the body:
917     let mut rename_fld = IdentRenamer{renames: &renames};
918     let rewritten_body = fld.fold_block(rename_fld.fold_block(block));
919     (rewritten_fn_decl,rewritten_body)
920 }
921
922 /// A tree-folder that performs macro expansion
923 pub struct MacroExpander<'a, 'b:'a> {
924     pub cx: &'a mut ExtCtxt<'b>,
925 }
926
927 impl<'a, 'b> Folder for MacroExpander<'a, 'b> {
928     fn fold_expr(&mut self, expr: Gc<ast::Expr>) -> Gc<ast::Expr> {
929         expand_expr(expr, self)
930     }
931
932     fn fold_pat(&mut self, pat: Gc<ast::Pat>) -> Gc<ast::Pat> {
933         expand_pat(pat, self)
934     }
935
936     fn fold_item(&mut self, item: Gc<ast::Item>) -> SmallVector<Gc<ast::Item>> {
937         expand_item(item, self)
938     }
939
940     fn fold_item_underscore(&mut self, item: &ast::Item_) -> ast::Item_ {
941         expand_item_underscore(item, self)
942     }
943
944     fn fold_stmt(&mut self, stmt: &ast::Stmt) -> SmallVector<Gc<ast::Stmt>> {
945         expand_stmt(stmt, self)
946     }
947
948     fn fold_block(&mut self, block: P<Block>) -> P<Block> {
949         expand_block(&*block, self)
950     }
951
952     fn fold_arm(&mut self, arm: &ast::Arm) -> ast::Arm {
953         expand_arm(arm, self)
954     }
955
956     fn fold_method(&mut self, method: Gc<ast::Method>) -> SmallVector<Gc<ast::Method>> {
957         expand_method(&*method, self)
958     }
959
960     fn new_span(&mut self, span: Span) -> Span {
961         new_span(self.cx, span)
962     }
963 }
964
965 fn new_span(cx: &ExtCtxt, sp: Span) -> Span {
966     /* this discards information in the case of macro-defining macros */
967     Span {
968         lo: sp.lo,
969         hi: sp.hi,
970         expn_info: cx.backtrace(),
971     }
972 }
973
974 pub struct ExpansionConfig {
975     pub deriving_hash_type_parameter: bool,
976     pub crate_name: String,
977 }
978
979 pub struct ExportedMacros {
980     pub crate_name: Ident,
981     pub macros: Vec<String>,
982 }
983
984 pub fn expand_crate(parse_sess: &parse::ParseSess,
985                     cfg: ExpansionConfig,
986                     // these are the macros being imported to this crate:
987                     imported_macros: Vec<ExportedMacros>,
988                     user_exts: Vec<NamedSyntaxExtension>,
989                     c: Crate) -> Crate {
990     let mut cx = ExtCtxt::new(parse_sess, c.config.clone(), cfg);
991     let mut expander = MacroExpander {
992         cx: &mut cx,
993     };
994
995     for ExportedMacros { crate_name, macros } in imported_macros.move_iter() {
996         let name = format!("<{} macros>", token::get_ident(crate_name))
997             .into_string();
998
999         for source in macros.move_iter() {
1000             let item = parse::parse_item_from_source_str(name.clone(),
1001                                                          source,
1002                                                          expander.cx.cfg(),
1003                                                          expander.cx.parse_sess())
1004                     .expect("expected a serialized item");
1005             expand_item_mac(item, &mut expander);
1006         }
1007     }
1008
1009     for (name, extension) in user_exts.move_iter() {
1010         expander.cx.syntax_env.insert(name, extension);
1011     }
1012
1013     let mut ret = expander.fold_crate(c);
1014     ret.exported_macros = expander.cx.exported_macros.clone();
1015     parse_sess.span_diagnostic.handler().abort_if_errors();
1016     return ret;
1017 }
1018
1019 // HYGIENIC CONTEXT EXTENSION:
1020 // all of these functions are for walking over
1021 // ASTs and making some change to the context of every
1022 // element that has one. a CtxtFn is a trait-ified
1023 // version of a closure in (SyntaxContext -> SyntaxContext).
1024 // the ones defined here include:
1025 // Marker - add a mark to a context
1026
1027 // A Marker adds the given mark to the syntax context
1028 struct Marker { mark: Mrk }
1029
1030 impl Folder for Marker {
1031     fn fold_ident(&mut self, id: Ident) -> Ident {
1032         ast::Ident {
1033             name: id.name,
1034             ctxt: mtwt::apply_mark(self.mark, id.ctxt)
1035         }
1036     }
1037     fn fold_mac(&mut self, m: &ast::Mac) -> ast::Mac {
1038         let macro = match m.node {
1039             MacInvocTT(ref path, ref tts, ctxt) => {
1040                 MacInvocTT(self.fold_path(path),
1041                            self.fold_tts(tts.as_slice()),
1042                            mtwt::apply_mark(self.mark, ctxt))
1043             }
1044         };
1045         Spanned {
1046             node: macro,
1047             span: m.span,
1048         }
1049     }
1050 }
1051
1052 // apply a given mark to the given token trees. Used prior to expansion of a macro.
1053 fn mark_tts(tts: &[TokenTree], m: Mrk) -> Vec<TokenTree> {
1054     noop_fold_tts(tts, &mut Marker{mark:m})
1055 }
1056
1057 // apply a given mark to the given expr. Used following the expansion of a macro.
1058 fn mark_expr(expr: Gc<ast::Expr>, m: Mrk) -> Gc<ast::Expr> {
1059     Marker{mark:m}.fold_expr(expr)
1060 }
1061
1062 // apply a given mark to the given pattern. Used following the expansion of a macro.
1063 fn mark_pat(pat: Gc<ast::Pat>, m: Mrk) -> Gc<ast::Pat> {
1064     Marker{mark:m}.fold_pat(pat)
1065 }
1066
1067 // apply a given mark to the given stmt. Used following the expansion of a macro.
1068 fn mark_stmt(expr: &ast::Stmt, m: Mrk) -> Gc<ast::Stmt> {
1069     Marker{mark:m}.fold_stmt(expr)
1070         .expect_one("marking a stmt didn't return exactly one stmt")
1071 }
1072
1073 // apply a given mark to the given item. Used following the expansion of a macro.
1074 fn mark_item(expr: Gc<ast::Item>, m: Mrk) -> Gc<ast::Item> {
1075     Marker{mark:m}.fold_item(expr)
1076         .expect_one("marking an item didn't return exactly one item")
1077 }
1078
1079 // apply a given mark to the given item. Used following the expansion of a macro.
1080 fn mark_method(expr: Gc<ast::Method>, m: Mrk) -> Gc<ast::Method> {
1081     Marker{mark:m}.fold_method(expr)
1082         .expect_one("marking an item didn't return exactly one method")
1083 }
1084
1085 fn original_span(cx: &ExtCtxt) -> Gc<codemap::ExpnInfo> {
1086     let mut relevant_info = cx.backtrace();
1087     let mut einfo = relevant_info.unwrap();
1088     loop {
1089         match relevant_info {
1090             None => { break }
1091             Some(e) => {
1092                 einfo = e;
1093                 relevant_info = einfo.call_site.expn_info;
1094             }
1095         }
1096     }
1097     return einfo;
1098 }
1099
1100 /// Check that there are no macro invocations left in the AST:
1101 pub fn check_for_macros(sess: &parse::ParseSess, krate: &ast::Crate) {
1102     visit::walk_crate(&mut MacroExterminator{sess:sess}, krate, ());
1103 }
1104
1105 /// A visitor that ensures that no macro invocations remain in an AST.
1106 struct MacroExterminator<'a>{
1107     sess: &'a parse::ParseSess
1108 }
1109
1110 impl<'a> visit::Visitor<()> for MacroExterminator<'a> {
1111     fn visit_mac(&mut self, macro: &ast::Mac, _:()) {
1112         self.sess.span_diagnostic.span_bug(macro.span,
1113                                            "macro exterminator: expected AST \
1114                                            with no macro invocations");
1115     }
1116 }
1117
1118
1119 #[cfg(test)]
1120 mod test {
1121     use super::{pattern_bindings, expand_crate, contains_macro_escape};
1122     use super::{PatIdentFinder, IdentRenamer, PatIdentRenamer};
1123     use ast;
1124     use ast::{Attribute_, AttrOuter, MetaWord, Name};
1125     use attr;
1126     use codemap;
1127     use codemap::Spanned;
1128     use ext::mtwt;
1129     use fold::Folder;
1130     use parse;
1131     use parse::token;
1132     use util::parser_testing::{string_to_parser};
1133     use util::parser_testing::{string_to_pat, string_to_crate, strs_to_idents};
1134     use visit;
1135     use visit::Visitor;
1136
1137     use std::gc::GC;
1138
1139     // a visitor that extracts the paths
1140     // from a given thingy and puts them in a mutable
1141     // array (passed in to the traversal)
1142     #[deriving(Clone)]
1143     struct PathExprFinderContext {
1144         path_accumulator: Vec<ast::Path> ,
1145     }
1146
1147     impl Visitor<()> for PathExprFinderContext {
1148
1149         fn visit_expr(&mut self, expr: &ast::Expr, _: ()) {
1150             match *expr {
1151                 ast::Expr{id:_,span:_,node:ast::ExprPath(ref p)} => {
1152                     self.path_accumulator.push(p.clone());
1153                     // not calling visit_path, but it should be fine.
1154                 }
1155                 _ => visit::walk_expr(self,expr,())
1156             }
1157         }
1158     }
1159
1160     // find the variable references in a crate
1161     fn crate_varrefs(the_crate : &ast::Crate) -> Vec<ast::Path> {
1162         let mut path_finder = PathExprFinderContext{path_accumulator:Vec::new()};
1163         visit::walk_crate(&mut path_finder, the_crate, ());
1164         path_finder.path_accumulator
1165     }
1166
1167     /// A Visitor that extracts the identifiers from a thingy.
1168     // as a side note, I'm starting to want to abstract over these....
1169     struct IdentFinder{
1170         ident_accumulator: Vec<ast::Ident>
1171     }
1172
1173     impl Visitor<()> for IdentFinder {
1174         fn visit_ident(&mut self, _: codemap::Span, id: ast::Ident, _: ()){
1175             self.ident_accumulator.push(id);
1176         }
1177     }
1178
1179     /// Find the idents in a crate
1180     fn crate_idents(the_crate: &ast::Crate) -> Vec<ast::Ident> {
1181         let mut ident_finder = IdentFinder{ident_accumulator: Vec::new()};
1182         visit::walk_crate(&mut ident_finder, the_crate, ());
1183         ident_finder.ident_accumulator
1184     }
1185
1186     // these following tests are quite fragile, in that they don't test what
1187     // *kind* of failure occurs.
1188
1189     // make sure that macros can't escape fns
1190     #[should_fail]
1191     #[test] fn macros_cant_escape_fns_test () {
1192         let src = "fn bogus() {macro_rules! z (() => (3+4))}\
1193                    fn inty() -> int { z!() }".to_string();
1194         let sess = parse::new_parse_sess();
1195         let crate_ast = parse::parse_crate_from_source_str(
1196             "<test>".to_string(),
1197             src,
1198             Vec::new(), &sess);
1199         // should fail:
1200         let cfg = ::syntax::ext::expand::ExpansionConfig {
1201             deriving_hash_type_parameter: false,
1202             crate_name: "test".to_string(),
1203         };
1204         expand_crate(&sess,cfg,vec!(),vec!(),crate_ast);
1205     }
1206
1207     // make sure that macros can't escape modules
1208     #[should_fail]
1209     #[test] fn macros_cant_escape_mods_test () {
1210         let src = "mod foo {macro_rules! z (() => (3+4))}\
1211                    fn inty() -> int { z!() }".to_string();
1212         let sess = parse::new_parse_sess();
1213         let crate_ast = parse::parse_crate_from_source_str(
1214             "<test>".to_string(),
1215             src,
1216             Vec::new(), &sess);
1217         let cfg = ::syntax::ext::expand::ExpansionConfig {
1218             deriving_hash_type_parameter: false,
1219             crate_name: "test".to_string(),
1220         };
1221         expand_crate(&sess,cfg,vec!(),vec!(),crate_ast);
1222     }
1223
1224     // macro_escape modules should allow macros to escape
1225     #[test] fn macros_can_escape_flattened_mods_test () {
1226         let src = "#[macro_escape] mod foo {macro_rules! z (() => (3+4))}\
1227                    fn inty() -> int { z!() }".to_string();
1228         let sess = parse::new_parse_sess();
1229         let crate_ast = parse::parse_crate_from_source_str(
1230             "<test>".to_string(),
1231             src,
1232             Vec::new(), &sess);
1233         let cfg = ::syntax::ext::expand::ExpansionConfig {
1234             deriving_hash_type_parameter: false,
1235             crate_name: "test".to_string(),
1236         };
1237         expand_crate(&sess, cfg, vec!(), vec!(), crate_ast);
1238     }
1239
1240     #[test] fn test_contains_flatten (){
1241         let attr1 = make_dummy_attr ("foo");
1242         let attr2 = make_dummy_attr ("bar");
1243         let escape_attr = make_dummy_attr ("macro_escape");
1244         let attrs1 = vec!(attr1, escape_attr, attr2);
1245         assert_eq!(contains_macro_escape(attrs1.as_slice()),true);
1246         let attrs2 = vec!(attr1,attr2);
1247         assert_eq!(contains_macro_escape(attrs2.as_slice()),false);
1248     }
1249
1250     // make a MetaWord outer attribute with the given name
1251     fn make_dummy_attr(s: &str) -> ast::Attribute {
1252         Spanned {
1253             span:codemap::DUMMY_SP,
1254             node: Attribute_ {
1255                 id: attr::mk_attr_id(),
1256                 style: AttrOuter,
1257                 value: box(GC) Spanned {
1258                     node: MetaWord(token::intern_and_get_ident(s)),
1259                     span: codemap::DUMMY_SP,
1260                 },
1261                 is_sugared_doc: false,
1262             }
1263         }
1264     }
1265
1266     fn expand_crate_str(crate_str: String) -> ast::Crate {
1267         let ps = parse::new_parse_sess();
1268         let crate_ast = string_to_parser(&ps, crate_str).parse_crate_mod();
1269         // the cfg argument actually does matter, here...
1270         let cfg = ::syntax::ext::expand::ExpansionConfig {
1271             deriving_hash_type_parameter: false,
1272             crate_name: "test".to_string(),
1273         };
1274         expand_crate(&ps,cfg,vec!(),vec!(),crate_ast)
1275     }
1276
1277     // find the pat_ident paths in a crate
1278     fn crate_bindings(the_crate : &ast::Crate) -> Vec<ast::Ident> {
1279         let mut name_finder = PatIdentFinder{ident_accumulator:Vec::new()};
1280         visit::walk_crate(&mut name_finder, the_crate, ());
1281         name_finder.ident_accumulator
1282     }
1283
1284     #[test] fn macro_tokens_should_match(){
1285         expand_crate_str(
1286             "macro_rules! m((a)=>(13)) fn main(){m!(a);}".to_string());
1287     }
1288
1289     // should be able to use a bound identifier as a literal in a macro definition:
1290     #[test] fn self_macro_parsing(){
1291         expand_crate_str(
1292             "macro_rules! foo ((zz) => (287u;))
1293             fn f(zz : int) {foo!(zz);}".to_string()
1294             );
1295     }
1296
1297     // renaming tests expand a crate and then check that the bindings match
1298     // the right varrefs. The specification of the test case includes the
1299     // text of the crate, and also an array of arrays.  Each element in the
1300     // outer array corresponds to a binding in the traversal of the AST
1301     // induced by visit.  Each of these arrays contains a list of indexes,
1302     // interpreted as the varrefs in the varref traversal that this binding
1303     // should match.  So, for instance, in a program with two bindings and
1304     // three varrefs, the array ~[~[1,2],~[0]] would indicate that the first
1305     // binding should match the second two varrefs, and the second binding
1306     // should match the first varref.
1307     //
1308     // Put differently; this is a sparse representation of a boolean matrix
1309     // indicating which bindings capture which identifiers.
1310     //
1311     // Note also that this matrix is dependent on the implicit ordering of
1312     // the bindings and the varrefs discovered by the name-finder and the path-finder.
1313     //
1314     // The comparisons are done post-mtwt-resolve, so we're comparing renamed
1315     // names; differences in marks don't matter any more.
1316     //
1317     // oog... I also want tests that check "bound-identifier-=?". That is,
1318     // not just "do these have the same name", but "do they have the same
1319     // name *and* the same marks"? Understanding this is really pretty painful.
1320     // in principle, you might want to control this boolean on a per-varref basis,
1321     // but that would make things even harder to understand, and might not be
1322     // necessary for thorough testing.
1323     type RenamingTest = (&'static str, Vec<Vec<uint>>, bool);
1324
1325     #[test]
1326     fn automatic_renaming () {
1327         let tests: Vec<RenamingTest> =
1328             vec!(// b & c should get new names throughout, in the expr too:
1329                 ("fn a() -> int { let b = 13; let c = b; b+c }",
1330                  vec!(vec!(0,1),vec!(2)), false),
1331                 // both x's should be renamed (how is this causing a bug?)
1332                 ("fn main () {let x: int = 13;x;}",
1333                  vec!(vec!(0)), false),
1334                 // the use of b after the + should be renamed, the other one not:
1335                 ("macro_rules! f (($x:ident) => (b + $x)) fn a() -> int { let b = 13; f!(b)}",
1336                  vec!(vec!(1)), false),
1337                 // the b before the plus should not be renamed (requires marks)
1338                 ("macro_rules! f (($x:ident) => ({let b=9; ($x + b)})) fn a() -> int { f!(b)}",
1339                  vec!(vec!(1)), false),
1340                 // the marks going in and out of letty should cancel, allowing that $x to
1341                 // capture the one following the semicolon.
1342                 // this was an awesome test case, and caught a *lot* of bugs.
1343                 ("macro_rules! letty(($x:ident) => (let $x = 15;))
1344                   macro_rules! user(($x:ident) => ({letty!($x); $x}))
1345                   fn main() -> int {user!(z)}",
1346                  vec!(vec!(0)), false)
1347                 );
1348         for (idx,s) in tests.iter().enumerate() {
1349             run_renaming_test(s,idx);
1350         }
1351     }
1352
1353     // no longer a fixme #8062: this test exposes a *potential* bug; our system does
1354     // not behave exactly like MTWT, but a conversation with Matthew Flatt
1355     // suggests that this can only occur in the presence of local-expand, which
1356     // we have no plans to support. ... unless it's needed for item hygiene....
1357     #[ignore]
1358     #[test] fn issue_8062(){
1359         run_renaming_test(
1360             &("fn main() {let hrcoo = 19; macro_rules! getx(()=>(hrcoo)); getx!();}",
1361               vec!(vec!(0)), true), 0)
1362     }
1363
1364     // FIXME #6994:
1365     // the z flows into and out of two macros (g & f) along one path, and one
1366     // (just g) along the other, so the result of the whole thing should
1367     // be "let z_123 = 3; z_123"
1368     #[ignore]
1369     #[test] fn issue_6994(){
1370         run_renaming_test(
1371             &("macro_rules! g (($x:ident) =>
1372               ({macro_rules! f(($y:ident)=>({let $y=3;$x}));f!($x)}))
1373               fn a(){g!(z)}",
1374               vec!(vec!(0)),false),
1375             0)
1376     }
1377
1378     // match variable hygiene. Should expand into
1379     // fn z() {match 8 {x_1 => {match 9 {x_2 | x_2 if x_2 == x_1 => x_2 + x_1}}}}
1380     #[test] fn issue_9384(){
1381         run_renaming_test(
1382             &("macro_rules! bad_macro (($ex:expr) => ({match 9 {x | x if x == $ex => x + $ex}}))
1383               fn z() {match 8 {x => bad_macro!(x)}}",
1384               // NB: the third "binding" is the repeat of the second one.
1385               vec!(vec!(1,3),vec!(0,2),vec!(0,2)),
1386               true),
1387             0)
1388     }
1389
1390     // interpolated nodes weren't getting labeled.
1391     // should expand into
1392     // fn main(){let g1_1 = 13; g1_1}}
1393     #[test] fn pat_expand_issue_15221(){
1394         run_renaming_test(
1395             &("macro_rules! inner ( ($e:pat ) => ($e))
1396               macro_rules! outer ( ($e:pat ) => (inner!($e)))
1397               fn main() { let outer!(g) = 13; g;}",
1398               vec!(vec!(0)),
1399               true),
1400             0)
1401     }
1402
1403     // create a really evil test case where a $x appears inside a binding of $x
1404     // but *shouldn't* bind because it was inserted by a different macro....
1405     // can't write this test case until we have macro-generating macros.
1406
1407     // method arg hygiene
1408     // method expands to fn get_x(&self_0, x_1:int) {self_0 + self_2 + x_3 + x_1}
1409     #[test] fn method_arg_hygiene(){
1410         run_renaming_test(
1411             &("macro_rules! inject_x (()=>(x))
1412               macro_rules! inject_self (()=>(self))
1413               struct A;
1414               impl A{fn get_x(&self, x: int) {self + inject_self!() + inject_x!() + x;} }",
1415               vec!(vec!(0),vec!(3)),
1416               true),
1417             0)
1418     }
1419
1420     // ooh, got another bite?
1421     // expands to struct A; impl A {fn thingy(&self_1) {self_1;}}
1422     #[test] fn method_arg_hygiene_2(){
1423         run_renaming_test(
1424             &("struct A;
1425               macro_rules! add_method (($T:ty) =>
1426               (impl $T {  fn thingy(&self) {self;} }))
1427               add_method!(A)",
1428               vec!(vec!(0)),
1429               true),
1430             0)
1431     }
1432
1433     // item fn hygiene
1434     // expands to fn q(x_1:int){fn g(x_2:int){x_2 + x_1};}
1435     #[test] fn issue_9383(){
1436         run_renaming_test(
1437             &("macro_rules! bad_macro (($ex:expr) => (fn g(x:int){ x + $ex }))
1438               fn q(x:int) { bad_macro!(x); }",
1439               vec!(vec!(1),vec!(0)),true),
1440             0)
1441     }
1442
1443     // closure arg hygiene (ExprFnBlock)
1444     // expands to fn f(){(|x_1 : int| {(x_2 + x_1)})(3);}
1445     #[test] fn closure_arg_hygiene(){
1446         run_renaming_test(
1447             &("macro_rules! inject_x (()=>(x))
1448             fn f(){(|x : int| {(inject_x!() + x)})(3);}",
1449               vec!(vec!(1)),
1450               true),
1451             0)
1452     }
1453
1454     // closure arg hygiene (ExprProc)
1455     // expands to fn f(){(proc(x_1 : int) {(x_2 + x_1)})(3);}
1456     #[test] fn closure_arg_hygiene_2(){
1457         run_renaming_test(
1458             &("macro_rules! inject_x (()=>(x))
1459               fn f(){ (proc(x : int){(inject_x!() + x)})(3); }",
1460               vec!(vec!(1)),
1461               true),
1462             0)
1463     }
1464
1465     // macro_rules in method position. Sadly, unimplemented.
1466     #[test] fn macro_in_method_posn(){
1467         expand_crate_str(
1468             "macro_rules! my_method (() => (fn thirteen(&self) -> int {13}))
1469             struct A;
1470             impl A{ my_method!()}
1471             fn f(){A.thirteen;}".to_string());
1472     }
1473
1474     // another nested macro
1475     // expands to impl Entries {fn size_hint(&self_1) {self_1;}
1476     #[test] fn item_macro_workaround(){
1477         run_renaming_test(
1478             &("macro_rules! item { ($i:item) => {$i}}
1479               struct Entries;
1480               macro_rules! iterator_impl {
1481               () => { item!( impl Entries { fn size_hint(&self) { self;}})}}
1482               iterator_impl! { }",
1483               vec!(vec!(0)), true),
1484             0)
1485     }
1486
1487     // run one of the renaming tests
1488     fn run_renaming_test(t: &RenamingTest, test_idx: uint) {
1489         let invalid_name = token::special_idents::invalid.name;
1490         let (teststr, bound_connections, bound_ident_check) = match *t {
1491             (ref str,ref conns, bic) => (str.to_string(), conns.clone(), bic)
1492         };
1493         let cr = expand_crate_str(teststr.to_string());
1494         let bindings = crate_bindings(&cr);
1495         let varrefs = crate_varrefs(&cr);
1496
1497         // must be one check clause for each binding:
1498         assert_eq!(bindings.len(),bound_connections.len());
1499         for (binding_idx,shouldmatch) in bound_connections.iter().enumerate() {
1500             let binding_name = mtwt::resolve(*bindings.get(binding_idx));
1501             let binding_marks = mtwt::marksof(bindings.get(binding_idx).ctxt, invalid_name);
1502             // shouldmatch can't name varrefs that don't exist:
1503             assert!((shouldmatch.len() == 0) ||
1504                     (varrefs.len() > *shouldmatch.iter().max().unwrap()));
1505             for (idx,varref) in varrefs.iter().enumerate() {
1506                 let print_hygiene_debug_info = || {
1507                     // good lord, you can't make a path with 0 segments, can you?
1508                     let final_varref_ident = match varref.segments.last() {
1509                         Some(pathsegment) => pathsegment.identifier,
1510                         None => fail!("varref with 0 path segments?")
1511                     };
1512                     let varref_name = mtwt::resolve(final_varref_ident);
1513                     let varref_idents : Vec<ast::Ident>
1514                         = varref.segments.iter().map(|s| s.identifier)
1515                         .collect();
1516                     println!("varref #{}: {}, resolves to {}",idx, varref_idents, varref_name);
1517                     let string = token::get_ident(final_varref_ident);
1518                     println!("varref's first segment's string: \"{}\"", string.get());
1519                     println!("binding #{}: {}, resolves to {}",
1520                              binding_idx, *bindings.get(binding_idx), binding_name);
1521                     mtwt::with_sctable(|x| mtwt::display_sctable(x));
1522                 };
1523                 if shouldmatch.contains(&idx) {
1524                     // it should be a path of length 1, and it should
1525                     // be free-identifier=? or bound-identifier=? to the given binding
1526                     assert_eq!(varref.segments.len(),1);
1527                     let varref_name = mtwt::resolve(varref.segments.get(0).identifier);
1528                     let varref_marks = mtwt::marksof(varref.segments
1529                                                            .get(0)
1530                                                            .identifier
1531                                                            .ctxt,
1532                                                      invalid_name);
1533                     if !(varref_name==binding_name) {
1534                         println!("uh oh, should match but doesn't:");
1535                         print_hygiene_debug_info();
1536                     }
1537                     assert_eq!(varref_name,binding_name);
1538                     if bound_ident_check {
1539                         // we're checking bound-identifier=?, and the marks
1540                         // should be the same, too:
1541                         assert_eq!(varref_marks,binding_marks.clone());
1542                     }
1543                 } else {
1544                     let varref_name = mtwt::resolve(varref.segments.get(0).identifier);
1545                     let fail = (varref.segments.len() == 1)
1546                         && (varref_name == binding_name);
1547                     // temp debugging:
1548                     if fail {
1549                         println!("failure on test {}",test_idx);
1550                         println!("text of test case: \"{}\"", teststr);
1551                         println!("");
1552                         println!("uh oh, matches but shouldn't:");
1553                         print_hygiene_debug_info();
1554                     }
1555                     assert!(!fail);
1556                 }
1557             }
1558         }
1559     }
1560
1561     #[test] fn fmt_in_macro_used_inside_module_macro() {
1562         let crate_str = "macro_rules! fmt_wrap(($b:expr)=>($b.to_string()))
1563 macro_rules! foo_module (() => (mod generated { fn a() { let xx = 147; fmt_wrap!(xx);}}))
1564 foo_module!()
1565 ".to_string();
1566         let cr = expand_crate_str(crate_str);
1567         // find the xx binding
1568         let bindings = crate_bindings(&cr);
1569         let cxbinds: Vec<&ast::Ident> =
1570             bindings.iter().filter(|b| {
1571                 let ident = token::get_ident(**b);
1572                 let string = ident.get();
1573                 "xx" == string
1574             }).collect();
1575         let cxbinds: &[&ast::Ident] = cxbinds.as_slice();
1576         let cxbind = match cxbinds {
1577             [b] => b,
1578             _ => fail!("expected just one binding for ext_cx")
1579         };
1580         let resolved_binding = mtwt::resolve(*cxbind);
1581         let varrefs = crate_varrefs(&cr);
1582
1583         // the xx binding should bind all of the xx varrefs:
1584         for (idx,v) in varrefs.iter().filter(|p| {
1585             p.segments.len() == 1
1586             && "xx" == token::get_ident(p.segments.get(0).identifier).get()
1587         }).enumerate() {
1588             if mtwt::resolve(v.segments.get(0).identifier) != resolved_binding {
1589                 println!("uh oh, xx binding didn't match xx varref:");
1590                 println!("this is xx varref \\# {:?}",idx);
1591                 println!("binding: {:?}",cxbind);
1592                 println!("resolves to: {:?}",resolved_binding);
1593                 println!("varref: {:?}",v.segments.get(0).identifier);
1594                 println!("resolves to: {:?}",
1595                          mtwt::resolve(v.segments.get(0).identifier));
1596                 mtwt::with_sctable(|x| mtwt::display_sctable(x));
1597             }
1598             assert_eq!(mtwt::resolve(v.segments.get(0).identifier),
1599                        resolved_binding);
1600         };
1601     }
1602
1603     #[test]
1604     fn pat_idents(){
1605         let pat = string_to_pat(
1606             "(a,Foo{x:c @ (b,9),y:Bar(4,d)})".to_string());
1607         let idents = pattern_bindings(&*pat);
1608         assert_eq!(idents, strs_to_idents(vec!("a","c","b","d")));
1609     }
1610
1611     // test the list of identifier patterns gathered by the visitor. Note that
1612     // 'None' is listed as an identifier pattern because we don't yet know that
1613     // it's the name of a 0-ary variant, and that 'i' appears twice in succession.
1614     #[test]
1615     fn crate_bindings_test(){
1616         let the_crate = string_to_crate("fn main (a : int) -> int {|b| {
1617         match 34 {None => 3, Some(i) | i => j, Foo{k:z,l:y} => \"banana\"}} }".to_string());
1618         let idents = crate_bindings(&the_crate);
1619         assert_eq!(idents, strs_to_idents(vec!("a","b","None","i","i","z","y")));
1620     }
1621
1622     // test the IdentRenamer directly
1623     #[test]
1624     fn ident_renamer_test () {
1625         let the_crate = string_to_crate("fn f(x : int){let x = x; x}".to_string());
1626         let f_ident = token::str_to_ident("f");
1627         let x_ident = token::str_to_ident("x");
1628         let int_ident = token::str_to_ident("int");
1629         let renames = vec!((x_ident,Name(16)));
1630         let mut renamer = IdentRenamer{renames: &renames};
1631         let renamed_crate = renamer.fold_crate(the_crate);
1632         let idents = crate_idents(&renamed_crate);
1633         let resolved : Vec<ast::Name> = idents.iter().map(|id| mtwt::resolve(*id)).collect();
1634         assert_eq!(resolved,vec!(f_ident.name,Name(16),int_ident.name,Name(16),Name(16),Name(16)));
1635     }
1636
1637     // test the PatIdentRenamer; only PatIdents get renamed
1638     #[test]
1639     fn pat_ident_renamer_test () {
1640         let the_crate = string_to_crate("fn f(x : int){let x = x; x}".to_string());
1641         let f_ident = token::str_to_ident("f");
1642         let x_ident = token::str_to_ident("x");
1643         let int_ident = token::str_to_ident("int");
1644         let renames = vec!((x_ident,Name(16)));
1645         let mut renamer = PatIdentRenamer{renames: &renames};
1646         let renamed_crate = renamer.fold_crate(the_crate);
1647         let idents = crate_idents(&renamed_crate);
1648         let resolved : Vec<ast::Name> = idents.iter().map(|id| mtwt::resolve(*id)).collect();
1649         let x_name = x_ident.name;
1650         assert_eq!(resolved,vec!(f_ident.name,Name(16),int_ident.name,Name(16),x_name,x_name));
1651     }
1652
1653
1654 }