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.
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.
11 use ast::{P, Block, Crate, DeclLocal, ExprMac};
12 use ast::{Local, Ident, MacInvocTT};
13 use ast::{ItemMac, Mrk, Stmt, StmtDecl, StmtMac, StmtExpr, StmtSemi};
16 use ast_util::{new_rename, new_mark};
17 use ext::build::AstBuilder;
19 use attr::AttrMetaMethods;
21 use codemap::{Span, Spanned, ExpnInfo, NameAndSpan, MacroBang, MacroAttribute};
25 use parse::token::{fresh_mark, fresh_name, intern};
29 use util::small_vector::SmallVector;
32 use std::unstable::dynamic_lib::DynamicLibrary;
35 pub fn expand_expr(e: @ast::Expr, fld: &mut MacroExpander) -> @ast::Expr {
37 // expr_mac should really be expr_ext or something; it's the
38 // entry-point for all syntax extensions.
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
47 MacInvocTT(ref pth, ref tts, _) => {
48 if pth.segments.len() > 1u {
51 format!("expected macro name without module \
53 // let compilation continue
54 return MacResult::raw_dummy_expr(e.span);
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) {
63 format!("macro undefined: '{}'",
66 // let compilation continue
67 return MacResult::raw_dummy_expr(e.span);
69 Some(&NormalTT(ref expandfun, exp_span)) => {
70 fld.cx.bt_push(ExpnInfo {
73 name: extnamestr.get().to_str(),
78 let fm = fresh_mark();
80 let marked_before = mark_tts(*tts,fm);
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
86 let mac_span = original_span(fld.cx);
88 let expanded = match expandfun.expand(fld.cx,
92 MRAny(any_macro) => any_macro.make_expr(),
97 "non-expr macro in expr pos: {}",
101 return MacResult::raw_dummy_expr(e.span);
106 mark_expr(expanded,fm)
111 format!("'{}' is not a tt-style macro",
114 return MacResult::raw_dummy_expr(e.span);
118 // Keep going, outside-in.
120 // FIXME(pcwalton): Is it necessary to clone the
123 fld.fold_expr(marked_after).node.clone();
127 id: ast::DUMMY_NODE_ID,
128 node: fully_expanded,
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);
148 // match &mut <src_expr> {
150 // ['<ident>:] loop {
153 // Some(<src_pat>) => <src_loop_block>
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");
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"));
166 // `None => break ['<ident>];`
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)
174 // `Some(<src_pat>) => <src_loop_block>`
177 ~[fld.cx.pat_enum(span, some_path, ~[src_pat])],
178 fld.cx.expr_block(src_loop_block));
180 // `match i.next() { ... }`
183 fld.cx.expr_method_call(span, fld.cx.expr_path(local_path), next_ident, ~[]);
185 fld.cx.expr_match(span, next_call_expr, ~[none_arm, some_arm])
188 // ['ident:] loop { ... }
189 let loop_expr = fld.cx.expr(span,
190 ast::ExprLoop(fld.cx.block_expr(match_expr),
193 // `i => loop { ... }`
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])
202 _ => noop_fold_expr(e, fld)
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;
212 $extsboxexpr.pop_frame();
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();
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,
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));
244 let decorator_items = decorator_items.move_iter()
245 .flat_map(|item| expand_item(item, fld).move_iter())
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,
255 noop_fold_item(it, fld));
259 _ => noop_fold_item(it, fld)
262 new_items.push_all(decorator_items);
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")
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, _),
280 (pth, (*tts).clone())
282 _ => fld.cx.span_bug(it.span, "invalid item macro invocation")
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) {
290 fld.cx.span_err(pth.span,
291 format!("macro undefined: '{}!'",
293 // let compilation continue
294 return SmallVector::zero();
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, \
303 token::get_ident(it.ident)));
304 return SmallVector::zero();
306 fld.cx.bt_push(ExpnInfo {
308 callee: NameAndSpan {
309 name: extnamestr.get().to_str(),
314 // mark before expansion:
315 let marked_before = mark_tts(tts,fm);
316 expander.expand(fld.cx, it.span, marked_before)
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",
323 return SmallVector::zero();
325 fld.cx.bt_push(ExpnInfo {
327 callee: NameAndSpan {
328 name: extnamestr.get().to_str(),
333 // mark before expansion:
334 let marked_tts = mark_tts(tts,fm);
335 expander.expand(fld.cx, it.span, it.ident, marked_tts)
338 fld.cx.span_err(it.span,
339 format!("{}! is not legal in item position",
341 return SmallVector::zero();
345 let items = match expanded {
347 mark_item(it,fm).move_iter()
348 .flat_map(|i| fld.fold_item(i).move_iter())
352 fld.cx.span_err(pth.span,
353 format!("expr macro in item position: {}",
355 return SmallVector::zero();
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())
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") {
378 // load macros from syntax-phase crates
379 pub fn expand_view_item(vi: &ast::ViewItem,
380 fld: &mut MacroExpander)
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")
392 load_extern_macros(vi, fld);
395 ast::ViewItemUse(_) => {}
398 noop_fold_view_item(vi, fld)
401 fn load_extern_macros(krate: &ast::ViewItem, fld: &mut MacroExpander) {
402 let MacroCrate { lib, cnum } = fld.cx.loader.load_crate(krate);
404 let crate_name = match krate.node {
405 ast::ViewItemExternMod(name, _, _) => name,
408 let name = format!("<{} macros>", token::get_ident(crate_name));
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(),
416 .expect("expected a serialized item");
417 expand_item_mac(item, fld);
420 let path = match lib {
424 // Make sure the path contains a / or the linker will search for it.
425 let path = os::make_absolute(&path);
427 let registrar = match fld.cx.loader.get_registrar_symbol(cnum) {
428 Some(registrar) => registrar,
432 let lib = match DynamicLibrary::open(Some(&path)) {
434 // this is fatal: there are almost certainly macros we need
435 // inside this crate, so continue would spew "macro undefined"
437 Err(err) => fld.cx.span_fatal(krate.span, err)
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)
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),
452 fld.extsbox.insert(name, extension);
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).
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) => {
469 MacInvocTT(ref pth, ref tts, _) => {
470 (pth, (*tts).clone(), semi)
474 _ => return expand_non_macro_stmt(s, fld)
476 if pth.segments.len() > 1u {
477 fld.cx.span_err(pth.span, "expected macro name without module separators");
478 return SmallVector::zero();
480 let extname = pth.segments[0].identifier;
481 let extnamestr = token::get_ident(extname);
482 let marked_after = match fld.extsbox.find(&extname.name) {
484 fld.cx.span_err(pth.span, format!("macro undefined: '{}'", extnamestr));
485 return SmallVector::zero();
488 Some(&NormalTT(ref expandfun, exp_span)) => {
489 fld.cx.bt_push(ExpnInfo {
491 callee: NameAndSpan {
492 name: extnamestr.get().to_str(),
497 let fm = fresh_mark();
498 // mark before expansion:
499 let marked_tts = mark_tts(tts,fm);
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);
505 let expanded = match expandfun.expand(fld.cx,
510 node: StmtExpr(e, ast::DUMMY_NODE_ID),
514 MRAny(any_macro) => any_macro.make_stmt(),
516 fld.cx.span_err(pth.span,
517 format!("non-stmt macro in stmt pos: {}",
519 return SmallVector::zero();
523 mark_stmt(expanded,fm)
527 fld.cx.span_err(pth.span, format!("'{}' is not a tt-style macro",
529 return SmallVector::zero();
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();
540 let fully_expanded: SmallVector<@Stmt> = fully_expanded.move_iter()
541 .map(|s| @Spanned { span: s.span, node: s.node.clone() })
544 fully_expanded.move_iter().map(|s| {
546 StmtExpr(e, stmt_id) if semi => {
549 node: StmtSemi(e, stmt_id)
552 _ => s /* might already have a semi */
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> {
563 StmtDecl(decl, node_id) => {
566 node: DeclLocal(ref 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));
591 let rewritten_pat = {
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)
598 // add them to the existing pending renames:
599 for pr in new_pending_renames.iter() {
600 fld.extsbox.info().pending_renames.push(*pr)
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 =
612 SmallVector::one(@Spanned {
613 node: StmtDecl(@Spanned {
614 node: DeclLocal(rewritten_local),
621 _ => noop_fold_stmt(s, fld),
624 _ => noop_fold_stmt(s, fld),
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)
632 struct NewNameFinderContext {
633 ident_accumulator: ~[ast::Ident],
636 impl Visitor<()> for NewNameFinderContext {
637 fn visit_pat(&mut self, pattern: &ast::Pat, _: ()) {
639 // we found a pat_ident!
642 node: ast::PatIdent(_, ref path, ref inner),
646 // a path of length one:
650 segments: ref segments
651 } if segments.len() == 1 => {
652 self.ident_accumulator.push(segments[0].identifier)
654 // I believe these must be enums...
657 // visit optional subpattern of pat_ident:
658 for subpat in inner.iter() {
659 self.visit_pat(*subpat, ())
662 // use the default traversal for non-pat_idents
663 _ => visit::walk_pat(self, pattern, ())
667 fn visit_ty(&mut self, typ: &ast::Ty, _: ()) {
668 visit::walk_ty(self, typ, ())
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,
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))
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));
693 b.stmts.iter().flat_map(|x| {
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")
699 fld.fold_stmt(renamed_stmt).move_iter()
701 let new_expr = b.expr.map(|x| {
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)
710 view_items: new_view_items,
713 id: fld.new_id(b.id),
719 struct IdentRenamer<'a> {
720 renames: &'a mut RenameList,
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)
735 // given a mutable list of renames, return a tree-folder that applies those
737 pub fn renames_to_fold<'a>(renames: &'a mut RenameList) -> IdentRenamer<'a> {
743 pub fn new_span(cx: &ExtCtxt, sp: Span) -> Span {
744 /* this discards information in the case of macro-defining macros */
748 expn_info: cx.backtrace(),
752 pub struct MacroExpander<'a> {
754 cx: &'a mut ExtCtxt<'a>,
757 impl<'a> Folder for MacroExpander<'a> {
758 fn fold_expr(&mut self, expr: @ast::Expr) -> @ast::Expr {
759 expand_expr(expr, self)
762 fn fold_item(&mut self, item: @ast::Item) -> SmallVector<@ast::Item> {
763 expand_item(item, self)
766 fn fold_view_item(&mut self, vi: &ast::ViewItem) -> ast::ViewItem {
767 expand_view_item(vi, self)
770 fn fold_stmt(&mut self, stmt: &ast::Stmt) -> SmallVector<@ast::Stmt> {
771 expand_stmt(stmt, self)
774 fn fold_block(&mut self, block: P<Block>) -> P<Block> {
775 expand_block(block, self)
778 fn new_span(&mut self, span: Span) -> Span {
779 new_span(self.cx, span)
783 pub fn expand_crate(parse_sess: @parse::ParseSess,
784 loader: &mut CrateLoader,
786 let mut cx = ExtCtxt::new(parse_sess, c.config.clone(), loader);
787 let mut expander = MacroExpander {
788 extsbox: syntax_expander_table(),
792 let ret = expander.fold_crate(c);
793 parse_sess.span_diagnostic.handler().abort_if_errors();
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
805 // A Marker adds the given mark to the syntax context
806 struct Marker { mark: Mrk }
808 impl Folder for Marker {
809 fn fold_ident(&mut self, id: ast::Ident) -> ast::Ident {
812 ctxt: new_mark(self.mark, id.ctxt)
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))
830 // just a convenience:
831 fn new_mark_folder(m: Mrk) -> Marker {
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))
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)
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")
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)
856 fn original_span(cx: &ExtCtxt) -> @codemap::ExpnInfo {
857 let mut relevant_info = cx.backtrace();
858 let mut einfo = relevant_info.unwrap();
860 match relevant_info {
864 relevant_info = einfo.call_site.expn_info;
875 use ast::{Attribute_, AttrOuter, MetaWord};
876 use ast_util::{get_sctable, mtwt_marksof, mtwt_resolve};
879 use codemap::Spanned;
880 use ext::base::{CrateLoader, MacroCrate};
883 use util::parser_testing::{string_to_crate_and_sess};
884 use util::parser_testing::{string_to_pat, strs_to_idents};
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)
892 struct NewPathExprFinderContext {
893 path_accumulator: ~[ast::Path],
896 impl Visitor<()> for NewPathExprFinderContext {
898 fn visit_expr(&mut self, expr: &ast::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.
904 _ => visit::walk_expr(self,expr,())
908 fn visit_ty(&mut self, typ: &ast::Ty, _: ()) {
909 visit::walk_ty(self, typ, ())
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
925 impl CrateLoader for ErrLoader {
926 fn load_crate(&mut self, _: &ast::ViewItem) -> MacroCrate {
930 fn get_exported_macros(&mut self, _: ast::CrateNum) -> ~[~str] {
934 fn get_registrar_symbol(&mut self, _: ast::CrateNum) -> Option<~str> {
939 // these following tests are quite fragile, in that they don't test what
940 // *kind* of failure occurs.
942 // make sure that macros can leave scope
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(
953 let mut loader = ErrLoader;
954 expand_crate(sess,&mut loader,crate_ast);
957 // make sure that macros can leave scope for modules
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(
968 let mut loader = ErrLoader;
969 expand_crate(sess,&mut loader,crate_ast);
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(
982 let mut loader = ErrLoader;
983 expand_crate(sess, &mut loader, crate_ast);
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);
996 // make a MetaWord outer attribute with the given name
997 fn make_dummy_attr(s: &str) -> ast::Attribute {
999 span:codemap::DUMMY_SP,
1003 node: MetaWord(token::intern_and_get_ident(s)),
1004 span: codemap::DUMMY_SP,
1006 is_sugared_doc: false,
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);
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)
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)
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())
1034 #[test] fn macro_tokens_should_match(){
1035 expand_crate_str(~"macro_rules! m((a)=>(13)) fn main(){m!(a);}");
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.
1049 // The comparisons are done post-mtwt-resolve, so we're comparing renamed
1050 // names; differences in marks don't matter any more.
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);
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;}",
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)}",
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)}",
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)}",
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!();}",
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)}))
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.
1100 for (idx,s) in tests.iter().enumerate() {
1101 run_renaming_test(s,idx);
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)
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;
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;
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,
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());
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());
1151 let fail = (varref.segments.len() == 1)
1152 && (mtwt_resolve(varref.segments[0].identifier) == binding_name);
1155 println!("failure on test {}",test_idx);
1156 println!("text of test case: \"{}\"", teststr);
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,
1165 println!("binding: {:?}", bindings[binding_idx]);
1166 ast_util::display_sctable(get_sctable());
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);}}))
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;
1185 let cxbinds: ~[&ast::Ident] =
1186 bindings.iter().filter(|b| {
1187 let ident = token::get_ident(**b);
1188 let string = ident.get();
1191 let cxbinds: &[&ast::Ident] = cxbinds;
1192 let cxbind = match cxbinds {
1194 _ => fail!("expected just one binding for ext_cx")
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;
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()
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:");
1219 let table = table.table.borrow();
1220 for (idx,val) in table.get().iter().enumerate() {
1221 println!("{:4u}: {:?}",idx,val);
1225 assert_eq!(mtwt_resolve(v.segments[0].identifier),resolved_binding);
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"]));