auto merge of #15371 : pnkfelix/rust/fsk-render-dataflow-on-dot, r=alexcrichton
Use one or more of the following `-Z` flag options to tell the graphviz renderer to include the corresponding dataflow sets (after the iterative constraint propagation reaches a fixed-point solution): * `-Z flowgraph-print-loans` : loans computed via middle::borrowck * `-Z flowgraph-print-moves` : moves computed via middle::borrowck::move_data * `-Z flowgraph-print-assigns` : assignments, via middle::borrowck::move_data * `-Z flowgraph-print-all` : all of the available sets are included. Fix #15016.
This commit is contained in:
commit
d336c1a3d2
10 changed files with 627 additions and 47 deletions
|
@ -434,10 +434,37 @@ impl<'a> LabelText<'a> {
|
||||||
/// Renders text as string suitable for a label in a .dot file.
|
/// Renders text as string suitable for a label in a .dot file.
|
||||||
pub fn escape(&self) -> String {
|
pub fn escape(&self) -> String {
|
||||||
match self {
|
match self {
|
||||||
&LabelStr(ref s) => s.as_slice().escape_default().to_string(),
|
&LabelStr(ref s) => s.as_slice().escape_default(),
|
||||||
&EscStr(ref s) => LabelText::escape_str(s.as_slice()).to_string(),
|
&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>;
|
pub type Nodes<'a,N> = MaybeOwnedVector<'a,N>;
|
||||||
|
@ -664,10 +691,7 @@ mod tests {
|
||||||
let mut writer = MemWriter::new();
|
let mut writer = MemWriter::new();
|
||||||
render(&g, &mut writer).unwrap();
|
render(&g, &mut writer).unwrap();
|
||||||
let mut r = BufReader::new(writer.get_ref());
|
let mut r = BufReader::new(writer.get_ref());
|
||||||
match r.read_to_string() {
|
r.read_to_string()
|
||||||
Ok(string) => Ok(string.to_string()),
|
|
||||||
Err(err) => Err(err),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// All of the tests use raw-strings as the format for the expected outputs,
|
// All of the tests use raw-strings as the format for the expected outputs,
|
||||||
|
|
|
@ -179,7 +179,11 @@ debugging_opts!(
|
||||||
AST_JSON,
|
AST_JSON,
|
||||||
AST_JSON_NOEXPAND,
|
AST_JSON_NOEXPAND,
|
||||||
LS,
|
LS,
|
||||||
SAVE_ANALYSIS
|
SAVE_ANALYSIS,
|
||||||
|
FLOWGRAPH_PRINT_LOANS,
|
||||||
|
FLOWGRAPH_PRINT_MOVES,
|
||||||
|
FLOWGRAPH_PRINT_ASSIGNS,
|
||||||
|
FLOWGRAPH_PRINT_ALL
|
||||||
]
|
]
|
||||||
0
|
0
|
||||||
)
|
)
|
||||||
|
@ -215,7 +219,15 @@ pub fn debugging_opts_map() -> Vec<(&'static str, &'static str, u64)> {
|
||||||
("ast-json-noexpand", "Print the pre-expansion AST as JSON and halt", AST_JSON_NOEXPAND),
|
("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),
|
("ls", "List the symbols defined by a library crate", LS),
|
||||||
("save-analysis", "Write syntax and type analysis information \
|
("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
|
/// Declare a macro that will define all CodegenOptions fields and parsers all
|
||||||
|
|
|
@ -19,6 +19,9 @@ use lint;
|
||||||
use llvm::{ContextRef, ModuleRef};
|
use llvm::{ContextRef, ModuleRef};
|
||||||
use metadata::common::LinkMeta;
|
use metadata::common::LinkMeta;
|
||||||
use metadata::creader;
|
use metadata::creader;
|
||||||
|
use middle::borrowck::{FnPartsWithCFG};
|
||||||
|
use middle::borrowck;
|
||||||
|
use borrowck_dot = middle::borrowck::graphviz;
|
||||||
use middle::cfg;
|
use middle::cfg;
|
||||||
use middle::cfg::graphviz::LabelledCFG;
|
use middle::cfg::graphviz::LabelledCFG;
|
||||||
use middle::{trans, freevars, stability, kind, ty, typeck, reachable};
|
use middle::{trans, freevars, stability, kind, ty, typeck, reachable};
|
||||||
|
@ -40,6 +43,7 @@ use std::io;
|
||||||
use std::io::fs;
|
use std::io::fs;
|
||||||
use std::io::MemReader;
|
use std::io::MemReader;
|
||||||
use syntax::ast;
|
use syntax::ast;
|
||||||
|
use syntax::ast_map::blocks;
|
||||||
use syntax::attr;
|
use syntax::attr;
|
||||||
use syntax::attr::{AttrMetaMethods};
|
use syntax::attr::{AttrMetaMethods};
|
||||||
use syntax::diagnostics;
|
use syntax::diagnostics;
|
||||||
|
@ -662,6 +666,25 @@ impl pprust::PpAnn for TypedAnnotation {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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,
|
pub fn pretty_print_input(sess: Session,
|
||||||
cfg: ast::CrateConfig,
|
cfg: ast::CrateConfig,
|
||||||
input: &Input,
|
input: &Input,
|
||||||
|
@ -733,10 +756,17 @@ pub fn pretty_print_input(sess: Session,
|
||||||
sess.fatal(format!("--pretty flowgraph couldn't find id: {}",
|
sess.fatal(format!("--pretty flowgraph couldn't find id: {}",
|
||||||
nodeid).as_slice())
|
nodeid).as_slice())
|
||||||
});
|
});
|
||||||
let block = match node {
|
let code = blocks::Code::from_node(node);
|
||||||
syntax::ast_map::NodeBlock(block) => block,
|
match code {
|
||||||
_ => {
|
Some(code) => {
|
||||||
let message = format!("--pretty=flowgraph needs block, got {:?}",
|
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);
|
node);
|
||||||
|
|
||||||
// point to what was found, if there's an
|
// point to what was found, if there's an
|
||||||
|
@ -746,10 +776,7 @@ pub fn pretty_print_input(sess: Session,
|
||||||
None => sess.fatal(message.as_slice())
|
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(),
|
pprust::print_crate(sess.codemap(),
|
||||||
|
@ -765,17 +792,52 @@ pub fn pretty_print_input(sess: Session,
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_flowgraph<W:io::Writer>(analysis: CrateAnalysis,
|
fn print_flowgraph<W:io::Writer>(variants: Vec<borrowck_dot::Variant>,
|
||||||
block: ast::P<ast::Block>,
|
analysis: CrateAnalysis,
|
||||||
|
code: blocks::Code,
|
||||||
mut out: W) -> io::IoResult<()> {
|
mut out: W) -> io::IoResult<()> {
|
||||||
let ty_cx = &analysis.ty_cx;
|
let ty_cx = &analysis.ty_cx;
|
||||||
let cfg = cfg::CFG::new(ty_cx, &*block);
|
let cfg = match code {
|
||||||
let lcfg = LabelledCFG { ast_map: &ty_cx.map,
|
blocks::BlockCode(block) => cfg::CFG::new(ty_cx, &*block),
|
||||||
cfg: &cfg,
|
blocks::FnLikeCode(fn_like) => cfg::CFG::new(ty_cx, fn_like.body()),
|
||||||
name: format!("block{}", block.id), };
|
};
|
||||||
debug!("cfg: {:?}", cfg);
|
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<()> {
|
fn expand_err_details(r: io::IoResult<()>) -> io::IoResult<()> {
|
||||||
r.map_err(|ioerr| {
|
r.map_err(|ioerr| {
|
||||||
|
|
148
src/librustc/middle/borrowck/graphviz.rs
Normal file
148
src/librustc/middle/borrowck/graphviz.rs
Normal file
|
@ -0,0 +1,148 @@
|
||||||
|
// 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) }
|
||||||
|
}
|
|
@ -28,6 +28,7 @@ use std::gc::{Gc, GC};
|
||||||
use std::string::String;
|
use std::string::String;
|
||||||
use syntax::ast;
|
use syntax::ast;
|
||||||
use syntax::ast_map;
|
use syntax::ast_map;
|
||||||
|
use syntax::ast_map::blocks::{FnLikeNode, FnParts};
|
||||||
use syntax::ast_util;
|
use syntax::ast_util;
|
||||||
use syntax::codemap::Span;
|
use syntax::codemap::Span;
|
||||||
use syntax::parse::token;
|
use syntax::parse::token;
|
||||||
|
@ -50,6 +51,8 @@ pub mod check_loans;
|
||||||
|
|
||||||
pub mod gather_loans;
|
pub mod gather_loans;
|
||||||
|
|
||||||
|
pub mod graphviz;
|
||||||
|
|
||||||
pub mod move_data;
|
pub mod move_data;
|
||||||
|
|
||||||
#[deriving(Clone)]
|
#[deriving(Clone)]
|
||||||
|
@ -116,6 +119,13 @@ fn borrowck_item(this: &mut BorrowckCtxt, item: &ast::Item) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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,
|
fn borrowck_fn(this: &mut BorrowckCtxt,
|
||||||
fk: &FnKind,
|
fk: &FnKind,
|
||||||
decl: &ast::FnDecl,
|
decl: &ast::FnDecl,
|
||||||
|
@ -123,18 +133,35 @@ fn borrowck_fn(this: &mut BorrowckCtxt,
|
||||||
sp: Span,
|
sp: Span,
|
||||||
id: ast::NodeId) {
|
id: ast::NodeId) {
|
||||||
debug!("borrowck_fn(id={})", id);
|
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.
|
// Check the body of fn items.
|
||||||
let id_range = ast_util::compute_id_range_for_fn_body(fk, decl, body, sp, id);
|
let id_range = ast_util::compute_id_range_for_fn_body(fk, decl, body, sp, id);
|
||||||
let (all_loans, move_data) =
|
let (all_loans, move_data) =
|
||||||
gather_loans::gather_loans_in_fn(this, decl, body);
|
gather_loans::gather_loans_in_fn(this, decl, body);
|
||||||
let cfg = cfg::CFG::new(this.tcx, body);
|
|
||||||
|
|
||||||
let mut loan_dfcx =
|
let mut loan_dfcx =
|
||||||
DataFlowContext::new(this.tcx,
|
DataFlowContext::new(this.tcx,
|
||||||
"borrowck",
|
"borrowck",
|
||||||
Some(decl),
|
Some(decl),
|
||||||
&cfg,
|
cfg,
|
||||||
LoanDataFlowOperator,
|
LoanDataFlowOperator,
|
||||||
id_range,
|
id_range,
|
||||||
all_loans.len());
|
all_loans.len());
|
||||||
|
@ -142,20 +169,57 @@ fn borrowck_fn(this: &mut BorrowckCtxt,
|
||||||
loan_dfcx.add_gen(loan.gen_scope, loan_idx);
|
loan_dfcx.add_gen(loan.gen_scope, loan_idx);
|
||||||
loan_dfcx.add_kill(loan.kill_scope, loan_idx);
|
loan_dfcx.add_kill(loan.kill_scope, loan_idx);
|
||||||
}
|
}
|
||||||
loan_dfcx.add_kills_from_flow_exits(&cfg);
|
loan_dfcx.add_kills_from_flow_exits(cfg);
|
||||||
loan_dfcx.propagate(&cfg, body);
|
loan_dfcx.propagate(cfg, body);
|
||||||
|
|
||||||
let flowed_moves = move_data::FlowedMoveData::new(move_data,
|
let flowed_moves = move_data::FlowedMoveData::new(move_data,
|
||||||
this.tcx,
|
this.tcx,
|
||||||
&cfg,
|
cfg,
|
||||||
id_range,
|
id_range,
|
||||||
decl,
|
decl,
|
||||||
body);
|
body);
|
||||||
|
|
||||||
check_loans::check_loans(this, &loan_dfcx, flowed_moves,
|
AnalysisData { all_loans: all_loans,
|
||||||
all_loans.as_slice(), decl, body);
|
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------
|
// ----------------------------------------------------------------------
|
||||||
|
@ -198,6 +262,12 @@ pub struct Loan {
|
||||||
cause: euv::LoanCause,
|
cause: euv::LoanCause,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Loan {
|
||||||
|
pub fn loan_path(&self) -> Rc<LoanPath> {
|
||||||
|
self.loan_path.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[deriving(PartialEq, Eq, Hash)]
|
#[deriving(PartialEq, Eq, Hash)]
|
||||||
pub enum LoanPath {
|
pub enum LoanPath {
|
||||||
LpVar(ast::NodeId), // `x` in doc.rs
|
LpVar(ast::NodeId), // `x` in doc.rs
|
||||||
|
|
|
@ -189,7 +189,7 @@ impl MoveData {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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()
|
self.paths.borrow().get(index.get()).loan_path.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -534,7 +534,7 @@ impl MoveData {
|
||||||
impl<'a> FlowedMoveData<'a> {
|
impl<'a> FlowedMoveData<'a> {
|
||||||
pub fn new(move_data: MoveData,
|
pub fn new(move_data: MoveData,
|
||||||
tcx: &'a ty::ctxt,
|
tcx: &'a ty::ctxt,
|
||||||
cfg: &'a cfg::CFG,
|
cfg: &cfg::CFG,
|
||||||
id_range: ast_util::IdRange,
|
id_range: ast_util::IdRange,
|
||||||
decl: &ast::FnDecl,
|
decl: &ast::FnDecl,
|
||||||
body: &ast::Block)
|
body: &ast::Block)
|
||||||
|
|
|
@ -117,3 +117,4 @@ impl<'a> dot::GraphWalk<'a, Node<'a>, Edge<'a>> for LabelledCFG<'a>
|
||||||
fn source(&self, edge: &Edge<'a>) -> Node<'a> { self.cfg.source(edge) }
|
fn source(&self, edge: &Edge<'a>) -> Node<'a> { self.cfg.source(edge) }
|
||||||
fn target(&self, edge: &Edge<'a>) -> Node<'a> { self.cfg.target(edge) }
|
fn target(&self, edge: &Edge<'a>) -> Node<'a> { self.cfg.target(edge) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,9 @@ use syntax::visit;
|
||||||
use syntax::print::{pp, pprust};
|
use syntax::print::{pp, pprust};
|
||||||
use util::nodemap::NodeMap;
|
use util::nodemap::NodeMap;
|
||||||
|
|
||||||
|
#[deriving(Show)]
|
||||||
|
pub enum EntryOrExit { Entry, Exit }
|
||||||
|
|
||||||
#[deriving(Clone)]
|
#[deriving(Clone)]
|
||||||
pub struct DataFlowContext<'a, O> {
|
pub struct DataFlowContext<'a, O> {
|
||||||
tcx: &'a ty::ctxt,
|
tcx: &'a ty::ctxt,
|
||||||
|
@ -93,17 +96,18 @@ fn to_cfgidx_or_die(id: ast::NodeId, index: &NodeMap<CFGIndex>) -> CFGIndex {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, O:DataFlowOperator> DataFlowContext<'a, O> {
|
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);
|
assert!(n != ast::DUMMY_NODE_ID);
|
||||||
match self.nodeid_to_index.find(&n) {
|
match self.nodeid_to_index.find(&n) {
|
||||||
None => false,
|
None => false,
|
||||||
Some(&cfgidx) => {
|
Some(&cfgidx) => self.has_bitset_for_cfgidx(cfgidx),
|
||||||
let node_id = cfgidx.node_id();
|
|
||||||
node_id < self.index_to_bitset.len() &&
|
|
||||||
self.index_to_bitset.get(node_id).is_some()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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 {
|
fn get_bitset_index(&self, cfgidx: CFGIndex) -> uint {
|
||||||
let node_id = cfgidx.node_id();
|
let node_id = cfgidx.node_id();
|
||||||
self.index_to_bitset.get(node_id).unwrap()
|
self.index_to_bitset.get(node_id).unwrap()
|
||||||
|
@ -160,7 +164,7 @@ impl<'a, O:DataFlowOperator> pprust::PpAnn for DataFlowContext<'a, O> {
|
||||||
pprust::NodePat(pat) => pat.id
|
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 cfgidx = to_cfgidx_or_die(id, &self.nodeid_to_index);
|
||||||
let (start, end) = self.compute_id_range_frozen(cfgidx);
|
let (start, end) = self.compute_id_range_frozen(cfgidx);
|
||||||
let on_entry = self.on_entry.slice(start, end);
|
let on_entry = self.on_entry.slice(start, end);
|
||||||
|
@ -287,7 +291,7 @@ impl<'a, O:DataFlowOperator> DataFlowContext<'a, O> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_gen_kill(&mut self, cfgidx: CFGIndex, bits: &mut [uint]) {
|
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]",
|
debug!("{:s} apply_gen_kill(cfgidx={}, bits={}) [before]",
|
||||||
self.analysis_name, cfgidx, mut_bits_to_string(bits));
|
self.analysis_name, cfgidx, mut_bits_to_string(bits));
|
||||||
let (start, end) = self.compute_id_range(cfgidx);
|
let (start, end) = self.compute_id_range(cfgidx);
|
||||||
|
@ -300,6 +304,21 @@ impl<'a, O:DataFlowOperator> DataFlowContext<'a, O> {
|
||||||
self.analysis_name, cfgidx, mut_bits_to_string(bits));
|
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) {
|
fn compute_id_range_frozen(&self, cfgidx: CFGIndex) -> (uint, uint) {
|
||||||
let n = self.get_bitset_index(cfgidx);
|
let n = self.get_bitset_index(cfgidx);
|
||||||
let start = n * self.words_per_id;
|
let start = n * self.words_per_id;
|
||||||
|
@ -327,21 +346,45 @@ impl<'a, O:DataFlowOperator> DataFlowContext<'a, O> {
|
||||||
-> bool {
|
-> bool {
|
||||||
//! Iterates through each bit that is set on entry to `id`.
|
//! Iterates through each bit that is set on entry to `id`.
|
||||||
//! Only useful after `propagate()` has been called.
|
//! Only useful after `propagate()` has been called.
|
||||||
if !self.has_bitset(id) {
|
if !self.has_bitset_for_nodeid(id) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
let cfgidx = to_cfgidx_or_die(id, &self.nodeid_to_index);
|
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 (start, end) = self.compute_id_range_frozen(cfgidx);
|
||||||
let on_entry = self.on_entry.slice(start, end);
|
let on_entry = self.on_entry.slice(start, end);
|
||||||
debug!("{:s} each_bit_on_entry_frozen(id={:?}, on_entry={})",
|
let temp_bits;
|
||||||
self.analysis_name, id, bits_to_string(on_entry));
|
let slice = match e {
|
||||||
self.each_bit(on_entry, f)
|
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)
|
pub fn each_gen_bit_frozen(&self, id: ast::NodeId, f: |uint| -> bool)
|
||||||
-> bool {
|
-> bool {
|
||||||
//! Iterates through each bit in the gen set for `id`.
|
//! 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;
|
return true;
|
||||||
}
|
}
|
||||||
let cfgidx = to_cfgidx_or_die(id, &self.nodeid_to_index);
|
let cfgidx = to_cfgidx_or_die(id, &self.nodeid_to_index);
|
||||||
|
|
218
src/libsyntax/ast_map/blocks.rs
Normal file
218
src/libsyntax/ast_map/blocks.rs
Normal file
|
@ -0,0 +1,218 @@
|
||||||
|
// 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"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -24,6 +24,8 @@ use std::gc::{Gc, GC};
|
||||||
use std::iter;
|
use std::iter;
|
||||||
use std::slice;
|
use std::slice;
|
||||||
|
|
||||||
|
pub mod blocks;
|
||||||
|
|
||||||
#[deriving(Clone, PartialEq)]
|
#[deriving(Clone, PartialEq)]
|
||||||
pub enum PathElem {
|
pub enum PathElem {
|
||||||
PathMod(Name),
|
PathMod(Name),
|
Loading…
Add table
Add a link
Reference in a new issue