// 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 core::prelude::*; use abi::AbiSet; use ast::*; use ast; use codemap::span; use parse; use opt_vec; use opt_vec::OptVec; // Context-passing AST walker. Each overridden visit method has full control // over what happens with its node, it can do its own traversal of the node's // children (potentially passing in different contexts to each), call // visit::visit_* to apply the default traversal algorithm (again, it can // override the context), or prevent deeper traversal by doing nothing. // // Note: it is an important invariant that the default visitor walks the body // of a function in "execution order" (more concretely, reverse post-order // with respect to the CFG implied by the AST), meaning that if AST node A may // execute before AST node B, then A is visited first. The borrow checker in // particular relies on this property. // Our typesystem doesn't do circular types, so the visitor record can not // hold functions that take visitors. A vt enum is used to break the cycle. pub enum vt { mk_vt(visitor), } pub enum fn_kind<'self> { // fn foo() or extern "Abi" fn foo() fk_item_fn(ident, &'self Generics, purity, AbiSet), // fn foo(&self) fk_method(ident, &'self Generics, &'self method), // fn@(x, y) { ... } fk_anon(ast::Sigil), // |x, y| ... fk_fn_block, } pub fn name_of_fn(fk: &fn_kind) -> ident { match *fk { fk_item_fn(name, _, _, _) | fk_method(name, _, _) => { name } fk_anon(*) | fk_fn_block(*) => parse::token::special_idents::anon, } } pub fn generics_of_fn(fk: &fn_kind) -> Generics { match *fk { fk_item_fn(_, generics, _, _) | fk_method(_, generics, _) => { copy *generics } fk_anon(*) | fk_fn_block(*) => { Generics { lifetimes: opt_vec::Empty, ty_params: opt_vec::Empty, } } } } pub struct Visitor { visit_mod: @fn(&_mod, span, node_id, E, vt), visit_view_item: @fn(@view_item, E, vt), visit_foreign_item: @fn(@foreign_item, E, vt), visit_item: @fn(@item, E, vt), visit_local: @fn(@local, E, vt), visit_block: @fn(&blk, E, vt), visit_stmt: @fn(@stmt, E, vt), visit_arm: @fn(&arm, E, vt), visit_pat: @fn(@pat, E, vt), visit_decl: @fn(@decl, E, vt), visit_expr: @fn(@expr, E, vt), visit_expr_post: @fn(@expr, E, vt), visit_ty: @fn(@Ty, E, vt), visit_generics: @fn(&Generics, E, vt), visit_fn: @fn(&fn_kind, &fn_decl, &blk, span, node_id, E, vt), visit_ty_method: @fn(&ty_method, E, vt), visit_trait_method: @fn(&trait_method, E, vt), visit_struct_def: @fn(@struct_def, ident, &Generics, node_id, E, vt), visit_struct_field: @fn(@struct_field, E, vt), visit_struct_method: @fn(@method, E, vt) } pub type visitor = @Visitor; pub fn default_visitor() -> visitor { return @Visitor { visit_mod: |a,b,c,d,e|visit_mod::(a, b, c, d, e), visit_view_item: |a,b,c|visit_view_item::(a, b, c), visit_foreign_item: |a,b,c|visit_foreign_item::(a, b, c), visit_item: |a,b,c|visit_item::(a, b, c), visit_local: |a,b,c|visit_local::(a, b, c), visit_block: |a,b,c|visit_block::(a, b, c), visit_stmt: |a,b,c|visit_stmt::(a, b, c), visit_arm: |a,b,c|visit_arm::(a, b, c), visit_pat: |a,b,c|visit_pat::(a, b, c), visit_decl: |a,b,c|visit_decl::(a, b, c), visit_expr: |a,b,c|visit_expr::(a, b, c), visit_expr_post: |_a,_b,_c| (), visit_ty: |a,b,c|skip_ty::(a, b, c), visit_generics: |a,b,c|visit_generics::(a, b, c), visit_fn: |a,b,c,d,e,f,g|visit_fn::(a, b, c, d, e, f, g), visit_ty_method: |a,b,c|visit_ty_method::(a, b, c), visit_trait_method: |a,b,c|visit_trait_method::(a, b, c), visit_struct_def: |a,b,c,d,e,f|visit_struct_def::(a, b, c, d, e, f), visit_struct_field: |a,b,c|visit_struct_field::(a, b, c), visit_struct_method: |a,b,c|visit_struct_method::(a, b, c) }; } pub fn visit_crate(c: &crate, e: E, v: vt) { (v.visit_mod)(&c.node.module, c.span, crate_node_id, e, v); } pub fn visit_mod(m: &_mod, _sp: span, _id: node_id, e: E, v: vt) { for m.view_items.each |vi| { (v.visit_view_item)(*vi, e, v); } for m.items.each |i| { (v.visit_item)(*i, e, v); } } pub fn visit_view_item(_vi: @view_item, _e: E, _v: vt) { } pub fn visit_local(loc: @local, e: E, v: vt) { (v.visit_pat)(loc.node.pat, e, v); (v.visit_ty)(loc.node.ty, e, v); match loc.node.init { None => (), Some(ex) => (v.visit_expr)(ex, e, v) } } fn visit_trait_ref(tref: @ast::trait_ref, e: E, v: vt) { visit_path(tref.path, e, v); } pub fn visit_item(i: @item, e: E, v: vt) { match i.node { item_const(t, ex) => { (v.visit_ty)(t, e, v); (v.visit_expr)(ex, e, v); } item_fn(ref decl, purity, abi, ref generics, ref body) => { (v.visit_fn)( &fk_item_fn( i.ident, generics, purity, abi ), decl, body, i.span, i.id, e, v ); } item_mod(ref m) => (v.visit_mod)(m, i.span, i.id, e, v), item_foreign_mod(ref nm) => { for nm.view_items.each |vi| { (v.visit_view_item)(*vi, e, v); } for nm.items.each |ni| { (v.visit_foreign_item)(*ni, e, v); } } item_ty(t, ref tps) => { (v.visit_ty)(t, e, v); (v.visit_generics)(tps, e, v); } item_enum(ref enum_definition, ref tps) => { (v.visit_generics)(tps, e, v); visit_enum_def( enum_definition, tps, e, v ); } item_impl(ref tps, ref traits, ty, ref methods) => { (v.visit_generics)(tps, e, v); for traits.each |&p| { visit_trait_ref(p, e, v); } (v.visit_ty)(ty, e, v); for methods.each |m| { visit_method_helper(*m, e, v) } } item_struct(struct_def, ref generics) => { (v.visit_generics)(generics, e, v); (v.visit_struct_def)(struct_def, i.ident, generics, i.id, e, v); } item_trait(ref generics, ref traits, ref methods) => { (v.visit_generics)(generics, e, v); for traits.each |p| { visit_path(p.path, e, v); } for methods.each |m| { (v.visit_trait_method)(m, e, v); } } item_mac(ref m) => visit_mac(m, e, v) } } pub fn visit_enum_def(enum_definition: &ast::enum_def, tps: &Generics, e: E, v: vt) { for enum_definition.variants.each |vr| { match vr.node.kind { tuple_variant_kind(ref variant_args) => { for variant_args.each |va| { (v.visit_ty)(va.ty, e, v); } } struct_variant_kind(struct_def) => { (v.visit_struct_def)(struct_def, vr.node.name, tps, vr.node.id, e, v); } } // Visit the disr expr if it exists for vr.node.disr_expr.each |ex| { (v.visit_expr)(*ex, e, v) } } } pub fn skip_ty(_t: @Ty, _e: E, _v: vt) {} pub fn visit_ty(t: @Ty, e: E, v: vt) { match t.node { ty_box(mt) | ty_uniq(mt) | ty_vec(mt) | ty_ptr(mt) | ty_rptr(_, mt) => { (v.visit_ty)(mt.ty, e, v); }, ty_tup(ref ts) => { for ts.each |tt| { (v.visit_ty)(*tt, e, v); } }, ty_closure(ref f) => { for f.decl.inputs.each |a| { (v.visit_ty)(a.ty, e, v); } (v.visit_ty)(f.decl.output, e, v); }, ty_bare_fn(ref f) => { for f.decl.inputs.each |a| { (v.visit_ty)(a.ty, e, v); } (v.visit_ty)(f.decl.output, e, v); }, ty_path(p, _) => visit_path(p, e, v), ty_fixed_length_vec(ref mt, ex) => { (v.visit_ty)(mt.ty, e, v); (v.visit_expr)(ex, e, v); }, ty_nil | ty_bot | ty_mac(_) | ty_infer => () } } pub fn visit_path(p: @Path, e: E, v: vt) { for p.types.each |tp| { (v.visit_ty)(*tp, e, v); } } pub fn visit_pat(p: @pat, e: E, v: vt) { match p.node { pat_enum(path, ref children) => { visit_path(path, e, v); for children.each |children| { for children.each |child| { (v.visit_pat)(*child, e, v); } } } pat_struct(path, ref fields, _) => { visit_path(path, e, v); for fields.each |f| { (v.visit_pat)(f.pat, e, v); } } pat_tup(ref elts) => { for elts.each |elt| { (v.visit_pat)(*elt, e, v) } }, pat_box(inner) | pat_uniq(inner) | pat_region(inner) => { (v.visit_pat)(inner, e, v) }, pat_ident(_, path, ref inner) => { visit_path(path, e, v); for inner.each |subpat| { (v.visit_pat)(*subpat, e, v) } } pat_lit(ex) => (v.visit_expr)(ex, e, v), pat_range(e1, e2) => { (v.visit_expr)(e1, e, v); (v.visit_expr)(e2, e, v); } pat_wild => (), pat_vec(ref before, ref slice, ref after) => { for before.each |elt| { (v.visit_pat)(*elt, e, v); } for slice.each |elt| { (v.visit_pat)(*elt, e, v); } for after.each |tail| { (v.visit_pat)(*tail, e, v); } } } } pub fn visit_foreign_item(ni: @foreign_item, e: E, v: vt) { match ni.node { foreign_item_fn(ref fd, _, ref generics) => { visit_fn_decl(fd, e, v); (v.visit_generics)(generics, e, v); } foreign_item_const(t) => { (v.visit_ty)(t, e, v); } } } pub fn visit_ty_param_bounds(bounds: @OptVec, e: E, v: vt) { for bounds.each |bound| { match *bound { TraitTyParamBound(ty) => visit_trait_ref(ty, e, v), RegionTyParamBound => {} } } } pub fn visit_generics(generics: &Generics, e: E, v: vt) { for generics.ty_params.each |tp| { visit_ty_param_bounds(tp.bounds, e, v); } } pub fn visit_fn_decl(fd: &fn_decl, e: E, v: vt) { for fd.inputs.each |a| { (v.visit_pat)(a.pat, e, v); (v.visit_ty)(a.ty, e, v); } (v.visit_ty)(fd.output, e, v); } // Note: there is no visit_method() method in the visitor, instead override // visit_fn() and check for fk_method(). I named this visit_method_helper() // because it is not a default impl of any method, though I doubt that really // clarifies anything. - Niko pub fn visit_method_helper(m: &method, e: E, v: vt) { (v.visit_fn)( &fk_method( /* FIXME (#2543) */ copy m.ident, &m.generics, m ), &m.decl, &m.body, m.span, m.id, e, v ); } pub fn visit_fn(fk: &fn_kind, decl: &fn_decl, body: &blk, _sp: span, _id: node_id, e: E, v: vt) { visit_fn_decl(decl, e, v); let generics = generics_of_fn(fk); (v.visit_generics)(&generics, e, v); (v.visit_block)(body, e, v); } pub fn visit_ty_method(m: &ty_method, e: E, v: vt) { for m.decl.inputs.each |a| { (v.visit_ty)(a.ty, e, v); } (v.visit_generics)(&m.generics, e, v); (v.visit_ty)(m.decl.output, e, v); } pub fn visit_trait_method(m: &trait_method, e: E, v: vt) { match *m { required(ref ty_m) => (v.visit_ty_method)(ty_m, e, v), provided(m) => visit_method_helper(m, e, v) } } pub fn visit_struct_def( sd: @struct_def, _nm: ast::ident, _generics: &Generics, _id: node_id, e: E, v: vt ) { for sd.fields.each |f| { (v.visit_struct_field)(*f, e, v); } } pub fn visit_struct_field(sf: @struct_field, e: E, v: vt) { (v.visit_ty)(sf.node.ty, e, v); } pub fn visit_struct_method(m: @method, e: E, v: vt) { visit_method_helper(m, e, v); } pub fn visit_block(b: &blk, e: E, v: vt) { for b.node.view_items.each |vi| { (v.visit_view_item)(*vi, e, v); } for b.node.stmts.each |s| { (v.visit_stmt)(*s, e, v); } visit_expr_opt(b.node.expr, e, v); } pub fn visit_stmt(s: @stmt, e: E, v: vt) { match s.node { stmt_decl(d, _) => (v.visit_decl)(d, e, v), stmt_expr(ex, _) => (v.visit_expr)(ex, e, v), stmt_semi(ex, _) => (v.visit_expr)(ex, e, v), stmt_mac(ref mac, _) => visit_mac(mac, e, v) } } pub fn visit_decl(d: @decl, e: E, v: vt) { match d.node { decl_local(ref loc) => (v.visit_local)(*loc, e, v), decl_item(it) => (v.visit_item)(it, e, v) } } pub fn visit_expr_opt(eo: Option<@expr>, e: E, v: vt) { match eo { None => (), Some(ex) => (v.visit_expr)(ex, e, v) } } pub fn visit_exprs(exprs: &[@expr], e: E, v: vt) { for exprs.each |ex| { (v.visit_expr)(*ex, e, v); } } pub fn visit_mac(_m: &mac, _e: E, _v: vt) { /* no user-serviceable parts inside */ } pub fn visit_expr(ex: @expr, e: E, v: vt) { match ex.node { expr_vstore(x, _) => (v.visit_expr)(x, e, v), expr_vec(ref es, _) => visit_exprs(*es, e, v), expr_repeat(element, count, _) => { (v.visit_expr)(element, e, v); (v.visit_expr)(count, e, v); } expr_struct(p, ref flds, base) => { visit_path(p, e, v); for flds.each |f| { (v.visit_expr)(f.node.expr, e, v); } visit_expr_opt(base, e, v); } expr_tup(ref elts) => { for elts.each |el| { (v.visit_expr)(*el, e, v) } } expr_call(callee, ref args, _) => { visit_exprs(*args, e, v); (v.visit_expr)(callee, e, v); } expr_method_call(_, callee, _, ref tys, ref args, _) => { visit_exprs(*args, e, v); for tys.each |tp| { (v.visit_ty)(*tp, e, v); } (v.visit_expr)(callee, e, v); } expr_binary(_, _, a, b) => { (v.visit_expr)(a, e, v); (v.visit_expr)(b, e, v); } expr_addr_of(_, x) | expr_unary(_, _, x) | expr_loop_body(x) | expr_do_body(x) => (v.visit_expr)(x, e, v), expr_lit(_) => (), expr_cast(x, t) => { (v.visit_expr)(x, e, v); (v.visit_ty)(t, e, v); } expr_if(x, ref b, eo) => { (v.visit_expr)(x, e, v); (v.visit_block)(b, e, v); visit_expr_opt(eo, e, v); } expr_while(x, ref b) => { (v.visit_expr)(x, e, v); (v.visit_block)(b, e, v); } expr_loop(ref b, _) => (v.visit_block)(b, e, v), expr_match(x, ref arms) => { (v.visit_expr)(x, e, v); for arms.each |a| { (v.visit_arm)(a, e, v); } } expr_fn_block(ref decl, ref body) => { (v.visit_fn)( &fk_fn_block, decl, body, ex.span, ex.id, e, v ); } expr_block(ref b) => (v.visit_block)(b, e, v), expr_assign(a, b) => { (v.visit_expr)(b, e, v); (v.visit_expr)(a, e, v); } expr_copy(a) => (v.visit_expr)(a, e, v), expr_assign_op(_, _, a, b) => { (v.visit_expr)(b, e, v); (v.visit_expr)(a, e, v); } expr_field(x, _, ref tys) => { (v.visit_expr)(x, e, v); for tys.each |tp| { (v.visit_ty)(*tp, e, v); } } expr_index(_, a, b) => { (v.visit_expr)(a, e, v); (v.visit_expr)(b, e, v); } expr_path(p) => visit_path(p, e, v), expr_self => (), expr_break(_) => (), expr_again(_) => (), expr_ret(eo) => visit_expr_opt(eo, e, v), expr_log(lv, x) => { (v.visit_expr)(lv, e, v); (v.visit_expr)(x, e, v); } expr_mac(ref mac) => visit_mac(mac, e, v), expr_paren(x) => (v.visit_expr)(x, e, v), expr_inline_asm(ref a) => { for a.inputs.each |&(_, in)| { (v.visit_expr)(in, e, v); } for a.outputs.each |&(_, out)| { (v.visit_expr)(out, e, v); } } } (v.visit_expr_post)(ex, e, v); } pub fn visit_arm(a: &arm, e: E, v: vt) { for a.pats.each |p| { (v.visit_pat)(*p, e, v); } visit_expr_opt(a.guard, e, v); (v.visit_block)(&a.body, e, v); } // Simpler, non-context passing interface. Always walks the whole tree, simply // calls the given functions on the nodes. pub struct SimpleVisitor { visit_mod: @fn(&_mod, span, node_id), visit_view_item: @fn(@view_item), visit_foreign_item: @fn(@foreign_item), visit_item: @fn(@item), visit_local: @fn(@local), visit_block: @fn(&blk), visit_stmt: @fn(@stmt), visit_arm: @fn(&arm), visit_pat: @fn(@pat), visit_decl: @fn(@decl), visit_expr: @fn(@expr), visit_expr_post: @fn(@expr), visit_ty: @fn(@Ty), visit_generics: @fn(&Generics), visit_fn: @fn(&fn_kind, &fn_decl, &blk, span, node_id), visit_ty_method: @fn(&ty_method), visit_trait_method: @fn(&trait_method), visit_struct_def: @fn(@struct_def, ident, &Generics, node_id), visit_struct_field: @fn(@struct_field), visit_struct_method: @fn(@method) } pub type simple_visitor = @SimpleVisitor; pub fn simple_ignore_ty(_t: @Ty) {} pub fn default_simple_visitor() -> @SimpleVisitor { @SimpleVisitor { visit_mod: |_m, _sp, _id| { }, visit_view_item: |_vi| { }, visit_foreign_item: |_ni| { }, visit_item: |_i| { }, visit_local: |_l| { }, visit_block: |_b| { }, visit_stmt: |_s| { }, visit_arm: |_a| { }, visit_pat: |_p| { }, visit_decl: |_d| { }, visit_expr: |_e| { }, visit_expr_post: |_e| { }, visit_ty: simple_ignore_ty, visit_generics: |_| {}, visit_fn: |_, _, _, _, _| {}, visit_ty_method: |_| {}, visit_trait_method: |_| {}, visit_struct_def: |_, _, _, _| {}, visit_struct_field: |_| {}, visit_struct_method: |_| {}, } } pub fn mk_simple_visitor(v: simple_visitor) -> vt<()> { fn v_mod( f: @fn(&_mod, span, node_id), m: &_mod, sp: span, id: node_id, e: (), v: vt<()> ) { f(m, sp, id); visit_mod(m, sp, id, e, v); } fn v_view_item(f: @fn(@view_item), vi: @view_item, e: (), v: vt<()>) { f(vi); visit_view_item(vi, e, v); } fn v_foreign_item(f: @fn(@foreign_item), ni: @foreign_item, e: (), v: vt<()>) { f(ni); visit_foreign_item(ni, e, v); } fn v_item(f: @fn(@item), i: @item, e: (), v: vt<()>) { f(i); visit_item(i, e, v); } fn v_local(f: @fn(@local), l: @local, e: (), v: vt<()>) { f(l); visit_local(l, e, v); } fn v_block(f: @fn(&ast::blk), bl: &ast::blk, e: (), v: vt<()>) { f(bl); visit_block(bl, e, v); } fn v_stmt(f: @fn(@stmt), st: @stmt, e: (), v: vt<()>) { f(st); visit_stmt(st, e, v); } fn v_arm(f: @fn(&arm), a: &arm, e: (), v: vt<()>) { f(a); visit_arm(a, e, v); } fn v_pat(f: @fn(@pat), p: @pat, e: (), v: vt<()>) { f(p); visit_pat(p, e, v); } fn v_decl(f: @fn(@decl), d: @decl, e: (), v: vt<()>) { f(d); visit_decl(d, e, v); } fn v_expr(f: @fn(@expr), ex: @expr, e: (), v: vt<()>) { f(ex); visit_expr(ex, e, v); } fn v_expr_post(f: @fn(@expr), ex: @expr, _e: (), _v: vt<()>) { f(ex); } fn v_ty(f: @fn(@Ty), ty: @Ty, e: (), v: vt<()>) { f(ty); visit_ty(ty, e, v); } fn v_ty_method(f: @fn(&ty_method), ty: &ty_method, e: (), v: vt<()>) { f(ty); visit_ty_method(ty, e, v); } fn v_trait_method(f: @fn(&trait_method), m: &trait_method, e: (), v: vt<()>) { f(m); visit_trait_method(m, e, v); } fn v_struct_def( f: @fn(@struct_def, ident, &Generics, node_id), sd: @struct_def, nm: ident, generics: &Generics, id: node_id, e: (), v: vt<()> ) { f(sd, nm, generics, id); visit_struct_def(sd, nm, generics, id, e, v); } fn v_generics( f: @fn(&Generics), ps: &Generics, e: (), v: vt<()> ) { f(ps); visit_generics(ps, e, v); } fn v_fn( f: @fn(&fn_kind, &fn_decl, &blk, span, node_id), fk: &fn_kind, decl: &fn_decl, body: &blk, sp: span, id: node_id, e: (), v: vt<()> ) { f(fk, decl, body, sp, id); visit_fn(fk, decl, body, sp, id, e, v); } let visit_ty: @fn(@Ty, x: (), vt<()>) = |a,b,c| v_ty(v.visit_ty, a, b, c); fn v_struct_field(f: @fn(@struct_field), sf: @struct_field, e: (), v: vt<()>) { f(sf); visit_struct_field(sf, e, v); } fn v_struct_method(f: @fn(@method), m: @method, e: (), v: vt<()>) { f(m); visit_struct_method(m, e, v); } return mk_vt(@Visitor { visit_mod: |a,b,c,d,e|v_mod(v.visit_mod, a, b, c, d, e), visit_view_item: |a,b,c| v_view_item(v.visit_view_item, a, b, c), visit_foreign_item: |a,b,c|v_foreign_item(v.visit_foreign_item, a, b, c), visit_item: |a,b,c|v_item(v.visit_item, a, b, c), visit_local: |a,b,c|v_local(v.visit_local, a, b, c), visit_block: |a,b,c|v_block(v.visit_block, a, b, c), visit_stmt: |a,b,c|v_stmt(v.visit_stmt, a, b, c), visit_arm: |a,b,c|v_arm(v.visit_arm, a, b, c), visit_pat: |a,b,c|v_pat(v.visit_pat, a, b, c), visit_decl: |a,b,c|v_decl(v.visit_decl, a, b, c), visit_expr: |a,b,c|v_expr(v.visit_expr, a, b, c), visit_expr_post: |a,b,c| v_expr_post(v.visit_expr_post, a, b, c), visit_ty: visit_ty, visit_generics: |a,b,c| v_generics(v.visit_generics, a, b, c), visit_fn: |a,b,c,d,e,f,g| v_fn(v.visit_fn, a, b, c, d, e, f, g), visit_ty_method: |a,b,c| v_ty_method(v.visit_ty_method, a, b, c), visit_trait_method: |a,b,c| v_trait_method(v.visit_trait_method, a, b, c), visit_struct_def: |a,b,c,d,e,f| v_struct_def(v.visit_struct_def, a, b, c, d, e, f), visit_struct_field: |a,b,c| v_struct_field(v.visit_struct_field, a, b, c), visit_struct_method: |a,b,c| v_struct_method(v.visit_struct_method, a, b, c) }); }