// 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. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. use ast::*; use ast; use codemap::{respan, Span, Spanned}; use parse::token; use opt_vec::OptVec; // We may eventually want to be able to fold over type parameters, too. pub trait ast_fold { fn fold_crate(&self, c: Crate) -> Crate { noop_fold_crate(c, self) } fn fold_meta_items(&self, meta_items: &[@MetaItem]) -> ~[@MetaItem] { meta_items.map(|x| fold_meta_item_(*x, self)) } fn fold_view_paths(&self, view_paths: &[@view_path]) -> ~[@view_path] { view_paths.map(|view_path| { let inner_view_path = match view_path.node { view_path_simple(ref ident, ref path, node_id) => { view_path_simple(ident.clone(), self.fold_path(path), self.new_id(node_id)) } view_path_glob(ref path, node_id) => { view_path_glob(self.fold_path(path), self.new_id(node_id)) } view_path_list(ref path, ref path_list_idents, node_id) => { view_path_list(self.fold_path(path), path_list_idents.map(|path_list_ident| { let id = self.new_id(path_list_ident.node .id); Spanned { node: path_list_ident_ { name: path_list_ident.node .name .clone(), id: id, }, span: self.new_span( path_list_ident.span) } }), self.new_id(node_id)) } }; @Spanned { node: inner_view_path, span: self.new_span(view_path.span), } }) } fn fold_view_item(&self, vi: &view_item) -> view_item { let inner_view_item = match vi.node { view_item_extern_mod(ref ident, string, ref meta_items, node_id) => { view_item_extern_mod(ident.clone(), string, self.fold_meta_items(*meta_items), self.new_id(node_id)) } view_item_use(ref view_paths) => { view_item_use(self.fold_view_paths(*view_paths)) } }; view_item { node: inner_view_item, attrs: vi.attrs.map(|a| fold_attribute_(*a, self)), vis: vi.vis, span: self.new_span(vi.span), } } fn fold_foreign_item(&self, ni: @foreign_item) -> @foreign_item { let fold_attribute = |x| fold_attribute_(x, self); @ast::foreign_item { ident: self.fold_ident(ni.ident), attrs: ni.attrs.map(|x| fold_attribute(*x)), node: match ni.node { foreign_item_fn(ref fdec, ref generics) => { foreign_item_fn( ast::fn_decl { inputs: fdec.inputs.map(|a| fold_arg_(a, self)), output: self.fold_ty(&fdec.output), cf: fdec.cf, }, fold_generics(generics, self)) } foreign_item_static(ref t, m) => { foreign_item_static(self.fold_ty(t), m) } }, id: self.new_id(ni.id), span: self.new_span(ni.span), vis: ni.vis, } } fn fold_item(&self, i: @item) -> Option<@item> { noop_fold_item(i, self) } fn fold_struct_field(&self, sf: @struct_field) -> @struct_field { let fold_attribute = |x| fold_attribute_(x, self); @Spanned { node: ast::struct_field_ { kind: sf.node.kind, id: self.new_id(sf.node.id), ty: self.fold_ty(&sf.node.ty), attrs: sf.node.attrs.map(|e| fold_attribute(*e)) }, span: self.new_span(sf.span) } } fn fold_item_underscore(&self, i: &item_) -> item_ { noop_fold_item_underscore(i, self) } fn fold_type_method(&self, m: &TypeMethod) -> TypeMethod { noop_fold_type_method(m, self) } fn fold_method(&self, m: @method) -> @method { @ast::method { ident: self.fold_ident(m.ident), attrs: m.attrs.map(|a| fold_attribute_(*a, self)), generics: fold_generics(&m.generics, self), explicit_self: m.explicit_self, purity: m.purity, decl: fold_fn_decl(&m.decl, self), body: self.fold_block(&m.body), id: self.new_id(m.id), span: self.new_span(m.span), self_id: self.new_id(m.self_id), vis: m.vis, } } fn fold_block(&self, b: &Block) -> Block { noop_fold_block(b, self) } fn fold_stmt(&self, s: &Stmt) -> Option<@Stmt> { noop_fold_stmt(s, self) } fn fold_arm(&self, a: &Arm) -> Arm { Arm { pats: a.pats.map(|x| self.fold_pat(*x)), guard: a.guard.map(|x| self.fold_expr(x)), body: self.fold_block(&a.body), } } fn fold_pat(&self, p: @Pat) -> @Pat { let node = match p.node { PatWild => PatWild, PatIdent(binding_mode, ref pth, ref sub) => { PatIdent(binding_mode, self.fold_path(pth), sub.map(|x| self.fold_pat(x))) } PatLit(e) => PatLit(self.fold_expr(e)), PatEnum(ref pth, ref pats) => { PatEnum(self.fold_path(pth), pats.as_ref().map(|pats| pats.map(|x| self.fold_pat(*x)))) } PatStruct(ref pth, ref fields, etc) => { let pth_ = self.fold_path(pth); let fs = do fields.map |f| { ast::FieldPat { ident: f.ident, pat: self.fold_pat(f.pat) } }; PatStruct(pth_, fs, etc) } PatTup(ref elts) => PatTup(elts.map(|x| self.fold_pat(*x))), PatBox(inner) => PatBox(self.fold_pat(inner)), PatUniq(inner) => PatUniq(self.fold_pat(inner)), PatRegion(inner) => PatRegion(self.fold_pat(inner)), PatRange(e1, e2) => { PatRange(self.fold_expr(e1), self.fold_expr(e2)) }, PatVec(ref before, ref slice, ref after) => { PatVec(before.map(|x| self.fold_pat(*x)), slice.map(|x| self.fold_pat(x)), after.map(|x| self.fold_pat(*x))) } }; @Pat { id: self.new_id(p.id), span: self.new_span(p.span), node: node, } } fn fold_decl(&self, d: @Decl) -> Option<@Decl> { let node = match d.node { DeclLocal(ref l) => Some(DeclLocal(self.fold_local(*l))), DeclItem(it) => { match self.fold_item(it) { Some(it_folded) => Some(DeclItem(it_folded)), None => None, } } }; node.map(|node| { @Spanned { node: node, span: d.span, } }) } fn fold_expr(&self, e: @Expr) -> @Expr { noop_fold_expr(e, self) } fn fold_ty(&self, t: &Ty) -> Ty { let node = match t.node { ty_nil | ty_bot | ty_infer => t.node.clone(), ty_box(ref mt) => ty_box(fold_mt(mt, self)), ty_uniq(ref mt) => ty_uniq(fold_mt(mt, self)), ty_vec(ref mt) => ty_vec(fold_mt(mt, self)), ty_ptr(ref mt) => ty_ptr(fold_mt(mt, self)), ty_rptr(region, ref mt) => ty_rptr(region, fold_mt(mt, self)), ty_closure(ref f) => { ty_closure(@TyClosure { sigil: f.sigil, purity: f.purity, region: f.region, onceness: f.onceness, bounds: fold_opt_bounds(&f.bounds, self), decl: fold_fn_decl(&f.decl, self), lifetimes: f.lifetimes.map(|l| fold_lifetime(l, self)), }) } ty_bare_fn(ref f) => { ty_bare_fn(@TyBareFn { lifetimes: f.lifetimes.map(|l| fold_lifetime(l, self)), purity: f.purity, abis: f.abis, decl: fold_fn_decl(&f.decl, self) }) } ty_tup(ref tys) => ty_tup(tys.map(|ty| self.fold_ty(ty))), ty_path(ref path, ref bounds, id) => { ty_path(self.fold_path(path), fold_opt_bounds(bounds, self), self.new_id(id)) } ty_fixed_length_vec(ref mt, e) => { ty_fixed_length_vec(fold_mt(mt, self), self.fold_expr(e)) } ty_mac(ref mac) => ty_mac(self.fold_mac(mac)), ty_typeof(expr) => ty_typeof(self.fold_expr(expr)), }; Ty { id: self.new_id(t.id), span: self.new_span(t.span), node: node, } } fn fold_mod(&self, m: &_mod) -> _mod { noop_fold_mod(m, self) } fn fold_foreign_mod(&self, nm: &foreign_mod) -> foreign_mod { ast::foreign_mod { abis: nm.abis, view_items: nm.view_items .iter() .map(|x| self.fold_view_item(x)) .collect(), items: nm.items .iter() .map(|x| self.fold_foreign_item(*x)) .collect(), } } fn fold_variant(&self, v: &variant) -> variant { let kind; match v.node.kind { tuple_variant_kind(ref variant_args) => { kind = tuple_variant_kind(variant_args.map(|x| fold_variant_arg_(x, self))) } struct_variant_kind(ref struct_def) => { kind = struct_variant_kind(@ast::struct_def { fields: struct_def.fields.iter() .map(|f| self.fold_struct_field(*f)).collect(), ctor_id: struct_def.ctor_id.map(|c| self.new_id(c)) }) } } let fold_attribute = |x| fold_attribute_(x, self); let attrs = v.node.attrs.map(|x| fold_attribute(*x)); let de = match v.node.disr_expr { Some(e) => Some(self.fold_expr(e)), None => None }; let node = ast::variant_ { name: v.node.name, attrs: attrs, kind: kind, id: self.new_id(v.node.id), disr_expr: de, vis: v.node.vis, }; Spanned { node: node, span: self.new_span(v.span), } } fn fold_ident(&self, i: Ident) -> Ident { i } fn fold_path(&self, p: &Path) -> Path { ast::Path { span: self.new_span(p.span), global: p.global, segments: p.segments.map(|segment| ast::PathSegment { identifier: self.fold_ident(segment.identifier), lifetime: segment.lifetime, types: segment.types.map(|typ| self.fold_ty(typ)), }) } } fn fold_local(&self, l: @Local) -> @Local { @Local { ty: self.fold_ty(&l.ty), pat: self.fold_pat(l.pat), init: l.init.map(|e| self.fold_expr(e)), id: self.new_id(l.id), span: self.new_span(l.span), } } fn fold_mac(&self, macro: &mac) -> mac { Spanned { node: match macro.node { mac_invoc_tt(ref p, ref tts, ctxt) => { mac_invoc_tt(self.fold_path(p), fold_tts(*tts, self), ctxt) } }, span: self.new_span(macro.span) } } fn map_exprs(&self, f: &fn(@Expr) -> @Expr, es: &[@Expr]) -> ~[@Expr] { es.map(|x| f(*x)) } fn new_id(&self, i: NodeId) -> NodeId { i } fn new_span(&self, sp: Span) -> Span { sp } } /* some little folds that probably aren't useful to have in ast_fold itself*/ //used in noop_fold_item and noop_fold_crate and noop_fold_crate_directive fn fold_meta_item_(mi: @MetaItem, fld: &T) -> @MetaItem { @Spanned { node: match mi.node { MetaWord(id) => MetaWord(id), MetaList(id, ref mis) => { let fold_meta_item = |x| fold_meta_item_(x, fld); MetaList( id, mis.map(|e| fold_meta_item(*e)) ) } MetaNameValue(id, s) => MetaNameValue(id, s) }, span: fld.new_span(mi.span) } } //used in noop_fold_item and noop_fold_crate fn fold_attribute_(at: Attribute, fld: &T) -> Attribute { Spanned { span: fld.new_span(at.span), node: ast::Attribute_ { style: at.node.style, value: fold_meta_item_(at.node.value, fld), is_sugared_doc: at.node.is_sugared_doc } } } //used in noop_fold_foreign_item and noop_fold_fn_decl fn fold_arg_(a: &arg, fld: &T) -> arg { ast::arg { ty: fld.fold_ty(&a.ty), pat: fld.fold_pat(a.pat), id: fld.new_id(a.id), } } // build a new vector of tts by appling the ast_fold's fold_ident to // all of the identifiers in the token trees. pub fn fold_tts(tts: &[token_tree], fld: &T) -> ~[token_tree] { do tts.map |tt| { match *tt { tt_tok(span, ref tok) => tt_tok(span,maybe_fold_ident(tok,fld)), tt_delim(ref tts) => tt_delim(@mut fold_tts(**tts, fld)), tt_seq(span, ref pattern, ref sep, is_optional) => tt_seq(span, @mut fold_tts(**pattern, fld), sep.as_ref().map(|tok|maybe_fold_ident(tok,fld)), is_optional), tt_nonterminal(sp,ref ident) => tt_nonterminal(sp,fld.fold_ident(*ident)) } } } // apply ident folder if it's an ident, otherwise leave it alone fn maybe_fold_ident(t: &token::Token, fld: &T) -> token::Token { match *t { token::IDENT(id, followed_by_colons) => { token::IDENT(fld.fold_ident(id), followed_by_colons) } _ => (*t).clone() } } pub fn fold_fn_decl(decl: &ast::fn_decl, fld: &T) -> ast::fn_decl { ast::fn_decl { inputs: decl.inputs.map(|x| fold_arg_(x, fld)), // bad copy output: fld.fold_ty(&decl.output), cf: decl.cf, } } fn fold_ty_param_bound(tpb: &TyParamBound, fld: &T) -> TyParamBound { match *tpb { TraitTyParamBound(ref ty) => TraitTyParamBound(fold_trait_ref(ty, fld)), RegionTyParamBound => RegionTyParamBound } } pub fn fold_ty_param(tp: &TyParam, fld: &T) -> TyParam { TyParam { ident: tp.ident, id: fld.new_id(tp.id), bounds: tp.bounds.map(|x| fold_ty_param_bound(x, fld)), } } pub fn fold_ty_params(tps: &OptVec, fld: &T) -> OptVec { tps.map(|tp| fold_ty_param(tp, fld)) } pub fn fold_lifetime(l: &Lifetime, fld: &T) -> Lifetime { Lifetime { id: fld.new_id(l.id), span: fld.new_span(l.span), ident: l.ident } } pub fn fold_lifetimes(lts: &OptVec, fld: &T) -> OptVec { lts.map(|l| fold_lifetime(l, fld)) } pub fn fold_generics(generics: &Generics, fld: &T) -> Generics { Generics {ty_params: fold_ty_params(&generics.ty_params, fld), lifetimes: fold_lifetimes(&generics.lifetimes, fld)} } fn fold_struct_def(struct_def: @ast::struct_def, fld: &T) -> @ast::struct_def { @ast::struct_def { fields: struct_def.fields.map(|f| fold_struct_field(*f, fld)), ctor_id: struct_def.ctor_id.map(|cid| fld.new_id(cid)), } } fn noop_fold_view_item(vi: &view_item_, fld: @ast_fold) -> view_item_ { match *vi { view_item_extern_mod(ident, name, ref meta_items, node_id) => { view_item_extern_mod(ident, name, fld.fold_meta_items(*meta_items), fld.new_id(node_id)) } view_item_use(ref view_paths) => { view_item_use(fld.fold_view_paths(*view_paths)) } } } fn fold_trait_ref(p: &trait_ref, fld: &T) -> trait_ref { ast::trait_ref { path: fld.fold_path(&p.path), ref_id: fld.new_id(p.ref_id), } } fn fold_struct_field(f: @struct_field, fld: &T) -> @struct_field { @Spanned { node: ast::struct_field_ { kind: f.node.kind, id: fld.new_id(f.node.id), ty: fld.fold_ty(&f.node.ty), attrs: f.node.attrs.map(|a| fold_attribute_(*a, fld)), }, span: fld.new_span(f.span), } } fn fold_field_(field: Field, folder: &T) -> Field { ast::Field { ident: respan(field.ident.span, folder.fold_ident(field.ident.node)), expr: folder.fold_expr(field.expr), span: folder.new_span(field.span), } } fn fold_mt(mt: &mt, folder: &T) -> mt { mt { ty: ~folder.fold_ty(mt.ty), mutbl: mt.mutbl, } } fn fold_field(f: TypeField, folder: &T) -> TypeField { ast::TypeField { ident: folder.fold_ident(f.ident), mt: fold_mt(&f.mt, folder), span: folder.new_span(f.span), } } fn fold_opt_bounds(b: &Option>, folder: &T) -> Option> { do b.as_ref().map |bounds| { do bounds.map |bound| { fold_ty_param_bound(bound, folder) } } } fn fold_variant_arg_(va: &variant_arg, folder: &T) -> variant_arg { ast::variant_arg { ty: folder.fold_ty(&va.ty), id: folder.new_id(va.id) } } pub fn noop_fold_block(b: &Block, folder: &T) -> Block { let view_items = b.view_items.map(|x| folder.fold_view_item(x)); let mut stmts = ~[]; for stmt in b.stmts.iter() { match folder.fold_stmt(*stmt) { None => {} Some(stmt) => stmts.push(stmt) } } ast::Block { view_items: view_items, stmts: stmts, expr: b.expr.map(|x| folder.fold_expr(x)), id: folder.new_id(b.id), rules: b.rules, span: folder.new_span(b.span), } } pub fn noop_fold_item_underscore(i: &item_, folder: &T) -> item_ { match *i { item_static(ref t, m, e) => { item_static(folder.fold_ty(t), m, folder.fold_expr(e)) } item_fn(ref decl, purity, abi, ref generics, ref body) => { item_fn( fold_fn_decl(decl, folder), purity, abi, fold_generics(generics, folder), folder.fold_block(body) ) } item_mod(ref m) => item_mod(folder.fold_mod(m)), item_foreign_mod(ref nm) => { item_foreign_mod(folder.fold_foreign_mod(nm)) } item_ty(ref t, ref generics) => { item_ty(folder.fold_ty(t), fold_generics(generics, folder)) } item_enum(ref enum_definition, ref generics) => { item_enum( ast::enum_def { variants: do enum_definition.variants.map |x| { folder.fold_variant(x) }, }, fold_generics(generics, folder)) } item_struct(ref struct_def, ref generics) => { let struct_def = fold_struct_def(*struct_def, folder); item_struct(struct_def, fold_generics(generics, folder)) } item_impl(ref generics, ref ifce, ref ty, ref methods) => { item_impl(fold_generics(generics, folder), ifce.as_ref().map(|p| fold_trait_ref(p, folder)), folder.fold_ty(ty), methods.map(|x| folder.fold_method(*x)) ) } item_trait(ref generics, ref traits, ref methods) => { let methods = do methods.map |method| { match *method { required(ref m) => required(folder.fold_type_method(m)), provided(method) => provided(folder.fold_method(method)) } }; item_trait(fold_generics(generics, folder), traits.map(|p| fold_trait_ref(p, folder)), methods) } item_mac(ref m) => item_mac(folder.fold_mac(m)), } } pub fn noop_fold_type_method(m: &TypeMethod, fld: &T) -> TypeMethod { TypeMethod { ident: fld.fold_ident(m.ident), attrs: m.attrs.map(|a| fold_attribute_(*a, fld)), purity: m.purity, decl: fold_fn_decl(&m.decl, fld), generics: fold_generics(&m.generics, fld), explicit_self: m.explicit_self, id: fld.new_id(m.id), span: fld.new_span(m.span), } } pub fn noop_fold_mod(m: &_mod, folder: &T) -> _mod { ast::_mod { view_items: m.view_items .iter() .map(|x| folder.fold_view_item(x)).collect(), items: m.items.iter().filter_map(|x| folder.fold_item(*x)).collect(), } } pub fn noop_fold_crate(c: Crate, folder: &T) -> Crate { let fold_meta_item = |x| fold_meta_item_(x, folder); let fold_attribute = |x| fold_attribute_(x, folder); Crate { module: folder.fold_mod(&c.module), attrs: c.attrs.map(|x| fold_attribute(*x)), config: c.config.map(|x| fold_meta_item(*x)), span: folder.new_span(c.span), } } pub fn noop_fold_item(i: @ast::item, folder: &T) -> Option<@ast::item> { let fold_attribute = |x| fold_attribute_(x, folder); Some(@ast::item { ident: folder.fold_ident(i.ident), attrs: i.attrs.map(|e| fold_attribute(*e)), id: folder.new_id(i.id), node: folder.fold_item_underscore(&i.node), vis: i.vis, span: folder.new_span(i.span) }) } pub fn noop_fold_expr(e: @ast::Expr, folder: &T) -> @ast::Expr { let fold_field = |x| fold_field_(x, folder); let node = match e.node { ExprVstore(e, v) => { ExprVstore(folder.fold_expr(e), v) } ExprVec(ref exprs, mutt) => { ExprVec(folder.map_exprs(|x| folder.fold_expr(x), *exprs), mutt) } ExprRepeat(expr, count, mutt) => { ExprRepeat(folder.fold_expr(expr), folder.fold_expr(count), mutt) } ExprTup(ref elts) => ExprTup(elts.map(|x| folder.fold_expr(*x))), ExprCall(f, ref args, blk) => { ExprCall(folder.fold_expr(f), folder.map_exprs(|x| folder.fold_expr(x), *args), blk) } ExprMethodCall(callee_id, f, i, ref tps, ref args, blk) => { ExprMethodCall( folder.new_id(callee_id), folder.fold_expr(f), folder.fold_ident(i), tps.map(|x| folder.fold_ty(x)), folder.map_exprs(|x| folder.fold_expr(x), *args), blk ) } ExprBinary(callee_id, binop, lhs, rhs) => { ExprBinary(folder.new_id(callee_id), binop, folder.fold_expr(lhs), folder.fold_expr(rhs)) } ExprUnary(callee_id, binop, ohs) => { ExprUnary(folder.new_id(callee_id), binop, folder.fold_expr(ohs)) } ExprDoBody(f) => ExprDoBody(folder.fold_expr(f)), ExprLit(_) => e.node.clone(), ExprCast(expr, ref ty) => { ExprCast(folder.fold_expr(expr), folder.fold_ty(ty)) } ExprAddrOf(m, ohs) => ExprAddrOf(m, folder.fold_expr(ohs)), ExprIf(cond, ref tr, fl) => { ExprIf(folder.fold_expr(cond), folder.fold_block(tr), fl.map(|x| folder.fold_expr(x))) } ExprWhile(cond, ref body) => { ExprWhile(folder.fold_expr(cond), folder.fold_block(body)) } ExprForLoop(pat, iter, ref body, ref maybe_ident) => { ExprForLoop(folder.fold_pat(pat), folder.fold_expr(iter), folder.fold_block(body), maybe_ident.map(|i| folder.fold_ident(i))) } ExprLoop(ref body, opt_ident) => { ExprLoop(folder.fold_block(body), opt_ident.map(|x| folder.fold_ident(x))) } ExprMatch(expr, ref arms) => { ExprMatch(folder.fold_expr(expr), arms.map(|x| folder.fold_arm(x))) } ExprFnBlock(ref decl, ref body) => { ExprFnBlock( fold_fn_decl(decl, folder), folder.fold_block(body) ) } ExprProc(ref decl, ref body) => { ExprProc(fold_fn_decl(decl, folder), folder.fold_block(body)) } ExprBlock(ref blk) => ExprBlock(folder.fold_block(blk)), ExprAssign(el, er) => { ExprAssign(folder.fold_expr(el), folder.fold_expr(er)) } ExprAssignOp(callee_id, op, el, er) => { ExprAssignOp(folder.new_id(callee_id), op, folder.fold_expr(el), folder.fold_expr(er)) } ExprField(el, id, ref tys) => { ExprField(folder.fold_expr(el), folder.fold_ident(id), tys.map(|x| folder.fold_ty(x))) } ExprIndex(callee_id, el, er) => { ExprIndex(folder.new_id(callee_id), folder.fold_expr(el), folder.fold_expr(er)) } ExprPath(ref pth) => ExprPath(folder.fold_path(pth)), ExprSelf => ExprSelf, ExprLogLevel => ExprLogLevel, ExprBreak(opt_ident) => ExprBreak(opt_ident), ExprAgain(opt_ident) => ExprAgain(opt_ident), ExprRet(ref e) => { ExprRet(e.map(|x| folder.fold_expr(x))) } ExprInlineAsm(ref a) => { ExprInlineAsm(inline_asm { inputs: a.inputs.map(|&(c, input)| (c, folder.fold_expr(input))), outputs: a.outputs.map(|&(c, out)| (c, folder.fold_expr(out))), .. (*a).clone() }) } ExprMac(ref mac) => ExprMac(folder.fold_mac(mac)), ExprStruct(ref path, ref fields, maybe_expr) => { ExprStruct(folder.fold_path(path), fields.map(|x| fold_field(*x)), maybe_expr.map(|x| folder.fold_expr(x))) }, ExprParen(ex) => ExprParen(folder.fold_expr(ex)) }; @Expr { id: folder.new_id(e.id), node: node, span: folder.new_span(e.span), } } pub fn noop_fold_stmt(s: &Stmt, folder: &T) -> Option<@Stmt> { let node = match s.node { StmtDecl(d, nid) => { match folder.fold_decl(d) { Some(d) => Some(StmtDecl(d, folder.new_id(nid))), None => None, } } StmtExpr(e, nid) => { Some(StmtExpr(folder.fold_expr(e), folder.new_id(nid))) } StmtSemi(e, nid) => { Some(StmtSemi(folder.fold_expr(e), folder.new_id(nid))) } StmtMac(ref mac, semi) => Some(StmtMac(folder.fold_mac(mac), semi)) }; node.map(|node| @Spanned { node: node, span: folder.new_span(s.span), }) } #[cfg(test)] mod test { use ast; use util::parser_testing::{string_to_crate, matches_codepattern}; use parse::token; use print::pprust; use super::*; // this version doesn't care about getting comments or docstrings in. fn fake_print_crate(s: @pprust::ps, crate: &ast::Crate) { pprust::print_mod(s, &crate.module, crate.attrs); } // change every identifier to "zz" struct ToZzIdentFolder; impl ast_fold for ToZzIdentFolder { fn fold_ident(&self, _: ast::Ident) -> ast::Ident { token::str_to_ident("zz") } } // maybe add to expand.rs... macro_rules! assert_pred ( ($pred:expr, $predname:expr, $a:expr , $b:expr) => ( { let pred_val = $pred; let a_val = $a; let b_val = $b; if !(pred_val(a_val,b_val)) { fail!("expected args satisfying {}, got {:?} and {:?}", $predname, a_val, b_val); } } ) ) // make sure idents get transformed everywhere #[test] fn ident_transformation () { let zz_fold = ToZzIdentFolder; let ast = string_to_crate(@"#[a] mod b {fn c (d : e, f : g) {h!(i,j,k);l;m}}"); assert_pred!(matches_codepattern, "matches_codepattern", pprust::to_str(&zz_fold.fold_crate(ast),fake_print_crate, token::get_ident_interner()), ~"#[a]mod zz{fn zz(zz:zz,zz:zz){zz!(zz,zz,zz);zz;zz}}"); } // even inside macro defs.... #[test] fn ident_transformation_in_defs () { let zz_fold = ToZzIdentFolder; let ast = string_to_crate(@"macro_rules! a {(b $c:expr $(d $e:token)f+ => (g $(d $d $e)+))} "); assert_pred!(matches_codepattern, "matches_codepattern", pprust::to_str(&zz_fold.fold_crate(ast),fake_print_crate, token::get_ident_interner()), ~"zz!zz((zz$zz:zz$(zz $zz:zz)zz+=>(zz$(zz$zz$zz)+)))"); } }