]> git.lizzy.rs Git - rust.git/blob - src/libsyntax/ext/expand.rs
auto merge of #12714 : michaelwoerister/rust/limited-debuginfo, 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};
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.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.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.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 fn expand_crate(parse_sess: @parse::ParseSess,
827                     loader: &mut CrateLoader,
828                     c: Crate) -> Crate {
829     let mut cx = ExtCtxt::new(parse_sess, c.config.clone(), loader);
830     let mut expander = MacroExpander {
831         extsbox: syntax_expander_table(),
832         cx: &mut cx,
833     };
834
835     let ret = expander.fold_crate(c);
836     parse_sess.span_diagnostic.handler().abort_if_errors();
837     return ret;
838 }
839
840 // HYGIENIC CONTEXT EXTENSION:
841 // all of these functions are for walking over
842 // ASTs and making some change to the context of every
843 // element that has one. a CtxtFn is a trait-ified
844 // version of a closure in (SyntaxContext -> SyntaxContext).
845 // the ones defined here include:
846 // Marker - add a mark to a context
847
848 // A Marker adds the given mark to the syntax context
849 struct Marker { mark: Mrk }
850
851 impl Folder for Marker {
852     fn fold_ident(&mut self, id: Ident) -> Ident {
853         ast::Ident {
854             name: id.name,
855             ctxt: mtwt::new_mark(self.mark, id.ctxt)
856         }
857     }
858     fn fold_mac(&mut self, m: &ast::Mac) -> ast::Mac {
859         let macro = match m.node {
860             MacInvocTT(ref path, ref tts, ctxt) => {
861                 MacInvocTT(self.fold_path(path),
862                            fold_tts(tts.as_slice(), self),
863                            mtwt::new_mark(self.mark, ctxt))
864             }
865         };
866         Spanned {
867             node: macro,
868             span: m.span,
869         }
870     }
871 }
872
873 // just a convenience:
874 fn new_mark_folder(m: Mrk) -> Marker {
875     Marker {mark: m}
876 }
877
878 // apply a given mark to the given token trees. Used prior to expansion of a macro.
879 fn mark_tts(tts: &[TokenTree], m: Mrk) -> Vec<TokenTree> {
880     fold_tts(tts, &mut new_mark_folder(m))
881 }
882
883 // apply a given mark to the given expr. Used following the expansion of a macro.
884 fn mark_expr(expr: @ast::Expr, m: Mrk) -> @ast::Expr {
885     new_mark_folder(m).fold_expr(expr)
886 }
887
888 // apply a given mark to the given stmt. Used following the expansion of a macro.
889 fn mark_stmt(expr: &ast::Stmt, m: Mrk) -> @ast::Stmt {
890     new_mark_folder(m).fold_stmt(expr)
891             .expect_one("marking a stmt didn't return a stmt")
892 }
893
894 // apply a given mark to the given item. Used following the expansion of a macro.
895 fn mark_item(expr: @ast::Item, m: Mrk) -> SmallVector<@ast::Item> {
896     new_mark_folder(m).fold_item(expr)
897 }
898
899 fn original_span(cx: &ExtCtxt) -> @codemap::ExpnInfo {
900     let mut relevant_info = cx.backtrace();
901     let mut einfo = relevant_info.unwrap();
902     loop {
903         match relevant_info {
904             None => { break }
905             Some(e) => {
906                 einfo = e;
907                 relevant_info = einfo.call_site.expn_info;
908             }
909         }
910     }
911     return einfo;
912 }
913
914 #[cfg(test)]
915 mod test {
916     use super::*;
917     use ast;
918     use ast::{Attribute_, AttrOuter, MetaWord};
919     use codemap;
920     use codemap::Spanned;
921     use ext::base::{CrateLoader, MacroCrate};
922     use ext::mtwt;
923     use parse;
924     use parse::token;
925     use util::parser_testing::{string_to_crate_and_sess};
926     use util::parser_testing::{string_to_pat, strs_to_idents};
927     use visit;
928     use visit::Visitor;
929
930     use std::vec_ng::Vec;
931
932     // a visitor that extracts the paths
933     // from a given thingy and puts them in a mutable
934     // array (passed in to the traversal)
935     #[deriving(Clone)]
936     struct NewPathExprFinderContext {
937         path_accumulator: Vec<ast::Path> ,
938     }
939
940     impl Visitor<()> for NewPathExprFinderContext {
941
942         fn visit_expr(&mut self, expr: &ast::Expr, _: ()) {
943             match *expr {
944                 ast::Expr{id:_,span:_,node:ast::ExprPath(ref p)} => {
945                     self.path_accumulator.push(p.clone());
946                     // not calling visit_path, should be fine.
947                 }
948                 _ => visit::walk_expr(self,expr,())
949             }
950         }
951
952         fn visit_ty(&mut self, typ: &ast::Ty, _: ()) {
953             visit::walk_ty(self, typ, ())
954         }
955
956     }
957
958     // return a visitor that extracts the paths
959     // from a given pattern and puts them in a mutable
960     // array (passed in to the traversal)
961     pub fn new_path_finder(paths: Vec<ast::Path> ) -> NewPathExprFinderContext {
962         NewPathExprFinderContext {
963             path_accumulator: paths
964         }
965     }
966
967     struct ErrLoader;
968
969     impl CrateLoader for ErrLoader {
970         fn load_crate(&mut self, _: &ast::ViewItem) -> MacroCrate {
971             fail!("lolwut")
972         }
973
974         fn get_exported_macros(&mut self, _: ast::CrateNum) -> Vec<~str> {
975             fail!("lolwut")
976         }
977
978         fn get_registrar_symbol(&mut self, _: ast::CrateNum) -> Option<~str> {
979             fail!("lolwut")
980         }
981     }
982
983     // these following tests are quite fragile, in that they don't test what
984     // *kind* of failure occurs.
985
986     // make sure that macros can leave scope
987     #[should_fail]
988     #[test] fn macros_cant_escape_fns_test () {
989         let src = ~"fn bogus() {macro_rules! z (() => (3+4))}\
990                     fn inty() -> int { z!() }";
991         let sess = parse::new_parse_sess();
992         let crate_ast = parse::parse_crate_from_source_str(
993             ~"<test>",
994             src,
995             Vec::new(),sess);
996         // should fail:
997         let mut loader = ErrLoader;
998         expand_crate(sess,&mut loader,crate_ast);
999     }
1000
1001     // make sure that macros can leave scope for modules
1002     #[should_fail]
1003     #[test] fn macros_cant_escape_mods_test () {
1004         let src = ~"mod foo {macro_rules! z (() => (3+4))}\
1005                     fn inty() -> int { z!() }";
1006         let sess = parse::new_parse_sess();
1007         let crate_ast = parse::parse_crate_from_source_str(
1008             ~"<test>",
1009             src,
1010             Vec::new(),sess);
1011         // should fail:
1012         let mut loader = ErrLoader;
1013         expand_crate(sess,&mut loader,crate_ast);
1014     }
1015
1016     // macro_escape modules shouldn't cause macros to leave scope
1017     #[test] fn macros_can_escape_flattened_mods_test () {
1018         let src = ~"#[macro_escape] mod foo {macro_rules! z (() => (3+4))}\
1019                     fn inty() -> int { z!() }";
1020         let sess = parse::new_parse_sess();
1021         let crate_ast = parse::parse_crate_from_source_str(
1022             ~"<test>",
1023             src,
1024             Vec::new(), sess);
1025         // should fail:
1026         let mut loader = ErrLoader;
1027         expand_crate(sess, &mut loader, crate_ast);
1028     }
1029
1030     #[test] fn test_contains_flatten (){
1031         let attr1 = make_dummy_attr ("foo");
1032         let attr2 = make_dummy_attr ("bar");
1033         let escape_attr = make_dummy_attr ("macro_escape");
1034         let attrs1 = vec!(attr1, escape_attr, attr2);
1035         assert_eq!(contains_macro_escape(attrs1.as_slice()),true);
1036         let attrs2 = vec!(attr1,attr2);
1037         assert_eq!(contains_macro_escape(attrs2.as_slice()),false);
1038     }
1039
1040     // make a MetaWord outer attribute with the given name
1041     fn make_dummy_attr(s: &str) -> ast::Attribute {
1042         Spanned {
1043             span:codemap::DUMMY_SP,
1044             node: Attribute_ {
1045                 style: AttrOuter,
1046                 value: @Spanned {
1047                     node: MetaWord(token::intern_and_get_ident(s)),
1048                     span: codemap::DUMMY_SP,
1049                 },
1050                 is_sugared_doc: false,
1051             }
1052         }
1053     }
1054
1055     //fn fake_print_crate(krate: &ast::Crate) {
1056     //    let mut out = ~std::io::stderr() as ~std::io::Writer;
1057     //    let mut s = pprust::rust_printer(out, get_ident_interner());
1058     //    pprust::print_crate_(&mut s, krate);
1059     //}
1060
1061     fn expand_crate_str(crate_str: ~str) -> ast::Crate {
1062         let (crate_ast,ps) = string_to_crate_and_sess(crate_str);
1063         // the cfg argument actually does matter, here...
1064         let mut loader = ErrLoader;
1065         expand_crate(ps,&mut loader,crate_ast)
1066     }
1067
1068     //fn expand_and_resolve(crate_str: @str) -> ast::crate {
1069         //let expanded_ast = expand_crate_str(crate_str);
1070         // println!("expanded: {:?}\n",expanded_ast);
1071         //mtwt_resolve_crate(expanded_ast)
1072     //}
1073     //fn expand_and_resolve_and_pretty_print (crate_str: @str) -> ~str {
1074         //let resolved_ast = expand_and_resolve(crate_str);
1075         //pprust::to_str(&resolved_ast,fake_print_crate,get_ident_interner())
1076     //}
1077
1078     #[test] fn macro_tokens_should_match(){
1079         expand_crate_str(~"macro_rules! m((a)=>(13)) fn main(){m!(a);}");
1080     }
1081
1082     // renaming tests expand a crate and then check that the bindings match
1083     // the right varrefs. The specification of the test case includes the
1084     // text of the crate, and also an array of arrays.  Each element in the
1085     // outer array corresponds to a binding in the traversal of the AST
1086     // induced by visit.  Each of these arrays contains a list of indexes,
1087     // interpreted as the varrefs in the varref traversal that this binding
1088     // should match.  So, for instance, in a program with two bindings and
1089     // three varrefs, the array ~[~[1,2],~[0]] would indicate that the first
1090     // binding should match the second two varrefs, and the second binding
1091     // should match the first varref.
1092     //
1093     // The comparisons are done post-mtwt-resolve, so we're comparing renamed
1094     // names; differences in marks don't matter any more.
1095     //
1096     // oog... I also want tests that check "binding-identifier-=?". That is,
1097     // not just "do these have the same name", but "do they have the same
1098     // name *and* the same marks"? Understanding this is really pretty painful.
1099     // in principle, you might want to control this boolean on a per-varref basis,
1100     // but that would make things even harder to understand, and might not be
1101     // necessary for thorough testing.
1102     type RenamingTest = (&'static str, Vec<Vec<uint>>, bool);
1103
1104     #[test]
1105     fn automatic_renaming () {
1106         let tests: Vec<RenamingTest> =
1107             vec!(// b & c should get new names throughout, in the expr too:
1108                 ("fn a() -> int { let b = 13; let c = b; b+c }",
1109                  vec!(vec!(0,1),vec!(2)), false),
1110                 // both x's should be renamed (how is this causing a bug?)
1111                 ("fn main () {let x: int = 13;x;}",
1112                  vec!(vec!(0)), false),
1113                 // the use of b after the + should be renamed, the other one not:
1114                 ("macro_rules! f (($x:ident) => (b + $x)) fn a() -> int { let b = 13; f!(b)}",
1115                  vec!(vec!(1)), false),
1116                 // the b before the plus should not be renamed (requires marks)
1117                 ("macro_rules! f (($x:ident) => ({let b=9; ($x + b)})) fn a() -> int { f!(b)}",
1118                  vec!(vec!(1)), false),
1119                 // the marks going in and out of letty should cancel, allowing that $x to
1120                 // capture the one following the semicolon.
1121                 // this was an awesome test case, and caught a *lot* of bugs.
1122                 ("macro_rules! letty(($x:ident) => (let $x = 15;))
1123                   macro_rules! user(($x:ident) => ({letty!($x); $x}))
1124                   fn main() -> int {user!(z)}",
1125                  vec!(vec!(0)), false));
1126         for (idx,s) in tests.iter().enumerate() {
1127             run_renaming_test(s,idx);
1128         }
1129     }
1130
1131     // run one of the renaming tests
1132     fn run_renaming_test(t: &RenamingTest, test_idx: uint) {
1133         let invalid_name = token::special_idents::invalid.name;
1134         let (teststr, bound_connections, bound_ident_check) = match *t {
1135             (ref str,ref conns, bic) => (str.to_owned(), conns.clone(), bic)
1136         };
1137         let cr = expand_crate_str(teststr.to_owned());
1138         // find the bindings:
1139         let mut name_finder = new_name_finder(Vec::new());
1140         visit::walk_crate(&mut name_finder,&cr,());
1141         let bindings = name_finder.ident_accumulator;
1142
1143         // find the varrefs:
1144         let mut path_finder = new_path_finder(Vec::new());
1145         visit::walk_crate(&mut path_finder,&cr,());
1146         let varrefs = path_finder.path_accumulator;
1147
1148         // must be one check clause for each binding:
1149         assert_eq!(bindings.len(),bound_connections.len());
1150         for (binding_idx,shouldmatch) in bound_connections.iter().enumerate() {
1151             let binding_name = mtwt::resolve(*bindings.get(binding_idx));
1152             let binding_marks = mtwt::marksof(bindings.get(binding_idx).ctxt, invalid_name);
1153             // shouldmatch can't name varrefs that don't exist:
1154             assert!((shouldmatch.len() == 0) ||
1155                     (varrefs.len() > *shouldmatch.iter().max().unwrap()));
1156             for (idx,varref) in varrefs.iter().enumerate() {
1157                 if shouldmatch.contains(&idx) {
1158                     // it should be a path of length 1, and it should
1159                     // be free-identifier=? or bound-identifier=? to the given binding
1160                     assert_eq!(varref.segments.len(),1);
1161                     let varref_name = mtwt::resolve(varref.segments
1162                                                           .get(0)
1163                                                           .identifier);
1164                     let varref_marks = mtwt::marksof(varref.segments
1165                                                            .get(0)
1166                                                            .identifier
1167                                                            .ctxt,
1168                                                      invalid_name);
1169                     if !(varref_name==binding_name) {
1170                         println!("uh oh, should match but doesn't:");
1171                         println!("varref: {:?}",varref);
1172                         println!("binding: {:?}", *bindings.get(binding_idx));
1173                         mtwt::with_sctable(|x| mtwt::display_sctable(x));
1174                     }
1175                     assert_eq!(varref_name,binding_name);
1176                     if bound_ident_check {
1177                         // we're checking bound-identifier=?, and the marks
1178                         // should be the same, too:
1179                         assert_eq!(varref_marks,binding_marks.clone());
1180                     }
1181                 } else {
1182                     let fail = (varref.segments.len() == 1)
1183                         && (mtwt::resolve(varref.segments.get(0).identifier)
1184                             == binding_name);
1185                     // temp debugging:
1186                     if fail {
1187                         println!("failure on test {}",test_idx);
1188                         println!("text of test case: \"{}\"", teststr);
1189                         println!("");
1190                         println!("uh oh, matches but shouldn't:");
1191                         println!("varref: {:?}",varref);
1192                         // good lord, you can't make a path with 0 segments, can you?
1193                         let string = token::get_ident(varref.segments
1194                                                             .get(0)
1195                                                             .identifier);
1196                         println!("varref's first segment's uint: {}, and string: \"{}\"",
1197                                  varref.segments.get(0).identifier.name,
1198                                  string.get());
1199                         println!("binding: {:?}", *bindings.get(binding_idx));
1200                         mtwt::with_sctable(|x| mtwt::display_sctable(x));
1201                     }
1202                     assert!(!fail);
1203                 }
1204             }
1205         }
1206     }
1207
1208     #[test] fn fmt_in_macro_used_inside_module_macro() {
1209         let crate_str = ~"macro_rules! fmt_wrap(($b:expr)=>($b.to_str()))
1210 macro_rules! foo_module (() => (mod generated { fn a() { let xx = 147; fmt_wrap!(xx);}}))
1211 foo_module!()
1212 ";
1213         let cr = expand_crate_str(crate_str);
1214         // find the xx binding
1215         let mut name_finder = new_name_finder(Vec::new());
1216         visit::walk_crate(&mut name_finder, &cr, ());
1217         let bindings = name_finder.ident_accumulator;
1218
1219         let cxbinds: Vec<&ast::Ident> =
1220             bindings.iter().filter(|b| {
1221                 let ident = token::get_ident(**b);
1222                 let string = ident.get();
1223                 "xx" == string
1224             }).collect();
1225         let cxbinds: &[&ast::Ident] = cxbinds.as_slice();
1226         let cxbind = match cxbinds {
1227             [b] => b,
1228             _ => fail!("expected just one binding for ext_cx")
1229         };
1230         let resolved_binding = mtwt::resolve(*cxbind);
1231         // find all the xx varrefs:
1232         let mut path_finder = new_path_finder(Vec::new());
1233         visit::walk_crate(&mut path_finder, &cr, ());
1234         let varrefs = path_finder.path_accumulator;
1235
1236         // the xx binding should bind all of the xx varrefs:
1237         for (idx,v) in varrefs.iter().filter(|p| {
1238             p.segments.len() == 1
1239             && "xx" == token::get_ident(p.segments.get(0).identifier).get()
1240         }).enumerate() {
1241             if mtwt::resolve(v.segments.get(0).identifier) != resolved_binding {
1242                 println!("uh oh, xx binding didn't match xx varref:");
1243                 println!("this is xx varref \\# {:?}",idx);
1244                 println!("binding: {:?}",cxbind);
1245                 println!("resolves to: {:?}",resolved_binding);
1246                 println!("varref: {:?}",v.segments.get(0).identifier);
1247                 println!("resolves to: {:?}",
1248                          mtwt::resolve(v.segments.get(0).identifier));
1249                 mtwt::with_sctable(|x| mtwt::display_sctable(x));
1250             }
1251             assert_eq!(mtwt::resolve(v.segments.get(0).identifier),
1252                        resolved_binding);
1253         };
1254     }
1255
1256     #[test]
1257     fn pat_idents(){
1258         let pat = string_to_pat(~"(a,Foo{x:c @ (b,9),y:Bar(4,d)})");
1259         let mut pat_idents = new_name_finder(Vec::new());
1260         pat_idents.visit_pat(pat, ());
1261         assert_eq!(pat_idents.ident_accumulator,
1262                    strs_to_idents(vec!("a","c","b","d")));
1263     }
1264
1265 }