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