(index<- )        ./libsyntax/ext/expand.rs

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


libsyntax/ext/expand.rs:771:35-771:35 -fn- definition:
// expand the elements of a block.
pub fn expand_block_elts(b: &Block, fld: &mut MacroExpander) -> P<Block> {
    let new_view_items = b.view_items.iter().map(|x| fld.fold_view_item(x)).collect();
references:- 2
238:             fld.extsbox.info().pending_renames.push(rename);
239:             let expanded_block = expand_block_elts(loop_block, fld);
240:             fld.extsbox.pop_frame();
--
767:     with_exts_frame!(fld.extsbox,false,
768:                      expand_block_elts(blk, fld))
769: }


libsyntax/ext/expand.rs:833:1-833:1 -struct- definition:
pub struct MacroExpander<'a, 'b> {
    pub extsbox: SyntaxEnv,
    pub cx: &'a mut ExtCtxt<'b>,
references:- 14
874:     let mut cx = ExtCtxt::new(parse_sess, c.config.clone(), cfg);
875:     let mut expander = MacroExpander {
876:         extsbox: syntax_expander_table(),
libsyntax/ext/base.rs:
401:                 ast::ExprMac(..) => {
402:                     let mut expander = expand::MacroExpander {
403:                         extsbox: syntax_expander_table(),
libsyntax/ext/expand.rs:
36: pub fn expand_expr(e: @ast::Expr, fld: &mut MacroExpander) -> @ast::Expr {
37:     match e.node {


libsyntax/ext/expand.rs:217:47-217:47 -fn- definition:
// must be propagated to the enclosed context.
fn expand_loop_block(loop_block: P<Block>,
                     opt_ident: Option<Ident>,
references:- 2
204:         ast::ExprLoop(loop_block, opt_ident) => {
205:             let (loop_block, opt_ident) = expand_loop_block(loop_block, opt_ident, fld);
206:             fld.cx.expr(e.span, ast::ExprLoop(loop_block, opt_ident))


libsyntax/ext/expand.rs:943:1-943:1 -fn- definition:
fn original_span(cx: &ExtCtxt) -> @codemap::ExpnInfo {
    let mut relevant_info = cx.backtrace();
    let mut einfo = relevant_info.unwrap();
references:- 2
86:                             // the macro.
87:                             let mac_span = original_span(fld.cx);
--
591:             // not the current mac.span.
592:             let mac_span = original_span(fld.cx);


libsyntax/ext/expand.rs:923:84-923:84 -fn- definition:
// apply a given mark to the given token trees. Used prior to expansion of a macro.
fn mark_tts(tts: &[TokenTree], m: Mrk) -> Vec<TokenTree> {
    fold_tts(tts, &mut new_mark_folder(m))
references:- 4
401:             // mark before expansion:
402:             let marked_before = mark_tts(tts.as_slice(), fm);
403:             expander.expand(fld.cx, it.span, marked_before.as_slice())
--
587:             // mark before expansion:
588:             let marked_tts = mark_tts(tts.as_slice(), fm);


libsyntax/ext/expand.rs:310:1-310:1 -fn- definition:
fn expand_item_modifiers(mut it: @ast::Item, fld: &mut MacroExpander)
                         -> @ast::Item {
    let (modifiers, attrs) = it.attrs.partitioned(|attr| {
references:- 2
349:     // expansion may have added new ItemModifiers
350:     expand_item_modifiers(it, fld)
351: }


libsyntax/ext/expand.rs:893:54-893:54 -struct- definition:
// A Marker adds the given mark to the syntax context
struct Marker { mark: Mrk }
impl Folder for Marker {
references:- 3
896: impl Folder for Marker {
897:     fn fold_ident(&mut self, id: Ident) -> Ident {
--
918: // just a convenience:
919: fn new_mark_folder(m: Mrk) -> Marker {
920:     Marker {mark: m}
921: }


libsyntax/ext/expand.rs:359:55-359:55 -fn- definition:
// logic as for expression-position macro invocations.
pub fn expand_item_mac(it: @ast::Item, fld: &mut MacroExpander)
                       -> SmallVector<@ast::Item> {
references:- 2
293:     let mut new_items = match it.node {
294:         ast::ItemMac(..) => expand_item_mac(it, fld),
295:         ast::ItemMod(_) | ast::ItemForeignMod(_) => {
--
503:                 .expect("expected a serialized item");
504:         expand_item_mac(item, fld);
505:     }


libsyntax/ext/expand.rs:818:12-818:12 -fn- definition:
// renames.
pub fn renames_to_fold<'a>(renames: &'a mut RenameList) -> IdentRenamer<'a> {
    IdentRenamer {
references:- 4
785:             let pending_renames = &mut fld.extsbox.info().pending_renames;
786:             let mut rename_fld = renames_to_fold(pending_renames);
787:             rename_fld.fold_expr(x)


libsyntax/ext/expand.rs:259:64-259:64 -fn- definition:
// When we enter a module, record it, for the sake of `module!`
pub fn expand_item(it: @ast::Item, fld: &mut MacroExpander)
                   -> SmallVector<@ast::Item> {
references:- 2
284:                 decorator_items.extend(items.move_iter()
285:                     .flat_map(|item| expand_item(item, fld).move_iter()));
--
844:     fn fold_item(&mut self, item: @ast::Item) -> SmallVector<@ast::Item> {
845:         expand_item(item, self)
846:     }


libsyntax/ext/expand.rs:712:19-712:19 -struct- definition:
pub struct NewNameFinderContext {
    ident_accumulator: Vec<ast::Ident> ,
}
references:- 7
758: pub fn new_name_finder(idents: Vec<ast::Ident> ) -> NewNameFinderContext {
759:     NewNameFinderContext {
760:         ident_accumulator: idents,


libsyntax/ext/expand.rs:918:23-918:23 -fn- definition:
// just a convenience:
fn new_mark_folder(m: Mrk) -> Marker {
    Marker {mark: m}
references:- 4
940: fn mark_item(expr: @ast::Item, m: Mrk) -> SmallVector<@ast::Item> {
941:     new_mark_folder(m).fold_item(expr)
942: }


libsyntax/ext/expand.rs:35:1-35:1 -fn- definition:
pub fn expand_expr(e: @ast::Expr, fld: &mut MacroExpander) -> @ast::Expr {
    match e.node {
        // expr_mac should really be expr_ext or something; it's the
references:- 2
840:     fn fold_expr(&mut self, expr: @ast::Expr) -> @ast::Expr {
841:         expand_expr(expr, self)
842:     }
libsyntax/ext/base.rs:
405:                     };
406:                     e = expand::expand_expr(e, &mut expander);
407:                 }


libsyntax/ext/expand.rs:800:1-800:1 -struct- definition:
pub struct IdentRenamer<'a> {
    renames: &'a mut RenameList,
}
references:- 3
819: pub fn renames_to_fold<'a>(renames: &'a mut RenameList) -> IdentRenamer<'a> {
820:     IdentRenamer {
821:         renames: renames,


libsyntax/ext/expand.rs:864:1-864:1 -struct- definition:
pub struct ExpansionConfig<'a> {
    pub loader: &'a mut CrateLoader,
    pub deriving_hash_type_parameter: bool,
references:- 3
871: pub fn expand_crate(parse_sess: &parse::ParseSess,
872:                     cfg: ExpansionConfig,
873:                     c: Crate) -> Crate {
libsyntax/ext/base.rs:
386:     pub fn new<'a>(parse_sess: &'a parse::ParseSess, cfg: ast::CrateConfig,
387:                    ecfg: expand::ExpansionConfig<'a>) -> ExtCtxt<'a> {
388:         ExtCtxt {