librustc: Change fold to use traits instead of `@fn`.

This commit is contained in:
Patrick Walton 2013-08-29 12:10:02 -07:00
parent 9705399504
commit 3e5de06135
12 changed files with 1335 additions and 1258 deletions

View File

@ -11,9 +11,22 @@
use driver::session::Session;
use syntax::ast;
use syntax::ast_util;
use syntax::fold::ast_fold;
struct NodeIdAssigner {
sess: Session,
}
impl ast_fold for NodeIdAssigner {
fn new_id(&self, old_id: ast::NodeId) -> ast::NodeId {
assert_eq!(old_id, ast::DUMMY_NODE_ID);
self.sess.next_node_id()
}
}
pub fn assign_node_ids(sess: Session, crate: @ast::Crate) -> @ast::Crate {
let fold = ast_util::node_id_assigner(|| sess.next_node_id());
let fold = NodeIdAssigner {
sess: sess,
};
@fold.fold_crate(crate)
}

View File

@ -10,6 +10,7 @@
use std::option;
use syntax::fold::ast_fold;
use syntax::{ast, fold, attr};
type in_cfg_pred = @fn(attrs: &[ast::Attribute]) -> bool;
@ -26,21 +27,34 @@ pub fn strip_unconfigured_items(crate: @ast::Crate) -> @ast::Crate {
}
}
pub fn strip_items(crate: &ast::Crate, in_cfg: in_cfg_pred)
-> @ast::Crate {
struct ItemRemover {
ctxt: @Context,
}
let ctxt = @Context { in_cfg: in_cfg };
impl fold::ast_fold for ItemRemover {
fn fold_mod(&self, module: &ast::_mod) -> ast::_mod {
fold_mod(self.ctxt, module, self)
}
fn fold_block(&self, block: &ast::Block) -> ast::Block {
fold_block(self.ctxt, block, self)
}
fn fold_foreign_mod(&self, foreign_module: &ast::foreign_mod)
-> ast::foreign_mod {
fold_foreign_mod(self.ctxt, foreign_module, self)
}
fn fold_item_underscore(&self, item: &ast::item_) -> ast::item_ {
fold_item_underscore(self.ctxt, item, self)
}
}
let precursor = @fold::AstFoldFns {
fold_mod: |a,b| fold_mod(ctxt, a, b),
fold_block: |a,b| fold_block(ctxt, a, b),
fold_foreign_mod: |a,b| fold_foreign_mod(ctxt, a, b),
fold_item_underscore: |a,b| fold_item_underscore(ctxt, a, b),
.. *fold::default_ast_fold()
pub fn strip_items(crate: &ast::Crate, in_cfg: in_cfg_pred) -> @ast::Crate {
let ctxt = @Context {
in_cfg: in_cfg,
};
let fold = fold::make_fold(precursor);
@fold.fold_crate(crate)
let precursor = ItemRemover {
ctxt: ctxt,
};
@precursor.fold_crate(crate)
}
fn filter_item(cx: @Context, item: @ast::item) ->
@ -56,7 +70,7 @@ fn filter_view_item<'r>(cx: @Context, view_item: &'r ast::view_item)-> Option<&'
}
}
fn fold_mod(cx: @Context, m: &ast::_mod, fld: @fold::ast_fold) -> ast::_mod {
fn fold_mod(cx: @Context, m: &ast::_mod, fld: &ItemRemover) -> ast::_mod {
let filtered_items = do m.items.iter().filter_map |a| {
filter_item(cx, *a).and_then(|x| fld.fold_item(x))
}.collect();
@ -78,12 +92,12 @@ fn filter_foreign_item(cx: @Context, item: @ast::foreign_item) ->
} else { option::None }
}
fn fold_foreign_mod(
cx: @Context,
nm: &ast::foreign_mod,
fld: @fold::ast_fold
) -> ast::foreign_mod {
let filtered_items = nm.items.iter().filter_map(|a| filter_foreign_item(cx, *a)).collect();
fn fold_foreign_mod(cx: @Context, nm: &ast::foreign_mod, fld: &ItemRemover)
-> ast::foreign_mod {
let filtered_items = nm.items
.iter()
.filter_map(|a| filter_foreign_item(cx, *a))
.collect();
let filtered_view_items = do nm.view_items.iter().filter_map |a| {
do filter_view_item(cx, a).map_move |x| {
fld.fold_view_item(x)
@ -97,8 +111,8 @@ fn fold_foreign_mod(
}
}
fn fold_item_underscore(cx: @Context, item: &ast::item_,
fld: @fold::ast_fold) -> ast::item_ {
fn fold_item_underscore(cx: @Context, item: &ast::item_, fld: &ItemRemover)
-> ast::item_ {
let item = match *item {
ast::item_impl(ref a, ref b, ref c, ref methods) => {
let methods = methods.iter().filter(|m| method_in_cfg(cx, **m))
@ -133,11 +147,7 @@ fn filter_stmt(cx: @Context, stmt: @ast::Stmt) ->
}
}
fn fold_block(
cx: @Context,
b: &ast::Block,
fld: @fold::ast_fold
) -> ast::Block {
fn fold_block(cx: @Context, b: &ast::Block, fld: &ItemRemover) -> ast::Block {
let resulting_stmts = do b.stmts.iter().filter_map |a| {
filter_stmt(cx, *a).and_then(|stmt| fld.fold_stmt(stmt))
}.collect();

View File

@ -16,6 +16,7 @@ use syntax::ast;
use syntax::attr;
use syntax::codemap::dummy_sp;
use syntax::codemap;
use syntax::fold::ast_fold;
use syntax::fold;
use syntax::opt_vec;
@ -38,91 +39,103 @@ fn no_prelude(attrs: &[ast::Attribute]) -> bool {
attr::contains_name(attrs, "no_implicit_prelude")
}
fn inject_libstd_ref(sess: Session, crate: &ast::Crate) -> @ast::Crate {
fn spanned<T>(x: T) -> codemap::Spanned<T> {
codemap::Spanned { node: x, span: dummy_sp() }
fn spanned<T>(x: T) -> codemap::Spanned<T> {
codemap::Spanned {
node: x,
span: dummy_sp(),
}
}
struct StandardLibraryInjector {
sess: Session,
}
impl fold::ast_fold for StandardLibraryInjector {
fn fold_crate(&self, crate: &ast::Crate) -> ast::Crate {
let version = STD_VERSION.to_managed();
let vi1 = ast::view_item {
node: ast::view_item_extern_mod(self.sess.ident_of("std"),
None,
~[],
ast::DUMMY_NODE_ID),
attrs: ~[
attr::mk_attr(attr::mk_name_value_item_str(@"vers", version))
],
vis: ast::private,
span: dummy_sp()
};
let vis = vec::append(~[vi1], crate.module.view_items);
let mut new_module = ast::_mod {
view_items: vis,
..crate.module.clone()
};
if !no_prelude(crate.attrs) {
// only add `use std::prelude::*;` if there wasn't a
// `#[no_implicit_prelude];` at the crate level.
new_module = self.fold_mod(&new_module);
}
// FIXME #2543: Bad copy.
ast::Crate {
module: new_module,
..(*crate).clone()
}
}
let precursor = @fold::AstFoldFns {
fold_crate: |crate, fld| {
let n1 = ast::DUMMY_NODE_ID;
let vi1 = ast::view_item {
node: ast::view_item_extern_mod(
sess.ident_of("std"), None, ~[], n1),
attrs: ~[
attr::mk_attr(
attr::mk_name_value_item_str(@"vers", STD_VERSION.to_managed()))
],
vis: ast::private,
span: dummy_sp()
};
fn fold_item(&self, item: @ast::item) -> Option<@ast::item> {
if !no_prelude(item.attrs) {
// only recur if there wasn't `#[no_implicit_prelude];`
// on this item, i.e. this means that the prelude is not
// implicitly imported though the whole subtree
fold::noop_fold_item(item, self)
} else {
Some(item)
}
}
let vis = vec::append(~[vi1], crate.module.view_items);
let mut new_module = ast::_mod {
view_items: vis,
..crate.module.clone()
};
fn fold_mod(&self, module: &ast::_mod) -> ast::_mod {
let prelude_path = ast::Path {
span: dummy_sp(),
global: false,
segments: ~[
ast::PathSegment {
identifier: self.sess.ident_of("std"),
lifetime: None,
types: opt_vec::Empty,
},
ast::PathSegment {
identifier: self.sess.ident_of("prelude"),
lifetime: None,
types: opt_vec::Empty,
},
],
};
if !no_prelude(crate.attrs) {
// only add `use std::prelude::*;` if there wasn't a
// `#[no_implicit_prelude];` at the crate level.
new_module = fld.fold_mod(&new_module);
}
let vp = @spanned(ast::view_path_glob(prelude_path,
ast::DUMMY_NODE_ID));
let vi2 = ast::view_item {
node: ast::view_item_use(~[vp]),
attrs: ~[],
vis: ast::private,
span: dummy_sp(),
};
// FIXME #2543: Bad copy.
ast::Crate {
module: new_module,
..(*crate).clone()
}
},
fold_item: |item, fld| {
if !no_prelude(item.attrs) {
// only recur if there wasn't `#[no_implicit_prelude];`
// on this item, i.e. this means that the prelude is not
// implicitly imported though the whole subtree
fold::noop_fold_item(item, fld)
} else {
Some(item)
}
},
fold_mod: |module, fld| {
let n2 = ast::DUMMY_NODE_ID;
let vis = vec::append(~[vi2], module.view_items);
let prelude_path = ast::Path {
span: dummy_sp(),
global: false,
segments: ~[
ast::PathSegment {
identifier: sess.ident_of("std"),
lifetime: None,
types: opt_vec::Empty,
},
ast::PathSegment {
identifier: sess.ident_of("prelude"),
lifetime: None,
types: opt_vec::Empty,
},
],
};
// FIXME #2543: Bad copy.
let new_module = ast::_mod {
view_items: vis,
..(*module).clone()
};
fold::noop_fold_mod(&new_module, self)
}
}
let vp = @spanned(ast::view_path_glob(prelude_path, n2));
let vi2 = ast::view_item { node: ast::view_item_use(~[vp]),
attrs: ~[],
vis: ast::private,
span: dummy_sp() };
let vis = vec::append(~[vi2], module.view_items);
// FIXME #2543: Bad copy.
let new_module = ast::_mod {
view_items: vis,
..(*module).clone()
};
fold::noop_fold_mod(&new_module, fld)
},
..*fold::default_ast_fold()
fn inject_libstd_ref(sess: Session, crate: &ast::Crate) -> @ast::Crate {
let fold = StandardLibraryInjector {
sess: sess,
};
let fold = fold::make_fold(precursor);
@fold.fold_crate(crate)
}

View File

@ -21,6 +21,7 @@ use syntax::attr;
use syntax::codemap::{dummy_sp, Span, ExpnInfo, NameAndSpan};
use syntax::codemap;
use syntax::ext::base::ExtCtxt;
use syntax::fold::ast_fold;
use syntax::fold;
use syntax::opt_vec;
use syntax::print::pprust;
@ -61,9 +62,89 @@ pub fn modify_for_testing(sess: session::Session,
}
}
fn generate_test_harness(sess: session::Session,
crate: @ast::Crate)
-> @ast::Crate {
struct TestHarnessGenerator {
cx: @mut TestCtxt,
}
impl fold::ast_fold for TestHarnessGenerator {
fn fold_crate(&self, c: &ast::Crate) -> ast::Crate {
let folded = fold::noop_fold_crate(c, self);
// Add a special __test module to the crate that will contain code
// generated for the test harness
ast::Crate {
module: add_test_module(self.cx, &folded.module),
.. folded
}
}
fn fold_item(&self, i: @ast::item) -> Option<@ast::item> {
self.cx.path.push(i.ident);
debug!("current path: %s",
ast_util::path_name_i(self.cx.path.clone()));
if is_test_fn(self.cx, i) || is_bench_fn(i) {
match i.node {
ast::item_fn(_, purity, _, _, _)
if purity == ast::unsafe_fn => {
let sess = self.cx.sess;
sess.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(i),
ignore: is_ignored(self.cx, i),
should_fail: should_fail(i)
};
self.cx.testfns.push(test);
// debug!("have %u test/bench functions",
// cx.testfns.len());
}
}
}
let res = fold::noop_fold_item(i, self);
self.cx.path.pop();
return res;
}
fn fold_mod(&self, m: &ast::_mod) -> ast::_mod {
// Remove any #[main] from the AST so it doesn't clash with
// the one we're going to add. Only if compiling an executable.
fn nomain(cx: @mut TestCtxt, item: @ast::item) -> @ast::item {
if !*cx.sess.building_library {
@ast::item {
attrs: do item.attrs.iter().filter_map |attr| {
if "main" != attr.name() {
Some(*attr)
} else {
None
}
}.collect(),
.. (*item).clone()
}
} else {
item
}
}
let mod_nomain = ast::_mod {
view_items: m.view_items.clone(),
items: m.items.iter().map(|i| nomain(self.cx, *i)).collect(),
};
fold::noop_fold_mod(&mod_nomain, self)
}
}
fn generate_test_harness(sess: session::Session, crate: @ast::Crate)
-> @ast::Crate {
let cx: @mut TestCtxt = @mut TestCtxt {
sess: sess,
crate: crate,
@ -81,12 +162,9 @@ fn generate_test_harness(sess: session::Session,
}
});
let precursor = @fold::AstFoldFns {
fold_crate: |a,b| fold_crate(cx, a, b),
fold_item: |a,b| fold_item(cx, a, b),
fold_mod: |a,b| fold_mod(cx, a, b),.. *fold::default_ast_fold()};
let fold = fold::make_fold(precursor);
let fold = TestHarnessGenerator {
cx: cx
};
let res = @fold.fold_crate(&*crate);
ext_cx.bt_pop();
return res;
@ -101,85 +179,6 @@ fn strip_test_functions(crate: &ast::Crate) -> @ast::Crate {
}
}
fn fold_mod(cx: @mut TestCtxt,
m: &ast::_mod,
fld: @fold::ast_fold)
-> ast::_mod {
// Remove any #[main] from the AST so it doesn't clash with
// the one we're going to add. Only if compiling an executable.
fn nomain(cx: @mut TestCtxt, item: @ast::item) -> @ast::item {
if !*cx.sess.building_library {
@ast::item {
attrs: do item.attrs.iter().filter_map |attr| {
if "main" != attr.name() {
Some(*attr)
} else {
None
}
}.collect(),
.. (*item).clone()
}
} else {
item
}
}
let mod_nomain = ast::_mod {
view_items: m.view_items.clone(),
items: m.items.iter().map(|i| nomain(cx, *i)).collect(),
};
fold::noop_fold_mod(&mod_nomain, fld)
}
fn fold_crate(cx: @mut TestCtxt, c: &ast::Crate, fld: @fold::ast_fold)
-> ast::Crate {
let folded = fold::noop_fold_crate(c, fld);
// Add a special __test module to the crate that will contain code
// generated for the test harness
ast::Crate {
module: add_test_module(cx, &folded.module),
.. folded
}
}
fn fold_item(cx: @mut TestCtxt, i: @ast::item, fld: @fold::ast_fold)
-> Option<@ast::item> {
cx.path.push(i.ident);
debug!("current path: %s",
ast_util::path_name_i(cx.path.clone()));
if is_test_fn(cx, i) || is_bench_fn(i) {
match i.node {
ast::item_fn(_, purity, _, _, _) if purity == ast::unsafe_fn => {
let sess = cx.sess;
sess.span_fatal(
i.span,
"unsafe functions cannot be used for tests");
}
_ => {
debug!("this is a test function");
let test = Test {
span: i.span,
path: cx.path.clone(),
bench: is_bench_fn(i),
ignore: is_ignored(cx, i),
should_fail: should_fail(i)
};
cx.testfns.push(test);
// debug!("have %u test/bench functions", cx.testfns.len());
}
}
}
let res = fold::noop_fold_item(i, fld);
cx.path.pop();
return res;
}
fn is_test_fn(cx: @mut TestCtxt, i: @ast::item) -> bool {
let has_test_attr = attr::contains_name(i.attrs, "test");

View File

@ -287,26 +287,24 @@ fn encode_ast(ebml_w: &mut writer::Encoder, item: ast::inlined_item) {
ebml_w.end_tag();
}
// Produces a simplified copy of the AST which does not include things
// that we do not need to or do not want to export. For example, we
// do not include any nested items: if these nested items are to be
// inlined, their AST will be exported separately (this only makes
// sense because, in Rust, nested items are independent except for
// their visibility).
//
// As it happens, trans relies on the fact that we do not export
// nested items, as otherwise it would get confused when translating
// inlined items.
fn simplify_ast(ii: &ast::inlined_item) -> ast::inlined_item {
fn drop_nested_items(blk: &ast::Block, fld: @fold::ast_fold) -> ast::Block {
struct NestedItemsDropper {
contents: (),
}
impl fold::ast_fold for NestedItemsDropper {
fn fold_block(&self, blk: &ast::Block) -> ast::Block {
let stmts_sans_items = do blk.stmts.iter().filter_map |stmt| {
match stmt.node {
ast::StmtExpr(_, _) | ast::StmtSemi(_, _) |
ast::StmtDecl(@codemap::Spanned { node: ast::DeclLocal(_), span: _}, _)
=> Some(*stmt),
ast::StmtDecl(@codemap::Spanned { node: ast::DeclItem(_), span: _}, _)
=> None,
ast::StmtMac(*) => fail!("unexpanded macro in astencode")
ast::StmtExpr(_, _) | ast::StmtSemi(_, _) |
ast::StmtDecl(@codemap::Spanned {
node: ast::DeclLocal(_),
span: _
}, _) => Some(*stmt),
ast::StmtDecl(@codemap::Spanned {
node: ast::DeclItem(_),
span: _
}, _) => None,
ast::StmtMac(*) => fail!("unexpanded macro in astencode")
}
}.collect();
let blk_sans_items = ast::Block {
@ -318,13 +316,24 @@ fn simplify_ast(ii: &ast::inlined_item) -> ast::inlined_item {
rules: blk.rules,
span: blk.span,
};
fold::noop_fold_block(&blk_sans_items, fld)
fold::noop_fold_block(&blk_sans_items, self)
}
}
let fld = fold::make_fold(@fold::AstFoldFns {
fold_block: drop_nested_items,
.. *fold::default_ast_fold()
});
// Produces a simplified copy of the AST which does not include things
// that we do not need to or do not want to export. For example, we
// do not include any nested items: if these nested items are to be
// inlined, their AST will be exported separately (this only makes
// sense because, in Rust, nested items are independent except for
// their visibility).
//
// As it happens, trans relies on the fact that we do not export
// nested items, as otherwise it would get confused when translating
// inlined items.
fn simplify_ast(ii: &ast::inlined_item) -> ast::inlined_item {
let fld = NestedItemsDropper {
contents: (),
};
match *ii {
//hack: we're not dropping items
@ -341,14 +350,24 @@ fn decode_ast(par_doc: ebml::Doc) -> ast::inlined_item {
Decodable::decode(&mut d)
}
struct AstRenumberer {
xcx: @ExtendedDecodeContext,
}
impl fold::ast_fold for AstRenumberer {
fn new_id(&self, id: ast::NodeId) -> ast::NodeId {
self.xcx.tr_id(id)
}
fn new_span(&self, span: Span) -> Span {
self.xcx.tr_span(span)
}
}
fn renumber_ast(xcx: @ExtendedDecodeContext, ii: ast::inlined_item)
-> ast::inlined_item {
let fld = fold::make_fold(@fold::AstFoldFns{
new_id: |a| xcx.tr_id(a),
new_span: |a| xcx.tr_span(a),
.. *fold::default_ast_fold()
});
let fld = AstRenumberer {
xcx: xcx,
};
match ii {
ast::ii_item(i) => ast::ii_item(fld.fold_item(i).unwrap()),
ast::ii_method(d, is_provided, m) =>

View File

@ -18,6 +18,8 @@ use syntax::codemap::{dummy_sp, Spanned};
use syntax::ext::base::ExtCtxt;
use syntax::{ast, attr, codemap, diagnostic, fold};
use syntax::attr::AttrMetaMethods;
use syntax::fold::ast_fold;
use rustc::back::link::output_type_exe;
use rustc::back::link;
use rustc::driver::session::{lib_crate, bin_crate};
use context::{in_target, StopBefore, Link, Assemble, BuildContext};
@ -70,9 +72,8 @@ struct ReadyCtx {
fns: ~[ListenerFn]
}
fn fold_mod(_ctx: @mut ReadyCtx,
m: &ast::_mod,
fold: @fold::ast_fold) -> ast::_mod {
fn fold_mod(_ctx: @mut ReadyCtx, m: &ast::_mod, fold: &CrateSetup)
-> ast::_mod {
fn strip_main(item: @ast::item) -> @ast::item {
@ast::item {
attrs: do item.attrs.iter().filter_map |attr| {
@ -94,9 +95,8 @@ fn fold_mod(_ctx: @mut ReadyCtx,
}, fold)
}
fn fold_item(ctx: @mut ReadyCtx,
item: @ast::item,
fold: @fold::ast_fold) -> Option<@ast::item> {
fn fold_item(ctx: @mut ReadyCtx, item: @ast::item, fold: &CrateSetup)
-> Option<@ast::item> {
ctx.path.push(item.ident);
let mut cmds = ~[];
@ -134,6 +134,19 @@ fn fold_item(ctx: @mut ReadyCtx,
res
}
struct CrateSetup {
ctx: @mut ReadyCtx,
}
impl fold::ast_fold for CrateSetup {
fn fold_item(&self, item: @ast::item) -> Option<@ast::item> {
fold_item(self.ctx, item, self)
}
fn fold_mod(&self, module: &ast::_mod) -> ast::_mod {
fold_mod(self.ctx, module, self)
}
}
/// Generate/filter main function, add the list of commands, etc.
pub fn ready_crate(sess: session::Session,
crate: @ast::Crate) -> @ast::Crate {
@ -144,15 +157,9 @@ pub fn ready_crate(sess: session::Session,
path: ~[],
fns: ~[]
};
let precursor = @fold::AstFoldFns {
// fold_crate: fold::wrap(|a, b| fold_crate(ctx, a, b)),
fold_item: |a, b| fold_item(ctx, a, b),
fold_mod: |a, b| fold_mod(ctx, a, b),
.. *fold::default_ast_fold()
let fold = CrateSetup {
ctx: ctx,
};
let fold = fold::make_fold(precursor);
@fold.fold_crate(crate)
}

View File

@ -12,7 +12,6 @@ use ast::*;
use ast;
use ast_util;
use codemap::{Span, dummy_sp};
use fold;
use opt_vec;
use parse::token;
use visit::Visitor;
@ -371,21 +370,6 @@ pub fn empty_generics() -> Generics {
ty_params: opt_vec::Empty}
}
///////////////////////////////////////////////////////////////////////////
// Assigning node ids
fn node_id_assigner(next_id: @fn() -> ast::NodeId) -> @fold::ast_fold {
let precursor = @fold::AstFoldFns {
new_id: |old_id| {
assert_eq!(old_id, ast::DUMMY_NODE_ID);
next_id()
},
..*fold::default_ast_fold()
};
fold::make_fold(precursor)
}
// ______________________________________________________________________
// Enumerating the IDs which appear in an AST

View File

@ -15,6 +15,7 @@ use ast_util;
use codemap::{Span, respan, dummy_sp};
use ext::base::ExtCtxt;
use ext::quote::rt::*;
use fold;
use opt_vec;
use opt_vec::OptVec;
@ -862,3 +863,32 @@ impl AstBuilder for @ExtCtxt {
ast::view_path_glob(self.path(sp, path), ast::DUMMY_NODE_ID))])
}
}
struct Duplicator {
cx: @ExtCtxt,
}
impl fold::ast_fold for Duplicator {
fn new_id(&self, _: NodeId) -> NodeId {
ast::DUMMY_NODE_ID
}
}
pub trait Duplicate {
//
// Duplication functions
//
// These functions just duplicate AST nodes.
//
fn duplicate(&self, cx: @ExtCtxt) -> Self;
}
impl Duplicate for @ast::Expr {
fn duplicate(&self, cx: @ExtCtxt) -> @ast::Expr {
let folder = @Duplicator {
cx: cx,
} as @fold::ast_fold;
folder.fold_expr(*self)
}
}

View File

@ -10,7 +10,7 @@
use ast::{Block, Crate, DeclLocal, Expr_, ExprMac, SyntaxContext};
use ast::{Local, Ident, mac_invoc_tt};
use ast::{item_mac, Mrk, Stmt_, StmtDecl, StmtMac, StmtExpr, StmtSemi};
use ast::{item_mac, Mrk, Stmt, StmtDecl, StmtMac, StmtExpr, StmtSemi};
use ast::{token_tree};
use ast;
use ast_util::{mtwt_outer_mark, new_rename, new_mark};
@ -21,6 +21,7 @@ use codemap;
use codemap::{Span, Spanned, ExpnInfo, NameAndSpan};
use ext::base::*;
use fold::*;
use opt_vec;
use parse;
use parse::{parse_item_from_source_str};
use parse::token;
@ -32,12 +33,10 @@ use std::vec;
pub fn expand_expr(extsbox: @mut SyntaxEnv,
cx: @ExtCtxt,
e: &Expr_,
span: Span,
fld: @ast_fold,
orig: @fn(&Expr_, Span, @ast_fold) -> (Expr_, Span))
-> (Expr_, Span) {
match *e {
e: @ast::Expr,
fld: &MacroExpander)
-> @ast::Expr {
match e.node {
// expr_mac should really be expr_ext or something; it's the
// entry-point for all syntax extensions.
ExprMac(ref mac) => {
@ -66,7 +65,7 @@ pub fn expand_expr(extsbox: @mut SyntaxEnv,
}
Some(@SE(NormalTT(expandfun, exp_span))) => {
cx.bt_push(ExpnInfo {
call_site: span,
call_site: e.span,
callee: NameAndSpan {
name: extnamestr,
span: exp_span,
@ -101,12 +100,19 @@ pub fn expand_expr(extsbox: @mut SyntaxEnv,
// mark after:
let marked_after = mark_expr(expanded,fm);
//keep going, outside-in
// Keep going, outside-in.
//
// XXX(pcwalton): Is it necessary to clone the
// node here?
let fully_expanded =
fld.fold_expr(marked_after).node.clone();
cx.bt_pop();
(fully_expanded, span)
@ast::Expr {
id: ast::DUMMY_NODE_ID,
node: fully_expanded,
span: e.span,
}
}
_ => {
cx.span_fatal(
@ -125,8 +131,48 @@ pub fn expand_expr(extsbox: @mut SyntaxEnv,
ast::ExprForLoop(src_pat, src_expr, ref src_loop_block, opt_ident) => {
// Expand any interior macros etc.
// NB: we don't fold pats yet. Curious.
let src_expr = fld.fold_expr(src_expr);
let src_loop_block = fld.fold_block(src_loop_block);
let src_expr = fld.fold_expr(src_expr).clone();
let src_loop_block = fld.fold_block(src_loop_block).clone();
let span = e.span;
pub fn mk_expr(_: @ExtCtxt, span: Span, node: Expr_)
-> @ast::Expr {
@ast::Expr {
id: ast::DUMMY_NODE_ID,
node: node,
span: span,
}
}
fn mk_block(_: @ExtCtxt,
stmts: &[@ast::Stmt],
expr: Option<@ast::Expr>,
span: Span)
-> ast::Block {
ast::Block {
view_items: ~[],
stmts: stmts.to_owned(),
expr: expr,
id: ast::DUMMY_NODE_ID,
rules: ast::DefaultBlock,
span: span,
}
}
fn mk_simple_path(ident: ast::Ident, span: Span) -> ast::Path {
ast::Path {
span: span,
global: false,
segments: ~[
ast::PathSegment {
identifier: ident,
lifetime: None,
types: opt_vec::Empty,
}
],
}
}
// to:
//
@ -182,10 +228,14 @@ pub fn expand_expr(extsbox: @mut SyntaxEnv,
~[iter_decl_stmt],
Some(loop_expr));
(ast::ExprBlock(block), span)
@ast::Expr {
id: ast::DUMMY_NODE_ID,
node: ast::ExprBlock(block),
span: span,
}
}
_ => orig(e, span, fld)
_ => noop_fold_expr(e, fld)
}
}
@ -201,12 +251,10 @@ pub fn expand_expr(extsbox: @mut SyntaxEnv,
pub fn expand_mod_items(extsbox: @mut SyntaxEnv,
cx: @ExtCtxt,
module_: &ast::_mod,
fld: @ast_fold,
orig: @fn(&ast::_mod, @ast_fold) -> ast::_mod)
-> ast::_mod {
fld: &MacroExpander)
-> ast::_mod {
// Fold the contents first:
let module_ = orig(module_, fld);
let module_ = noop_fold_mod(module_, fld);
// For each item, look through the attributes. If any of them are
// decorated with "item decorators", then use that function to transform
@ -233,7 +281,10 @@ pub fn expand_mod_items(extsbox: @mut SyntaxEnv,
}
};
ast::_mod { items: new_items, ..module_ }
ast::_mod {
items: new_items,
..module_
}
}
// eval $e with a new exts frame:
@ -256,19 +307,20 @@ static special_block_name : &'static str = " block";
pub fn expand_item(extsbox: @mut SyntaxEnv,
cx: @ExtCtxt,
it: @ast::item,
fld: @ast_fold,
orig: @fn(@ast::item, @ast_fold) -> Option<@ast::item>)
-> Option<@ast::item> {
fld: &MacroExpander)
-> Option<@ast::item> {
match it.node {
ast::item_mac(*) => expand_item_mac(extsbox, cx, it, fld),
ast::item_mod(_) | ast::item_foreign_mod(_) => {
cx.mod_push(it.ident);
let macro_escape = contains_macro_escape(it.attrs);
let result = with_exts_frame!(extsbox,macro_escape,orig(it,fld));
let result = with_exts_frame!(extsbox,
macro_escape,
noop_fold_item(it, fld));
cx.mod_pop();
result
},
_ => orig(it,fld)
_ => noop_fold_item(it, fld)
}
}
@ -280,11 +332,15 @@ pub fn contains_macro_escape(attrs: &[ast::Attribute]) -> bool {
// Support for item-position macro invocations, exactly the same
// logic as for expression-position macro invocations.
pub fn expand_item_mac(extsbox: @mut SyntaxEnv,
cx: @ExtCtxt, it: @ast::item,
fld: @ast_fold)
-> Option<@ast::item> {
cx: @ExtCtxt,
it: @ast::item,
fld: &MacroExpander)
-> Option<@ast::item> {
let (pth, tts, ctxt) = match it.node {
item_mac(codemap::Spanned { node: mac_invoc_tt(ref pth, ref tts, ctxt), _}) => {
item_mac(codemap::Spanned {
node: mac_invoc_tt(ref pth, ref tts, ctxt),
_
}) => {
(pth, (*tts).clone(), ctxt)
}
_ => cx.span_bug(it.span, "invalid item macro invocation")
@ -382,15 +438,12 @@ fn insert_macro(exts: SyntaxEnv, name: ast::Name, transformer: @Transformer) {
// expand a stmt
pub fn expand_stmt(extsbox: @mut SyntaxEnv,
cx: @ExtCtxt,
s: &Stmt_,
sp: Span,
fld: @ast_fold,
orig: @fn(&Stmt_, Span, @ast_fold)
-> (Option<Stmt_>, Span))
-> (Option<Stmt_>, Span) {
s: &Stmt,
fld: &MacroExpander)
-> Option<@Stmt> {
// why the copying here and not in expand_expr?
// looks like classic changed-in-only-one-place
let (pth, tts, semi, ctxt) = match *s {
let (pth, tts, semi, ctxt) = match s.node {
StmtMac(ref mac, semi) => {
match mac.node {
mac_invoc_tt(ref pth, ref tts, ctxt) => {
@ -398,24 +451,26 @@ pub fn expand_stmt(extsbox: @mut SyntaxEnv,
}
}
}
_ => return expand_non_macro_stmt(*extsbox,s,sp,fld,orig)
_ => return expand_non_macro_stmt(*extsbox, s, fld)
};
if (pth.segments.len() > 1u) {
cx.span_fatal(
pth.span,
fmt!("expected macro name without module \
separators"));
cx.span_fatal(pth.span,
"expected macro name without module separators");
}
let extname = &pth.segments[0].identifier;
let extnamestr = ident_to_str(extname);
let (fully_expanded, sp) = match (*extsbox).find(&extname.name) {
None =>
cx.span_fatal(pth.span, fmt!("macro undefined: '%s'", extnamestr)),
let fully_expanded: @ast::Stmt = match (*extsbox).find(&extname.name) {
None => {
cx.span_fatal(pth.span, fmt!("macro undefined: '%s'", extnamestr))
}
Some(@SE(NormalTT(expandfun, exp_span))) => {
cx.bt_push(ExpnInfo {
call_site: sp,
callee: NameAndSpan { name: extnamestr, span: exp_span }
call_site: s.span,
callee: NameAndSpan {
name: extnamestr,
span: exp_span,
}
});
let fm = fresh_mark();
// mark before expansion:
@ -426,11 +481,16 @@ pub fn expand_stmt(extsbox: @mut SyntaxEnv,
// not the current mac.span.
let mac_span = original_span(cx);
let expanded = match expandfun(cx, mac_span.call_site,
marked_tts, marked_ctxt) {
MRExpr(e) =>
@codemap::Spanned { node: StmtExpr(e, ast::DUMMY_NODE_ID),
span: e.span},
let expanded = match expandfun(cx,
mac_span.call_site,
marked_tts,
marked_ctxt) {
MRExpr(e) => {
@codemap::Spanned {
node: StmtExpr(e, ast::DUMMY_NODE_ID),
span: e.span,
}
}
MRAny(_,_,stmt_mkr) => stmt_mkr(),
_ => cx.span_fatal(
pth.span,
@ -438,12 +498,15 @@ pub fn expand_stmt(extsbox: @mut SyntaxEnv,
};
let marked_after = mark_stmt(expanded,fm);
//keep going, outside-in
// Keep going, outside-in.
let fully_expanded = match fld.fold_stmt(marked_after) {
Some(stmt) => {
let fully_expanded = &stmt.node;
cx.bt_pop();
(*fully_expanded).clone()
@Spanned {
span: stmt.span,
node: (*fully_expanded).clone(),
}
}
None => {
cx.span_fatal(pth.span,
@ -451,7 +514,7 @@ pub fn expand_stmt(extsbox: @mut SyntaxEnv,
}
};
(fully_expanded, sp)
fully_expanded
}
_ => {
@ -460,24 +523,28 @@ pub fn expand_stmt(extsbox: @mut SyntaxEnv,
}
};
(match fully_expanded {
StmtExpr(e, stmt_id) if semi => Some(StmtSemi(e, stmt_id)),
_ => { Some(fully_expanded) } /* might already have a semi */
}, sp)
match fully_expanded.node {
StmtExpr(e, stmt_id) if semi => {
Some(@Spanned {
span: fully_expanded.span,
node: StmtSemi(e, stmt_id),
})
}
_ => Some(fully_expanded), /* might already have a semi */
}
}
// expand a non-macro stmt. this is essentially the fallthrough for
// expand_stmt, above.
fn expand_non_macro_stmt (exts: SyntaxEnv,
s: &Stmt_,
sp: Span,
fld: @ast_fold,
orig: @fn(&Stmt_, Span, @ast_fold) -> (Option<Stmt_>, Span))
-> (Option<Stmt_>,Span) {
fn expand_non_macro_stmt(exts: SyntaxEnv, s: &Stmt, fld: &MacroExpander)
-> Option<@Stmt> {
// is it a let?
match *s {
StmtDecl(@Spanned{node: DeclLocal(ref local), span: stmt_span}, node_id) => {
match s.node {
StmtDecl(@Spanned {
node: DeclLocal(ref local),
span: stmt_span
},
node_id) => {
let block_info = get_block_info(exts);
let pending_renames = block_info.pending_renames;
@ -515,19 +582,24 @@ fn expand_non_macro_stmt (exts: SyntaxEnv,
// also, don't forget to expand the init:
let new_init_opt = init.map(|e| fld.fold_expr(*e));
let rewritten_local =
@Local{is_mutbl:is_mutbl,
ty:ty,
pat:rewritten_pat,
init:new_init_opt,
id:id,
span:span};
(Some(StmtDecl(@Spanned{node:DeclLocal(rewritten_local),
span: stmt_span},node_id)),
sp)
@Local {
is_mutbl: is_mutbl,
ty: ty,
pat: rewritten_pat,
init: new_init_opt,
id: id,
span: span,
};
Some(@Spanned {
node: StmtDecl(@Spanned {
node: DeclLocal(rewritten_local),
span: stmt_span
},
node_id),
span: span
})
},
_ => {
orig(s, sp, fld)
}
_ => noop_fold_stmt(s, fld),
}
}
@ -628,18 +700,18 @@ pub fn new_path_finder(paths: @mut ~[ast::Path]) -> @mut Visitor<()> {
// expand a block. pushes a new exts_frame, then calls expand_block_elts
pub fn expand_block(extsbox: @mut SyntaxEnv,
_cx: @ExtCtxt,
_: @ExtCtxt,
blk: &Block,
fld: @ast_fold,
_orig: @fn(&Block, @ast_fold) -> Block)
-> Block {
fld: &MacroExpander)
-> Block {
// see note below about treatment of exts table
with_exts_frame!(extsbox,false,
expand_block_elts(*extsbox, blk, fld))
}
// expand the elements of a block.
pub fn expand_block_elts(exts: SyntaxEnv, b: &Block, fld: @ast_fold) -> Block {
pub fn expand_block_elts(exts: SyntaxEnv, b: &Block, fld: &MacroExpander)
-> Block {
let block_info = get_block_info(exts);
let pending_renames = block_info.pending_renames;
let rename_fld = renames_to_fold(pending_renames);
@ -680,9 +752,47 @@ fn get_block_info(exts : SyntaxEnv) -> BlockInfo {
}
}
struct IdentRenamer {
renames: @mut ~[(ast::Ident,ast::Name)],
}
impl ast_fold for IdentRenamer {
fn fold_ident(&self, id: ast::Ident) -> ast::Ident {
let new_ctxt = self.renames.iter().fold(id.ctxt, |ctxt, &(from, to)| {
new_rename(from, to, ctxt)
});
ast::Ident {
name: id.name,
ctxt: new_ctxt,
}
}
}
// given a mutable list of renames, return a tree-folder that applies those
// renames.
fn renames_to_fold(renames: @mut ~[(ast::Ident,ast::Name)]) -> @ast_fold {
@IdentRenamer {
renames: renames,
} as @ast_fold
}
// perform a bunch of renames
fn apply_pending_renames(folder : @ast_fold, stmt : ast::Stmt) -> @ast::Stmt {
match folder.fold_stmt(&stmt) {
Some(s) => s,
None => fail!(fmt!("renaming of stmt produced None"))
}
}
pub fn new_span(cx: @ExtCtxt, sp: Span) -> Span {
/* this discards information in the case of macro-defining macros */
return Span {lo: sp.lo, hi: sp.hi, expn_info: cx.backtrace()};
Span {
lo: sp.lo,
hi: sp.hi,
expn_info: cx.backtrace(),
}
}
// FIXME (#2247): this is a moderately bad kludge to inject some macros into
@ -1025,10 +1135,28 @@ pub fn std_macros() -> @str {
}";
}
struct Injector {
sm: @ast::item,
}
impl ast_fold for Injector {
fn fold_mod(&self, module: &ast::_mod) -> ast::_mod {
// Just inject the standard macros at the start of the first module
// in the crate: that is, at the start of the crate file itself.
let items = vec::append(~[ self.sm ], module.items);
ast::_mod {
items: items,
..(*module).clone() // FIXME #2543: Bad copy.
}
}
}
// add a bunch of macros as though they were placed at the head of the
// program (ick). This should run before cfg stripping.
pub fn inject_std_macros(parse_sess: @mut parse::ParseSess,
cfg: ast::CrateConfig, c: &Crate) -> @Crate {
cfg: ast::CrateConfig,
c: @Crate)
-> @Crate {
let sm = match parse_item_from_source_str(@"<std-macros>",
std_macros(),
cfg.clone(),
@ -1038,48 +1166,80 @@ pub fn inject_std_macros(parse_sess: @mut parse::ParseSess,
None => fail!("expected core macros to parse correctly")
};
let injecter = @AstFoldFns {
fold_mod: |modd, _| {
// just inject the std macros at the start of the first
// module in the crate (i.e the crate file itself.)
let items = vec::append(~[sm], modd.items);
ast::_mod {
items: items,
// FIXME #2543: Bad copy.
.. (*modd).clone()
}
},
.. *default_ast_fold()
};
@make_fold(injecter).fold_crate(c)
let injector = @Injector {
sm: sm,
} as @ast_fold;
@injector.fold_crate(c)
}
struct NoOpFolder {
contents: (),
}
impl ast_fold for NoOpFolder {}
struct MacroExpander {
extsbox: @mut SyntaxEnv,
cx: @ExtCtxt,
}
impl ast_fold for MacroExpander {
fn fold_expr(&self, expr: @ast::Expr) -> @ast::Expr {
expand_expr(self.extsbox,
self.cx,
expr,
self)
}
fn fold_mod(&self, module: &ast::_mod) -> ast::_mod {
expand_mod_items(self.extsbox,
self.cx,
module,
self)
}
fn fold_item(&self, item: @ast::item) -> Option<@ast::item> {
expand_item(self.extsbox,
self.cx,
item,
self)
}
fn fold_stmt(&self, stmt: &ast::Stmt) -> Option<@ast::Stmt> {
expand_stmt(self.extsbox,
self.cx,
stmt,
self)
}
fn fold_block(&self, block: &ast::Block) -> ast::Block {
expand_block(self.extsbox,
self.cx,
block,
self)
}
fn new_span(&self, span: Span) -> Span {
new_span(self.cx, span)
}
}
pub fn expand_crate(parse_sess: @mut parse::ParseSess,
cfg: ast::CrateConfig, c: &Crate) -> @Crate {
cfg: ast::CrateConfig,
c: &Crate) -> @Crate {
// adding *another* layer of indirection here so that the block
// visitor can swap out one exts table for another for the duration
// of the block. The cleaner alternative would be to thread the
// exts table through the fold, but that would require updating
// every method/element of AstFoldFns in fold.rs.
let extsbox = @mut syntax_expander_table();
let afp = default_ast_fold();
let extsbox = syntax_expander_table();
let cx = ExtCtxt::new(parse_sess, cfg.clone());
let f_pre = @AstFoldFns {
fold_expr: |expr,span,recur|
expand_expr(extsbox, cx, expr, span, recur, afp.fold_expr),
fold_mod: |modd,recur|
expand_mod_items(extsbox, cx, modd, recur, afp.fold_mod),
fold_item: |item,recur|
expand_item(extsbox, cx, item, recur, afp.fold_item),
fold_stmt: |stmt,span,recur|
expand_stmt(extsbox, cx, stmt, span, recur, afp.fold_stmt),
fold_block: |blk,recur|
expand_block(extsbox, cx, blk, recur, afp.fold_block),
new_span: |a| new_span(cx, a),
.. *afp};
let f = make_fold(f_pre);
let expander = @MacroExpander {
extsbox: @mut extsbox,
cx: cx,
} as @ast_fold;
let ret = @f.fold_crate(c);
let ret = @expander.fold_crate(c);
parse_sess.span_diagnostic.handler().abort_if_errors();
return ret;
}
@ -1145,53 +1305,56 @@ impl CtxtFn for Repainter {
}
}
// given a function from ctxts to ctxts, produce
// an ast_fold that applies that function to all ctxts:
pub fn fun_to_ctxt_folder<T : 'static + CtxtFn>(cf: @T) -> @AstFoldFns {
let afp = default_ast_fold();
let fi : @fn(ast::Ident, @ast_fold) -> ast::Ident =
|ast::Ident{name, ctxt}, _| {
ast::Ident{name:name,ctxt:cf.f(ctxt)}
};
let fm : @fn(&ast::mac_, Span, @ast_fold) -> (ast::mac_,Span) =
|m, sp, fld| {
match *m {
mac_invoc_tt(ref path, ref tts, ctxt) =>
(mac_invoc_tt(fld.fold_path(path),
fold_tts(*tts,fld),
cf.f(ctxt)),
sp)
}
pub struct ContextWrapper {
context_function: @CtxtFn,
}
};
@AstFoldFns{
fold_ident : fi,
fold_mac : fm,
.. *afp
impl ast_fold for ContextWrapper {
fn fold_ident(&self, id: ast::Ident) -> ast::Ident {
let ast::Ident {
name,
ctxt
} = id;
ast::Ident {
name: name,
ctxt: self.context_function.f(ctxt),
}
}
fn fold_mac(&self, m: &ast::mac) -> ast::mac {
let macro = match m.node {
mac_invoc_tt(ref path, ref tts, ctxt) => {
mac_invoc_tt(self.fold_path(path),
fold_tts(*tts, self),
self.context_function.f(ctxt))
}
};
Spanned {
node: macro,
span: m.span,
}
}
}
// given a mutable list of renames, return a tree-folder that applies those
// renames.
// FIXME #4536: currently pub to allow testing
pub fn renames_to_fold(renames : @mut ~[(ast::Ident,ast::Name)]) -> @AstFoldFns {
fun_to_ctxt_folder(@MultiRenamer{renames : renames})
// given a function from ctxts to ctxts, produce
// an ast_fold that applies that function to all ctxts:
pub fn fun_to_ctxt_folder<T : 'static + CtxtFn>(cf: @T) -> @ContextWrapper {
@ContextWrapper {
context_function: cf as @CtxtFn,
}
}
// just a convenience:
pub fn new_mark_folder(m : Mrk) -> @AstFoldFns {
pub fn new_mark_folder(m: Mrk) -> @ContextWrapper {
fun_to_ctxt_folder(@Marker{mark:m})
}
pub fn new_rename_folder(from : ast::Ident, to : ast::Name) -> @AstFoldFns {
pub fn new_rename_folder(from: ast::Ident, to: ast::Name) -> @ContextWrapper {
fun_to_ctxt_folder(@Renamer{from:from,to:to})
}
// apply a given mark to the given token trees. Used prior to expansion of a macro.
fn mark_tts(tts : &[token_tree], m : Mrk) -> ~[token_tree] {
fold_tts(tts,new_mark_folder(m) as @ast_fold)
fold_tts(tts,new_mark_folder(m))
}
// apply a given mark to the given expr. Used following the expansion of a macro.

File diff suppressed because it is too large Load Diff

View File

@ -1,3 +1,8 @@
// xfail-test
// xfail'd because the lint pass doesn't know to ignore standard library
// stuff.
// -*- rust -*-
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at

View File

@ -1,3 +1,7 @@
// xfail-test
// xfail'd because the lint pass doesn't know to ignore standard library
// stuff.
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.