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