about summary refs log tree commit diff
path: root/src/libsyntax
diff options
context:
space:
mode:
authorNick Cameron <ncameron@mozilla.com>2014-07-25 14:44:24 +1200
committerNick Cameron <ncameron@mozilla.com>2014-09-17 16:53:20 +1200
commit520671f1500885ffc2c5096e9150a665b63c2e0d (patch)
tree5ed7ce68faaa724c8c6655f4edc325aa533239d6 /src/libsyntax
parentb75b0f79235c919b3ab959fb79247d2997bd63e0 (diff)
downloadrust-520671f1500885ffc2c5096e9150a665b63c2e0d.tar.gz
rust-520671f1500885ffc2c5096e9150a665b63c2e0d.zip
move most of front to libsyntax
Diffstat (limited to 'src/libsyntax')
-rw-r--r--src/libsyntax/ast_map/mod.rs2
-rw-r--r--src/libsyntax/config.rs253
-rw-r--r--src/libsyntax/lib.rs3
-rw-r--r--src/libsyntax/parse/mod.rs19
-rw-r--r--src/libsyntax/show_span.rs39
-rw-r--r--src/libsyntax/test.rs573
6 files changed, 889 insertions, 0 deletions
diff --git a/src/libsyntax/ast_map/mod.rs b/src/libsyntax/ast_map/mod.rs
index ed0b8700bf3..9576cf26f58 100644
--- a/src/libsyntax/ast_map/mod.rs
+++ b/src/libsyntax/ast_map/mod.rs
@@ -15,6 +15,7 @@ use ast_util::PostExpansionMethod;
 use codemap::{DUMMY_SP, Span, Spanned};
 use fold::Folder;
 use parse::token;
+use parse::ParseSess;
 use print::pprust;
 use visit::{mod, Visitor};
 
