// 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 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. //! This module provides a simplified abstraction for working with //! code blocks identified by their integer node-id. In particular, //! it captures a common set of attributes that all "function-like //! things" (represented by `FnLike` instances) share. For example, //! all `FnLike` instances have a type signature (be it explicit or //! inferred). And all `FnLike` instances have a body, i.e. the code //! that is run when the function-like thing it represents is invoked. //! //! With the above abstraction in place, one can treat the program //! text as a collection of blocks of code (and most such blocks are //! nested within a uniquely determined `FnLike`), and users can ask //! for the `Code` associated with a particular NodeId. pub use self::Code::*; use abi; use ast::{Block, FnDecl, NodeId}; use ast; use ast_map::{Node}; use ast_map; use codemap::Span; use visit; /// An FnLikeNode is a Node that is like a fn, in that it has a decl /// and a body (as well as a NodeId, a span, etc). /// /// More specifically, it is one of either: /// - A function item, /// - A closure expr (i.e. an ExprClosure), or /// - The default implementation for a trait method. /// /// To construct one, use the `Code::from_node` function. #[derive(Copy)] pub struct FnLikeNode<'a> { node: ast_map::Node<'a> } /// MaybeFnLike wraps a method that indicates if an object /// corresponds to some FnLikeNode. pub trait MaybeFnLike { fn is_fn_like(&self) -> bool; } /// Components shared by fn-like things (fn items, methods, closures). pub struct FnParts<'a> { pub decl: &'a FnDecl, pub body: &'a Block, pub kind: visit::FnKind<'a>, pub span: Span, pub id: NodeId, } impl MaybeFnLike for ast::Item { fn is_fn_like(&self) -> bool { match self.node { ast::ItemFn(..) => true, _ => false, } } } impl MaybeFnLike for ast::TraitItem { fn is_fn_like(&self) -> bool { match self.node { ast::MethodTraitItem(_, Some(_)) => true, _ => false, } } } impl MaybeFnLike for ast::Expr { fn is_fn_like(&self) -> bool { match self.node { ast::ExprClosure(..) => true, _ => false, } } } /// Carries either an FnLikeNode or a Block, as these are the two /// constructs that correspond to "code" (as in, something from which /// we can construct a control-flow graph). #[derive(Copy)] pub enum Code<'a> { FnLikeCode(FnLikeNode<'a>), BlockCode(&'a Block), } impl<'a> Code<'a> { pub fn id(&self) -> ast::NodeId { match *self { FnLikeCode(node) => node.id(), BlockCode(block) => block.id, } } /// Attempts to construct a Code from presumed FnLike or Block node input. pub fn from_node(node: Node) -> Option { fn new(node: Node) -> FnLikeNode { FnLikeNode { node: node } } match node { ast_map::NodeItem(item) if item.is_fn_like() => Some(FnLikeCode(new(node))), ast_map::NodeTraitItem(tm) if tm.is_fn_like() => Some(FnLikeCode(new(node))), ast_map::NodeImplItem(_) => Some(FnLikeCode(new(node))), ast_map::NodeExpr(e) if e.is_fn_like() => Some(FnLikeCode(new(node))), ast_map::NodeBlock(block) => Some(BlockCode(block)), _ => None, } } } /// These are all the components one can extract from a fn item for /// use when implementing FnLikeNode operations. struct ItemFnParts<'a> { ident: ast::Ident, decl: &'a ast::FnDecl, unsafety: ast::Unsafety, abi: abi::Abi, generics: &'a ast::Generics, body: &'a Block, id: ast::NodeId, span: Span } /// These are all the components one can extract from a closure expr /// for use when implementing FnLikeNode operations. struct ClosureParts<'a> { decl: &'a FnDecl, body: &'a Block, id: NodeId, span: Span } impl<'a> ClosureParts<'a> { fn new(d: &'a FnDecl, b: &'a Block, id: NodeId, s: Span) -> ClosureParts<'a> { ClosureParts { decl: d, body: b, id: id, span: s } } } impl<'a> FnLikeNode<'a> { pub fn to_fn_parts(self) -> FnParts<'a> { FnParts { decl: self.decl(), body: self.body(), kind: self.kind(), span: self.span(), id: self.id(), } } pub fn body(self) -> &'a Block { self.handle(|i: ItemFnParts<'a>| &*i.body, |_, _, _: &'a ast::MethodSig, body: &'a ast::Block, _| body, |c: ClosureParts<'a>| c.body) } pub fn decl(self) -> &'a FnDecl { self.handle(|i: ItemFnParts<'a>| &*i.decl, |_, _, sig: &'a ast::MethodSig, _, _| &sig.decl, |c: ClosureParts<'a>| c.decl) } pub fn span(self) -> Span { self.handle(|i: ItemFnParts| i.span, |_, _, _: &'a ast::MethodSig, _, span| span, |c: ClosureParts| c.span) } pub fn id(self) -> NodeId { self.handle(|i: ItemFnParts| i.id, |id, _, _: &'a ast::MethodSig, _, _| id, |c: ClosureParts| c.id) } pub fn kind(self) -> visit::FnKind<'a> { let item = |p: ItemFnParts<'a>| -> visit::FnKind<'a> { visit::FkItemFn(p.ident, p.generics, p.unsafety, p.abi) }; let closure = |_: ClosureParts| { visit::FkFnBlock }; let method = |_, ident, sig: &'a ast::MethodSig, _, _| { visit::FkMethod(ident, sig) }; self.handle(item, method, closure) } fn handle(self, item_fn: I, method: M, closure: C) -> A where I: FnOnce(ItemFnParts<'a>) -> A, M: FnOnce(NodeId, ast::Ident, &'a ast::MethodSig, &'a ast::Block, Span) -> A, C: FnOnce(ClosureParts<'a>) -> A, { match self.node { ast_map::NodeItem(i) => match i.node { ast::ItemFn(ref decl, unsafety, abi, ref generics, ref block) => item_fn(ItemFnParts{ ident: i.ident, decl: &**decl, unsafety: unsafety, body: &**block, generics: generics, abi: abi, id: i.id, span: i.span }), _ => panic!("item FnLikeNode that is not fn-like"), }, ast_map::NodeTraitItem(ti) => match ti.node { ast::MethodTraitItem(ref sig, Some(ref body)) => { method(ti.id, ti.ident, sig, body, ti.span) } _ => panic!("trait method FnLikeNode that is not fn-like"), }, ast_map::NodeImplItem(ii) => { match ii.node { ast::MethodImplItem(ref sig, ref body) => { method(ii.id, ii.ident, sig, body, ii.span) } ast::TypeImplItem(_) | ast::MacImplItem(_) => { panic!("impl method FnLikeNode that is not fn-like") } } } ast_map::NodeExpr(e) => match e.node { ast::ExprClosure(_, ref decl, ref block) => closure(ClosureParts::new(&**decl, &**block, e.id, e.span)), _ => panic!("expr FnLikeNode that is not fn-like"), }, _ => panic!("other FnLikeNode that is not fn-like"), } } }