1 // Copyright 2012-2013 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::{Block, Crate, DeclLocal, Expr_, ExprMac, SyntaxContext};
12 use ast::{Local, Ident, mac_invoc_tt};
13 use ast::{item_mac, Mrk, Stmt, StmtDecl, StmtMac, StmtExpr, StmtSemi};
14 use ast::{token_tree};
16 use ast_util::{mtwt_outer_mark, new_rename, new_mark};
17 use ext::build::AstBuilder;
19 use attr::AttrMetaMethods;
21 use codemap::{Span, Spanned, ExpnInfo, NameAndSpan};
26 use parse::{parse_item_from_source_str};
28 use parse::token::{fresh_mark, fresh_name, ident_to_str, intern};
34 pub fn expand_expr(extsbox: @mut SyntaxEnv,
40 // expr_mac should really be expr_ext or something; it's the
41 // entry-point for all syntax extensions.
44 // it would almost certainly be cleaner to pass the whole
45 // macro invocation in, rather than pulling it apart and
46 // marking the tts and the ctxt separately. This also goes
47 // for the other three macro invocation chunks of code
50 mac_invoc_tt(ref pth, ref tts, ctxt) => {
51 if (pth.segments.len() > 1u) {
54 format!("expected macro name without module \
57 let extname = &pth.segments[0].identifier;
58 let extnamestr = ident_to_str(extname);
59 // leaving explicit deref here to highlight unbox op:
60 match (*extsbox).find(&extname.name) {
64 format!("macro undefined: '{}'", extnamestr))
66 Some(@SE(NormalTT(expandfun, exp_span))) => {
74 let fm = fresh_mark();
76 let marked_before = mark_tts(*tts,fm);
77 let marked_ctxt = new_mark(fm, ctxt);
79 // The span that we pass to the expanders we want to
80 // be the root of the call stack. That's the most
81 // relevant span and it's the actual invocation of
83 let mac_span = original_span(cx);
86 match expandfun.expand(cx,
91 MRAny(any_macro) => any_macro.make_expr(),
96 "non-expr macro in expr pos: {}",
103 let marked_after = mark_expr(expanded,fm);
105 // Keep going, outside-in.
107 // XXX(pcwalton): Is it necessary to clone the
110 fld.fold_expr(marked_after).node.clone();
114 id: ast::DUMMY_NODE_ID,
115 node: fully_expanded,
122 format!("'{}' is not a tt-style macro", extnamestr)
130 // Desugar expr_for_loop
131 // From: `['<ident>:] for <src_pat> in <src_expr> <src_loop_block>`
132 // FIXME #6993 : change type of opt_ident to Option<Name>
133 ast::ExprForLoop(src_pat, src_expr, ref src_loop_block, opt_ident) => {
134 // Expand any interior macros etc.
135 // NB: we don't fold pats yet. Curious.
136 let src_expr = fld.fold_expr(src_expr).clone();
137 let src_loop_block = fld.fold_block(src_loop_block).clone();
141 pub fn mk_expr(_: @ExtCtxt, span: Span, node: Expr_)
144 id: ast::DUMMY_NODE_ID,
150 fn mk_block(_: @ExtCtxt,
151 stmts: &[@ast::Stmt],
152 expr: Option<@ast::Expr>,
157 stmts: stmts.to_owned(),
159 id: ast::DUMMY_NODE_ID,
160 rules: ast::DefaultBlock,
165 fn mk_simple_path(ident: ast::Ident, span: Span) -> ast::Path {
173 types: opt_vec::Empty,
182 // let _i = &mut <src_expr>;
183 // ['<ident>:] loop {
186 // Some(<src_pat>) => <src_loop_block>
191 let local_ident = token::gensym_ident("i");
192 let next_ident = cx.ident_of("next");
193 let none_ident = cx.ident_of("None");
195 let local_path = cx.path_ident(span, local_ident);
196 let some_path = cx.path_ident(span, cx.ident_of("Some"));
198 // `let i = &mut <src_expr>`
199 let iter_decl_stmt = cx.stmt_let(span, false, local_ident,
200 cx.expr_mut_addr_of(span, src_expr));
202 // `None => break ['<ident>];`
204 // FIXME #6993: this map goes away:
205 let break_expr = cx.expr(span, ast::ExprBreak(opt_ident.map(|x| x.name)));
206 let none_pat = cx.pat_ident(span, none_ident);
207 cx.arm(span, ~[none_pat], break_expr)
210 // `Some(<src_pat>) => <src_loop_block>`
213 ~[cx.pat_enum(span, some_path, ~[src_pat])],
214 cx.expr_block(src_loop_block));
216 // `match i.next() { ... }`
219 cx.expr_method_call(span, cx.expr_path(local_path), next_ident, ~[]);
221 cx.expr_match(span, next_call_expr, ~[none_arm, some_arm])
224 // ['ident:] loop { ... }
225 let loop_expr = cx.expr(span,
226 ast::ExprLoop(cx.block_expr(match_expr), opt_ident));
228 // `{ let ... ; loop { ... } }`
229 let block = cx.block(span,
234 id: ast::DUMMY_NODE_ID,
235 node: ast::ExprBlock(block),
240 _ => noop_fold_expr(e, fld)
244 // This is a secondary mechanism for invoking syntax extensions on items:
245 // "decorator" attributes, such as #[auto_encode]. These are invoked by an
246 // attribute prefixing an item, and are interpreted by feeding the item
247 // through the named attribute _as a syntax extension_ and splicing in the
248 // resulting item vec into place in favour of the decorator. Note that
249 // these do _not_ work for macro extensions, just ItemDecorator ones.
251 // NB: there is some redundancy between this and expand_item, below, and
252 // they might benefit from some amount of semantic and language-UI merger.
253 pub fn expand_mod_items(extsbox: @mut SyntaxEnv,
258 // Fold the contents first:
259 let module_ = noop_fold_mod(module_, fld);
261 // For each item, look through the attributes. If any of them are
262 // decorated with "item decorators", then use that function to transform
263 // the item into a new set of items.
264 let new_items = do vec::flat_map(module_.items) |item| {
265 do item.attrs.rev_iter().fold(~[*item]) |items, attr| {
266 let mname = attr.name();
268 match (*extsbox).find(&intern(mname)) {
269 Some(@SE(ItemDecorator(dec_fn))) => {
270 cx.bt_push(ExpnInfo {
271 call_site: attr.span,
272 callee: NameAndSpan {
277 let r = dec_fn(cx, attr.span, attr.node.value, items);
292 // eval $e with a new exts frame:
293 macro_rules! with_exts_frame (
294 ($extsboxexpr:expr,$macros_escape:expr,$e:expr) =>
295 ({let extsbox = $extsboxexpr;
296 let oldexts = *extsbox;
297 *extsbox = oldexts.push_frame();
298 extsbox.insert(intern(special_block_name),
299 @BlockInfo(BlockInfo{macros_escape:$macros_escape,pending_renames:@mut ~[]}));
306 static special_block_name : &'static str = " block";
308 // When we enter a module, record it, for the sake of `module!`
309 pub fn expand_item(extsbox: @mut SyntaxEnv,
313 -> Option<@ast::item> {
315 ast::item_mac(*) => expand_item_mac(extsbox, cx, it, fld),
316 ast::item_mod(_) | ast::item_foreign_mod(_) => {
317 cx.mod_push(it.ident);
318 let macro_escape = contains_macro_escape(it.attrs);
319 let result = with_exts_frame!(extsbox,
321 noop_fold_item(it, fld));
325 _ => noop_fold_item(it, fld)
329 // does this attribute list contain "macro_escape" ?
330 pub fn contains_macro_escape(attrs: &[ast::Attribute]) -> bool {
331 attr::contains_name(attrs, "macro_escape")
334 // Support for item-position macro invocations, exactly the same
335 // logic as for expression-position macro invocations.
336 pub fn expand_item_mac(extsbox: @mut SyntaxEnv,
340 -> Option<@ast::item> {
341 let (pth, tts, ctxt) = match it.node {
342 item_mac(codemap::Spanned {
343 node: mac_invoc_tt(ref pth, ref tts, ctxt),
346 (pth, (*tts).clone(), ctxt)
348 _ => cx.span_bug(it.span, "invalid item macro invocation")
351 let extname = &pth.segments[0].identifier;
352 let extnamestr = ident_to_str(extname);
353 let fm = fresh_mark();
354 let expanded = match (*extsbox).find(&extname.name) {
355 None => cx.span_fatal(pth.span,
356 format!("macro undefined: '{}!'", extnamestr)),
358 Some(@SE(NormalTT(expander, span))) => {
359 if it.ident.name != parse::token::special_idents::invalid.name {
360 cx.span_fatal(pth.span,
361 format!("macro {}! expects no ident argument, \
362 given '{}'", extnamestr,
363 ident_to_str(&it.ident)));
365 cx.bt_push(ExpnInfo {
367 callee: NameAndSpan {
372 // mark before expansion:
373 let marked_before = mark_tts(tts,fm);
374 let marked_ctxt = new_mark(fm,ctxt);
375 expander.expand(cx, it.span, marked_before, marked_ctxt)
377 Some(@SE(IdentTT(expander, span))) => {
378 if it.ident.name == parse::token::special_idents::invalid.name {
379 cx.span_fatal(pth.span,
380 format!("macro {}! expects an ident argument",
383 cx.bt_push(ExpnInfo {
385 callee: NameAndSpan {
390 // mark before expansion:
391 let marked_tts = mark_tts(tts,fm);
392 let marked_ctxt = new_mark(fm,ctxt);
393 expander.expand(cx, it.span, it.ident, marked_tts, marked_ctxt)
396 it.span, format!("{}! is not legal in item position", extnamestr))
399 let maybe_it = match expanded {
402 .and_then(|i| fld.fold_item(i))
405 cx.span_fatal(pth.span, format!("expr macro in item position: {}", extnamestr))
407 MRAny(any_macro) => {
408 any_macro.make_item()
409 .and_then(|i| mark_item(i,fm))
410 .and_then(|i| fld.fold_item(i))
413 // yikes... no idea how to apply the mark to this. I'm afraid
414 // we're going to have to wait-and-see on this one.
415 insert_macro(*extsbox,intern(mdef.name), @SE((*mdef).ext));
424 // insert a macro into the innermost frame that doesn't have the
426 fn insert_macro(exts: SyntaxEnv, name: ast::Name, transformer: @Transformer) {
427 let is_non_escaping_block =
428 |t : &@Transformer| -> bool{
430 &@BlockInfo(BlockInfo {macros_escape:false,_}) => true,
431 &@BlockInfo(BlockInfo {_}) => false,
432 _ => fail!("special identifier {:?} was bound to a non-BlockInfo",
436 exts.insert_into_frame(name,transformer,intern(special_block_name),
437 is_non_escaping_block)
441 pub fn expand_stmt(extsbox: @mut SyntaxEnv,
446 // why the copying here and not in expand_expr?
447 // looks like classic changed-in-only-one-place
448 let (pth, tts, semi, ctxt) = match s.node {
449 StmtMac(ref mac, semi) => {
451 mac_invoc_tt(ref pth, ref tts, ctxt) => {
452 (pth, (*tts).clone(), semi, ctxt)
456 _ => return expand_non_macro_stmt(*extsbox, s, fld)
458 if (pth.segments.len() > 1u) {
459 cx.span_fatal(pth.span,
460 "expected macro name without module separators");
462 let extname = &pth.segments[0].identifier;
463 let extnamestr = ident_to_str(extname);
464 let fully_expanded: @ast::Stmt = match (*extsbox).find(&extname.name) {
466 cx.span_fatal(pth.span, format!("macro undefined: '{}'", extnamestr))
469 Some(@SE(NormalTT(expandfun, exp_span))) => {
470 cx.bt_push(ExpnInfo {
472 callee: NameAndSpan {
477 let fm = fresh_mark();
478 // mark before expansion:
479 let marked_tts = mark_tts(tts,fm);
480 let marked_ctxt = new_mark(fm,ctxt);
482 // See the comment in expand_expr for why we want the original span,
483 // not the current mac.span.
484 let mac_span = original_span(cx);
486 let expanded = match expandfun.expand(cx,
492 node: StmtExpr(e, ast::DUMMY_NODE_ID),
496 MRAny(any_macro) => any_macro.make_stmt(),
499 format!("non-stmt macro in stmt pos: {}", extnamestr))
501 let marked_after = mark_stmt(expanded,fm);
503 // Keep going, outside-in.
504 let fully_expanded = match fld.fold_stmt(marked_after) {
506 let fully_expanded = &stmt.node;
510 node: (*fully_expanded).clone(),
514 cx.span_fatal(pth.span,
515 "macro didn't expand to a statement")
523 cx.span_fatal(pth.span,
524 format!("'{}' is not a tt-style macro", extnamestr))
528 match fully_expanded.node {
529 StmtExpr(e, stmt_id) if semi => {
531 span: fully_expanded.span,
532 node: StmtSemi(e, stmt_id),
535 _ => Some(fully_expanded), /* might already have a semi */
539 // expand a non-macro stmt. this is essentially the fallthrough for
540 // expand_stmt, above.
541 fn expand_non_macro_stmt(exts: SyntaxEnv, s: &Stmt, fld: &MacroExpander)
546 node: DeclLocal(ref local),
550 let block_info = get_block_info(exts);
551 let pending_renames = block_info.pending_renames;
554 let @Local{is_mutbl:is_mutbl,
561 // types can't be copied automatically because of the owned ptr in ty_tup...
562 let ty = local.ty.clone();
563 // expand the pat (it might contain exprs... #:(o)>
564 let expanded_pat = fld.fold_pat(pat);
565 // find the pat_idents in the pattern:
566 // oh dear heaven... this is going to include the enum names, as well....
567 // ... but that should be okay, as long as the new names are gensyms
569 let idents = @mut ~[];
570 let name_finder = new_name_finder(idents);
571 name_finder.visit_pat(expanded_pat,());
572 // generate fresh names, push them to a new pending list
573 let new_pending_renames = @mut ~[];
574 for ident in idents.iter() {
575 let new_name = fresh_name(ident);
576 new_pending_renames.push((*ident,new_name));
578 let rename_fld = renames_to_fold(new_pending_renames);
579 // rewrite the pattern using the new names (the old ones
580 // have already been applied):
581 let rewritten_pat = rename_fld.fold_pat(expanded_pat);
582 // add them to the existing pending renames:
583 for pr in new_pending_renames.iter() {pending_renames.push(*pr)}
584 // also, don't forget to expand the init:
585 let new_init_opt = init.map(|e| fld.fold_expr(e));
586 let rewritten_local =
596 node: StmtDecl(@Spanned {
597 node: DeclLocal(rewritten_local),
604 _ => noop_fold_stmt(s, fld),
608 // a visitor that extracts the pat_ident paths
609 // from a given thingy and puts them in a mutable
610 // array (passed in to the traversal)
612 struct NewNameFinderContext {
613 ident_accumulator: @mut ~[ast::Ident],
616 impl Visitor<()> for NewNameFinderContext {
617 fn visit_pat(&mut self, pattern: @ast::Pat, _: ()) {
619 // we found a pat_ident!
622 node: ast::PatIdent(_, ref path, ref inner),
626 // a path of length one:
637 } => self.ident_accumulator.push(id),
638 // I believe these must be enums...
641 // visit optional subpattern of pat_ident:
642 for subpat in inner.iter() {
643 self.visit_pat(*subpat, ())
646 // use the default traversal for non-pat_idents
647 _ => visit::walk_pat(self, pattern, ())
651 fn visit_ty(&mut self, typ: &ast::Ty, _: ()) {
652 visit::walk_ty(self, typ, ())
657 // a visitor that extracts the paths
658 // from a given thingy and puts them in a mutable
659 // array (passed in to the traversal)
661 struct NewPathExprFinderContext {
662 path_accumulator: @mut ~[ast::Path],
665 impl Visitor<()> for NewPathExprFinderContext {
667 fn visit_expr(&mut self, expr: @ast::Expr, _: ()) {
669 ast::Expr{id:_,span:_,node:ast::ExprPath(ref p)} => {
670 self.path_accumulator.push(p.clone());
671 // not calling visit_path, should be fine.
673 _ => visit::walk_expr(self,expr,())
677 fn visit_ty(&mut self, typ: &ast::Ty, _: ()) {
678 visit::walk_ty(self, typ, ())
683 // return a visitor that extracts the pat_ident paths
684 // from a given thingy and puts them in a mutable
685 // array (passed in to the traversal)
686 pub fn new_name_finder(idents: @mut ~[ast::Ident]) -> @mut Visitor<()> {
687 let context = @mut NewNameFinderContext {
688 ident_accumulator: idents,
690 context as @mut Visitor<()>
693 // return a visitor that extracts the paths
694 // from a given pattern and puts them in a mutable
695 // array (passed in to the traversal)
696 pub fn new_path_finder(paths: @mut ~[ast::Path]) -> @mut Visitor<()> {
697 let context = @mut NewPathExprFinderContext {
698 path_accumulator: paths,
700 context as @mut Visitor<()>
703 // expand a block. pushes a new exts_frame, then calls expand_block_elts
704 pub fn expand_block(extsbox: @mut SyntaxEnv,
709 // see note below about treatment of exts table
710 with_exts_frame!(extsbox,false,
711 expand_block_elts(*extsbox, blk, fld))
714 // expand the elements of a block.
715 pub fn expand_block_elts(exts: SyntaxEnv, b: &Block, fld: &MacroExpander)
717 let block_info = get_block_info(exts);
718 let pending_renames = block_info.pending_renames;
719 let rename_fld = renames_to_fold(pending_renames);
720 let new_view_items = b.view_items.map(|x| fld.fold_view_item(x));
721 let mut new_stmts = ~[];
722 for x in b.stmts.iter() {
723 match fld.fold_stmt(mustbesome(rename_fld.fold_stmt(*x))) {
724 Some(s) => new_stmts.push(s),
728 let new_expr = b.expr.map(|x| fld.fold_expr(rename_fld.fold_expr(x)));
730 view_items: new_view_items,
733 id: fld.new_id(b.id),
739 // rename_fold should never return "None".
740 // (basically, just .get() with a better message...)
741 fn mustbesome<T>(val : Option<T>) -> T {
744 None => fail!("rename_fold returned None")
748 // get the (innermost) BlockInfo from an exts stack
749 fn get_block_info(exts : SyntaxEnv) -> BlockInfo {
750 match exts.find_in_topmost_frame(&intern(special_block_name)) {
751 Some(@BlockInfo(bi)) => bi,
752 _ => fail!("special identifier {:?} was bound to a non-BlockInfo",
757 struct IdentRenamer {
758 renames: @mut ~[(ast::Ident,ast::Name)],
761 impl ast_fold for IdentRenamer {
762 fn fold_ident(&self, id: ast::Ident) -> ast::Ident {
763 let new_ctxt = self.renames.iter().fold(id.ctxt, |ctxt, &(from, to)| {
764 new_rename(from, to, ctxt)
773 // given a mutable list of renames, return a tree-folder that applies those
775 pub fn renames_to_fold(renames: @mut ~[(ast::Ident,ast::Name)]) -> @ast_fold {
781 // perform a bunch of renames
782 fn apply_pending_renames(folder : @ast_fold, stmt : ast::Stmt) -> @ast::Stmt {
783 match folder.fold_stmt(&stmt) {
785 None => fail!("renaming of stmt produced None")
791 pub fn new_span(cx: @ExtCtxt, sp: Span) -> Span {
792 /* this discards information in the case of macro-defining macros */
796 expn_info: cx.backtrace(),
800 // FIXME (#2247): this is a moderately bad kludge to inject some macros into
801 // the default compilation environment in that it injects strings, rather than
804 pub fn std_macros() -> @str {
810 macro_rules! ignore (($($x:tt)*) => (()))
813 ($lvl:expr, $($arg:tt)+) => ({
815 if lvl <= __log_level() {
816 format_args!(|args| {
817 ::std::logging::log(lvl, args)
822 macro_rules! error( ($($arg:tt)*) => (log!(1u32, $($arg)*)) )
823 macro_rules! warn ( ($($arg:tt)*) => (log!(2u32, $($arg)*)) )
824 macro_rules! info ( ($($arg:tt)*) => (log!(3u32, $($arg)*)) )
825 macro_rules! debug( ($($arg:tt)*) => (
826 if cfg!(not(ndebug)) { log!(4u32, $($arg)*) }
831 fail!(\"explicit failure\")
834 ::std::sys::FailWithCause::fail_with($fmt, file!(), line!())
836 ($fmt:expr, $($arg:tt)*) => (
837 ::std::sys::FailWithCause::fail_with(format!($fmt, $($arg)*), file!(), line!())
844 ::std::sys::FailWithCause::fail_with(
845 \"assertion failed: \" + stringify!($cond), file!(), line!())
848 ($cond:expr, $msg:expr) => {
850 ::std::sys::FailWithCause::fail_with($msg, file!(), line!())
853 ($cond:expr, $( $arg:expr ),+) => {
855 ::std::sys::FailWithCause::fail_with(format!( $($arg),+ ), file!(), line!())
860 macro_rules! assert_eq (
861 ($given:expr , $expected:expr) => (
863 let given_val = &($given);
864 let expected_val = &($expected);
865 // check both directions of equality....
866 if !((*given_val == *expected_val) &&
867 (*expected_val == *given_val)) {
868 fail!(\"assertion failed: `(left == right) && (right == \
869 left)` (left: `{:?}`, right: `{:?}`)\",
870 *given_val, *expected_val);
876 macro_rules! assert_approx_eq (
877 ($given:expr , $expected:expr) => (
879 use std::cmp::ApproxEq;
881 let given_val = $given;
882 let expected_val = $expected;
883 // check both directions of equality....
885 given_val.approx_eq(&expected_val) &&
886 expected_val.approx_eq(&given_val)
888 fail!(\"left: {:?} does not approximately equal right: {:?}\",
889 given_val, expected_val);
893 ($given:expr , $expected:expr , $epsilon:expr) => (
895 use std::cmp::ApproxEq;
897 let given_val = $given;
898 let expected_val = $expected;
899 let epsilon_val = $epsilon;
900 // check both directions of equality....
902 given_val.approx_eq_eps(&expected_val, &epsilon_val) &&
903 expected_val.approx_eq_eps(&given_val, &epsilon_val)
905 fail!(\"left: {:?} does not approximately equal right: \
906 {:?} with epsilon: {:?}\",
907 given_val, expected_val, epsilon_val);
913 // FIXME(#6266): change the /* to /** when attributes are supported on macros
914 // (Though even then—is it going to work according to the clear intent here?)
916 A utility macro for indicating unreachable code. It will fail if
917 executed. This is occasionally useful to put after loops that never
918 terminate normally, but instead directly return from a function.
923 fn choose_weighted_item(v: &[Item]) -> Item {
924 assert!(!v.is_empty());
927 so_far += item.weight;
932 // The above loop always returns, so we must hint to the
933 // type checker that it isn't possible to get down here
939 macro_rules! unreachable (() => (
940 fail!(\"internal error: entered unreachable code\");
943 macro_rules! condition (
945 { pub $c:ident: $input:ty -> $out:ty; } => {
948 #[allow(unused_imports)];
949 #[allow(non_uppercase_statics)];
950 #[allow(missing_doc)];
954 local_data_key!(key: @::std::condition::Handler<$input, $out>)
957 ::std::condition::Condition<$input,$out> =
958 ::std::condition::Condition {
959 name: stringify!($c),
965 { $c:ident: $input:ty -> $out:ty; } => {
968 #[allow(unused_imports)];
969 #[allow(non_uppercase_statics)];
973 local_data_key!(key: @::std::condition::Handler<$input, $out>)
976 ::std::condition::Condition<$input,$out> =
977 ::std::condition::Condition {
978 name: stringify!($c),
985 macro_rules! format(($($arg:tt)*) => (
986 format_args!(::std::fmt::format, $($arg)*)
988 macro_rules! write(($dst:expr, $($arg:tt)*) => (
989 format_args!(|args| { ::std::fmt::write($dst, args) }, $($arg)*)
991 macro_rules! writeln(($dst:expr, $($arg:tt)*) => (
992 format_args!(|args| { ::std::fmt::writeln($dst, args) }, $($arg)*)
995 ($($arg:tt)*) => (format_args!(::std::rt::io::stdio::print_args, $($arg)*))
997 macro_rules! println (
998 ($($arg:tt)*) => (format_args!(::std::rt::io::stdio::println_args, $($arg)*))
1001 macro_rules! local_data_key (
1002 ($name:ident: $ty:ty) => (
1003 static $name: ::std::local_data::Key<$ty> = &::std::local_data::Key;
1005 (pub $name:ident: $ty:ty) => (
1006 pub static $name: ::std::local_data::Key<$ty> = &::std::local_data::Key;
1010 // externfn! declares a wrapper for an external function.
1011 // It is intended to be used like:
1013 // externfn!(#[nolink]
1014 // fn memcmp(cx: *u8, ct: *u8, n: u32) -> u32)
1016 // Due to limitations in the macro parser, this pattern must be
1017 // implemented with 4 distinct patterns (with attrs / without
1018 // attrs CROSS with args / without ARGS).
1020 // Also, this macro grammar allows for any number of return types
1021 // because I couldn't figure out the syntax to specify at most one.
1022 macro_rules! externfn(
1023 (fn $name:ident () $(-> $ret_ty:ty),*) => (
1024 pub unsafe fn $name() $(-> $ret_ty),* {
1025 // Note: to avoid obscure bug in macros, keep these
1026 // attributes *internal* to the fn
1027 #[fixed_stack_segment];
1029 #[allow(missing_doc)];
1034 fn $name() $(-> $ret_ty),*;
1038 (fn $name:ident ($($arg_name:ident : $arg_ty:ty),*) $(-> $ret_ty:ty),*) => (
1039 pub unsafe fn $name($($arg_name : $arg_ty),*) $(-> $ret_ty),* {
1040 // Note: to avoid obscure bug in macros, keep these
1041 // attributes *internal* to the fn
1042 #[fixed_stack_segment];
1044 #[allow(missing_doc)];
1046 return $name($($arg_name),*);
1049 fn $name($($arg_name : $arg_ty),*) $(-> $ret_ty),*;
1053 ($($attrs:attr)* fn $name:ident () $(-> $ret_ty:ty),*) => (
1054 pub unsafe fn $name() $(-> $ret_ty),* {
1055 // Note: to avoid obscure bug in macros, keep these
1056 // attributes *internal* to the fn
1057 #[fixed_stack_segment];
1059 #[allow(missing_doc)];
1065 fn $name() $(-> $ret_ty),*;
1069 ($($attrs:attr)* fn $name:ident ($($arg_name:ident : $arg_ty:ty),*) $(-> $ret_ty:ty),*) => (
1070 pub unsafe fn $name($($arg_name : $arg_ty),*) $(-> $ret_ty),* {
1071 // Note: to avoid obscure bug in macros, keep these
1072 // attributes *internal* to the fn
1073 #[fixed_stack_segment];
1075 #[allow(missing_doc)];
1077 return $name($($arg_name),*);
1081 fn $name($($arg_name : $arg_ty),*) $(-> $ret_ty),*;
1094 impl ast_fold for Injector {
1095 fn fold_mod(&self, module: &ast::_mod) -> ast::_mod {
1096 // Just inject the standard macros at the start of the first module
1097 // in the crate: that is, at the start of the crate file itself.
1098 let items = vec::append(~[ self.sm ], module.items);
1101 ..(*module).clone() // FIXME #2543: Bad copy.
1106 // add a bunch of macros as though they were placed at the head of the
1107 // program (ick). This should run before cfg stripping.
1108 pub fn inject_std_macros(parse_sess: @mut parse::ParseSess,
1109 cfg: ast::CrateConfig,
1112 let sm = match parse_item_from_source_str(@"<std-macros>",
1118 None => fail!("expected core macros to parse correctly")
1121 let injector = @Injector {
1124 injector.fold_crate(c)
1131 impl ast_fold for NoOpFolder {}
1133 struct MacroExpander {
1134 extsbox: @mut SyntaxEnv,
1138 impl ast_fold for MacroExpander {
1139 fn fold_expr(&self, expr: @ast::Expr) -> @ast::Expr {
1140 expand_expr(self.extsbox,
1146 fn fold_mod(&self, module: &ast::_mod) -> ast::_mod {
1147 expand_mod_items(self.extsbox,
1153 fn fold_item(&self, item: @ast::item) -> Option<@ast::item> {
1154 expand_item(self.extsbox,
1160 fn fold_stmt(&self, stmt: &ast::Stmt) -> Option<@ast::Stmt> {
1161 expand_stmt(self.extsbox,
1167 fn fold_block(&self, block: &ast::Block) -> ast::Block {
1168 expand_block(self.extsbox,
1174 fn new_span(&self, span: Span) -> Span {
1175 new_span(self.cx, span)
1179 pub fn expand_crate(parse_sess: @mut parse::ParseSess,
1180 cfg: ast::CrateConfig,
1181 c: Crate) -> Crate {
1182 // adding *another* layer of indirection here so that the block
1183 // visitor can swap out one exts table for another for the duration
1184 // of the block. The cleaner alternative would be to thread the
1185 // exts table through the fold, but that would require updating
1186 // every method/element of AstFoldFns in fold.rs.
1187 let extsbox = syntax_expander_table();
1188 let cx = ExtCtxt::new(parse_sess, cfg.clone());
1189 let expander = @MacroExpander {
1190 extsbox: @mut extsbox,
1194 let ret = expander.fold_crate(c);
1195 parse_sess.span_diagnostic.handler().abort_if_errors();
1199 // HYGIENIC CONTEXT EXTENSION:
1200 // all of these functions are for walking over
1201 // ASTs and making some change to the context of every
1202 // element that has one. a CtxtFn is a trait-ified
1203 // version of a closure in (SyntaxContext -> SyntaxContext).
1204 // the ones defined here include:
1205 // Renamer - add a rename to a context
1206 // MultiRenamer - add a set of renames to a context
1207 // Marker - add a mark to a context
1208 // Repainter - replace a context (maybe Replacer would be a better name?)
1210 // a function in SyntaxContext -> SyntaxContext
1212 fn f(&self, ast::SyntaxContext) -> ast::SyntaxContext;
1215 // a renamer adds a rename to the syntax context
1216 pub struct Renamer {
1221 impl CtxtFn for Renamer {
1222 fn f(&self, ctxt : ast::SyntaxContext) -> ast::SyntaxContext {
1223 new_rename(self.from,self.to,ctxt)
1227 // a renamer that performs a whole bunch of renames
1228 pub struct MultiRenamer {
1229 renames : @mut ~[(ast::Ident,ast::Name)]
1232 impl CtxtFn for MultiRenamer {
1233 fn f(&self, starting_ctxt : ast::SyntaxContext) -> ast::SyntaxContext {
1234 // the individual elements are memoized... it would
1235 // also be possible to memoize on the whole list at once.
1236 self.renames.iter().fold(starting_ctxt,|ctxt,&(from,to)| {
1237 new_rename(from,to,ctxt)
1242 // a marker adds the given mark to the syntax context
1243 pub struct Marker { mark : Mrk }
1245 impl CtxtFn for Marker {
1246 fn f(&self, ctxt : ast::SyntaxContext) -> ast::SyntaxContext {
1247 new_mark(self.mark,ctxt)
1251 // a repainter just replaces the given context with the one it's closed over
1252 pub struct Repainter { ctxt : SyntaxContext }
1254 impl CtxtFn for Repainter {
1255 fn f(&self, _ctxt : ast::SyntaxContext) -> ast::SyntaxContext {
1260 pub struct ContextWrapper {
1261 context_function: @CtxtFn,
1264 impl ast_fold for ContextWrapper {
1265 fn fold_ident(&self, id: ast::Ident) -> ast::Ident {
1272 ctxt: self.context_function.f(ctxt),
1275 fn fold_mac(&self, m: &ast::mac) -> ast::mac {
1276 let macro = match m.node {
1277 mac_invoc_tt(ref path, ref tts, ctxt) => {
1278 mac_invoc_tt(self.fold_path(path),
1279 fold_tts(*tts, self),
1280 self.context_function.f(ctxt))
1290 // given a function from ctxts to ctxts, produce
1291 // an ast_fold that applies that function to all ctxts:
1292 pub fn fun_to_ctxt_folder<T : 'static + CtxtFn>(cf: @T) -> @ContextWrapper {
1294 context_function: cf as @CtxtFn,
1298 // just a convenience:
1299 pub fn new_mark_folder(m: Mrk) -> @ContextWrapper {
1300 fun_to_ctxt_folder(@Marker{mark:m})
1303 pub fn new_rename_folder(from: ast::Ident, to: ast::Name) -> @ContextWrapper {
1304 fun_to_ctxt_folder(@Renamer{from:from,to:to})
1307 // apply a given mark to the given token trees. Used prior to expansion of a macro.
1308 fn mark_tts(tts : &[token_tree], m : Mrk) -> ~[token_tree] {
1309 fold_tts(tts,new_mark_folder(m))
1312 // apply a given mark to the given expr. Used following the expansion of a macro.
1313 fn mark_expr(expr : @ast::Expr, m : Mrk) -> @ast::Expr {
1314 new_mark_folder(m).fold_expr(expr)
1317 // apply a given mark to the given stmt. Used following the expansion of a macro.
1318 fn mark_stmt(expr : &ast::Stmt, m : Mrk) -> @ast::Stmt {
1319 new_mark_folder(m).fold_stmt(expr).unwrap()
1322 // apply a given mark to the given item. Used following the expansion of a macro.
1323 fn mark_item(expr : @ast::item, m : Mrk) -> Option<@ast::item> {
1324 new_mark_folder(m).fold_item(expr)
1327 // replace all contexts in a given expr with the given mark. Used
1328 // for capturing macros
1329 pub fn replace_ctxts(expr : @ast::Expr, ctxt : SyntaxContext) -> @ast::Expr {
1330 fun_to_ctxt_folder(@Repainter{ctxt:ctxt}).fold_expr(expr)
1333 // take the mark from the given ctxt (that has a mark at the outside),
1334 // and apply it to everything in the token trees, thereby cancelling
1336 pub fn mtwt_cancel_outer_mark(tts: &[ast::token_tree], ctxt: ast::SyntaxContext)
1337 -> ~[ast::token_tree] {
1338 let outer_mark = mtwt_outer_mark(ctxt);
1339 mark_tts(tts,outer_mark)
1342 fn original_span(cx: @ExtCtxt) -> @codemap::ExpnInfo {
1343 let mut relevant_info = cx.backtrace();
1344 let mut einfo = relevant_info.unwrap();
1346 match relevant_info {
1350 relevant_info = einfo.call_site.expn_info;
1361 use ast::{Attribute_, AttrOuter, MetaWord, EMPTY_CTXT};
1362 use ast_util::{get_sctable, mtwt_marksof, mtwt_resolve, new_rename};
1365 use codemap::Spanned;
1368 use parse::token::{fresh_mark, gensym, intern, get_ident_interner, ident_to_str};
1372 use util::parser_testing::{string_to_crate, string_to_crate_and_sess};
1373 use util::parser_testing::{string_to_pat, string_to_tts, strs_to_idents};
1376 // make sure that fail! is present
1377 #[test] fn fail_exists_test () {
1378 let src = @"fn main() { fail!(\"something appropriately gloomy\");}";
1379 let sess = parse::new_parse_sess(None);
1380 let crate_ast = parse::parse_crate_from_source_str(
1384 let crate_ast = inject_std_macros(sess, ~[], crate_ast);
1385 // don't bother with striping, doesn't affect fail!.
1386 expand_crate(sess,~[],crate_ast);
1389 // these following tests are quite fragile, in that they don't test what
1390 // *kind* of failure occurs.
1392 // make sure that macros can leave scope
1394 #[test] fn macros_cant_escape_fns_test () {
1395 let src = @"fn bogus() {macro_rules! z (() => (3+4))}\
1396 fn inty() -> int { z!() }";
1397 let sess = parse::new_parse_sess(None);
1398 let crate_ast = parse::parse_crate_from_source_str(
1403 expand_crate(sess,~[],crate_ast);
1406 // make sure that macros can leave scope for modules
1408 #[test] fn macros_cant_escape_mods_test () {
1409 let src = @"mod foo {macro_rules! z (() => (3+4))}\
1410 fn inty() -> int { z!() }";
1411 let sess = parse::new_parse_sess(None);
1412 let crate_ast = parse::parse_crate_from_source_str(
1417 expand_crate(sess,~[],crate_ast);
1420 // macro_escape modules shouldn't cause macros to leave scope
1421 #[test] fn macros_can_escape_flattened_mods_test () {
1422 let src = @"#[macro_escape] mod foo {macro_rules! z (() => (3+4))}\
1423 fn inty() -> int { z!() }";
1424 let sess = parse::new_parse_sess(None);
1425 let crate_ast = parse::parse_crate_from_source_str(
1430 expand_crate(sess,~[],crate_ast);
1433 #[test] fn std_macros_must_parse () {
1434 let src = super::std_macros();
1435 let sess = parse::new_parse_sess(None);
1437 let item_ast = parse::parse_item_from_source_str(
1442 Some(_) => (), // success
1443 None => fail!("expected this to parse")
1447 #[test] fn test_contains_flatten (){
1448 let attr1 = make_dummy_attr (@"foo");
1449 let attr2 = make_dummy_attr (@"bar");
1450 let escape_attr = make_dummy_attr (@"macro_escape");
1451 let attrs1 = ~[attr1, escape_attr, attr2];
1452 assert_eq!(contains_macro_escape (attrs1),true);
1453 let attrs2 = ~[attr1,attr2];
1454 assert_eq!(contains_macro_escape (attrs2),false);
1457 // make a MetaWord outer attribute with the given name
1458 fn make_dummy_attr(s: @str) -> ast::Attribute {
1460 span:codemap::dummy_sp(),
1465 span: codemap::dummy_sp(),
1467 is_sugared_doc: false,
1472 #[test] fn cancel_outer_mark_test(){
1473 let invalid_name = token::special_idents::invalid.name;
1474 let ident_str = @"x";
1475 let tts = string_to_tts(ident_str);
1476 let fm = fresh_mark();
1477 let marked_once = fold::fold_tts(tts,new_mark_folder(fm));
1478 assert_eq!(marked_once.len(),1);
1479 let marked_once_ctxt =
1480 match marked_once[0] {
1481 ast::tt_tok(_,token::IDENT(id,_)) => id.ctxt,
1482 _ => fail!(format!("unexpected shape for marked tts: {:?}",marked_once[0]))
1484 assert_eq!(mtwt_marksof(marked_once_ctxt,invalid_name),~[fm]);
1485 let remarked = mtwt_cancel_outer_mark(marked_once,marked_once_ctxt);
1486 assert_eq!(remarked.len(),1);
1488 ast::tt_tok(_,token::IDENT(id,_)) =>
1489 assert_eq!(mtwt_marksof(id.ctxt,invalid_name),~[]),
1490 _ => fail!(format!("unexpected shape for marked tts: {:?}",remarked[0]))
1496 let item_ast = string_to_crate(@"fn f() -> int { a }");
1497 let a_name = intern("a");
1498 let a2_name = gensym("a2");
1499 let renamer = new_rename_folder(ast::Ident{name:a_name,ctxt:EMPTY_CTXT},
1501 let renamed_ast = renamer.fold_crate(item_ast.clone());
1502 let varrefs = @mut ~[];
1503 visit::walk_crate(&mut new_path_finder(varrefs), &renamed_ast, ());
1505 @[ast::Path{segments:[ref seg],_}] =>
1506 assert_eq!(mtwt_resolve(seg.identifier),a2_name),
1507 _ => assert_eq!(0,1)
1510 // try a double-rename, with pending_renames.
1511 let a3_name = gensym("a3");
1512 // a context that renames from ("a",empty) to "a2" :
1513 let ctxt2 = new_rename(ast::Ident::new(a_name),a2_name,EMPTY_CTXT);
1514 let pending_renames = @mut ~[(ast::Ident::new(a_name),a2_name),
1515 (ast::Ident{name:a_name,ctxt:ctxt2},a3_name)];
1516 let double_renamed = renames_to_fold(pending_renames).fold_crate(item_ast);
1517 let varrefs = @mut ~[];
1518 visit::walk_crate(&mut new_path_finder(varrefs), &double_renamed, ());
1520 @[ast::Path{segments:[ref seg],_}] =>
1521 assert_eq!(mtwt_resolve(seg.identifier),a3_name),
1522 _ => assert_eq!(0,1)
1526 fn fake_print_crate(crate: &ast::Crate) {
1527 let out = @mut std::rt::io::stderr() as @mut std::rt::io::Writer;
1528 let s = pprust::rust_printer(out, get_ident_interner());
1529 pprust::print_crate_(s, crate);
1532 fn expand_crate_str(crate_str: @str) -> ast::Crate {
1533 let (crate_ast,ps) = string_to_crate_and_sess(crate_str);
1534 // the cfg argument actually does matter, here...
1535 expand_crate(ps,~[],crate_ast)
1538 //fn expand_and_resolve(crate_str: @str) -> ast::crate {
1539 //let expanded_ast = expand_crate_str(crate_str);
1540 // println(format!("expanded: {:?}\n",expanded_ast));
1541 //mtwt_resolve_crate(expanded_ast)
1543 //fn expand_and_resolve_and_pretty_print (crate_str : @str) -> ~str {
1544 //let resolved_ast = expand_and_resolve(crate_str);
1545 //pprust::to_str(&resolved_ast,fake_print_crate,get_ident_interner())
1548 #[test] fn macro_tokens_should_match(){
1549 expand_crate_str(@"macro_rules! m((a)=>(13)) fn main(){m!(a);}");
1552 // renaming tests expand a crate and then check that the bindings match
1553 // the right varrefs. The specification of the test case includes the
1554 // text of the crate, and also an array of arrays. Each element in the
1555 // outer array corresponds to a binding in the traversal of the AST
1556 // induced by visit. Each of these arrays contains a list of indexes,
1557 // interpreted as the varrefs in the varref traversal that this binding
1558 // should match. So, for instance, in a program with two bindings and
1559 // three varrefs, the array ~[~[1,2],~[0]] would indicate that the first
1560 // binding should match the second two varrefs, and the second binding
1561 // should match the first varref.
1563 // The comparisons are done post-mtwt-resolve, so we're comparing renamed
1564 // names; differences in marks don't matter any more.
1566 // oog... I also want tests that check "binding-identifier-=?". That is,
1567 // not just "do these have the same name", but "do they have the same
1568 // name *and* the same marks"? Understanding this is really pretty painful.
1569 // in principle, you might want to control this boolean on a per-varref basis,
1570 // but that would make things even harder to understand, and might not be
1571 // necessary for thorough testing.
1572 type renaming_test = (&'static str, ~[~[uint]], bool);
1575 fn automatic_renaming () {
1576 let tests : ~[renaming_test] =
1577 ~[// b & c should get new names throughout, in the expr too:
1578 ("fn a() -> int { let b = 13; let c = b; b+c }",
1579 ~[~[0,1],~[2]], false),
1580 // both x's should be renamed (how is this causing a bug?)
1581 ("fn main () {let x : int = 13;x;}",
1583 // the use of b after the + should be renamed, the other one not:
1584 ("macro_rules! f (($x:ident) => (b + $x)) fn a() -> int { let b = 13; f!(b)}",
1586 // the b before the plus should not be renamed (requires marks)
1587 ("macro_rules! f (($x:ident) => ({let b=9; ($x + b)})) fn a() -> int { f!(b)}",
1589 // the marks going in and out of letty should cancel, allowing that $x to
1590 // capture the one following the semicolon.
1591 // this was an awesome test case, and caught a *lot* of bugs.
1592 ("macro_rules! letty(($x:ident) => (let $x = 15;))
1593 macro_rules! user(($x:ident) => ({letty!($x); $x}))
1594 fn main() -> int {user!(z)}",
1596 // no longer a fixme #8062: this test exposes a *potential* bug; our system does
1597 // not behave exactly like MTWT, but a conversation with Matthew Flatt
1598 // suggests that this can only occur in the presence of local-expand, which
1599 // we have no plans to support.
1600 // ("fn main() {let hrcoo = 19; macro_rules! getx(()=>(hrcoo)); getx!();}",
1602 // FIXME #6994: the next string exposes the bug referred to in issue 6994, so I'm
1603 // commenting it out.
1604 // the z flows into and out of two macros (g & f) along one path, and one
1605 // (just g) along the other, so the result of the whole thing should
1606 // be "let z_123 = 3; z_123"
1607 //"macro_rules! g (($x:ident) =>
1608 // ({macro_rules! f(($y:ident)=>({let $y=3;$x}));f!($x)}))
1610 // create a really evil test case where a $x appears inside a binding of $x
1611 // but *shouldnt* bind because it was inserted by a different macro....
1612 // can't write this test case until we have macro-generating macros.
1614 for (idx,s) in tests.iter().enumerate() {
1615 run_renaming_test(s,idx);
1619 // run one of the renaming tests
1620 fn run_renaming_test(t : &renaming_test, test_idx: uint) {
1621 let invalid_name = token::special_idents::invalid.name;
1622 let (teststr, bound_connections, bound_ident_check) = match *t {
1623 (ref str,ref conns, bic) => (str.to_managed(), conns.clone(), bic)
1625 let cr = expand_crate_str(teststr.to_managed());
1626 // find the bindings:
1627 let bindings = @mut ~[];
1628 visit::walk_crate(&mut new_name_finder(bindings),&cr,());
1629 // find the varrefs:
1630 let varrefs = @mut ~[];
1631 visit::walk_crate(&mut new_path_finder(varrefs),&cr,());
1632 // must be one check clause for each binding:
1633 assert_eq!(bindings.len(),bound_connections.len());
1634 for (binding_idx,shouldmatch) in bound_connections.iter().enumerate() {
1635 let binding_name = mtwt_resolve(bindings[binding_idx]);
1636 let binding_marks = mtwt_marksof(bindings[binding_idx].ctxt,invalid_name);
1637 // shouldmatch can't name varrefs that don't exist:
1638 assert!((shouldmatch.len() == 0) ||
1639 (varrefs.len() > *shouldmatch.iter().max().unwrap()));
1640 for (idx,varref) in varrefs.iter().enumerate() {
1641 if shouldmatch.contains(&idx) {
1642 // it should be a path of length 1, and it should
1643 // be free-identifier=? or bound-identifier=? to the given binding
1644 assert_eq!(varref.segments.len(),1);
1645 let varref_name = mtwt_resolve(varref.segments[0].identifier);
1646 let varref_marks = mtwt_marksof(varref.segments[0].identifier.ctxt,
1648 if (!(varref_name==binding_name)){
1649 println("uh oh, should match but doesn't:");
1650 println!("varref: {:?}",varref);
1651 println!("binding: {:?}", bindings[binding_idx]);
1652 ast_util::display_sctable(get_sctable());
1654 assert_eq!(varref_name,binding_name);
1655 if (bound_ident_check) {
1656 // we're checking bound-identifier=?, and the marks
1657 // should be the same, too:
1658 assert_eq!(varref_marks,binding_marks.clone());
1661 let fail = (varref.segments.len() == 1)
1662 && (mtwt_resolve(varref.segments[0].identifier) == binding_name);
1665 println!("failure on test {}",test_idx);
1666 println!("text of test case: \"{}\"", teststr);
1668 println!("uh oh, matches but shouldn't:");
1669 println!("varref: {:?}",varref);
1670 // good lord, you can't make a path with 0 segments, can you?
1671 println!("varref's first segment's uint: {}, and string: \"{}\"",
1672 varref.segments[0].identifier.name,
1673 ident_to_str(&varref.segments[0].identifier));
1674 println!("binding: {:?}", bindings[binding_idx]);
1675 ast_util::display_sctable(get_sctable());
1683 #[test] fn fmt_in_macro_used_inside_module_macro() {
1684 let crate_str = @"macro_rules! fmt_wrap(($b:expr)=>($b.to_str()))
1685 macro_rules! foo_module (() => (mod generated { fn a() { let xx = 147; fmt_wrap!(xx);}}))
1688 let cr = expand_crate_str(crate_str);
1689 // find the xx binding
1690 let bindings = @mut ~[];
1691 visit::walk_crate(&mut new_name_finder(bindings), &cr, ());
1692 let cxbinds : ~[&ast::Ident] =
1693 bindings.iter().filter(|b|{@"xx" == (ident_to_str(*b))}).collect();
1694 let cxbind = match cxbinds {
1696 _ => fail!("expected just one binding for ext_cx")
1698 let resolved_binding = mtwt_resolve(*cxbind);
1699 // find all the xx varrefs:
1700 let varrefs = @mut ~[];
1701 visit::walk_crate(&mut new_path_finder(varrefs), &cr, ());
1702 // the xx binding should bind all of the xx varrefs:
1703 for (idx,v) in varrefs.iter().filter(|p|{ p.segments.len() == 1
1704 && (@"xx" == (ident_to_str(&p.segments[0].identifier)))
1706 if (mtwt_resolve(v.segments[0].identifier) != resolved_binding) {
1707 println("uh oh, xx binding didn't match xx varref:");
1708 println!("this is xx varref \\# {:?}",idx);
1709 println!("binding: {:?}",cxbind);
1710 println!("resolves to: {:?}",resolved_binding);
1711 println!("varref: {:?}",v.segments[0].identifier);
1712 println!("resolves to: {:?}",
1713 mtwt_resolve(v.segments[0].identifier));
1714 let table = get_sctable();
1715 println("SC table:");
1716 for (idx,val) in table.table.iter().enumerate() {
1717 println!("{:4u} : {:?}",idx,val);
1720 assert_eq!(mtwt_resolve(v.segments[0].identifier),resolved_binding);
1726 let pat = string_to_pat(@"(a,Foo{x:c @ (b,9),y:Bar(4,d)})");
1727 let idents = @mut ~[];
1728 let pat_idents = new_name_finder(idents);
1729 pat_idents.visit_pat(pat, ());
1730 assert_eq!(idents, @mut strs_to_idents(~["a","c","b","d"]));