@@ -250,6 +251,7 @@ pub struct Map<'ast> {
 }
 
 impl<'ast> Map<'ast> {
+impl Map {
     fn entry_count(&self) -> uint {
         self.map.borrow().len()
     }
diff --git a/src/libsyntax/config.rs b/src/libsyntax/config.rs
new file mode 100644
index 00000000000..cb96cd911b5
--- /dev/null
+++ b/src/libsyntax/config.rs
@@ -0,0 +1,253 @@
+// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use fold::Folder;
+use {ast, fold, attr};
+use codemap::Spanned;
+use ptr::P;
+
+/// A folder that strips out items that do not belong in the current
+/// configuration.
+struct Context<'a> {
+    in_cfg: |attrs: &[ast::Attribute]|: 'a -> bool,
+}
+
+// Support conditional compilation by transforming the AST, stripping out
+// any items that do not belong in the current configuration
+pub fn strip_unconfigured_items(krate: ast::Crate) -> ast::Crate {
+    let config = krate.config.clone();
+    strip_items(krate, |attrs| in_cfg(config.as_slice(), attrs))
+}
+
+impl<'a> fold::Folder for Context<'a> {
+    fn fold_mod(&mut self, module: ast::Mod) -> ast::Mod {
+        fold_mod(self, module)
+    }
+    fn fold_block(&mut self, block: P<ast::Block>) -> P<ast::Block> {
+        fold_block(self, block)
+    }
+    fn fold_foreign_mod(&mut self, foreign_mod: ast::ForeignMod) -> ast::ForeignMod {
+        fold_foreign_mod(self, foreign_mod)
+    }
+    fn fold_item_underscore(&mut self, item: ast::Item_) -> ast::Item_ {
+        fold_item_underscore(self, item)
+    }
+    fn fold_expr(&mut self, expr: P<ast::Expr>) -> P<ast::Expr> {
+        fold_expr(self, expr)
+    }
+    fn fold_mac(&mut self, mac: ast::Mac) -> ast::Mac {
+        fold::noop_fold_mac(mac, self)
+    }
+}
+
+pub fn strip_items(krate: ast::Crate,
+                   in_cfg: |attrs: &[ast::Attribute]| -> bool)
+                   -> ast::Crate {
+    let mut ctxt = Context {
+        in_cfg: in_cfg,
+    };
+    ctxt.fold_crate(krate)
+}
+
+fn filter_view_item(cx: &mut Context, view_item: ast::ViewItem) -> Option<ast::ViewItem> {
+    if view_item_in_cfg(cx, &view_item) {
+        Some(view_item)
+    } else {
+        None
+    }
+}
+
+fn fold_mod(cx: &mut Context, ast::Mod {inner, view_items, items}: ast::Mod) -> ast::Mod {
+    ast::Mod {
+        inner: inner,
+        view_items: view_items.into_iter().filter_map(|a| {
+            filter_view_item(cx, a).map(|x| cx.fold_view_item(x))
+        }).collect(),
+        items: items.into_iter().filter_map(|a| {
+            if item_in_cfg(cx, &*a) {
+                Some(cx.fold_item(a))
+            } else {
+                None
+            }
+        }).flat_map(|x| x.into_iter()).collect()
+    }
+}
+
+fn filter_foreign_item(cx: &mut Context, item: P<ast::ForeignItem>)
+                       -> Option<P<ast::ForeignItem>> {
+    if foreign_item_in_cfg(cx, &*item) {
+        Some(item)
+    } else {
+        None
+    }
+}
+
+fn fold_foreign_mod(cx: &mut Context, ast::ForeignMod {abi, view_items, items}: ast::ForeignMod)
+                    -> ast::ForeignMod {
+    ast::ForeignMod {
+        abi: abi,
+        view_items: view_items.into_iter().filter_map(|a| {
+            filter_view_item(cx, a).map(|x| cx.fold_view_item(x))
+        }).collect(),
+        items: items.into_iter()
+                    .filter_map(|a| filter_foreign_item(cx, a))
+                    .collect()
+    }
+}
+
+fn fold_item_underscore(cx: &mut Context, item: ast::Item_) -> ast::Item_ {
+    let item = match item {
+        ast::ItemImpl(a, b, c, impl_items) => {
+            let impl_items = impl_items.into_iter()
+                                       .filter(|ii| impl_item_in_cfg(cx, ii))
+                                       .collect();
+            ast::ItemImpl(a, b, c, impl_items)
+        }
+        ast::ItemTrait(a, b, c, methods) => {
+            let methods = methods.into_iter()
+                                 .filter(|m| trait_method_in_cfg(cx, m))
+                                 .collect();
+            ast::ItemTrait(a, b, c, methods)
+        }
+        ast::ItemStruct(def, generics) => {
+            ast::ItemStruct(fold_struct(cx, def), generics)
+        }
+        ast::ItemEnum(def, generics) => {
+            let mut variants = def.variants.into_iter().filter_map(|v| {
+                if !(cx.in_cfg)(v.node.attrs.as_slice()) {
+                    None
+                } else {
+                    Some(v.map(|Spanned {node: ast::Variant_ {id, name, attrs, kind,
+                                                              disr_expr, vis}, span}| {
+                        Spanned {
+                            node: ast::Variant_ {
+                                id: id,
+                                name: name,
+                                attrs: attrs,
+                                kind: match kind {
+                                    ast::TupleVariantKind(..) => kind,
+                                    ast::StructVariantKind(def) => {
+                                        ast::StructVariantKind(fold_struct(cx, def))
+                                    }
+                                },
+                                disr_expr: disr_expr,
+                                vis: vis
+                            },
+                            span: span
+                        }
+                    }))
+                }
+            });
+            ast::ItemEnum(ast::EnumDef {
+                variants: variants.collect(),
+            }, generics)
+        }
+        item => item,
+    };
+
+    fold::noop_fold_item_underscore(item, cx)
+}
+
+fn fold_struct(cx: &mut Context, def: P<ast::StructDef>) -> P<ast::StructDef> {
+    def.map(|ast::StructDef {fields, ctor_id, super_struct, is_virtual}| {
+        ast::StructDef {
+            fields: fields.into_iter().filter(|m| {
+                (cx.in_cfg)(m.node.attrs.as_slice())
+            }).collect(),
+            ctor_id: ctor_id,
+            super_struct: super_struct,
+            is_virtual: is_virtual,
+        }
+    })
+}
+
+fn retain_stmt(cx: &mut Context, stmt: &ast::Stmt) -> bool {
+    match stmt.node {
+        ast::StmtDecl(ref decl, _) => {
+            match decl.node {
+                ast::DeclItem(ref item) => {
+                    item_in_cfg(cx, &**item)
+                }
+                _ => true
+            }
+        }
+        _ => true
+    }
+}
+
+fn fold_block(cx: &mut Context, b: P<ast::Block>) -> P<ast::Block> {
+    b.map(|ast::Block {id, view_items, stmts, expr, rules, span}| {
+        let resulting_stmts: Vec<P<ast::Stmt>> =
+            stmts.into_iter().filter(|a| retain_stmt(cx, &**a)).collect();
+        let resulting_stmts = resulting_stmts.into_iter()
+            .flat_map(|stmt| cx.fold_stmt(stmt).into_iter())
+            .collect();
+        let filtered_view_items = view_items.into_iter().filter_map(|a| {
+            filter_view_item(cx, a).map(|x| cx.fold_view_item(x))
+        }).collect();
+        ast::Block {
+            id: id,
+            view_items: filtered_view_items,
+            stmts: resulting_stmts,
+            expr: expr.map(|x| cx.fold_expr(x)),
+            rules: rules,
+            span: span,
+        }
+    })
+}
+
+fn fold_expr(cx: &mut Context, expr: P<ast::Expr>) -> P<ast::Expr> {
+    expr.map(|ast::Expr {id, span, node}| {
+        fold::noop_fold_expr(ast::Expr {
+            id: id,
+            node: match node {
+                ast::ExprMatch(m, arms) => {
+                    ast::ExprMatch(m, arms.into_iter()
+                                        .filter(|a| (cx.in_cfg)(a.attrs.as_slice()))
+                                        .collect())
+                }
+                _ => node
+            },
+            span: span
+        }, cx)
+    })
+}
+
+fn item_in_cfg(cx: &mut Context, item: &ast::Item) -> bool {
+    return (cx.in_cfg)(item.attrs.as_slice());
+}
+
+fn foreign_item_in_cfg(cx: &mut Context, item: &ast::ForeignItem) -> bool {
+    return (cx.in_cfg)(item.attrs.as_slice());
+}
+
+fn view_item_in_cfg(cx: &mut Context, item: &ast::ViewItem) -> bool {
+    return (cx.in_cfg)(item.attrs.as_slice());
+}
+
+fn trait_method_in_cfg(cx: &mut Context, meth: &ast::TraitItem) -> bool {
+    match *meth {
+        ast::RequiredMethod(ref meth) => (cx.in_cfg)(meth.attrs.as_slice()),
+        ast::ProvidedMethod(ref meth) => (cx.in_cfg)(meth.attrs.as_slice())
+    }
+}
+
+fn impl_item_in_cfg(cx: &mut Context, impl_item: &ast::ImplItem) -> bool {
+    match *impl_item {
+        ast::MethodImplItem(ref meth) => (cx.in_cfg)(meth.attrs.as_slice()),
+    }
+}
+
+// Determine if an item should be translated in the current crate
+// configuration based on the item's attributes
+fn in_cfg(cfg: &[P<ast::MetaItem>], attrs: &[ast::Attribute]) -> bool {
+    attr::test_cfg(cfg, attrs.iter())
+}
+
diff --git a/src/libsyntax/lib.rs b/src/libsyntax/lib.rs
index 146b5a5b348..ff481dd7299 100644
--- a/src/libsyntax/lib.rs
+++ b/src/libsyntax/lib.rs
@@ -59,12 +59,15 @@ pub mod ast_map;
 pub mod ast_util;
 pub mod attr;
 pub mod codemap;
+pub mod config;
 pub mod crateid;
 pub mod diagnostic;
 pub mod fold;
 pub mod owned_slice;
 pub mod parse;
 pub mod ptr;
+pub mod show_span;
+pub mod test;
 pub mod visit;
 
 pub mod print {
diff --git a/src/libsyntax/parse/mod.rs b/src/libsyntax/parse/mod.rs
index e5b6359000b..506b607d02c 100644
--- a/src/libsyntax/parse/mod.rs
+++ b/src/libsyntax/parse/mod.rs
@@ -37,12 +37,14 @@ pub struct ParseSess {
     pub span_diagnostic: SpanHandler, // better be the same as the one in the reader!
     /// Used to determine and report recursive mod inclusions
     included_mod_stack: RefCell<Vec<Path>>,
+    pub node_id: Cell<ast::NodeId>,
 }
 
 pub fn new_parse_sess() -> ParseSess {
     ParseSess {
         span_diagnostic: mk_span_handler(default_handler(Auto, None), CodeMap::new()),
         included_mod_stack: RefCell::new(Vec::new()),
+        node_id: Cell::new(1),
     }
 }
 
@@ -50,6 +52,23 @@ pub fn new_parse_sess_special_handler(sh: SpanHandler) -> ParseSess {
     ParseSess {
         span_diagnostic: sh,
         included_mod_stack: RefCell::new(Vec::new()),
+        node_id: Cell::new(1),
+    }
+}
+
+impl ParseSess {
+    pub fn next_node_id(&self) -> ast::NodeId {
+        self.reserve_node_ids(1)
+    }
+    pub fn reserve_node_ids(&self, count: ast::NodeId) -> ast::NodeId {
+        let v = self.node_id.get();
+
+        match v.checked_add(&count) {
+            Some(next) => { self.node_id.set(next); }
+            None => fail!("Input too large, ran out of node ids!")
+        }
+
+        v
     }
 }
 
diff --git a/src/libsyntax/show_span.rs b/src/libsyntax/show_span.rs
new file mode 100644
index 00000000000..4036ab04aa9
--- /dev/null
+++ b/src/libsyntax/show_span.rs
@@ -0,0 +1,39 @@
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! Span debugger
+//!
+//! This module shows spans for all expressions in the crate
+//! to help with compiler debugging.
+
+use ast;
+use diagnostic;
+use visit;
+use visit::Visitor;
+
+struct ShowSpanVisitor<'a> {
+    span_diagnostic: &'a diagnostic::SpanHandler,
+}
+
+impl<'a> Visitor<()> for ShowSpanVisitor<'a> {
+    fn visit_expr(&mut self, e: &ast::Expr, _: ()) {
+        self.span_diagnostic.span_note(e.span, "expression");
+        visit::walk_expr(self, e, ());
+    }
+
+    fn visit_mac(&mut self, macro: &ast::Mac) {
+        visit::walk_mac(self, macro);
+    }
+}
+
+pub fn run(span_diagnostic: &diagnostic::SpanHandler, krate: &ast::Crate) {
+    let mut v = ShowSpanVisitor { span_diagnostic: span_diagnostic };
+    visit::walk_crate(&mut v, krate);
+}
diff --git a/src/libsyntax/test.rs b/src/libsyntax/test.rs
new file mode 100644
index 00000000000..f0e69712714
--- /dev/null
+++ b/src/libsyntax/test.rs
@@ -0,0 +1,573 @@
+// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Code that generates a test runner to run all the tests in a crate
+
+#![allow(dead_code)]
+#![allow(unused_imports)]
+
+use std::gc::{Gc, GC};
+use std::slice;
+use std::mem;
+use std::vec;
+use ast_util::*;
+use attr::AttrMetaMethods;
+use attr;
+use codemap::{DUMMY_SP, Span, ExpnInfo, NameAndSpan, MacroAttribute};
+use codemap;
+use diagnostic;
+use config;
+use ext::base::ExtCtxt;
+use ext::build::AstBuilder;
+use ext::expand::ExpansionConfig;
+use fold::{Folder, MoveMap};
+use fold;
+use owned_slice::OwnedSlice;
+use parse::token::InternedString;
+use parse::{token, ParseSess};
+use print::pprust;
+use {ast, ast_util};
+use ptr::P;
+use util::small_vector::SmallVector;
+
+struct Test {
+    span: Span,
+    path: Vec<ast::Ident> ,
+    bench: bool,
+    ignore: bool,
+    should_fail: bool
+}
+
+struct TestCtxt<'a> {
+    sess: &'a ParseSess,
+    span_diagnostic: &'a diagnostic::SpanHandler,
+    path: Vec<ast::Ident>,
+    ext_cx: ExtCtxt<'a>,
+    testfns: Vec<Test>,
+    reexport_test_harness_main: Option<InternedString>,
+    is_test_crate: bool,
+    config: ast::CrateConfig,
+
+    // top-level re-export submodule, filled out after folding is finished
+    toplevel_reexport: Option<ast::Ident>,
+}
+
+// Traverse the crate, collecting all the test functions, eliding any
+// existing main functions, and synthesizing a main test harness
+pub fn modify_for_testing(sess: &ParseSess,
+                          cfg: &ast::CrateConfig,
+                          krate: ast::Crate,
+                          span_diagnostic: &diagnostic::SpanHandler) -> ast::Crate {
+    // We generate the test harness when building in the 'test'
+    // configuration, either with the '--test' or '--cfg test'
+    // command line options.
+    let should_test = attr::contains_name(krate.config.as_slice(), "test");
+
+    // Check for #[reexport_test_harness_main = "some_name"] which
+    // creates a `use some_name = __test::main;`. This needs to be
+    // unconditional, so that the attribute is still marked as used in
+    // non-test builds.
+    let reexport_test_harness_main =
+        attr::first_attr_value_str_by_name(krate.attrs.as_slice(),
+                                           "reexport_test_harness_main");
+
+    if should_test {
+        generate_test_harness(sess, reexport_test_harness_main, krate, cfg, span_diagnostic)
+    } else {
+        strip_test_functions(krate)
+    }
+}
+
+struct TestHarnessGenerator<'a> {
+    cx: TestCtxt<'a>,
+    tests: Vec<ast::Ident>,
+
+    // submodule name, gensym'd identifier for re-exports
+    tested_submods: Vec<(ast::Ident, ast::Ident)>,
+}
+
+impl<'a> fold::Folder for TestHarnessGenerator<'a> {
+    fn fold_crate(&mut self, c: ast::Crate) -> ast::Crate {
+        let mut folded = fold::noop_fold_crate(c, self);
+
+        // Add a special __test module to the crate that will contain code
+        // generated for the test harness
+        let (mod_, reexport) = mk_test_module(&mut self.cx);
+        folded.module.items.push(mod_);
+        match reexport {
+            Some(re) => folded.module.view_items.push(re),
+            None => {}
+        }
+        folded
+    }
+
+    fn fold_item(&mut self, i: P<ast::Item>) -> SmallVector<P<ast::Item>> {
+        self.cx.path.push(i.ident);
+        debug!("current path: {}",
+               ast_util::path_name_i(self.cx.path.as_slice()));
+
+        if is_test_fn(&self.cx, &*i) || is_bench_fn(&self.cx, &*i) {
+            match i.node {
+                ast::ItemFn(_, ast::UnsafeFn, _, _, _) => {
+                    let diag = self.cx.span_diagnostic;
+                    diag.span_fatal(i.span,
+                                    "unsafe functions cannot be used for \
+                                     tests");
+                }
+                _ => {
+                    debug!("this is a test function");
+                    let test = Test {
+                        span: i.span,
+                        path: self.cx.path.clone(),
+                        bench: is_bench_fn(&self.cx, &*i),
+                        ignore: is_ignored(&self.cx, &*i),
+                        should_fail: should_fail(&*i)
+                    };
+                    self.cx.testfns.push(test);
+                    self.tests.push(i.ident);
+                    // debug!("have {} test/bench functions",
+                    //        cx.testfns.len());
+                }
+            }
+        }
+
+        // We don't want to recurse into anything other than mods, since
+        // mods or tests inside of functions will break things
+        let res = match i.node {
+            ast::ItemMod(..) => fold::noop_fold_item(i, self),
+            _ => SmallVector::one(i),
+        };
+        self.cx.path.pop();
+        res
+    }
+
+    fn fold_mod(&mut self, m: ast::Mod) -> ast::Mod {
+        let tests = mem::replace(&mut self.tests, Vec::new());
+        let tested_submods = mem::replace(&mut self.tested_submods, Vec::new());
+        let mut mod_folded = fold::noop_fold_mod(m, self);
+        let tests = mem::replace(&mut self.tests, tests);
+        let tested_submods = mem::replace(&mut self.tested_submods, tested_submods);
+
+        // Remove any #[main] from the AST so it doesn't clash with
+        // the one we're going to add. Only if compiling an executable.
+
+        mod_folded.items = mem::replace(&mut mod_folded.items, vec![]).move_map(|item| {
+            item.map(|ast::Item {id, ident, attrs, node, vis, span}| {
+                ast::Item {
+                    id: id,
+                    ident: ident,
+                    attrs: attrs.into_iter().filter_map(|attr| {
+                        if !attr.check_name("main") {
+                            Some(attr)
+                        } else {
+                            None
+                        }
+                    }).collect(),
+                    node: node,
+                    vis: vis,
+                    span: span
+                }
+            })
+        });
+
+        if !tests.is_empty() || !tested_submods.is_empty() {
+            let (it, sym) = mk_reexport_mod(&mut self.cx, tests, tested_submods);
+            mod_folded.items.push(it);
+
+            if !self.cx.path.is_empty() {
+                self.tested_submods.push((self.cx.path[self.cx.path.len()-1], sym));
+            } else {
+                debug!("pushing nothing, sym: {}", sym);
+                self.cx.toplevel_reexport = Some(sym);
+            }
+        }
+
+        mod_folded
+    }
+}
+
+fn mk_reexport_mod(cx: &mut TestCtxt, tests: Vec<ast::Ident>,
+                   tested_submods: Vec<(ast::Ident, ast::Ident)>) -> (P<ast::Item>, ast::Ident) {
+    let mut view_items = Vec::new();
+    let super_ = token::str_to_ident("super");
+
+    view_items.extend(tests.into_iter().map(|r| {
+        cx.ext_cx.view_use_simple(DUMMY_SP, ast::Public,
+                                  cx.ext_cx.path(DUMMY_SP, vec![super_, r]))
+    }));
+    view_items.extend(tested_submods.into_iter().map(|(r, sym)| {
+        let path = cx.ext_cx.path(DUMMY_SP, vec![super_, r, sym]);
+        cx.ext_cx.view_use_simple_(DUMMY_SP, ast::Public, r, path)
+    }));
+
+    let reexport_mod = ast::Mod {
+        inner: DUMMY_SP,
+        view_items: view_items,
+        items: Vec::new(),
+    };
+
+    let sym = token::gensym_ident("__test_reexports");
+    let it = P(ast::Item {
+        ident: sym.clone(),
+        attrs: Vec::new(),
+        id: ast::DUMMY_NODE_ID,
+        node: ast::ItemMod(reexport_mod),
+        vis: ast::Public,
+        span: DUMMY_SP,
+    });
+
+    (it, sym)
+}
+
+fn generate_test_harness(sess: &ParseSess,
+                         reexport_test_harness_main: Option<InternedString>,
+                         krate: ast::Crate,
+                         cfg: &ast::CrateConfig,
+                         sd: &diagnostic::SpanHandler) -> ast::Crate {
+    let mut cx: TestCtxt = TestCtxt {
+        sess: sess,
+        span_diagnostic: sd,
+        ext_cx: ExtCtxt::new(sess, cfg.clone(),
+                             ExpansionConfig {
+                                 deriving_hash_type_parameter: false,
+                                 crate_name: "test".to_string(),
+                             }),
+        path: Vec::new(),
+        testfns: Vec::new(),
+        reexport_test_harness_main: reexport_test_harness_main,
+        is_test_crate: is_test_crate(&krate),
+        config: krate.config.clone(),
+        toplevel_reexport: None,
+    };
+
+    cx.ext_cx.bt_push(ExpnInfo {
+        call_site: DUMMY_SP,
+        callee: NameAndSpan {
+            name: "test".to_string(),
+            format: MacroAttribute,
+            span: None
+        }
+    });
+
+    let mut fold = TestHarnessGenerator {
+        cx: cx,
+        tests: Vec::new(),
+        tested_submods: Vec::new(),
+    };
+    let res = fold.fold_crate(krate);
+    fold.cx.ext_cx.bt_pop();
+    return res;
+}
+
+fn strip_test_functions(krate: ast::Crate) -> ast::Crate {
+    // When not compiling with --test we should not compile the
+    // #[test] functions
+    config::strip_items(krate, |attrs| {
+        !attr::contains_name(attrs.as_slice(), "test") &&
+        !attr::contains_name(attrs.as_slice(), "bench")
+    })
+}
+
+fn is_test_fn(cx: &TestCtxt, i: &ast::Item) -> bool {
+    let has_test_attr = attr::contains_name(i.attrs.as_slice(), "test");
+
+    fn has_test_signature(i: &ast::Item) -> bool {
+        match &i.node {
+          &ast::ItemFn(ref decl, _, _, ref generics, _) => {
+            let no_output = match decl.output.node {
+                ast::TyNil => true,
+                _ => false
+            };
+            decl.inputs.is_empty()
+                && no_output
+                && !generics.is_parameterized()
+          }
+          _ => false
+        }
+    }
+
+    if has_test_attr && !has_test_signature(i) {
+        let diag = cx.span_diagnostic;
+        diag.span_err(
+            i.span,
+            "functions used as tests must have signature fn() -> ()."
+        );
+    }
+
+    return has_test_attr && has_test_signature(i);
+}
+
+fn is_bench_fn(cx: &TestCtxt, i: &ast::Item) -> bool {
+    let has_bench_attr = attr::contains_name(i.attrs.as_slice(), "bench");
+
+    fn has_test_signature(i: &ast::Item) -> bool {
+        match i.node {
+            ast::ItemFn(ref decl, _, _, ref generics, _) => {
+                let input_cnt = decl.inputs.len();
+                let no_output = match decl.output.node {
+                    ast::TyNil => true,
+                    _ => false
+                };
+                let tparm_cnt = generics.ty_params.len();
+                // NB: inadequate check, but we're running
+                // well before resolve, can't get too deep.
+                input_cnt == 1u
+                    && no_output && tparm_cnt == 0u
+            }
+          _ => false
+        }
+    }
+
+    if has_bench_attr && !has_test_signature(i) {
+        let diag = cx.span_diagnostic;
+        diag.span_err(i.span, "functions used as benches must have signature \
+                      `fn(&mut Bencher) -> ()`");
+    }
+
+    return has_bench_attr && has_test_signature(i);
+}
+
+fn is_ignored(cx: &TestCtxt, i: &ast::Item) -> bool {
+    i.attrs.iter().any(|attr| {
+        // check ignore(cfg(foo, bar))
+        attr.check_name("ignore") && match attr.meta_item_list() {
+            Some(ref cfgs) => {
+                attr::test_cfg(cx.config.as_slice(), cfgs.iter())
+            }
+            None => true
+        }
+    })
+}
+
+fn should_fail(i: &ast::Item) -> bool {
+    attr::contains_name(i.attrs.as_slice(), "should_fail")
+}
+
+/*
+
+We're going to be building a module that looks more or less like:
+
+mod __test {
+  extern crate test (name = "test", vers = "...");
+  fn main() {
+    test::test_main_static(::os::args().as_slice(), tests)
+  }
+
+  static tests : &'static [test::TestDescAndFn] = &[
+    ... the list of tests in the crate ...
+  ];
+}
+
+*/
+
+fn mk_std(cx: &TestCtxt) -> ast::ViewItem {
+    let id_test = token::str_to_ident("test");
+    let (vi, vis) = if cx.is_test_crate {
+        (ast::ViewItemUse(
+            P(nospan(ast::ViewPathSimple(id_test,
+                                         path_node(vec!(id_test)),
+                                         ast::DUMMY_NODE_ID)))),
+         ast::Public)
+    } else {
+        (ast::ViewItemExternCrate(id_test, None, ast::DUMMY_NODE_ID),
+         ast::Inherited)
+    };
+    ast::ViewItem {
+        node: vi,
+        attrs: Vec::new(),
+        vis: vis,
+        span: DUMMY_SP
+    }
+}
+
+fn mk_test_module(cx: &mut TestCtxt) -> (P<ast::Item>, Option<ast::ViewItem>) {
+    // Link to test crate
+    let view_items = vec!(mk_std(cx));
+
+    // A constant vector of test descriptors.
+    let tests = mk_tests(cx);
+
+    // The synthesized main function which will call the console test runner
+    // with our list of tests
+    let mainfn = (quote_item!(&mut cx.ext_cx,
+        pub fn main() {
+            #![main]
+            use std::slice::Slice;
+            test::test_main_static(::std::os::args().as_slice(), TESTS);
+        }
+    )).unwrap();
+
+    let testmod = ast::Mod {
+        inner: DUMMY_SP,
+        view_items: view_items,
+        items: vec!(mainfn, tests),
+    };
+    let item_ = ast::ItemMod(testmod);
+
+    let mod_ident = token::gensym_ident("__test");
+    let item = ast::Item {
+        ident: mod_ident,
+        attrs: Vec::new(),
+        id: ast::DUMMY_NODE_ID,
+        node: item_,
+        vis: ast::Public,
+        span: DUMMY_SP,
+    };
+    let reexport = cx.reexport_test_harness_main.as_ref().map(|s| {
+        // building `use <ident> = __test::main`
+        let reexport_ident = token::str_to_ident(s.get());
+
+        let use_path =
+            nospan(ast::ViewPathSimple(reexport_ident,
+                                       path_node(vec![mod_ident, token::str_to_ident("main")]),
+                                       ast::DUMMY_NODE_ID));
+
+        ast::ViewItem {
+            node: ast::ViewItemUse(P(use_path)),
+            attrs: vec![],
+            vis: ast::Inherited,
+            span: DUMMY_SP
+        }
+    });
+
+    debug!("Synthetic test module:\n{}\n", pprust::item_to_string(&item));
+
+    (P(item), reexport)
+}
+
+fn nospan<T>(t: T) -> codemap::Spanned<T> {
+    codemap::Spanned { node: t, span: DUMMY_SP }
+}
+
+fn path_node(ids: Vec<ast::Ident> ) -> ast::Path {
+    ast::Path {
+        span: DUMMY_SP,
+        global: false,
+        segments: ids.into_iter().map(|identifier| ast::PathSegment {
+            identifier: identifier,
+            lifetimes: Vec::new(),
+            types: OwnedSlice::empty(),
+        }).collect()
+    }
+}
+
+fn mk_tests(cx: &TestCtxt) -> P<ast::Item> {
+    // The vector of test_descs for this crate
+    let test_descs = mk_test_descs(cx);
+
+    // FIXME #15962: should be using quote_item, but that stringifies
+    // __test_reexports, causing it to be reinterned, losing the
+    // gensym information.
+    let sp = DUMMY_SP;
+    let ecx = &cx.ext_cx;
+    let struct_type = ecx.ty_path(ecx.path(sp, vec![ecx.ident_of("self"),
+                                                    ecx.ident_of("test"),
+                                                    ecx.ident_of("TestDescAndFn")]),
+                                  None);
+    let static_lt = ecx.lifetime(sp, token::special_idents::static_lifetime.name);
+    // &'static [self::test::TestDescAndFn]
+    let static_type = ecx.ty_rptr(sp,
+                                  ecx.ty(sp, ast::TyVec(struct_type)),
+                                  Some(static_lt),
+                                  ast::MutImmutable);
+    // static TESTS: $static_type = &[...];
+    ecx.item_static(sp,
+                    ecx.ident_of("TESTS"),
+                    static_type,
+                    ast::MutImmutable,
+                    test_descs)
+}
+
+fn is_test_crate(krate: &ast::Crate) -> bool {
+    match attr::find_crate_name(krate.attrs.as_slice()) {
+        Some(ref s) if "test" == s.get().as_slice() => true,
+        _ => false
+    }
+}
+
+fn mk_test_descs(cx: &TestCtxt) -> P<ast::Expr> {
+    debug!("building test vector from {} tests", cx.testfns.len());
+
+    P(ast::Expr {
+        id: ast::DUMMY_NODE_ID,
+        node: ast::ExprAddrOf(ast::MutImmutable,
+            P(ast::Expr {
+                id: ast::DUMMY_NODE_ID,
+                node: ast::ExprVec(cx.testfns.iter().map(|test| {
+                    mk_test_desc_and_fn_rec(cx, test)
+                }).collect()),
+                span: DUMMY_SP,
+            })),
+        span: DUMMY_SP,
+    })
+}
+
+fn mk_test_desc_and_fn_rec(cx: &TestCtxt, test: &Test) -> P<ast::Expr> {
+    // FIXME #15962: should be using quote_expr, but that stringifies
+    // __test_reexports, causing it to be reinterned, losing the
+    // gensym information.
+
+    let span = test.span;
+    let path = test.path.clone();
+    let ecx = &cx.ext_cx;
+    let self_id = ecx.ident_of("self");
+    let test_id = ecx.ident_of("test");
+
+    // creates self::test::$name
+    let test_path = |name| {
+        ecx.path(span, vec![self_id, test_id, ecx.ident_of(name)])
+    };
+    // creates $name: $expr
+    let field = |name, expr| ecx.field_imm(span, ecx.ident_of(name), expr);
+
+    debug!("encoding {}", ast_util::path_name_i(path.as_slice()));
+
+    // path to the #[test] function: "foo::bar::baz"
+    let path_string = ast_util::path_name_i(path.as_slice());
+    let name_expr = ecx.expr_str(span, token::intern_and_get_ident(path_string.as_slice()));
+
+    // self::test::StaticTestName($name_expr)
+    let name_expr = ecx.expr_call(span,
+                                  ecx.expr_path(test_path("StaticTestName")),
+                                  vec![name_expr]);
+
+    let ignore_expr = ecx.expr_bool(span, test.ignore);
+    let fail_expr = ecx.expr_bool(span, test.should_fail);
+
+    // self::test::TestDesc { ... }
+    let desc_expr = ecx.expr_struct(
+        span,
+        test_path("TestDesc"),
+        vec![field("name", name_expr),
+             field("ignore", ignore_expr),
+             field("should_fail", fail_expr)]);
+
+
+    let mut visible_path = match cx.toplevel_reexport {
+        Some(id) => vec![id],
+        None => {
+            let diag = cx.span_diagnostic;
+            diag.handler.bug("expected to find top-level re-export name, but found None");
+        }
+    };
+    visible_path.extend(path.into_iter());
+
+    let fn_expr = ecx.expr_path(ecx.path_global(span, visible_path));
+
+    let variant_name = if test.bench { "StaticBenchFn" } else { "StaticTestFn" };
+    // self::test::$variant_name($fn_expr)
+    let testfn_expr = ecx.expr_call(span, ecx.expr_path(test_path(variant_name)), vec![fn_expr]);
+
+    // self::test::TestDescAndFn { ... }
+    ecx.expr_struct(span,
+                    test_path("TestDescAndFn"),
+                    vec![field("desc", desc_expr),
+                         field("testfn", testfn_expr)])
+}