]> git.lizzy.rs Git - rust.git/blob - src/libsyntax/ext/expand.rs
auto merge of #17134 : vberger/rust/lint_unused_extern_crate, r=alexcrichton
[rust.git] / src / libsyntax / ext / expand.rs
1 // Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 use ast::{P, Block, Crate, DeclLocal, ExprMac, 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<'v> Visitor<'v> 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, 'v> Visitor<'v> 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<'v> Visitor<'v> for PathExprFinderContext {
1148         fn visit_expr(&mut self, expr: &ast::Expr) {
1149             match expr.node {
1150                 ast::ExprPath(ref p) => {
1151                     self.path_accumulator.push(p.clone());
1152                     // not calling visit_path, but it should be fine.
1153                 }
1154                 _ => visit::walk_expr(self, expr)
1155             }
1156         }
1157     }
1158
1159     // find the variable references in a crate
1160     fn crate_varrefs(the_crate : &ast::Crate) -> Vec<ast::Path> {
1161         let mut path_finder = PathExprFinderContext{path_accumulator:Vec::new()};
1162         visit::walk_crate(&mut path_finder, the_crate);
1163         path_finder.path_accumulator
1164     }
1165
1166     /// A Visitor that extracts the identifiers from a thingy.
1167     // as a side note, I'm starting to want to abstract over these....
1168     struct IdentFinder {
1169         ident_accumulator: Vec<ast::Ident>
1170     }
1171
1172     impl<'v> Visitor<'v> for IdentFinder {
1173         fn visit_ident(&mut self, _: codemap::Span, id: ast::Ident){
1174             self.ident_accumulator.push(id);
1175         }
1176     }
1177
1178     /// Find the idents in a crate
1179     fn crate_idents(the_crate: &ast::Crate) -> Vec<ast::Ident> {
1180         let mut ident_finder = IdentFinder{ident_accumulator: Vec::new()};
1181         visit::walk_crate(&mut ident_finder, the_crate);
1182         ident_finder.ident_accumulator
1183     }
1184
1185     // these following tests are quite fragile, in that they don't test what
1186     // *kind* of failure occurs.
1187
1188     // make sure that macros can't escape fns
1189     #[should_fail]
1190     #[test] fn macros_cant_escape_fns_test () {
1191         let src = "fn bogus() {macro_rules! z (() => (3+4))}\
1192                    fn inty() -> int { z!() }".to_string();
1193         let sess = parse::new_parse_sess();
1194         let crate_ast = parse::parse_crate_from_source_str(
1195             "<test>".to_string(),
1196             src,
1197             Vec::new(), &sess);
1198         // should fail:
1199         let cfg = ::syntax::ext::expand::ExpansionConfig {
1200             deriving_hash_type_parameter: false,
1201             crate_name: "test".to_string(),
1202         };
1203         expand_crate(&sess,cfg,vec!(),vec!(),crate_ast);
1204     }
1205
1206     // make sure that macros can't escape modules
1207     #[should_fail]
1208     #[test] fn macros_cant_escape_mods_test () {
1209         let src = "mod foo {macro_rules! z (() => (3+4))}\
1210                    fn inty() -> int { z!() }".to_string();
1211         let sess = parse::new_parse_sess();
1212         let crate_ast = parse::parse_crate_from_source_str(
1213             "<test>".to_string(),
1214             src,
1215             Vec::new(), &sess);
1216         let cfg = ::syntax::ext::expand::ExpansionConfig {
1217             deriving_hash_type_parameter: false,
1218             crate_name: "test".to_string(),
1219         };
1220         expand_crate(&sess,cfg,vec!(),vec!(),crate_ast);
1221     }
1222
1223     // macro_escape modules should allow macros to escape
1224     #[test] fn macros_can_escape_flattened_mods_test () {
1225         let src = "#[macro_escape] mod foo {macro_rules! z (() => (3+4))}\
1226                    fn inty() -> int { z!() }".to_string();
1227         let sess = parse::new_parse_sess();
1228         let crate_ast = parse::parse_crate_from_source_str(
1229             "<test>".to_string(),
1230             src,
1231             Vec::new(), &sess);
1232         let cfg = ::syntax::ext::expand::ExpansionConfig {
1233             deriving_hash_type_parameter: false,
1234             crate_name: "test".to_string(),
1235         };
1236         expand_crate(&sess, cfg, vec!(), vec!(), crate_ast);
1237     }
1238
1239     #[test] fn test_contains_flatten (){
1240         let attr1 = make_dummy_attr ("foo");
1241         let attr2 = make_dummy_attr ("bar");
1242         let escape_attr = make_dummy_attr ("macro_escape");
1243         let attrs1 = vec!(attr1, escape_attr, attr2);
1244         assert_eq!(contains_macro_escape(attrs1.as_slice()),true);
1245         let attrs2 = vec!(attr1,attr2);
1246         assert_eq!(contains_macro_escape(attrs2.as_slice()),false);
1247     }
1248
1249     // make a MetaWord outer attribute with the given name
1250     fn make_dummy_attr(s: &str) -> ast::Attribute {
1251         Spanned {
1252             span:codemap::DUMMY_SP,
1253             node: Attribute_ {
1254                 id: attr::mk_attr_id(),
1255                 style: AttrOuter,
1256                 value: box(GC) Spanned {
1257                     node: MetaWord(token::intern_and_get_ident(s)),
1258                     span: codemap::DUMMY_SP,
1259                 },
1260                 is_sugared_doc: false,
1261             }
1262         }
1263     }
1264
1265     fn expand_crate_str(crate_str: String) -> ast::Crate {
1266         let ps = parse::new_parse_sess();
1267         let crate_ast = string_to_parser(&ps, crate_str).parse_crate_mod();
1268         // the cfg argument actually does matter, here...
1269         let cfg = ::syntax::ext::expand::ExpansionConfig {
1270             deriving_hash_type_parameter: false,
1271             crate_name: "test".to_string(),
1272         };
1273         expand_crate(&ps,cfg,vec!(),vec!(),crate_ast)
1274     }
1275
1276     // find the pat_ident paths in a crate
1277     fn crate_bindings(the_crate : &ast::Crate) -> Vec<ast::Ident> {
1278         let mut name_finder = PatIdentFinder{ident_accumulator:Vec::new()};
1279         visit::walk_crate(&mut name_finder, the_crate);
1280         name_finder.ident_accumulator
1281     }
1282
1283     #[test] fn macro_tokens_should_match(){
1284         expand_crate_str(
1285             "macro_rules! m((a)=>(13)) fn main(){m!(a);}".to_string());
1286     }
1287
1288     // should be able to use a bound identifier as a literal in a macro definition:
1289     #[test] fn self_macro_parsing(){
1290         expand_crate_str(
1291             "macro_rules! foo ((zz) => (287u;))
1292             fn f(zz : int) {foo!(zz);}".to_string()
1293             );
1294     }
1295
1296     // renaming tests expand a crate and then check that the bindings match
1297     // the right varrefs. The specification of the test case includes the
1298     // text of the crate, and also an array of arrays.  Each element in the
1299     // outer array corresponds to a binding in the traversal of the AST
1300     // induced by visit.  Each of these arrays contains a list of indexes,
1301     // interpreted as the varrefs in the varref traversal that this binding
1302     // should match.  So, for instance, in a program with two bindings and
1303     // three varrefs, the array ~[~[1,2],~[0]] would indicate that the first
1304     // binding should match the second two varrefs, and the second binding
1305     // should match the first varref.
1306     //
1307     // Put differently; this is a sparse representation of a boolean matrix
1308     // indicating which bindings capture which identifiers.
1309     //
1310     // Note also that this matrix is dependent on the implicit ordering of
1311     // the bindings and the varrefs discovered by the name-finder and the path-finder.
1312     //
1313     // The comparisons are done post-mtwt-resolve, so we're comparing renamed
1314     // names; differences in marks don't matter any more.
1315     //
1316     // oog... I also want tests that check "bound-identifier-=?". That is,
1317     // not just "do these have the same name", but "do they have the same
1318     // name *and* the same marks"? Understanding this is really pretty painful.
1319     // in principle, you might want to control this boolean on a per-varref basis,
1320     // but that would make things even harder to understand, and might not be
1321     // necessary for thorough testing.
1322     type RenamingTest = (&'static str, Vec<Vec<uint>>, bool);
1323
1324     #[test]
1325     fn automatic_renaming () {
1326         let tests: Vec<RenamingTest> =
1327             vec!(// b & c should get new names throughout, in the expr too:
1328                 ("fn a() -> int { let b = 13; let c = b; b+c }",
1329                  vec!(vec!(0,1),vec!(2)), false),
1330                 // both x's should be renamed (how is this causing a bug?)
1331                 ("fn main () {let x: int = 13;x;}",
1332                  vec!(vec!(0)), false),
1333                 // the use of b after the + should be renamed, the other one not:
1334                 ("macro_rules! f (($x:ident) => (b + $x)) fn a() -> int { let b = 13; f!(b)}",
1335                  vec!(vec!(1)), false),
1336                 // the b before the plus should not be renamed (requires marks)
1337                 ("macro_rules! f (($x:ident) => ({let b=9; ($x + b)})) fn a() -> int { f!(b)}",
1338                  vec!(vec!(1)), false),
1339                 // the marks going in and out of letty should cancel, allowing that $x to
1340                 // capture the one following the semicolon.
1341                 // this was an awesome test case, and caught a *lot* of bugs.
1342                 ("macro_rules! letty(($x:ident) => (let $x = 15;))
1343                   macro_rules! user(($x:ident) => ({letty!($x); $x}))
1344                   fn main() -> int {user!(z)}",
1345                  vec!(vec!(0)), false)
1346                 );
1347         for (idx,s) in tests.iter().enumerate() {
1348             run_renaming_test(s,idx);
1349         }
1350     }
1351
1352     // no longer a fixme #8062: this test exposes a *potential* bug; our system does
1353     // not behave exactly like MTWT, but a conversation with Matthew Flatt
1354     // suggests that this can only occur in the presence of local-expand, which
1355     // we have no plans to support. ... unless it's needed for item hygiene....
1356     #[ignore]
1357     #[test] fn issue_8062(){
1358         run_renaming_test(
1359             &("fn main() {let hrcoo = 19; macro_rules! getx(()=>(hrcoo)); getx!();}",
1360               vec!(vec!(0)), true), 0)
1361     }
1362
1363     // FIXME #6994:
1364     // the z flows into and out of two macros (g & f) along one path, and one
1365     // (just g) along the other, so the result of the whole thing should
1366     // be "let z_123 = 3; z_123"
1367     #[ignore]
1368     #[test] fn issue_6994(){
1369         run_renaming_test(
1370             &("macro_rules! g (($x:ident) =>
1371               ({macro_rules! f(($y:ident)=>({let $y=3;$x}));f!($x)}))
1372               fn a(){g!(z)}",
1373               vec!(vec!(0)),false),
1374             0)
1375     }
1376
1377     // match variable hygiene. Should expand into
1378     // fn z() {match 8 {x_1 => {match 9 {x_2 | x_2 if x_2 == x_1 => x_2 + x_1}}}}
1379     #[test] fn issue_9384(){
1380         run_renaming_test(
1381             &("macro_rules! bad_macro (($ex:expr) => ({match 9 {x | x if x == $ex => x + $ex}}))
1382               fn z() {match 8 {x => bad_macro!(x)}}",
1383               // NB: the third "binding" is the repeat of the second one.
1384               vec!(vec!(1,3),vec!(0,2),vec!(0,2)),
1385               true),
1386             0)
1387     }
1388
1389     // interpolated nodes weren't getting labeled.
1390     // should expand into
1391     // fn main(){let g1_1 = 13; g1_1}}
1392     #[test] fn pat_expand_issue_15221(){
1393         run_renaming_test(
1394             &("macro_rules! inner ( ($e:pat ) => ($e))
1395               macro_rules! outer ( ($e:pat ) => (inner!($e)))
1396               fn main() { let outer!(g) = 13; g;}",
1397               vec!(vec!(0)),
1398               true),
1399             0)
1400     }
1401
1402     // create a really evil test case where a $x appears inside a binding of $x
1403     // but *shouldn't* bind because it was inserted by a different macro....
1404     // can't write this test case until we have macro-generating macros.
1405
1406     // method arg hygiene
1407     // method expands to fn get_x(&self_0, x_1:int) {self_0 + self_2 + x_3 + x_1}
1408     #[test] fn method_arg_hygiene(){
1409         run_renaming_test(
1410             &("macro_rules! inject_x (()=>(x))
1411               macro_rules! inject_self (()=>(self))
1412               struct A;
1413               impl A{fn get_x(&self, x: int) {self + inject_self!() + inject_x!() + x;} }",
1414               vec!(vec!(0),vec!(3)),
1415               true),
1416             0)
1417     }
1418
1419     // ooh, got another bite?
1420     // expands to struct A; impl A {fn thingy(&self_1) {self_1;}}
1421     #[test] fn method_arg_hygiene_2(){
1422         run_renaming_test(
1423             &("struct A;
1424               macro_rules! add_method (($T:ty) =>
1425               (impl $T {  fn thingy(&self) {self;} }))
1426               add_method!(A)",
1427               vec!(vec!(0)),
1428               true),
1429             0)
1430     }
1431
1432     // item fn hygiene
1433     // expands to fn q(x_1:int){fn g(x_2:int){x_2 + x_1};}
1434     #[test] fn issue_9383(){
1435         run_renaming_test(
1436             &("macro_rules! bad_macro (($ex:expr) => (fn g(x:int){ x + $ex }))
1437               fn q(x:int) { bad_macro!(x); }",
1438               vec!(vec!(1),vec!(0)),true),
1439             0)
1440     }
1441
1442     // closure arg hygiene (ExprFnBlock)
1443     // expands to fn f(){(|x_1 : int| {(x_2 + x_1)})(3);}
1444     #[test] fn closure_arg_hygiene(){
1445         run_renaming_test(
1446             &("macro_rules! inject_x (()=>(x))
1447             fn f(){(|x : int| {(inject_x!() + x)})(3);}",
1448               vec!(vec!(1)),
1449               true),
1450             0)
1451     }
1452
1453     // closure arg hygiene (ExprProc)
1454     // expands to fn f(){(proc(x_1 : int) {(x_2 + x_1)})(3);}
1455     #[test] fn closure_arg_hygiene_2(){
1456         run_renaming_test(
1457             &("macro_rules! inject_x (()=>(x))
1458               fn f(){ (proc(x : int){(inject_x!() + x)})(3); }",
1459               vec!(vec!(1)),
1460               true),
1461             0)
1462     }
1463
1464     // macro_rules in method position. Sadly, unimplemented.
1465     #[test] fn macro_in_method_posn(){
1466         expand_crate_str(
1467             "macro_rules! my_method (() => (fn thirteen(&self) -> int {13}))
1468             struct A;
1469             impl A{ my_method!()}
1470             fn f(){A.thirteen;}".to_string());
1471     }
1472
1473     // another nested macro
1474     // expands to impl Entries {fn size_hint(&self_1) {self_1;}
1475     #[test] fn item_macro_workaround(){
1476         run_renaming_test(
1477             &("macro_rules! item { ($i:item) => {$i}}
1478               struct Entries;
1479               macro_rules! iterator_impl {
1480               () => { item!( impl Entries { fn size_hint(&self) { self;}})}}
1481               iterator_impl! { }",
1482               vec!(vec!(0)), true),
1483             0)
1484     }
1485
1486     // run one of the renaming tests
1487     fn run_renaming_test(t: &RenamingTest, test_idx: uint) {
1488         let invalid_name = token::special_idents::invalid.name;
1489         let (teststr, bound_connections, bound_ident_check) = match *t {
1490             (ref str,ref conns, bic) => (str.to_string(), conns.clone(), bic)
1491         };
1492         let cr = expand_crate_str(teststr.to_string());
1493         let bindings = crate_bindings(&cr);
1494         let varrefs = crate_varrefs(&cr);
1495
1496         // must be one check clause for each binding:
1497         assert_eq!(bindings.len(),bound_connections.len());
1498         for (binding_idx,shouldmatch) in bound_connections.iter().enumerate() {
1499             let binding_name = mtwt::resolve(*bindings.get(binding_idx));
1500             let binding_marks = mtwt::marksof(bindings.get(binding_idx).ctxt, invalid_name);
1501             // shouldmatch can't name varrefs that don't exist:
1502             assert!((shouldmatch.len() == 0) ||
1503                     (varrefs.len() > *shouldmatch.iter().max().unwrap()));
1504             for (idx,varref) in varrefs.iter().enumerate() {
1505                 let print_hygiene_debug_info = || {
1506                     // good lord, you can't make a path with 0 segments, can you?
1507                     let final_varref_ident = match varref.segments.last() {
1508                         Some(pathsegment) => pathsegment.identifier,
1509                         None => fail!("varref with 0 path segments?")
1510                     };
1511                     let varref_name = mtwt::resolve(final_varref_ident);
1512                     let varref_idents : Vec<ast::Ident>
1513                         = varref.segments.iter().map(|s| s.identifier)
1514                         .collect();
1515                     println!("varref #{}: {}, resolves to {}",idx, varref_idents, varref_name);
1516                     let string = token::get_ident(final_varref_ident);
1517                     println!("varref's first segment's string: \"{}\"", string.get());
1518                     println!("binding #{}: {}, resolves to {}",
1519                              binding_idx, *bindings.get(binding_idx), binding_name);
1520                     mtwt::with_sctable(|x| mtwt::display_sctable(x));
1521                 };
1522                 if shouldmatch.contains(&idx) {
1523                     // it should be a path of length 1, and it should
1524                     // be free-identifier=? or bound-identifier=? to the given binding
1525                     assert_eq!(varref.segments.len(),1);
1526                     let varref_name = mtwt::resolve(varref.segments.get(0).identifier);
1527                     let varref_marks = mtwt::marksof(varref.segments
1528                                                            .get(0)
1529                                                            .identifier
1530                                                            .ctxt,
1531                                                      invalid_name);
1532                     if !(varref_name==binding_name) {
1533                         println!("uh oh, should match but doesn't:");
1534                         print_hygiene_debug_info();
1535                     }
1536                     assert_eq!(varref_name,binding_name);
1537                     if bound_ident_check {
1538                         // we're checking bound-identifier=?, and the marks
1539                         // should be the same, too:
1540                         assert_eq!(varref_marks,binding_marks.clone());
1541                     }
1542                 } else {
1543                     let varref_name = mtwt::resolve(varref.segments.get(0).identifier);
1544                     let fail = (varref.segments.len() == 1)
1545                         && (varref_name == binding_name);
1546                     // temp debugging:
1547                     if fail {
1548                         println!("failure on test {}",test_idx);
1549                         println!("text of test case: \"{}\"", teststr);
1550                         println!("");
1551                         println!("uh oh, matches but shouldn't:");
1552                         print_hygiene_debug_info();
1553                     }
1554                     assert!(!fail);
1555                 }
1556             }
1557         }
1558     }
1559
1560     #[test] fn fmt_in_macro_used_inside_module_macro() {
1561         let crate_str = "macro_rules! fmt_wrap(($b:expr)=>($b.to_string()))
1562 macro_rules! foo_module (() => (mod generated { fn a() { let xx = 147; fmt_wrap!(xx);}}))
1563 foo_module!()
1564 ".to_string();
1565         let cr = expand_crate_str(crate_str);
1566         // find the xx binding
1567         let bindings = crate_bindings(&cr);
1568         let cxbinds: Vec<&ast::Ident> =
1569             bindings.iter().filter(|b| {
1570                 let ident = token::get_ident(**b);
1571                 let string = ident.get();
1572                 "xx" == string
1573             }).collect();
1574         let cxbinds: &[&ast::Ident] = cxbinds.as_slice();
1575         let cxbind = match cxbinds {
1576             [b] => b,
1577             _ => fail!("expected just one binding for ext_cx")
1578         };
1579         let resolved_binding = mtwt::resolve(*cxbind);
1580         let varrefs = crate_varrefs(&cr);
1581
1582         // the xx binding should bind all of the xx varrefs:
1583         for (idx,v) in varrefs.iter().filter(|p| {
1584             p.segments.len() == 1
1585             && "xx" == token::get_ident(p.segments.get(0).identifier).get()
1586         }).enumerate() {
1587             if mtwt::resolve(v.segments.get(0).identifier) != resolved_binding {
1588                 println!("uh oh, xx binding didn't match xx varref:");
1589                 println!("this is xx varref \\# {:?}",idx);
1590                 println!("binding: {:?}",cxbind);
1591                 println!("resolves to: {:?}",resolved_binding);
1592                 println!("varref: {:?}",v.segments.get(0).identifier);
1593                 println!("resolves to: {:?}",
1594                          mtwt::resolve(v.segments.get(0).identifier));
1595                 mtwt::with_sctable(|x| mtwt::display_sctable(x));
1596             }
1597             assert_eq!(mtwt::resolve(v.segments.get(0).identifier),
1598                        resolved_binding);
1599         };
1600     }
1601
1602     #[test]
1603     fn pat_idents(){
1604         let pat = string_to_pat(
1605             "(a,Foo{x:c @ (b,9),y:Bar(4,d)})".to_string());
1606         let idents = pattern_bindings(&*pat);
1607         assert_eq!(idents, strs_to_idents(vec!("a","c","b","d")));
1608     }
1609
1610     // test the list of identifier patterns gathered by the visitor. Note that
1611     // 'None' is listed as an identifier pattern because we don't yet know that
1612     // it's the name of a 0-ary variant, and that 'i' appears twice in succession.
1613     #[test]
1614     fn crate_bindings_test(){
1615         let the_crate = string_to_crate("fn main (a : int) -> int {|b| {
1616         match 34 {None => 3, Some(i) | i => j, Foo{k:z,l:y} => \"banana\"}} }".to_string());
1617         let idents = crate_bindings(&the_crate);
1618         assert_eq!(idents, strs_to_idents(vec!("a","b","None","i","i","z","y")));
1619     }
1620
1621     // test the IdentRenamer directly
1622     #[test]
1623     fn ident_renamer_test () {
1624         let the_crate = string_to_crate("fn f(x : int){let x = x; x}".to_string());
1625         let f_ident = token::str_to_ident("f");
1626         let x_ident = token::str_to_ident("x");
1627         let int_ident = token::str_to_ident("int");
1628         let renames = vec!((x_ident,Name(16)));
1629         let mut renamer = IdentRenamer{renames: &renames};
1630         let renamed_crate = renamer.fold_crate(the_crate);
1631         let idents = crate_idents(&renamed_crate);
1632         let resolved : Vec<ast::Name> = idents.iter().map(|id| mtwt::resolve(*id)).collect();
1633         assert_eq!(resolved,vec!(f_ident.name,Name(16),int_ident.name,Name(16),Name(16),Name(16)));
1634     }
1635
1636     // test the PatIdentRenamer; only PatIdents get renamed
1637     #[test]
1638     fn pat_ident_renamer_test () {
1639         let the_crate = string_to_crate("fn f(x : int){let x = x; x}".to_string());
1640         let f_ident = token::str_to_ident("f");
1641         let x_ident = token::str_to_ident("x");
1642         let int_ident = token::str_to_ident("int");
1643         let renames = vec!((x_ident,Name(16)));
1644         let mut renamer = PatIdentRenamer{renames: &renames};
1645         let renamed_crate = renamer.fold_crate(the_crate);
1646         let idents = crate_idents(&renamed_crate);
1647         let resolved : Vec<ast::Name> = idents.iter().map(|id| mtwt::resolve(*id)).collect();
1648         let x_name = x_ident.name;
1649         assert_eq!(resolved,vec!(f_ident.name,Name(16),int_ident.name,Name(16),x_name,x_name));
1650     }
1651
1652
1653 }