/// Renders text as string suitable for a label in a .dot file.
pub fn escape(&self) -> String {
match self {
- &LabelStr(ref s) => s.as_slice().escape_default().to_string(),
- &EscStr(ref s) => LabelText::escape_str(s.as_slice()).to_string(),
+ &LabelStr(ref s) => s.as_slice().escape_default(),
+ &EscStr(ref s) => LabelText::escape_str(s.as_slice()),
}
}
+
+ /// Decomposes content into string suitable for making EscStr that
+ /// yields same content as self. The result obeys the law
+ /// render(`lt`) == render(`EscStr(lt.pre_escaped_content())`) for
+ /// all `lt: LabelText`.
+ fn pre_escaped_content(self) -> str::MaybeOwned<'a> {
+ match self {
+ EscStr(s) => s,
+ LabelStr(s) => if s.as_slice().contains_char('\\') {
+ str::Owned(s.as_slice().escape_default())
+ } else {
+ s
+ },
+ }
+ }
+
+ /// Puts `prefix` on a line above this label, with a blank line separator.
+ pub fn prefix_line(self, prefix: LabelText) -> LabelText {
+ prefix.suffix_line(self)
+ }
+
+ /// Puts `suffix` on a line below this label, with a blank line separator.
+ pub fn suffix_line(self, suffix: LabelText) -> LabelText {
+ let prefix = self.pre_escaped_content().into_string();
+ let suffix = suffix.pre_escaped_content();
+ EscStr(str::Owned(prefix.append(r"\n\n").append(suffix.as_slice())))
+ }
}
pub type Nodes<'a,N> = MaybeOwnedVector<'a,N>;
let mut writer = MemWriter::new();
render(&g, &mut writer).unwrap();
let mut r = BufReader::new(writer.get_ref());
- match r.read_to_string() {
- Ok(string) => Ok(string.to_string()),
- Err(err) => Err(err),
- }
+ r.read_to_string()
}
// All of the tests use raw-strings as the format for the expected outputs,
AST_JSON,
AST_JSON_NOEXPAND,
LS,
- SAVE_ANALYSIS
+ SAVE_ANALYSIS,
+ FLOWGRAPH_PRINT_LOANS,
+ FLOWGRAPH_PRINT_MOVES,
+ FLOWGRAPH_PRINT_ASSIGNS,
+ FLOWGRAPH_PRINT_ALL
]
0
)
("ast-json-noexpand", "Print the pre-expansion AST as JSON and halt", AST_JSON_NOEXPAND),
("ls", "List the symbols defined by a library crate", LS),
("save-analysis", "Write syntax and type analysis information \
- in addition to normal output", SAVE_ANALYSIS))
+ in addition to normal output", SAVE_ANALYSIS),
+ ("flowgraph-print-loans", "Include loan analysis data in \
+ --pretty flowgraph output", FLOWGRAPH_PRINT_LOANS),
+ ("flowgraph-print-moves", "Include move analysis data in \
+ --pretty flowgraph output", FLOWGRAPH_PRINT_MOVES),
+ ("flowgraph-print-assigns", "Include assignment analysis data in \
+ --pretty flowgraph output", FLOWGRAPH_PRINT_ASSIGNS),
+ ("flowgraph-print-all", "Include all dataflow analysis data in \
+ --pretty flowgraph output", FLOWGRAPH_PRINT_ALL))
}
/// Declare a macro that will define all CodegenOptions fields and parsers all
use lint;
use metadata::common::LinkMeta;
use metadata::creader;
+use middle::borrowck::{FnPartsWithCFG};
+use middle::borrowck;
+use borrowck_dot = middle::borrowck::graphviz;
use middle::cfg;
use middle::cfg::graphviz::LabelledCFG;
use middle::{trans, freevars, stability, kind, ty, typeck, reachable};
use std::io::fs;
use std::io::MemReader;
use syntax::ast;
+use syntax::ast_map::blocks;
use syntax::attr;
use syntax::attr::{AttrMetaMethods};
use syntax::diagnostics;
}
}
+fn gather_flowgraph_variants(sess: &Session) -> Vec<borrowck_dot::Variant> {
+ let print_loans = config::FLOWGRAPH_PRINT_LOANS;
+ let print_moves = config::FLOWGRAPH_PRINT_MOVES;
+ let print_assigns = config::FLOWGRAPH_PRINT_ASSIGNS;
+ let print_all = config::FLOWGRAPH_PRINT_ALL;
+ let opt = |print_which| sess.debugging_opt(print_which);
+ let mut variants = Vec::new();
+ if opt(print_all) || opt(print_loans) {
+ variants.push(borrowck_dot::Loans);
+ }
+ if opt(print_all) || opt(print_moves) {
+ variants.push(borrowck_dot::Moves);
+ }
+ if opt(print_all) || opt(print_assigns) {
+ variants.push(borrowck_dot::Assigns);
+ }
+ variants
+}
+
pub fn pretty_print_input(sess: Session,
cfg: ast::CrateConfig,
input: &Input,
sess.fatal(format!("--pretty flowgraph couldn't find id: {}",
nodeid).as_slice())
});
- let block = match node {
- syntax::ast_map::NodeBlock(block) => block,
- _ => {
- let message = format!("--pretty=flowgraph needs block, got {:?}",
+ let code = blocks::Code::from_node(node);
+ match code {
+ Some(code) => {
+ let variants = gather_flowgraph_variants(&sess);
+ let analysis = phase_3_run_analysis_passes(sess, &krate,
+ ast_map, id);
+ print_flowgraph(variants, analysis, code, out)
+ }
+ None => {
+ let message = format!("--pretty=flowgraph needs \
+ block, fn, or method; got {:?}",
node);
// point to what was found, if there's an
None => sess.fatal(message.as_slice())
}
}
- };
- let analysis = phase_3_run_analysis_passes(sess, &krate,
- ast_map, id);
- print_flowgraph(analysis, block, out)
+ }
}
_ => {
pprust::print_crate(sess.codemap(),
}
-fn print_flowgraph<W:io::Writer>(analysis: CrateAnalysis,
- block: ast::P<ast::Block>,
+fn print_flowgraph<W:io::Writer>(variants: Vec<borrowck_dot::Variant>,
+ analysis: CrateAnalysis,
+ code: blocks::Code,
mut out: W) -> io::IoResult<()> {
let ty_cx = &analysis.ty_cx;
- let cfg = cfg::CFG::new(ty_cx, &*block);
- let lcfg = LabelledCFG { ast_map: &ty_cx.map,
- cfg: &cfg,
- name: format!("block{}", block.id), };
+ let cfg = match code {
+ blocks::BlockCode(block) => cfg::CFG::new(ty_cx, &*block),
+ blocks::FnLikeCode(fn_like) => cfg::CFG::new(ty_cx, fn_like.body()),
+ };
debug!("cfg: {:?}", cfg);
- let r = dot::render(&lcfg, &mut out);
- return expand_err_details(r);
+
+ match code {
+ _ if variants.len() == 0 => {
+ let lcfg = LabelledCFG {
+ ast_map: &ty_cx.map,
+ cfg: &cfg,
+ name: format!("node_{}", code.id()),
+ };
+ let r = dot::render(&lcfg, &mut out);
+ return expand_err_details(r);
+ }
+ blocks::BlockCode(_) => {
+ ty_cx.sess.err("--pretty flowgraph with -Z flowgraph-print \
+ annotations requires fn-like node id.");
+ return Ok(())
+ }
+ blocks::FnLikeCode(fn_like) => {
+ let fn_parts = FnPartsWithCFG::from_fn_like(&fn_like, &cfg);
+ let (bccx, analysis_data) =
+ borrowck::build_borrowck_dataflow_data_for_fn(ty_cx, fn_parts);
+
+ let lcfg = LabelledCFG {
+ ast_map: &ty_cx.map,
+ cfg: &cfg,
+ name: format!("node_{}", code.id()),
+ };
+ let lcfg = borrowck_dot::DataflowLabeller {
+ inner: lcfg,
+ variants: variants,
+ borrowck_ctxt: &bccx,
+ analysis_data: &analysis_data,
+ };
+ let r = dot::render(&lcfg, &mut out);
+ return expand_err_details(r);
+ }
+ }
fn expand_err_details(r: io::IoResult<()>) -> io::IoResult<()> {
r.map_err(|ioerr| {
--- /dev/null
+// 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.
+
+//! This module provides linkage between rustc::middle::graph and
+//! libgraphviz traits, specialized to attaching borrowck analysis
+//! data to rendered labels.
+
+/// For clarity, rename the graphviz crate locally to dot.
+use dot = graphviz;
+pub use middle::cfg::graphviz::{Node, Edge};
+use cfg_dot = middle::cfg::graphviz;
+
+use middle::borrowck;
+use middle::borrowck::{BorrowckCtxt, LoanPath};
+use middle::cfg::{CFGIndex};
+use middle::dataflow::{DataFlowOperator, DataFlowContext, EntryOrExit};
+use middle::dataflow;
+
+use std::rc::Rc;
+use std::str;
+
+#[deriving(Show)]
+pub enum Variant {
+ Loans,
+ Moves,
+ Assigns,
+}
+
+impl Variant {
+ pub fn short_name(&self) -> &'static str {
+ match *self {
+ Loans => "loans",
+ Moves => "moves",
+ Assigns => "assigns",
+ }
+ }
+}
+
+pub struct DataflowLabeller<'a> {
+ pub inner: cfg_dot::LabelledCFG<'a>,
+ pub variants: Vec<Variant>,
+ pub borrowck_ctxt: &'a BorrowckCtxt<'a>,
+ pub analysis_data: &'a borrowck::AnalysisData<'a>,
+}
+
+impl<'a> DataflowLabeller<'a> {
+ fn dataflow_for(&self, e: EntryOrExit, n: &Node<'a>) -> String {
+ let id = n.val1().data.id;
+ debug!("dataflow_for({}, id={}) {}", e, id, self.variants);
+ let mut sets = "".to_string();
+ let mut seen_one = false;
+ for &variant in self.variants.iter() {
+ if seen_one { sets.push_str(" "); } else { seen_one = true; }
+ sets.push_str(variant.short_name());
+ sets.push_str(": ");
+ sets.push_str(self.dataflow_for_variant(e, n, variant).as_slice());
+ }
+ sets
+ }
+
+ fn dataflow_for_variant(&self, e: EntryOrExit, n: &Node, v: Variant) -> String {
+ let cfgidx = n.val0();
+ match v {
+ Loans => self.dataflow_loans_for(e, cfgidx),
+ Moves => self.dataflow_moves_for(e, cfgidx),
+ Assigns => self.dataflow_assigns_for(e, cfgidx),
+ }
+ }
+
+ fn build_set<O:DataFlowOperator>(&self,
+ e: EntryOrExit,
+ cfgidx: CFGIndex,
+ dfcx: &DataFlowContext<'a, O>,
+ to_lp: |uint| -> Rc<LoanPath>) -> String {
+ let mut saw_some = false;
+ let mut set = "{".to_string();
+ dfcx.each_bit_for_node(e, cfgidx, |index| {
+ let lp = to_lp(index);
+ if saw_some {
+ set.push_str(", ");
+ }
+ let loan_str = self.borrowck_ctxt.loan_path_to_string(&*lp);
+ set.push_str(loan_str.as_slice());
+ saw_some = true;
+ true
+ });
+ set.append("}")
+ }
+
+ fn dataflow_loans_for(&self, e: EntryOrExit, cfgidx: CFGIndex) -> String {
+ let dfcx = &self.analysis_data.loans;
+ let loan_index_to_path = |loan_index| {
+ let all_loans = &self.analysis_data.all_loans;
+ all_loans.get(loan_index).loan_path()
+ };
+ self.build_set(e, cfgidx, dfcx, loan_index_to_path)
+ }
+
+ fn dataflow_moves_for(&self, e: EntryOrExit, cfgidx: CFGIndex) -> String {
+ let dfcx = &self.analysis_data.move_data.dfcx_moves;
+ let move_index_to_path = |move_index| {
+ let move_data = &self.analysis_data.move_data.move_data;
+ let moves = move_data.moves.borrow();
+ let move = moves.get(move_index);
+ move_data.path_loan_path(move.path)
+ };
+ self.build_set(e, cfgidx, dfcx, move_index_to_path)
+ }
+
+ fn dataflow_assigns_for(&self, e: EntryOrExit, cfgidx: CFGIndex) -> String {
+ let dfcx = &self.analysis_data.move_data.dfcx_assign;
+ let assign_index_to_path = |assign_index| {
+ let move_data = &self.analysis_data.move_data.move_data;
+ let assignments = move_data.var_assignments.borrow();
+ let assignment = assignments.get(assign_index);
+ move_data.path_loan_path(assignment.path)
+ };
+ self.build_set(e, cfgidx, dfcx, assign_index_to_path)
+ }
+}
+
+impl<'a> dot::Labeller<'a, Node<'a>, Edge<'a>> for DataflowLabeller<'a> {
+ fn graph_id(&'a self) -> dot::Id<'a> { self.inner.graph_id() }
+ fn node_id(&'a self, n: &Node<'a>) -> dot::Id<'a> { self.inner.node_id(n) }
+ fn node_label(&'a self, n: &Node<'a>) -> dot::LabelText<'a> {
+ let prefix = self.dataflow_for(dataflow::Entry, n);
+ let suffix = self.dataflow_for(dataflow::Exit, n);
+ let inner_label = self.inner.node_label(n);
+ inner_label
+ .prefix_line(dot::LabelStr(str::Owned(prefix)))
+ .suffix_line(dot::LabelStr(str::Owned(suffix)))
+ }
+ fn edge_label(&'a self, e: &Edge<'a>) -> dot::LabelText<'a> { self.inner.edge_label(e) }
+}
+
+impl<'a> dot::GraphWalk<'a, Node<'a>, Edge<'a>> for DataflowLabeller<'a> {
+ fn nodes(&self) -> dot::Nodes<'a, Node<'a>> { self.inner.nodes() }
+ fn edges(&self) -> dot::Edges<'a, Edge<'a>> { self.inner.edges() }
+ fn source(&self, edge: &Edge<'a>) -> Node<'a> { self.inner.source(edge) }
+ fn target(&self, edge: &Edge<'a>) -> Node<'a> { self.inner.target(edge) }
+}
use std::string::String;
use syntax::ast;
use syntax::ast_map;
+use syntax::ast_map::blocks::{FnLikeNode, FnParts};
use syntax::ast_util;
use syntax::codemap::Span;
use syntax::parse::token;
pub mod gather_loans;
+pub mod graphviz;
+
pub mod move_data;
#[deriving(Clone)]
}
}
+/// Collection of conclusions determined via borrow checker analyses.
+pub struct AnalysisData<'a> {
+ pub all_loans: Vec<Loan>,
+ pub loans: DataFlowContext<'a, LoanDataFlowOperator>,
+ pub move_data: move_data::FlowedMoveData<'a>,
+}
+
fn borrowck_fn(this: &mut BorrowckCtxt,
fk: &FnKind,
decl: &ast::FnDecl,
sp: Span,
id: ast::NodeId) {
debug!("borrowck_fn(id={})", id);
+ let cfg = cfg::CFG::new(this.tcx, body);
+ let AnalysisData { all_loans,
+ loans: loan_dfcx,
+ move_data:flowed_moves } =
+ build_borrowck_dataflow_data(this, fk, decl, &cfg, body, sp, id);
+
+ check_loans::check_loans(this, &loan_dfcx, flowed_moves,
+ all_loans.as_slice(), decl, body);
+
+ visit::walk_fn(this, fk, decl, body, sp, ());
+}
+fn build_borrowck_dataflow_data<'a>(this: &mut BorrowckCtxt<'a>,
+ fk: &FnKind,
+ decl: &ast::FnDecl,
+ cfg: &cfg::CFG,
+ body: &ast::Block,
+ sp: Span,
+ id: ast::NodeId) -> AnalysisData<'a> {
// Check the body of fn items.
let id_range = ast_util::compute_id_range_for_fn_body(fk, decl, body, sp, id);
let (all_loans, move_data) =
gather_loans::gather_loans_in_fn(this, decl, body);
- let cfg = cfg::CFG::new(this.tcx, body);
let mut loan_dfcx =
DataFlowContext::new(this.tcx,
"borrowck",
Some(decl),
- &cfg,
+ cfg,
LoanDataFlowOperator,
id_range,
all_loans.len());
loan_dfcx.add_gen(loan.gen_scope, loan_idx);
loan_dfcx.add_kill(loan.kill_scope, loan_idx);
}
- loan_dfcx.add_kills_from_flow_exits(&cfg);
- loan_dfcx.propagate(&cfg, body);
+ loan_dfcx.add_kills_from_flow_exits(cfg);
+ loan_dfcx.propagate(cfg, body);
let flowed_moves = move_data::FlowedMoveData::new(move_data,
this.tcx,
- &cfg,
+ cfg,
id_range,
decl,
body);
- check_loans::check_loans(this, &loan_dfcx, flowed_moves,
- all_loans.as_slice(), decl, body);
+ AnalysisData { all_loans: all_loans,
+ loans: loan_dfcx,
+ move_data:flowed_moves }
+}
- visit::walk_fn(this, fk, decl, body, sp, ());
+/// This and a `ty::ctxt` is all you need to run the dataflow analyses
+/// used in the borrow checker.
+pub struct FnPartsWithCFG<'a> {
+ pub fn_parts: FnParts<'a>,
+ pub cfg: &'a cfg::CFG,
+}
+
+impl<'a> FnPartsWithCFG<'a> {
+ pub fn from_fn_like(f: &'a FnLikeNode,
+ g: &'a cfg::CFG) -> FnPartsWithCFG<'a> {
+ FnPartsWithCFG { fn_parts: f.to_fn_parts(), cfg: g }
+ }
+}
+
+/// Accessor for introspective clients inspecting `AnalysisData` and
+/// the `BorrowckCtxt` itself , e.g. the flowgraph visualizer.
+pub fn build_borrowck_dataflow_data_for_fn<'a>(
+ tcx: &'a ty::ctxt,
+ input: FnPartsWithCFG<'a>) -> (BorrowckCtxt<'a>, AnalysisData<'a>) {
+
+ let mut bccx = BorrowckCtxt {
+ tcx: tcx,
+ stats: box(GC) BorrowStats {
+ loaned_paths_same: Cell::new(0),
+ loaned_paths_imm: Cell::new(0),
+ stable_paths: Cell::new(0),
+ guaranteed_paths: Cell::new(0),
+ }
+ };
+
+ let p = input.fn_parts;
+
+ let dataflow_data = build_borrowck_dataflow_data(
+ &mut bccx, &p.kind, p.decl, input.cfg, p.body, p.span, p.id);
+
+ (bccx, dataflow_data)
}
// ----------------------------------------------------------------------
cause: euv::LoanCause,
}
+impl Loan {
+ pub fn loan_path(&self) -> Rc<LoanPath> {
+ self.loan_path.clone()
+ }
+}
+
#[deriving(PartialEq, Eq, Hash)]
pub enum LoanPath {
LpVar(ast::NodeId), // `x` in doc.rs
}
}
- fn path_loan_path(&self, index: MovePathIndex) -> Rc<LoanPath> {
+ pub fn path_loan_path(&self, index: MovePathIndex) -> Rc<LoanPath> {
self.paths.borrow().get(index.get()).loan_path.clone()
}
impl<'a> FlowedMoveData<'a> {
pub fn new(move_data: MoveData,
tcx: &'a ty::ctxt,
- cfg: &'a cfg::CFG,
+ cfg: &cfg::CFG,
id_range: ast_util::IdRange,
decl: &ast::FnDecl,
body: &ast::Block)
fn source(&self, edge: &Edge<'a>) -> Node<'a> { self.cfg.source(edge) }
fn target(&self, edge: &Edge<'a>) -> Node<'a> { self.cfg.target(edge) }
}
+
use syntax::print::{pp, pprust};
use util::nodemap::NodeMap;
+#[deriving(Show)]
+pub enum EntryOrExit { Entry, Exit }
+
#[deriving(Clone)]
pub struct DataFlowContext<'a, O> {
tcx: &'a ty::ctxt,
}
impl<'a, O:DataFlowOperator> DataFlowContext<'a, O> {
- fn has_bitset(&self, n: ast::NodeId) -> bool {
+ fn has_bitset_for_nodeid(&self, n: ast::NodeId) -> bool {
assert!(n != ast::DUMMY_NODE_ID);
match self.nodeid_to_index.find(&n) {
None => false,
- Some(&cfgidx) => {
- let node_id = cfgidx.node_id();
- node_id < self.index_to_bitset.len() &&
- self.index_to_bitset.get(node_id).is_some()
- }
+ Some(&cfgidx) => self.has_bitset_for_cfgidx(cfgidx),
}
}
+ fn has_bitset_for_cfgidx(&self, cfgidx: CFGIndex) -> bool {
+ let node_id = cfgidx.node_id();
+ node_id < self.index_to_bitset.len() &&
+ self.index_to_bitset.get(node_id).is_some()
+ }
fn get_bitset_index(&self, cfgidx: CFGIndex) -> uint {
let node_id = cfgidx.node_id();
self.index_to_bitset.get(node_id).unwrap()
pprust::NodePat(pat) => pat.id
};
- if self.has_bitset(id) {
+ if self.has_bitset_for_nodeid(id) {
let cfgidx = to_cfgidx_or_die(id, &self.nodeid_to_index);
let (start, end) = self.compute_id_range_frozen(cfgidx);
let on_entry = self.on_entry.slice(start, end);
}
fn apply_gen_kill(&mut self, cfgidx: CFGIndex, bits: &mut [uint]) {
- //! Applies the gen and kill sets for `id` to `bits`
+ //! Applies the gen and kill sets for `cfgidx` to `bits`
debug!("{:s} apply_gen_kill(cfgidx={}, bits={}) [before]",
self.analysis_name, cfgidx, mut_bits_to_string(bits));
let (start, end) = self.compute_id_range(cfgidx);
self.analysis_name, cfgidx, mut_bits_to_string(bits));
}
+ fn apply_gen_kill_frozen(&self, cfgidx: CFGIndex, bits: &mut [uint]) {
+ //! Applies the gen and kill sets for `cfgidx` to `bits`
+ //! Only useful after `propagate()` has been called.
+ debug!("{:s} apply_gen_kill(cfgidx={}, bits={}) [before]",
+ self.analysis_name, cfgidx, mut_bits_to_string(bits));
+ let (start, end) = self.compute_id_range_frozen(cfgidx);
+ let gens = self.gens.slice(start, end);
+ bitwise(bits, gens, &Union);
+ let kills = self.kills.slice(start, end);
+ bitwise(bits, kills, &Subtract);
+
+ debug!("{:s} apply_gen_kill(cfgidx={}, bits={}) [after]",
+ self.analysis_name, cfgidx, mut_bits_to_string(bits));
+ }
+
fn compute_id_range_frozen(&self, cfgidx: CFGIndex) -> (uint, uint) {
let n = self.get_bitset_index(cfgidx);
let start = n * self.words_per_id;
-> bool {
//! Iterates through each bit that is set on entry to `id`.
//! Only useful after `propagate()` has been called.
- if !self.has_bitset(id) {
+ if !self.has_bitset_for_nodeid(id) {
return true;
}
let cfgidx = to_cfgidx_or_die(id, &self.nodeid_to_index);
+ self.each_bit_for_node(Entry, cfgidx, f)
+ }
+
+ pub fn each_bit_for_node(&self,
+ e: EntryOrExit,
+ cfgidx: CFGIndex,
+ f: |uint| -> bool)
+ -> bool {
+ //! Iterates through each bit that is set on entry/exit to `cfgidx`.
+ //! Only useful after `propagate()` has been called.
+ if !self.has_bitset_for_cfgidx(cfgidx) {
+ return true;
+ }
let (start, end) = self.compute_id_range_frozen(cfgidx);
let on_entry = self.on_entry.slice(start, end);
- debug!("{:s} each_bit_on_entry_frozen(id={:?}, on_entry={})",
- self.analysis_name, id, bits_to_string(on_entry));
- self.each_bit(on_entry, f)
+ let temp_bits;
+ let slice = match e {
+ Entry => on_entry,
+ Exit => {
+ let mut t = on_entry.to_owned();
+ self.apply_gen_kill_frozen(cfgidx, t.as_mut_slice());
+ temp_bits = t;
+ temp_bits.as_slice()
+ }
+ };
+ debug!("{:s} each_bit_for_node({}, cfgidx={}) bits={}",
+ self.analysis_name, e, cfgidx, bits_to_string(slice));
+ self.each_bit(slice, f)
}
pub fn each_gen_bit_frozen(&self, id: ast::NodeId, f: |uint| -> bool)
-> bool {
//! Iterates through each bit in the gen set for `id`.
- if !self.has_bitset(id) {
+ //! Only useful after `propagate()` has been called.
+ if !self.has_bitset_for_nodeid(id) {
return true;
}
let cfgidx = to_cfgidx_or_die(id, &self.nodeid_to_index);
+++ /dev/null
-// Copyright 2012-2013 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 abi;
-use ast::*;
-use ast_util;
-use codemap::Span;
-use fold::Folder;
-use fold;
-use parse::token;
-use print::pprust;
-use util::small_vector::SmallVector;
-
-use std::cell::RefCell;
-use std::fmt;
-use std::gc::{Gc, GC};
-use std::iter;
-use std::slice;
-
-#[deriving(Clone, PartialEq)]
-pub enum PathElem {
- PathMod(Name),
- PathName(Name)
-}
-
-impl PathElem {
- pub fn name(&self) -> Name {
- match *self {
- PathMod(name) | PathName(name) => name
- }
- }
-}
-
-impl fmt::Show for PathElem {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- let slot = token::get_name(self.name());
- write!(f, "{}", slot)
- }
-}
-
-#[deriving(Clone)]
-struct LinkedPathNode<'a> {
- node: PathElem,
- next: LinkedPath<'a>,
-}
-
-type LinkedPath<'a> = Option<&'a LinkedPathNode<'a>>;
-
-impl<'a> Iterator<PathElem> for LinkedPath<'a> {
- fn next(&mut self) -> Option<PathElem> {
- match *self {
- Some(node) => {
- *self = node.next;
- Some(node.node)
- }
- None => None
- }
- }
-}
-
-// HACK(eddyb) move this into libstd (value wrapper for slice::Items).
-#[deriving(Clone)]
-pub struct Values<'a, T>(pub slice::Items<'a, T>);
-
-impl<'a, T: Copy> Iterator<T> for Values<'a, T> {
- fn next(&mut self) -> Option<T> {
- let &Values(ref mut items) = self;
- items.next().map(|&x| x)
- }
-}
-
-/// The type of the iterator used by with_path.
-pub type PathElems<'a, 'b> = iter::Chain<Values<'a, PathElem>, LinkedPath<'b>>;
-
-pub fn path_to_string<PI: Iterator<PathElem>>(mut path: PI) -> String {
- let itr = token::get_ident_interner();
-
- path.fold(String::new(), |mut s, e| {
- let e = itr.get(e.name());
- if !s.is_empty() {
- s.push_str("::");
- }
- s.push_str(e.as_slice());
- s
- }).to_string()
-}
-
-#[deriving(Clone)]
-pub enum Node {
- NodeItem(Gc<Item>),
- NodeForeignItem(Gc<ForeignItem>),
- NodeTraitMethod(Gc<TraitMethod>),
- NodeMethod(Gc<Method>),
- NodeVariant(P<Variant>),
- NodeExpr(Gc<Expr>),
- NodeStmt(Gc<Stmt>),
- NodeArg(Gc<Pat>),
- NodeLocal(Gc<Pat>),
- NodePat(Gc<Pat>),
- NodeBlock(P<Block>),
-
- /// NodeStructCtor represents a tuple struct.
- NodeStructCtor(Gc<StructDef>),
-
- NodeLifetime(Gc<Lifetime>),
-}
-
-/// Represents an entry and its parent Node ID
-/// The odd layout is to bring down the total size.
-#[deriving(Clone)]
-enum MapEntry {
- /// Placeholder for holes in the map.
- NotPresent,
-
- /// All the node types, with a parent ID.
- EntryItem(NodeId, Gc<Item>),
- EntryForeignItem(NodeId, Gc<ForeignItem>),
- EntryTraitMethod(NodeId, Gc<TraitMethod>),
- EntryMethod(NodeId, Gc<Method>),
- EntryVariant(NodeId, P<Variant>),
- EntryExpr(NodeId, Gc<Expr>),
- EntryStmt(NodeId, Gc<Stmt>),
- EntryArg(NodeId, Gc<Pat>),
- EntryLocal(NodeId, Gc<Pat>),
- EntryPat(NodeId, Gc<Pat>),
- EntryBlock(NodeId, P<Block>),
- EntryStructCtor(NodeId, Gc<StructDef>),
- EntryLifetime(NodeId, Gc<Lifetime>),
-
- /// Roots for node trees.
- RootCrate,
- RootInlinedParent(P<InlinedParent>)
-}
-
-struct InlinedParent {
- path: Vec<PathElem> ,
- /// Required by NodeTraitMethod and NodeMethod.
- def_id: DefId
-}
-
-impl MapEntry {
- fn parent(&self) -> Option<NodeId> {
- Some(match *self {
- EntryItem(id, _) => id,
- EntryForeignItem(id, _) => id,
- EntryTraitMethod(id, _) => id,
- EntryMethod(id, _) => id,
- EntryVariant(id, _) => id,
- EntryExpr(id, _) => id,
- EntryStmt(id, _) => id,
- EntryArg(id, _) => id,
- EntryLocal(id, _) => id,
- EntryPat(id, _) => id,
- EntryBlock(id, _) => id,
- EntryStructCtor(id, _) => id,
- EntryLifetime(id, _) => id,
- _ => return None
- })
- }
-
- fn to_node(&self) -> Option<Node> {
- Some(match *self {
- EntryItem(_, p) => NodeItem(p),
- EntryForeignItem(_, p) => NodeForeignItem(p),
- EntryTraitMethod(_, p) => NodeTraitMethod(p),
- EntryMethod(_, p) => NodeMethod(p),
- EntryVariant(_, p) => NodeVariant(p),
- EntryExpr(_, p) => NodeExpr(p),
- EntryStmt(_, p) => NodeStmt(p),
- EntryArg(_, p) => NodeArg(p),
- EntryLocal(_, p) => NodeLocal(p),
- EntryPat(_, p) => NodePat(p),
- EntryBlock(_, p) => NodeBlock(p),
- EntryStructCtor(_, p) => NodeStructCtor(p),
- EntryLifetime(_, p) => NodeLifetime(p),
- _ => return None
- })
- }
-}
-
-/// Represents a mapping from Node IDs to AST elements and their parent
-/// Node IDs
-pub struct Map {
- /// NodeIds are sequential integers from 0, so we can be
- /// super-compact by storing them in a vector. Not everything with
- /// a NodeId is in the map, but empirically the occupancy is about
- /// 75-80%, so there's not too much overhead (certainly less than
- /// a hashmap, since they (at the time of writing) have a maximum
- /// of 75% occupancy).
- ///
- /// Also, indexing is pretty quick when you've got a vector and
- /// plain old integers.
- map: RefCell<Vec<MapEntry> >
-}
-
-impl Map {
- fn find_entry(&self, id: NodeId) -> Option<MapEntry> {
- let map = self.map.borrow();
- if map.len() > id as uint {
- Some(*map.get(id as uint))
- } else {
- None
- }
- }
-
- /// Retrieve the Node corresponding to `id`, failing if it cannot
- /// be found.
- pub fn get(&self, id: NodeId) -> Node {
- match self.find(id) {
- Some(node) => node,
- None => fail!("couldn't find node id {} in the AST map", id)
- }
- }
-
- /// Retrieve the Node corresponding to `id`, returning None if
- /// cannot be found.
- pub fn find(&self, id: NodeId) -> Option<Node> {
- self.find_entry(id).and_then(|x| x.to_node())
- }
-
- /// Retrieve the parent NodeId for `id`, or `id` itself if no
- /// parent is registered in this map.
- pub fn get_parent(&self, id: NodeId) -> NodeId {
- self.find_entry(id).and_then(|x| x.parent()).unwrap_or(id)
- }
-
- pub fn get_parent_did(&self, id: NodeId) -> DefId {
- let parent = self.get_parent(id);
- match self.find_entry(parent) {
- Some(RootInlinedParent(data)) => data.def_id,
- _ => ast_util::local_def(parent)
- }
- }
-
- pub fn get_foreign_abi(&self, id: NodeId) -> abi::Abi {
- let parent = self.get_parent(id);
- let abi = match self.find_entry(parent) {
- Some(EntryItem(_, i)) => match i.node {
- ItemForeignMod(ref nm) => Some(nm.abi),
- _ => None
- },
- /// Wrong but OK, because the only inlined foreign items are intrinsics.
- Some(RootInlinedParent(_)) => Some(abi::RustIntrinsic),
- _ => None
- };
- match abi {
- Some(abi) => abi,
- None => fail!("expected foreign mod or inlined parent, found {}",
- self.node_to_string(parent))
- }
- }
-
- pub fn get_foreign_vis(&self, id: NodeId) -> Visibility {
- let vis = self.expect_foreign_item(id).vis;
- match self.find(self.get_parent(id)) {
- Some(NodeItem(i)) => vis.inherit_from(i.vis),
- _ => vis
- }
- }
-
- pub fn expect_item(&self, id: NodeId) -> Gc<Item> {
- match self.find(id) {
- Some(NodeItem(item)) => item,
- _ => fail!("expected item, found {}", self.node_to_string(id))
- }
- }
-
- pub fn expect_struct(&self, id: NodeId) -> Gc<StructDef> {
- match self.find(id) {
- Some(NodeItem(i)) => {
- match i.node {
- ItemStruct(struct_def, _) => struct_def,
- _ => fail!("struct ID bound to non-struct")
- }
- }
- Some(NodeVariant(ref variant)) => {
- match (*variant).node.kind {
- StructVariantKind(struct_def) => struct_def,
- _ => fail!("struct ID bound to enum variant that isn't struct-like"),
- }
- }
- _ => fail!(format!("expected struct, found {}", self.node_to_string(id))),
- }
- }
-
- pub fn expect_variant(&self, id: NodeId) -> P<Variant> {
- match self.find(id) {
- Some(NodeVariant(variant)) => variant,
- _ => fail!(format!("expected variant, found {}", self.node_to_string(id))),
- }
- }
-
- pub fn expect_foreign_item(&self, id: NodeId) -> Gc<ForeignItem> {
- match self.find(id) {
- Some(NodeForeignItem(item)) => item,
- _ => fail!("expected foreign item, found {}", self.node_to_string(id))
- }
- }
-
- /// returns the name associated with the given NodeId's AST
- pub fn get_path_elem(&self, id: NodeId) -> PathElem {
- let node = self.get(id);
- match node {
- NodeItem(item) => {
- match item.node {
- ItemMod(_) | ItemForeignMod(_) => {
- PathMod(item.ident.name)
- }
- _ => PathName(item.ident.name)
- }
- }
- NodeForeignItem(i) => PathName(i.ident.name),
- NodeMethod(m) => match m.node {
- MethDecl(ident, _, _, _, _, _, _) => PathName(ident.name),
- MethMac(_) => fail!("no path elem for {:?}", node)
- },
- NodeTraitMethod(tm) => match *tm {
- Required(ref m) => PathName(m.ident.name),
- Provided(m) => match m.node {
- MethDecl(ident, _, _, _, _, _, _) => PathName(ident.name),
- MethMac(_) => fail!("no path elem for {:?}", node),
- }
- },
- NodeVariant(v) => PathName(v.node.name.name),
- _ => fail!("no path elem for {:?}", node)
- }
- }
-
- pub fn with_path<T>(&self, id: NodeId, f: |PathElems| -> T) -> T {
- self.with_path_next(id, None, f)
- }
-
- pub fn path_to_string(&self, id: NodeId) -> String {
- self.with_path(id, |path| path_to_string(path))
- }
-
- fn path_to_str_with_ident(&self, id: NodeId, i: Ident) -> String {
- self.with_path(id, |path| {
- path_to_string(path.chain(Some(PathName(i.name)).move_iter()))
- })
- }
-
- fn with_path_next<T>(&self, id: NodeId, next: LinkedPath, f: |PathElems| -> T) -> T {
- let parent = self.get_parent(id);
- let parent = match self.find_entry(id) {
- Some(EntryForeignItem(..)) | Some(EntryVariant(..)) => {
- // Anonymous extern items, enum variants and struct ctors
- // go in the parent scope.
- self.get_parent(parent)
- }
- // But tuple struct ctors don't have names, so use the path of its
- // parent, the struct item. Similarly with closure expressions.
- Some(EntryStructCtor(..)) | Some(EntryExpr(..)) => {
- return self.with_path_next(parent, next, f);
- }
- _ => parent
- };
- if parent == id {
- match self.find_entry(id) {
- Some(RootInlinedParent(data)) => {
- f(Values(data.path.iter()).chain(next))
- }
- _ => f(Values([].iter()).chain(next))
- }
- } else {
- self.with_path_next(parent, Some(&LinkedPathNode {
- node: self.get_path_elem(id),
- next: next
- }), f)
- }
- }
-
- /// Given a node ID and a closure, apply the closure to the array
- /// of attributes associated with the AST corresponding to the Node ID
- pub fn with_attrs<T>(&self, id: NodeId, f: |Option<&[Attribute]>| -> T) -> T {
- let node = self.get(id);
- let attrs = match node {
- NodeItem(ref i) => Some(i.attrs.as_slice()),
- NodeForeignItem(ref fi) => Some(fi.attrs.as_slice()),
- NodeTraitMethod(ref tm) => match **tm {
- Required(ref type_m) => Some(type_m.attrs.as_slice()),
- Provided(ref m) => Some(m.attrs.as_slice())
- },
- NodeMethod(ref m) => Some(m.attrs.as_slice()),
- NodeVariant(ref v) => Some(v.node.attrs.as_slice()),
- // unit/tuple structs take the attributes straight from
- // the struct definition.
- // FIXME(eddyb) make this work again (requires access to the map).
- NodeStructCtor(_) => {
- return self.with_attrs(self.get_parent(id), f);
- }
- _ => None
- };
- f(attrs)
- }
-
- pub fn opt_span(&self, id: NodeId) -> Option<Span> {
- let sp = match self.find(id) {
- Some(NodeItem(item)) => item.span,
- Some(NodeForeignItem(foreign_item)) => foreign_item.span,
- Some(NodeTraitMethod(trait_method)) => {
- match *trait_method {
- Required(ref type_method) => type_method.span,
- Provided(ref method) => method.span,
- }
- }
- Some(NodeMethod(method)) => method.span,
- Some(NodeVariant(variant)) => variant.span,
- Some(NodeExpr(expr)) => expr.span,
- Some(NodeStmt(stmt)) => stmt.span,
- Some(NodeArg(pat)) | Some(NodeLocal(pat)) => pat.span,
- Some(NodePat(pat)) => pat.span,
- Some(NodeBlock(block)) => block.span,
- Some(NodeStructCtor(_)) => self.expect_item(self.get_parent(id)).span,
- _ => return None,
- };
- Some(sp)
- }
-
- pub fn span(&self, id: NodeId) -> Span {
- self.opt_span(id)
- .unwrap_or_else(|| fail!("AstMap.span: could not find span for id {}", id))
- }
-
- pub fn node_to_string(&self, id: NodeId) -> String {
- node_id_to_string(self, id)
- }
-}
-
-pub trait FoldOps {
- fn new_id(&self, id: NodeId) -> NodeId {
- id
- }
- fn new_span(&self, span: Span) -> Span {
- span
- }
-}
-
-/// A Folder that walks over an AST and constructs a Node ID Map. Its
-/// fold_ops argument has the opportunity to replace Node IDs and spans.
-pub struct Ctx<'a, F> {
- map: &'a Map,
- /// The node in which we are currently mapping (an item or a method).
- /// When equal to DUMMY_NODE_ID, the next mapped node becomes the parent.
- parent: NodeId,
- fold_ops: F
-}
-
-impl<'a, F> Ctx<'a, F> {
- fn insert(&self, id: NodeId, entry: MapEntry) {
- (*self.map.map.borrow_mut()).grow_set(id as uint, &NotPresent, entry);
- }
-}
-
-impl<'a, F: FoldOps> Folder for Ctx<'a, F> {
- fn new_id(&mut self, id: NodeId) -> NodeId {
- let id = self.fold_ops.new_id(id);
- if self.parent == DUMMY_NODE_ID {
- self.parent = id;
- }
- id
- }
-
- fn new_span(&mut self, span: Span) -> Span {
- self.fold_ops.new_span(span)
- }
-
- fn fold_item(&mut self, i: Gc<Item>) -> SmallVector<Gc<Item>> {
- let parent = self.parent;
- self.parent = DUMMY_NODE_ID;
-
- let i = fold::noop_fold_item(&*i, self).expect_one("expected one item");
- assert_eq!(self.parent, i.id);
-
- match i.node {
- ItemImpl(_, _, _, ref ms) => {
- for &m in ms.iter() {
- self.insert(m.id, EntryMethod(self.parent, m));
- }
- }
- ItemEnum(ref enum_definition, _) => {
- for &v in enum_definition.variants.iter() {
- self.insert(v.node.id, EntryVariant(self.parent, v));
- }
- }
- ItemForeignMod(ref nm) => {
- for nitem in nm.items.iter() {
- self.insert(nitem.id, EntryForeignItem(self.parent,
- nitem.clone()));
- }
- }
- ItemStruct(ref struct_def, _) => {
- // If this is a tuple-like struct, register the constructor.
- match struct_def.ctor_id {
- Some(ctor_id) => {
- self.insert(ctor_id, EntryStructCtor(self.parent,
- struct_def.clone()));
- }
- None => {}
- }
- }
- ItemTrait(_, _, ref traits, ref methods) => {
- for t in traits.iter() {
- self.insert(t.ref_id, EntryItem(self.parent, i));
- }
-
- for tm in methods.iter() {
- match *tm {
- Required(ref m) => {
- self.insert(m.id, EntryTraitMethod(self.parent,
- box(GC) (*tm).clone()));
- }
- Provided(m) => {
- self.insert(m.id, EntryTraitMethod(self.parent,
- box(GC) Provided(m)));
- }
- }
- }
- }
- _ => {}
- }
-
- self.parent = parent;
- self.insert(i.id, EntryItem(self.parent, i));
-
- SmallVector::one(i)
- }
-
- fn fold_pat(&mut self, pat: Gc<Pat>) -> Gc<Pat> {
- let pat = fold::noop_fold_pat(pat, self);
- match pat.node {
- PatIdent(..) => {
- // Note: this is at least *potentially* a pattern...
- self.insert(pat.id, EntryLocal(self.parent, pat));
- }
- _ => {
- self.insert(pat.id, EntryPat(self.parent, pat));
- }
- }
-
- pat
- }
-
- fn fold_expr(&mut self, expr: Gc<Expr>) -> Gc<Expr> {
- let expr = fold::noop_fold_expr(expr, self);
-
- self.insert(expr.id, EntryExpr(self.parent, expr));
-
- expr
- }
-
- fn fold_stmt(&mut self, stmt: &Stmt) -> SmallVector<Gc<Stmt>> {
- let stmt = fold::noop_fold_stmt(stmt, self).expect_one("expected one statement");
- self.insert(ast_util::stmt_id(&*stmt), EntryStmt(self.parent, stmt));
- SmallVector::one(stmt)
- }
-
- fn fold_type_method(&mut self, m: &TypeMethod) -> TypeMethod {
- let parent = self.parent;
- self.parent = DUMMY_NODE_ID;
- let m = fold::noop_fold_type_method(m, self);
- assert_eq!(self.parent, m.id);
- self.parent = parent;
- m
- }
-
- fn fold_method(&mut self, m: Gc<Method>) -> SmallVector<Gc<Method>> {
- let parent = self.parent;
- self.parent = DUMMY_NODE_ID;
- let m = fold::noop_fold_method(&*m, self).expect_one(
- "noop_fold_method must produce exactly one method");
- assert_eq!(self.parent, m.id);
- self.parent = parent;
- SmallVector::one(m)
- }
-
- fn fold_fn_decl(&mut self, decl: &FnDecl) -> P<FnDecl> {
- let decl = fold::noop_fold_fn_decl(decl, self);
- for a in decl.inputs.iter() {
- self.insert(a.id, EntryArg(self.parent, a.pat));
- }
- decl
- }
-
- fn fold_block(&mut self, block: P<Block>) -> P<Block> {
- let block = fold::noop_fold_block(block, self);
- self.insert(block.id, EntryBlock(self.parent, block));
- block
- }
-
- fn fold_lifetime(&mut self, lifetime: &Lifetime) -> Lifetime {
- let lifetime = fold::noop_fold_lifetime(lifetime, self);
- self.insert(lifetime.id, EntryLifetime(self.parent, box(GC) lifetime));
- lifetime
- }
-
- fn fold_mac(&mut self, mac: &Mac) -> Mac {
- fold::fold_mac(mac, self)
- }
-}
-
-pub fn map_crate<F: FoldOps>(krate: Crate, fold_ops: F) -> (Crate, Map) {
- let map = Map { map: RefCell::new(Vec::new()) };
- let krate = {
- let mut cx = Ctx {
- map: &map,
- parent: CRATE_NODE_ID,
- fold_ops: fold_ops
- };
- cx.insert(CRATE_NODE_ID, RootCrate);
- cx.fold_crate(krate)
- };
-
- if log_enabled!(::log::DEBUG) {
- let map = map.map.borrow();
- // This only makes sense for ordered stores; note the
- // enumerate to count the number of entries.
- let (entries_less_1, _) = (*map).iter().filter(|&x| {
- match *x {
- NotPresent => false,
- _ => true
- }
- }).enumerate().last().expect("AST map was empty after folding?");
-
- let entries = entries_less_1 + 1;
- let vector_length = (*map).len();
- debug!("The AST map has {} entries with a maximum of {}: occupancy {:.1}%",
- entries, vector_length, (entries as f64 / vector_length as f64) * 100.);
- }
-
- (krate, map)
-}
-
-/// Used for items loaded from external crate that are being inlined into this
-/// crate. The `path` should be the path to the item but should not include
-/// the item itself.
-pub fn map_decoded_item<F: FoldOps>(map: &Map,
- path: Vec<PathElem> ,
- fold_ops: F,
- fold: |&mut Ctx<F>| -> InlinedItem)
- -> InlinedItem {
- let mut cx = Ctx {
- map: map,
- parent: DUMMY_NODE_ID,
- fold_ops: fold_ops
- };
-
- // Generate a NodeId for the RootInlinedParent inserted below.
- cx.new_id(DUMMY_NODE_ID);
-
- // Methods get added to the AST map when their impl is visited. Since we
- // don't decode and instantiate the impl, but just the method, we have to
- // add it to the table now. Likewise with foreign items.
- let mut def_id = DefId { krate: LOCAL_CRATE, node: DUMMY_NODE_ID };
- let ii = fold(&mut cx);
- match ii {
- IIItem(_) => {}
- IIMethod(impl_did, is_provided, m) => {
- let entry = if is_provided {
- EntryTraitMethod(cx.parent, box(GC) Provided(m))
- } else {
- EntryMethod(cx.parent, m)
- };
- cx.insert(m.id, entry);
- def_id = impl_did;
- }
- IIForeign(i) => {
- cx.insert(i.id, EntryForeignItem(cx.parent, i));
- }
- }
-
- cx.insert(cx.parent, RootInlinedParent(P(InlinedParent {
- path: path,
- def_id: def_id
- })));
-
- ii
-}
-
-fn node_id_to_string(map: &Map, id: NodeId) -> String {
- match map.find(id) {
- Some(NodeItem(item)) => {
- let path_str = map.path_to_str_with_ident(id, item.ident);
- let item_str = match item.node {
- ItemStatic(..) => "static",
- ItemFn(..) => "fn",
- ItemMod(..) => "mod",
- ItemForeignMod(..) => "foreign mod",
- ItemTy(..) => "ty",
- ItemEnum(..) => "enum",
- ItemStruct(..) => "struct",
- ItemTrait(..) => "trait",
- ItemImpl(..) => "impl",
- ItemMac(..) => "macro"
- };
- format!("{} {} (id={})", item_str, path_str, id)
- }
- Some(NodeForeignItem(item)) => {
- let path_str = map.path_to_str_with_ident(id, item.ident);
- format!("foreign item {} (id={})", path_str, id)
- }
- Some(NodeMethod(m)) => match m.node {
- MethDecl(ident, _, _, _, _, _, _) =>
- format!("method {} in {} (id={})",
- token::get_ident(ident),
- map.path_to_string(id), id),
- MethMac(ref mac) =>
- format!("method macro {} (id={})",
- pprust::mac_to_string(mac), id)
- },
- Some(NodeTraitMethod(ref tm)) => {
- let m = ast_util::trait_method_to_ty_method(&**tm);
- format!("method {} in {} (id={})",
- token::get_ident(m.ident),
- map.path_to_string(id), id)
- }
- Some(NodeVariant(ref variant)) => {
- format!("variant {} in {} (id={})",
- token::get_ident(variant.node.name),
- map.path_to_string(id), id)
- }
- Some(NodeExpr(ref expr)) => {
- format!("expr {} (id={})", pprust::expr_to_string(&**expr), id)
- }
- Some(NodeStmt(ref stmt)) => {
- format!("stmt {} (id={})", pprust::stmt_to_string(&**stmt), id)
- }
- Some(NodeArg(ref pat)) => {
- format!("arg {} (id={})", pprust::pat_to_string(&**pat), id)
- }
- Some(NodeLocal(ref pat)) => {
- format!("local {} (id={})", pprust::pat_to_string(&**pat), id)
- }
- Some(NodePat(ref pat)) => {
- format!("pat {} (id={})", pprust::pat_to_string(&**pat), id)
- }
- Some(NodeBlock(ref block)) => {
- format!("block {} (id={})", pprust::block_to_string(&**block), id)
- }
- Some(NodeStructCtor(_)) => {
- format!("struct_ctor {} (id={})", map.path_to_string(id), id)
- }
- Some(NodeLifetime(ref l)) => {
- format!("lifetime {} (id={})",
- pprust::lifetime_to_string(&**l), id)
- }
- None => {
- format!("unknown node (id={})", id)
- }
- }
-}
--- /dev/null
+// 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.
+
+//! 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.
+
+use abi;
+use ast::{P, Block, FnDecl, NodeId};
+use ast;
+use ast_map::{Node};
+use ast_map;
+use ast_util;
+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 ExprFnBlock or ExprProc), or
+/// - The default implementation for a trait method.
+///
+/// To construct one, use the `Code::from_node` function.
+pub struct FnLikeNode { node: ast_map::Node }
+
+/// 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: P<FnDecl>,
+ pub body: P<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::TraitMethod {
+ fn is_fn_like(&self) -> bool {
+ match *self { ast::Provided(_) => true, _ => false, }
+ }
+}
+
+impl MaybeFnLike for ast::Expr {
+ fn is_fn_like(&self) -> bool {
+ match self.node {
+ ast::ExprFnBlock(..) | ast::ExprProc(..) => 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).
+pub enum Code {
+ FnLikeCode(FnLikeNode),
+ BlockCode(P<Block>),
+}
+
+impl Code {
+ 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<Code> {
+ 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::NodeTraitMethod(tm) if tm.is_fn_like() =>
+ Some(FnLikeCode(new(node))),
+ ast_map::NodeMethod(_) =>
+ 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: P<ast::FnDecl>,
+ style: ast::FnStyle,
+ abi: abi::Abi,
+ generics: &'a ast::Generics,
+ body: P<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 {
+ decl: P<FnDecl>,
+ body: P<Block>,
+ id: NodeId,
+ span: Span
+}
+
+impl ClosureParts {
+ fn new(d: P<FnDecl>, b: P<Block>, id: NodeId, s: Span) -> ClosureParts {
+ ClosureParts { decl: d, body: b, id: id, span: s }
+ }
+}
+
+impl FnLikeNode {
+ pub fn to_fn_parts<'a>(&'a self) -> FnParts<'a> {
+ FnParts {
+ decl: self.decl(),
+ body: self.body(),
+ kind: self.kind(),
+ span: self.span(),
+ id: self.id(),
+ }
+ }
+
+ pub fn body<'a>(&'a self) -> P<Block> {
+ self.handle(|i: ItemFnParts| i.body,
+ |m: &'a ast::Method| ast_util::method_body(m),
+ |c: ClosureParts| c.body)
+ }
+
+ pub fn decl<'a>(&'a self) -> P<FnDecl> {
+ self.handle(|i: ItemFnParts| i.decl,
+ |m: &'a ast::Method| ast_util::method_fn_decl(m),
+ |c: ClosureParts| c.decl)
+ }
+
+ pub fn span<'a>(&'a self) -> Span {
+ self.handle(|i: ItemFnParts| i.span,
+ |m: &'a ast::Method| m.span,
+ |c: ClosureParts| c.span)
+ }
+
+ pub fn id<'a>(&'a self) -> NodeId {
+ self.handle(|i: ItemFnParts| i.id,
+ |m: &'a ast::Method| m.id,
+ |c: ClosureParts| c.id)
+ }
+
+ pub fn kind<'a>(&'a self) -> visit::FnKind<'a> {
+ let item = |p: ItemFnParts<'a>| -> visit::FnKind<'a> {
+ visit::FkItemFn(p.ident, p.generics, p.style, p.abi)
+ };
+ let closure = |_: ClosureParts| {
+ visit::FkFnBlock
+ };
+ let method = |m: &'a ast::Method| {
+ visit::FkMethod(ast_util::method_ident(m), ast_util::method_generics(m), m)
+ };
+ self.handle(item, method, closure)
+ }
+
+ fn handle<'a, A>(&'a self,
+ item_fn: |ItemFnParts<'a>| -> A,
+ method: |&'a ast::Method| -> A,
+ closure: |ClosureParts| -> A) -> A {
+ match self.node {
+ ast_map::NodeItem(ref i) => match i.node {
+ ast::ItemFn(decl, style, abi, ref generics, block) =>
+ item_fn(ItemFnParts{
+ ident: i.ident, decl: decl, style: style, body: block,
+ generics: generics, abi: abi, id: i.id, span: i.span
+ }),
+ _ => fail!("item FnLikeNode that is not fn-like"),
+ },
+ ast_map::NodeTraitMethod(ref t) => match **t {
+ ast::Provided(ref m) => method(&**m),
+ _ => fail!("trait method FnLikeNode that is not fn-like"),
+ },
+ ast_map::NodeMethod(ref m) => method(&**m),
+ ast_map::NodeExpr(ref e) => match e.node {
+ ast::ExprFnBlock(ref decl, ref block) =>
+ closure(ClosureParts::new(*decl, *block, e.id, e.span)),
+ ast::ExprProc(ref decl, ref block) =>
+ closure(ClosureParts::new(*decl, *block, e.id, e.span)),
+ _ => fail!("expr FnLikeNode that is not fn-like"),
+ },
+ _ => fail!("other FnLikeNode that is not fn-like"),
+ }
+ }
+}
--- /dev/null
+// Copyright 2012-2013 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 abi;
+use ast::*;
+use ast_util;
+use codemap::Span;
+use fold::Folder;
+use fold;
+use parse::token;
+use print::pprust;
+use util::small_vector::SmallVector;
+
+use std::cell::RefCell;
+use std::fmt;
+use std::gc::{Gc, GC};
+use std::iter;
+use std::slice;
+
+pub mod blocks;
+
+#[deriving(Clone, PartialEq)]
+pub enum PathElem {
+ PathMod(Name),
+ PathName(Name)
+}
+
+impl PathElem {
+ pub fn name(&self) -> Name {
+ match *self {
+ PathMod(name) | PathName(name) => name
+ }
+ }
+}
+
+impl fmt::Show for PathElem {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ let slot = token::get_name(self.name());
+ write!(f, "{}", slot)
+ }
+}
+
+#[deriving(Clone)]
+struct LinkedPathNode<'a> {
+ node: PathElem,
+ next: LinkedPath<'a>,
+}
+
+type LinkedPath<'a> = Option<&'a LinkedPathNode<'a>>;
+
+impl<'a> Iterator<PathElem> for LinkedPath<'a> {
+ fn next(&mut self) -> Option<PathElem> {
+ match *self {
+ Some(node) => {
+ *self = node.next;
+ Some(node.node)
+ }
+ None => None
+ }
+ }
+}
+
+// HACK(eddyb) move this into libstd (value wrapper for slice::Items).
+#[deriving(Clone)]
+pub struct Values<'a, T>(pub slice::Items<'a, T>);
+
+impl<'a, T: Copy> Iterator<T> for Values<'a, T> {
+ fn next(&mut self) -> Option<T> {
+ let &Values(ref mut items) = self;
+ items.next().map(|&x| x)
+ }
+}
+
+/// The type of the iterator used by with_path.
+pub type PathElems<'a, 'b> = iter::Chain<Values<'a, PathElem>, LinkedPath<'b>>;
+
+pub fn path_to_string<PI: Iterator<PathElem>>(mut path: PI) -> String {
+ let itr = token::get_ident_interner();
+
+ path.fold(String::new(), |mut s, e| {
+ let e = itr.get(e.name());
+ if !s.is_empty() {
+ s.push_str("::");
+ }
+ s.push_str(e.as_slice());
+ s
+ }).to_string()
+}
+
+#[deriving(Clone)]
+pub enum Node {
+ NodeItem(Gc<Item>),
+ NodeForeignItem(Gc<ForeignItem>),
+ NodeTraitMethod(Gc<TraitMethod>),
+ NodeMethod(Gc<Method>),
+ NodeVariant(P<Variant>),
+ NodeExpr(Gc<Expr>),
+ NodeStmt(Gc<Stmt>),
+ NodeArg(Gc<Pat>),
+ NodeLocal(Gc<Pat>),
+ NodePat(Gc<Pat>),
+ NodeBlock(P<Block>),
+
+ /// NodeStructCtor represents a tuple struct.
+ NodeStructCtor(Gc<StructDef>),
+
+ NodeLifetime(Gc<Lifetime>),
+}
+
+/// Represents an entry and its parent Node ID
+/// The odd layout is to bring down the total size.
+#[deriving(Clone)]
+enum MapEntry {
+ /// Placeholder for holes in the map.
+ NotPresent,
+
+ /// All the node types, with a parent ID.
+ EntryItem(NodeId, Gc<Item>),
+ EntryForeignItem(NodeId, Gc<ForeignItem>),
+ EntryTraitMethod(NodeId, Gc<TraitMethod>),
+ EntryMethod(NodeId, Gc<Method>),
+ EntryVariant(NodeId, P<Variant>),
+ EntryExpr(NodeId, Gc<Expr>),
+ EntryStmt(NodeId, Gc<Stmt>),
+ EntryArg(NodeId, Gc<Pat>),
+ EntryLocal(NodeId, Gc<Pat>),
+ EntryPat(NodeId, Gc<Pat>),
+ EntryBlock(NodeId, P<Block>),
+ EntryStructCtor(NodeId, Gc<StructDef>),
+ EntryLifetime(NodeId, Gc<Lifetime>),
+
+ /// Roots for node trees.
+ RootCrate,
+ RootInlinedParent(P<InlinedParent>)
+}
+
+struct InlinedParent {
+ path: Vec<PathElem> ,
+ /// Required by NodeTraitMethod and NodeMethod.
+ def_id: DefId
+}
+
+impl MapEntry {
+ fn parent(&self) -> Option<NodeId> {
+ Some(match *self {
+ EntryItem(id, _) => id,
+ EntryForeignItem(id, _) => id,
+ EntryTraitMethod(id, _) => id,
+ EntryMethod(id, _) => id,
+ EntryVariant(id, _) => id,
+ EntryExpr(id, _) => id,
+ EntryStmt(id, _) => id,
+ EntryArg(id, _) => id,
+ EntryLocal(id, _) => id,
+ EntryPat(id, _) => id,
+ EntryBlock(id, _) => id,
+ EntryStructCtor(id, _) => id,
+ EntryLifetime(id, _) => id,
+ _ => return None
+ })
+ }
+
+ fn to_node(&self) -> Option<Node> {
+ Some(match *self {
+ EntryItem(_, p) => NodeItem(p),
+ EntryForeignItem(_, p) => NodeForeignItem(p),
+ EntryTraitMethod(_, p) => NodeTraitMethod(p),
+ EntryMethod(_, p) => NodeMethod(p),
+ EntryVariant(_, p) => NodeVariant(p),
+ EntryExpr(_, p) => NodeExpr(p),
+ EntryStmt(_, p) => NodeStmt(p),
+ EntryArg(_, p) => NodeArg(p),
+ EntryLocal(_, p) => NodeLocal(p),
+ EntryPat(_, p) => NodePat(p),
+ EntryBlock(_, p) => NodeBlock(p),
+ EntryStructCtor(_, p) => NodeStructCtor(p),
+ EntryLifetime(_, p) => NodeLifetime(p),
+ _ => return None
+ })
+ }
+}
+
+/// Represents a mapping from Node IDs to AST elements and their parent
+/// Node IDs
+pub struct Map {
+ /// NodeIds are sequential integers from 0, so we can be
+ /// super-compact by storing them in a vector. Not everything with
+ /// a NodeId is in the map, but empirically the occupancy is about
+ /// 75-80%, so there's not too much overhead (certainly less than
+ /// a hashmap, since they (at the time of writing) have a maximum
+ /// of 75% occupancy).
+ ///
+ /// Also, indexing is pretty quick when you've got a vector and
+ /// plain old integers.
+ map: RefCell<Vec<MapEntry> >
+}
+
+impl Map {
+ fn find_entry(&self, id: NodeId) -> Option<MapEntry> {
+ let map = self.map.borrow();
+ if map.len() > id as uint {
+ Some(*map.get(id as uint))
+ } else {
+ None
+ }
+ }
+
+ /// Retrieve the Node corresponding to `id`, failing if it cannot
+ /// be found.
+ pub fn get(&self, id: NodeId) -> Node {
+ match self.find(id) {
+ Some(node) => node,
+ None => fail!("couldn't find node id {} in the AST map", id)
+ }
+ }
+
+ /// Retrieve the Node corresponding to `id`, returning None if
+ /// cannot be found.
+ pub fn find(&self, id: NodeId) -> Option<Node> {
+ self.find_entry(id).and_then(|x| x.to_node())
+ }
+
+ /// Retrieve the parent NodeId for `id`, or `id` itself if no
+ /// parent is registered in this map.
+ pub fn get_parent(&self, id: NodeId) -> NodeId {
+ self.find_entry(id).and_then(|x| x.parent()).unwrap_or(id)
+ }
+
+ pub fn get_parent_did(&self, id: NodeId) -> DefId {
+ let parent = self.get_parent(id);
+ match self.find_entry(parent) {
+ Some(RootInlinedParent(data)) => data.def_id,
+ _ => ast_util::local_def(parent)
+ }
+ }
+
+ pub fn get_foreign_abi(&self, id: NodeId) -> abi::Abi {
+ let parent = self.get_parent(id);
+ let abi = match self.find_entry(parent) {
+ Some(EntryItem(_, i)) => match i.node {
+ ItemForeignMod(ref nm) => Some(nm.abi),
+ _ => None
+ },
+ /// Wrong but OK, because the only inlined foreign items are intrinsics.
+ Some(RootInlinedParent(_)) => Some(abi::RustIntrinsic),
+ _ => None
+ };
+ match abi {
+ Some(abi) => abi,
+ None => fail!("expected foreign mod or inlined parent, found {}",
+ self.node_to_string(parent))
+ }
+ }
+
+ pub fn get_foreign_vis(&self, id: NodeId) -> Visibility {
+ let vis = self.expect_foreign_item(id).vis;
+ match self.find(self.get_parent(id)) {
+ Some(NodeItem(i)) => vis.inherit_from(i.vis),
+ _ => vis
+ }
+ }
+
+ pub fn expect_item(&self, id: NodeId) -> Gc<Item> {
+ match self.find(id) {
+ Some(NodeItem(item)) => item,
+ _ => fail!("expected item, found {}", self.node_to_string(id))
+ }
+ }
+
+ pub fn expect_struct(&self, id: NodeId) -> Gc<StructDef> {
+ match self.find(id) {
+ Some(NodeItem(i)) => {
+ match i.node {
+ ItemStruct(struct_def, _) => struct_def,
+ _ => fail!("struct ID bound to non-struct")
+ }
+ }
+ Some(NodeVariant(ref variant)) => {
+ match (*variant).node.kind {
+ StructVariantKind(struct_def) => struct_def,
+ _ => fail!("struct ID bound to enum variant that isn't struct-like"),
+ }
+ }
+ _ => fail!(format!("expected struct, found {}", self.node_to_string(id))),
+ }
+ }
+
+ pub fn expect_variant(&self, id: NodeId) -> P<Variant> {
+ match self.find(id) {
+ Some(NodeVariant(variant)) => variant,
+ _ => fail!(format!("expected variant, found {}", self.node_to_string(id))),
+ }
+ }
+
+ pub fn expect_foreign_item(&self, id: NodeId) -> Gc<ForeignItem> {
+ match self.find(id) {
+ Some(NodeForeignItem(item)) => item,
+ _ => fail!("expected foreign item, found {}", self.node_to_string(id))
+ }
+ }
+
+ /// returns the name associated with the given NodeId's AST
+ pub fn get_path_elem(&self, id: NodeId) -> PathElem {
+ let node = self.get(id);
+ match node {
+ NodeItem(item) => {
+ match item.node {
+ ItemMod(_) | ItemForeignMod(_) => {
+ PathMod(item.ident.name)
+ }
+ _ => PathName(item.ident.name)
+ }
+ }
+ NodeForeignItem(i) => PathName(i.ident.name),
+ NodeMethod(m) => match m.node {
+ MethDecl(ident, _, _, _, _, _, _) => PathName(ident.name),
+ MethMac(_) => fail!("no path elem for {:?}", node)
+ },
+ NodeTraitMethod(tm) => match *tm {
+ Required(ref m) => PathName(m.ident.name),
+ Provided(m) => match m.node {
+ MethDecl(ident, _, _, _, _, _, _) => PathName(ident.name),
+ MethMac(_) => fail!("no path elem for {:?}", node),
+ }
+ },
+ NodeVariant(v) => PathName(v.node.name.name),
+ _ => fail!("no path elem for {:?}", node)
+ }
+ }
+
+ pub fn with_path<T>(&self, id: NodeId, f: |PathElems| -> T) -> T {
+ self.with_path_next(id, None, f)
+ }
+
+ pub fn path_to_string(&self, id: NodeId) -> String {
+ self.with_path(id, |path| path_to_string(path))
+ }
+
+ fn path_to_str_with_ident(&self, id: NodeId, i: Ident) -> String {
+ self.with_path(id, |path| {
+ path_to_string(path.chain(Some(PathName(i.name)).move_iter()))
+ })
+ }
+
+ fn with_path_next<T>(&self, id: NodeId, next: LinkedPath, f: |PathElems| -> T) -> T {
+ let parent = self.get_parent(id);
+ let parent = match self.find_entry(id) {
+ Some(EntryForeignItem(..)) | Some(EntryVariant(..)) => {
+ // Anonymous extern items, enum variants and struct ctors
+ // go in the parent scope.
+ self.get_parent(parent)
+ }
+ // But tuple struct ctors don't have names, so use the path of its
+ // parent, the struct item. Similarly with closure expressions.
+ Some(EntryStructCtor(..)) | Some(EntryExpr(..)) => {
+ return self.with_path_next(parent, next, f);
+ }
+ _ => parent
+ };
+ if parent == id {
+ match self.find_entry(id) {
+ Some(RootInlinedParent(data)) => {
+ f(Values(data.path.iter()).chain(next))
+ }
+ _ => f(Values([].iter()).chain(next))
+ }
+ } else {
+ self.with_path_next(parent, Some(&LinkedPathNode {
+ node: self.get_path_elem(id),
+ next: next
+ }), f)
+ }
+ }
+
+ /// Given a node ID and a closure, apply the closure to the array
+ /// of attributes associated with the AST corresponding to the Node ID
+ pub fn with_attrs<T>(&self, id: NodeId, f: |Option<&[Attribute]>| -> T) -> T {
+ let node = self.get(id);
+ let attrs = match node {
+ NodeItem(ref i) => Some(i.attrs.as_slice()),
+ NodeForeignItem(ref fi) => Some(fi.attrs.as_slice()),
+ NodeTraitMethod(ref tm) => match **tm {
+ Required(ref type_m) => Some(type_m.attrs.as_slice()),
+ Provided(ref m) => Some(m.attrs.as_slice())
+ },
+ NodeMethod(ref m) => Some(m.attrs.as_slice()),
+ NodeVariant(ref v) => Some(v.node.attrs.as_slice()),
+ // unit/tuple structs take the attributes straight from
+ // the struct definition.
+ // FIXME(eddyb) make this work again (requires access to the map).
+ NodeStructCtor(_) => {
+ return self.with_attrs(self.get_parent(id), f);
+ }
+ _ => None
+ };
+ f(attrs)
+ }
+
+ pub fn opt_span(&self, id: NodeId) -> Option<Span> {
+ let sp = match self.find(id) {
+ Some(NodeItem(item)) => item.span,
+ Some(NodeForeignItem(foreign_item)) => foreign_item.span,
+ Some(NodeTraitMethod(trait_method)) => {
+ match *trait_method {
+ Required(ref type_method) => type_method.span,
+ Provided(ref method) => method.span,
+ }
+ }
+ Some(NodeMethod(method)) => method.span,
+ Some(NodeVariant(variant)) => variant.span,
+ Some(NodeExpr(expr)) => expr.span,
+ Some(NodeStmt(stmt)) => stmt.span,
+ Some(NodeArg(pat)) | Some(NodeLocal(pat)) => pat.span,
+ Some(NodePat(pat)) => pat.span,
+ Some(NodeBlock(block)) => block.span,
+ Some(NodeStructCtor(_)) => self.expect_item(self.get_parent(id)).span,
+ _ => return None,
+ };
+ Some(sp)
+ }
+
+ pub fn span(&self, id: NodeId) -> Span {
+ self.opt_span(id)
+ .unwrap_or_else(|| fail!("AstMap.span: could not find span for id {}", id))
+ }
+
+ pub fn node_to_string(&self, id: NodeId) -> String {
+ node_id_to_string(self, id)
+ }
+}
+
+pub trait FoldOps {
+ fn new_id(&self, id: NodeId) -> NodeId {
+ id
+ }
+ fn new_span(&self, span: Span) -> Span {
+ span
+ }
+}
+
+/// A Folder that walks over an AST and constructs a Node ID Map. Its
+/// fold_ops argument has the opportunity to replace Node IDs and spans.
+pub struct Ctx<'a, F> {
+ map: &'a Map,
+ /// The node in which we are currently mapping (an item or a method).
+ /// When equal to DUMMY_NODE_ID, the next mapped node becomes the parent.
+ parent: NodeId,
+ fold_ops: F
+}
+
+impl<'a, F> Ctx<'a, F> {
+ fn insert(&self, id: NodeId, entry: MapEntry) {
+ (*self.map.map.borrow_mut()).grow_set(id as uint, &NotPresent, entry);
+ }
+}
+
+impl<'a, F: FoldOps> Folder for Ctx<'a, F> {
+ fn new_id(&mut self, id: NodeId) -> NodeId {
+ let id = self.fold_ops.new_id(id);
+ if self.parent == DUMMY_NODE_ID {
+ self.parent = id;
+ }
+ id
+ }
+
+ fn new_span(&mut self, span: Span) -> Span {
+ self.fold_ops.new_span(span)
+ }
+
+ fn fold_item(&mut self, i: Gc<Item>) -> SmallVector<Gc<Item>> {
+ let parent = self.parent;
+ self.parent = DUMMY_NODE_ID;
+
+ let i = fold::noop_fold_item(&*i, self).expect_one("expected one item");
+ assert_eq!(self.parent, i.id);
+
+ match i.node {
+ ItemImpl(_, _, _, ref ms) => {
+ for &m in ms.iter() {
+ self.insert(m.id, EntryMethod(self.parent, m));
+ }
+ }
+ ItemEnum(ref enum_definition, _) => {
+ for &v in enum_definition.variants.iter() {
+ self.insert(v.node.id, EntryVariant(self.parent, v));
+ }
+ }
+ ItemForeignMod(ref nm) => {
+ for nitem in nm.items.iter() {
+ self.insert(nitem.id, EntryForeignItem(self.parent,
+ nitem.clone()));
+ }
+ }
+ ItemStruct(ref struct_def, _) => {
+ // If this is a tuple-like struct, register the constructor.
+ match struct_def.ctor_id {
+ Some(ctor_id) => {
+ self.insert(ctor_id, EntryStructCtor(self.parent,
+ struct_def.clone()));
+ }
+ None => {}
+ }
+ }
+ ItemTrait(_, _, ref traits, ref methods) => {
+ for t in traits.iter() {
+ self.insert(t.ref_id, EntryItem(self.parent, i));
+ }
+
+ for tm in methods.iter() {
+ match *tm {
+ Required(ref m) => {
+ self.insert(m.id, EntryTraitMethod(self.parent,
+ box(GC) (*tm).clone()));
+ }
+ Provided(m) => {
+ self.insert(m.id, EntryTraitMethod(self.parent,
+ box(GC) Provided(m)));
+ }
+ }
+ }
+ }
+ _ => {}
+ }
+
+ self.parent = parent;
+ self.insert(i.id, EntryItem(self.parent, i));
+
+ SmallVector::one(i)
+ }
+
+ fn fold_pat(&mut self, pat: Gc<Pat>) -> Gc<Pat> {
+ let pat = fold::noop_fold_pat(pat, self);
+ match pat.node {
+ PatIdent(..) => {
+ // Note: this is at least *potentially* a pattern...
+ self.insert(pat.id, EntryLocal(self.parent, pat));
+ }
+ _ => {
+ self.insert(pat.id, EntryPat(self.parent, pat));
+ }
+ }
+
+ pat
+ }
+
+ fn fold_expr(&mut self, expr: Gc<Expr>) -> Gc<Expr> {
+ let expr = fold::noop_fold_expr(expr, self);
+
+ self.insert(expr.id, EntryExpr(self.parent, expr));
+
+ expr
+ }
+
+ fn fold_stmt(&mut self, stmt: &Stmt) -> SmallVector<Gc<Stmt>> {
+ let stmt = fold::noop_fold_stmt(stmt, self).expect_one("expected one statement");
+ self.insert(ast_util::stmt_id(&*stmt), EntryStmt(self.parent, stmt));
+ SmallVector::one(stmt)
+ }
+
+ fn fold_type_method(&mut self, m: &TypeMethod) -> TypeMethod {
+ let parent = self.parent;
+ self.parent = DUMMY_NODE_ID;
+ let m = fold::noop_fold_type_method(m, self);
+ assert_eq!(self.parent, m.id);
+ self.parent = parent;
+ m
+ }
+
+ fn fold_method(&mut self, m: Gc<Method>) -> SmallVector<Gc<Method>> {
+ let parent = self.parent;
+ self.parent = DUMMY_NODE_ID;
+ let m = fold::noop_fold_method(&*m, self).expect_one(
+ "noop_fold_method must produce exactly one method");
+ assert_eq!(self.parent, m.id);
+ self.parent = parent;
+ SmallVector::one(m)
+ }
+
+ fn fold_fn_decl(&mut self, decl: &FnDecl) -> P<FnDecl> {
+ let decl = fold::noop_fold_fn_decl(decl, self);
+ for a in decl.inputs.iter() {
+ self.insert(a.id, EntryArg(self.parent, a.pat));
+ }
+ decl
+ }
+
+ fn fold_block(&mut self, block: P<Block>) -> P<Block> {
+ let block = fold::noop_fold_block(block, self);
+ self.insert(block.id, EntryBlock(self.parent, block));
+ block
+ }
+
+ fn fold_lifetime(&mut self, lifetime: &Lifetime) -> Lifetime {
+ let lifetime = fold::noop_fold_lifetime(lifetime, self);
+ self.insert(lifetime.id, EntryLifetime(self.parent, box(GC) lifetime));
+ lifetime
+ }
+
+ fn fold_mac(&mut self, mac: &Mac) -> Mac {
+ fold::fold_mac(mac, self)
+ }
+}
+
+pub fn map_crate<F: FoldOps>(krate: Crate, fold_ops: F) -> (Crate, Map) {
+ let map = Map { map: RefCell::new(Vec::new()) };
+ let krate = {
+ let mut cx = Ctx {
+ map: &map,
+ parent: CRATE_NODE_ID,
+ fold_ops: fold_ops
+ };
+ cx.insert(CRATE_NODE_ID, RootCrate);
+ cx.fold_crate(krate)
+ };
+
+ if log_enabled!(::log::DEBUG) {
+ let map = map.map.borrow();
+ // This only makes sense for ordered stores; note the
+ // enumerate to count the number of entries.
+ let (entries_less_1, _) = (*map).iter().filter(|&x| {
+ match *x {
+ NotPresent => false,
+ _ => true
+ }
+ }).enumerate().last().expect("AST map was empty after folding?");
+
+ let entries = entries_less_1 + 1;
+ let vector_length = (*map).len();
+ debug!("The AST map has {} entries with a maximum of {}: occupancy {:.1}%",
+ entries, vector_length, (entries as f64 / vector_length as f64) * 100.);
+ }
+
+ (krate, map)
+}
+
+/// Used for items loaded from external crate that are being inlined into this
+/// crate. The `path` should be the path to the item but should not include
+/// the item itself.
+pub fn map_decoded_item<F: FoldOps>(map: &Map,
+ path: Vec<PathElem> ,
+ fold_ops: F,
+ fold: |&mut Ctx<F>| -> InlinedItem)
+ -> InlinedItem {
+ let mut cx = Ctx {
+ map: map,
+ parent: DUMMY_NODE_ID,
+ fold_ops: fold_ops
+ };
+
+ // Generate a NodeId for the RootInlinedParent inserted below.
+ cx.new_id(DUMMY_NODE_ID);
+
+ // Methods get added to the AST map when their impl is visited. Since we
+ // don't decode and instantiate the impl, but just the method, we have to
+ // add it to the table now. Likewise with foreign items.
+ let mut def_id = DefId { krate: LOCAL_CRATE, node: DUMMY_NODE_ID };
+ let ii = fold(&mut cx);
+ match ii {
+ IIItem(_) => {}
+ IIMethod(impl_did, is_provided, m) => {
+ let entry = if is_provided {
+ EntryTraitMethod(cx.parent, box(GC) Provided(m))
+ } else {
+ EntryMethod(cx.parent, m)
+ };
+ cx.insert(m.id, entry);
+ def_id = impl_did;
+ }
+ IIForeign(i) => {
+ cx.insert(i.id, EntryForeignItem(cx.parent, i));
+ }
+ }
+
+ cx.insert(cx.parent, RootInlinedParent(P(InlinedParent {
+ path: path,
+ def_id: def_id
+ })));
+
+ ii
+}
+
+fn node_id_to_string(map: &Map, id: NodeId) -> String {
+ match map.find(id) {
+ Some(NodeItem(item)) => {
+ let path_str = map.path_to_str_with_ident(id, item.ident);
+ let item_str = match item.node {
+ ItemStatic(..) => "static",
+ ItemFn(..) => "fn",
+ ItemMod(..) => "mod",
+ ItemForeignMod(..) => "foreign mod",
+ ItemTy(..) => "ty",
+ ItemEnum(..) => "enum",
+ ItemStruct(..) => "struct",
+ ItemTrait(..) => "trait",
+ ItemImpl(..) => "impl",
+ ItemMac(..) => "macro"
+ };
+ format!("{} {} (id={})", item_str, path_str, id)
+ }
+ Some(NodeForeignItem(item)) => {
+ let path_str = map.path_to_str_with_ident(id, item.ident);
+ format!("foreign item {} (id={})", path_str, id)
+ }
+ Some(NodeMethod(m)) => match m.node {
+ MethDecl(ident, _, _, _, _, _, _) =>
+ format!("method {} in {} (id={})",
+ token::get_ident(ident),
+ map.path_to_string(id), id),
+ MethMac(ref mac) =>
+ format!("method macro {} (id={})",
+ pprust::mac_to_string(mac), id)
+ },
+ Some(NodeTraitMethod(ref tm)) => {
+ let m = ast_util::trait_method_to_ty_method(&**tm);
+ format!("method {} in {} (id={})",
+ token::get_ident(m.ident),
+ map.path_to_string(id), id)
+ }
+ Some(NodeVariant(ref variant)) => {
+ format!("variant {} in {} (id={})",
+ token::get_ident(variant.node.name),
+ map.path_to_string(id), id)
+ }
+ Some(NodeExpr(ref expr)) => {
+ format!("expr {} (id={})", pprust::expr_to_string(&**expr), id)
+ }
+ Some(NodeStmt(ref stmt)) => {
+ format!("stmt {} (id={})", pprust::stmt_to_string(&**stmt), id)
+ }
+ Some(NodeArg(ref pat)) => {
+ format!("arg {} (id={})", pprust::pat_to_string(&**pat), id)
+ }
+ Some(NodeLocal(ref pat)) => {
+ format!("local {} (id={})", pprust::pat_to_string(&**pat), id)
+ }
+ Some(NodePat(ref pat)) => {
+ format!("pat {} (id={})", pprust::pat_to_string(&**pat), id)
+ }
+ Some(NodeBlock(ref block)) => {
+ format!("block {} (id={})", pprust::block_to_string(&**block), id)
+ }
+ Some(NodeStructCtor(_)) => {
+ format!("struct_ctor {} (id={})", map.path_to_string(id), id)
+ }
+ Some(NodeLifetime(ref l)) => {
+ format!("lifetime {} (id={})",
+ pprust::lifetime_to_string(&**l), id)
+ }
+ None => {
+ format!("unknown node (id={})", id)
+ }
+ }
+}