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