Skip to content

Commit d336c1a

Browse files
committed
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.
2 parents b74562b + e64f594 commit d336c1a

File tree

10 files changed

+627
-47
lines changed

10 files changed

+627
-47
lines changed

Diff for: src/libgraphviz/lib.rs

+30-6
Original file line numberDiff line numberDiff line change
@@ -434,10 +434,37 @@ impl<'a> LabelText<'a> {
434434
/// Renders text as string suitable for a label in a .dot file.
435435
pub fn escape(&self) -> String {
436436
match self {
437-
&LabelStr(ref s) => s.as_slice().escape_default().to_string(),
438-
&EscStr(ref s) => LabelText::escape_str(s.as_slice()).to_string(),
437+
&LabelStr(ref s) => s.as_slice().escape_default(),
438+
&EscStr(ref s) => LabelText::escape_str(s.as_slice()),
439439
}
440440
}
441+
442+
/// Decomposes content into string suitable for making EscStr that
443+
/// yields same content as self. The result obeys the law
444+
/// render(`lt`) == render(`EscStr(lt.pre_escaped_content())`) for
445+
/// all `lt: LabelText`.
446+
fn pre_escaped_content(self) -> str::MaybeOwned<'a> {
447+
match self {
448+
EscStr(s) => s,
449+
LabelStr(s) => if s.as_slice().contains_char('\\') {
450+
str::Owned(s.as_slice().escape_default())
451+
} else {
452+
s
453+
},
454+
}
455+
}
456+
457+
/// Puts `prefix` on a line above this label, with a blank line separator.
458+
pub fn prefix_line(self, prefix: LabelText) -> LabelText {
459+
prefix.suffix_line(self)
460+
}
461+
462+
/// Puts `suffix` on a line below this label, with a blank line separator.
463+
pub fn suffix_line(self, suffix: LabelText) -> LabelText {
464+
let prefix = self.pre_escaped_content().into_string();
465+
let suffix = suffix.pre_escaped_content();
466+
EscStr(str::Owned(prefix.append(r"\n\n").append(suffix.as_slice())))
467+
}
441468
}
442469

443470
pub type Nodes<'a,N> = MaybeOwnedVector<'a,N>;
@@ -664,10 +691,7 @@ mod tests {
664691
let mut writer = MemWriter::new();
665692
render(&g, &mut writer).unwrap();
666693
let mut r = BufReader::new(writer.get_ref());
667-
match r.read_to_string() {
668-
Ok(string) => Ok(string.to_string()),
669-
Err(err) => Err(err),
670-
}
694+
r.read_to_string()
671695
}
672696

673697
// All of the tests use raw-strings as the format for the expected outputs,

Diff for: src/librustc/driver/config.rs

+14-2
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,11 @@ debugging_opts!(
179179
AST_JSON,
180180
AST_JSON_NOEXPAND,
181181
LS,
182-
SAVE_ANALYSIS
182+
SAVE_ANALYSIS,
183+
FLOWGRAPH_PRINT_LOANS,
184+
FLOWGRAPH_PRINT_MOVES,
185+
FLOWGRAPH_PRINT_ASSIGNS,
186+
FLOWGRAPH_PRINT_ALL
183187
]
184188
0
185189
)
@@ -215,7 +219,15 @@ pub fn debugging_opts_map() -> Vec<(&'static str, &'static str, u64)> {
215219
("ast-json-noexpand", "Print the pre-expansion AST as JSON and halt", AST_JSON_NOEXPAND),
216220
("ls", "List the symbols defined by a library crate", LS),
217221
("save-analysis", "Write syntax and type analysis information \
218-
in addition to normal output", SAVE_ANALYSIS))
222+
in addition to normal output", SAVE_ANALYSIS),
223+
("flowgraph-print-loans", "Include loan analysis data in \
224+
--pretty flowgraph output", FLOWGRAPH_PRINT_LOANS),
225+
("flowgraph-print-moves", "Include move analysis data in \
226+
--pretty flowgraph output", FLOWGRAPH_PRINT_MOVES),
227+
("flowgraph-print-assigns", "Include assignment analysis data in \
228+
--pretty flowgraph output", FLOWGRAPH_PRINT_ASSIGNS),
229+
("flowgraph-print-all", "Include all dataflow analysis data in \
230+
--pretty flowgraph output", FLOWGRAPH_PRINT_ALL))
219231
}
220232

221233
/// Declare a macro that will define all CodegenOptions fields and parsers all

Diff for: src/librustc/driver/driver.rs

+78-16
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ use lint;
1919
use llvm::{ContextRef, ModuleRef};
2020
use metadata::common::LinkMeta;
2121
use metadata::creader;
22+
use middle::borrowck::{FnPartsWithCFG};
23+
use middle::borrowck;
24+
use borrowck_dot = middle::borrowck::graphviz;
2225
use middle::cfg;
2326
use middle::cfg::graphviz::LabelledCFG;
2427
use middle::{trans, freevars, stability, kind, ty, typeck, reachable};
@@ -40,6 +43,7 @@ use std::io;
4043
use std::io::fs;
4144
use std::io::MemReader;
4245
use syntax::ast;
46+
use syntax::ast_map::blocks;
4347
use syntax::attr;
4448
use syntax::attr::{AttrMetaMethods};
4549
use syntax::diagnostics;
@@ -662,6 +666,25 @@ impl pprust::PpAnn for TypedAnnotation {
662666
}
663667
}
664668

669+
fn gather_flowgraph_variants(sess: &Session) -> Vec<borrowck_dot::Variant> {
670+
let print_loans = config::FLOWGRAPH_PRINT_LOANS;
671+
let print_moves = config::FLOWGRAPH_PRINT_MOVES;
672+
let print_assigns = config::FLOWGRAPH_PRINT_ASSIGNS;
673+
let print_all = config::FLOWGRAPH_PRINT_ALL;
674+
let opt = |print_which| sess.debugging_opt(print_which);
675+
let mut variants = Vec::new();
676+
if opt(print_all) || opt(print_loans) {
677+
variants.push(borrowck_dot::Loans);
678+
}
679+
if opt(print_all) || opt(print_moves) {
680+
variants.push(borrowck_dot::Moves);
681+
}
682+
if opt(print_all) || opt(print_assigns) {
683+
variants.push(borrowck_dot::Assigns);
684+
}
685+
variants
686+
}
687+
665688
pub fn pretty_print_input(sess: Session,
666689
cfg: ast::CrateConfig,
667690
input: &Input,
@@ -733,10 +756,17 @@ pub fn pretty_print_input(sess: Session,
733756
sess.fatal(format!("--pretty flowgraph couldn't find id: {}",
734757
nodeid).as_slice())
735758
});
736-
let block = match node {
737-
syntax::ast_map::NodeBlock(block) => block,
738-
_ => {
739-
let message = format!("--pretty=flowgraph needs block, got {:?}",
759+
let code = blocks::Code::from_node(node);
760+
match code {
761+
Some(code) => {
762+
let variants = gather_flowgraph_variants(&sess);
763+
let analysis = phase_3_run_analysis_passes(sess, &krate,
764+
ast_map, id);
765+
print_flowgraph(variants, analysis, code, out)
766+
}
767+
None => {
768+
let message = format!("--pretty=flowgraph needs \
769+
block, fn, or method; got {:?}",
740770
node);
741771

742772
// point to what was found, if there's an
@@ -746,10 +776,7 @@ pub fn pretty_print_input(sess: Session,
746776
None => sess.fatal(message.as_slice())
747777
}
748778
}
749-
};
750-
let analysis = phase_3_run_analysis_passes(sess, &krate,
751-
ast_map, id);
752-
print_flowgraph(analysis, block, out)
779+
}
753780
}
754781
_ => {
755782
pprust::print_crate(sess.codemap(),
@@ -765,17 +792,52 @@ pub fn pretty_print_input(sess: Session,
765792

766793
}
767794

768-
fn print_flowgraph<W:io::Writer>(analysis: CrateAnalysis,
769-
block: ast::P<ast::Block>,
795+
fn print_flowgraph<W:io::Writer>(variants: Vec<borrowck_dot::Variant>,
796+
analysis: CrateAnalysis,
797+
code: blocks::Code,
770798
mut out: W) -> io::IoResult<()> {
771799
let ty_cx = &analysis.ty_cx;
772-
let cfg = cfg::CFG::new(ty_cx, &*block);
773-
let lcfg = LabelledCFG { ast_map: &ty_cx.map,
774-
cfg: &cfg,
775-
name: format!("block{}", block.id), };
800+
let cfg = match code {
801+
blocks::BlockCode(block) => cfg::CFG::new(ty_cx, &*block),
802+
blocks::FnLikeCode(fn_like) => cfg::CFG::new(ty_cx, fn_like.body()),
803+
};
776804
debug!("cfg: {:?}", cfg);
777-
let r = dot::render(&lcfg, &mut out);
778-
return expand_err_details(r);
805+
806+
match code {
807+
_ if variants.len() == 0 => {
808+
let lcfg = LabelledCFG {
809+
ast_map: &ty_cx.map,
810+
cfg: &cfg,
811+
name: format!("node_{}", code.id()),
812+
};
813+
let r = dot::render(&lcfg, &mut out);
814+
return expand_err_details(r);
815+
}
816+
blocks::BlockCode(_) => {
817+
ty_cx.sess.err("--pretty flowgraph with -Z flowgraph-print \
818+
annotations requires fn-like node id.");
819+
return Ok(())
820+
}
821+
blocks::FnLikeCode(fn_like) => {
822+
let fn_parts = FnPartsWithCFG::from_fn_like(&fn_like, &cfg);
823+
let (bccx, analysis_data) =
824+
borrowck::build_borrowck_dataflow_data_for_fn(ty_cx, fn_parts);
825+
826+
let lcfg = LabelledCFG {
827+
ast_map: &ty_cx.map,
828+
cfg: &cfg,
829+
name: format!("node_{}", code.id()),
830+
};
831+
let lcfg = borrowck_dot::DataflowLabeller {
832+
inner: lcfg,
833+
variants: variants,
834+
borrowck_ctxt: &bccx,
835+
analysis_data: &analysis_data,
836+
};
837+
let r = dot::render(&lcfg, &mut out);
838+
return expand_err_details(r);
839+
}
840+
}
779841

780842
fn expand_err_details(r: io::IoResult<()>) -> io::IoResult<()> {
781843
r.map_err(|ioerr| {

Diff for: src/librustc/middle/borrowck/graphviz.rs

+148
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
//! This module provides linkage between rustc::middle::graph and
12+
//! libgraphviz traits, specialized to attaching borrowck analysis
13+
//! data to rendered labels.
14+
15+
/// For clarity, rename the graphviz crate locally to dot.
16+
use dot = graphviz;
17+
pub use middle::cfg::graphviz::{Node, Edge};
18+
use cfg_dot = middle::cfg::graphviz;
19+
20+
use middle::borrowck;
21+
use middle::borrowck::{BorrowckCtxt, LoanPath};
22+
use middle::cfg::{CFGIndex};
23+
use middle::dataflow::{DataFlowOperator, DataFlowContext, EntryOrExit};
24+
use middle::dataflow;
25+
26+
use std::rc::Rc;
27+
use std::str;
28+
29+
#[deriving(Show)]
30+
pub enum Variant {
31+
Loans,
32+
Moves,
33+
Assigns,
34+
}
35+
36+
impl Variant {
37+
pub fn short_name(&self) -> &'static str {
38+
match *self {
39+
Loans => "loans",
40+
Moves => "moves",
41+
Assigns => "assigns",
42+
}
43+
}
44+
}
45+
46+
pub struct DataflowLabeller<'a> {
47+
pub inner: cfg_dot::LabelledCFG<'a>,
48+
pub variants: Vec<Variant>,
49+
pub borrowck_ctxt: &'a BorrowckCtxt<'a>,
50+
pub analysis_data: &'a borrowck::AnalysisData<'a>,
51+
}
52+
53+
impl<'a> DataflowLabeller<'a> {
54+
fn dataflow_for(&self, e: EntryOrExit, n: &Node<'a>) -> String {
55+
let id = n.val1().data.id;
56+
debug!("dataflow_for({}, id={}) {}", e, id, self.variants);
57+
let mut sets = "".to_string();
58+
let mut seen_one = false;
59+
for &variant in self.variants.iter() {
60+
if seen_one { sets.push_str(" "); } else { seen_one = true; }
61+
sets.push_str(variant.short_name());
62+
sets.push_str(": ");
63+
sets.push_str(self.dataflow_for_variant(e, n, variant).as_slice());
64+
}
65+
sets
66+
}
67+
68+
fn dataflow_for_variant(&self, e: EntryOrExit, n: &Node, v: Variant) -> String {
69+
let cfgidx = n.val0();
70+
match v {
71+
Loans => self.dataflow_loans_for(e, cfgidx),
72+
Moves => self.dataflow_moves_for(e, cfgidx),
73+
Assigns => self.dataflow_assigns_for(e, cfgidx),
74+
}
75+
}
76+
77+
fn build_set<O:DataFlowOperator>(&self,
78+
e: EntryOrExit,
79+
cfgidx: CFGIndex,
80+
dfcx: &DataFlowContext<'a, O>,
81+
to_lp: |uint| -> Rc<LoanPath>) -> String {
82+
let mut saw_some = false;
83+
let mut set = "{".to_string();
84+
dfcx.each_bit_for_node(e, cfgidx, |index| {
85+
let lp = to_lp(index);
86+
if saw_some {
87+
set.push_str(", ");
88+
}
89+
let loan_str = self.borrowck_ctxt.loan_path_to_string(&*lp);
90+
set.push_str(loan_str.as_slice());
91+
saw_some = true;
92+
true
93+
});
94+
set.append("}")
95+
}
96+
97+
fn dataflow_loans_for(&self, e: EntryOrExit, cfgidx: CFGIndex) -> String {
98+
let dfcx = &self.analysis_data.loans;
99+
let loan_index_to_path = |loan_index| {
100+
let all_loans = &self.analysis_data.all_loans;
101+
all_loans.get(loan_index).loan_path()
102+
};
103+
self.build_set(e, cfgidx, dfcx, loan_index_to_path)
104+
}
105+
106+
fn dataflow_moves_for(&self, e: EntryOrExit, cfgidx: CFGIndex) -> String {
107+
let dfcx = &self.analysis_data.move_data.dfcx_moves;
108+
let move_index_to_path = |move_index| {
109+
let move_data = &self.analysis_data.move_data.move_data;
110+
let moves = move_data.moves.borrow();
111+
let move = moves.get(move_index);
112+
move_data.path_loan_path(move.path)
113+
};
114+
self.build_set(e, cfgidx, dfcx, move_index_to_path)
115+
}
116+
117+
fn dataflow_assigns_for(&self, e: EntryOrExit, cfgidx: CFGIndex) -> String {
118+
let dfcx = &self.analysis_data.move_data.dfcx_assign;
119+
let assign_index_to_path = |assign_index| {
120+
let move_data = &self.analysis_data.move_data.move_data;
121+
let assignments = move_data.var_assignments.borrow();
122+
let assignment = assignments.get(assign_index);
123+
move_data.path_loan_path(assignment.path)
124+
};
125+
self.build_set(e, cfgidx, dfcx, assign_index_to_path)
126+
}
127+
}
128+
129+
impl<'a> dot::Labeller<'a, Node<'a>, Edge<'a>> for DataflowLabeller<'a> {
130+
fn graph_id(&'a self) -> dot::Id<'a> { self.inner.graph_id() }
131+
fn node_id(&'a self, n: &Node<'a>) -> dot::Id<'a> { self.inner.node_id(n) }
132+
fn node_label(&'a self, n: &Node<'a>) -> dot::LabelText<'a> {
133+
let prefix = self.dataflow_for(dataflow::Entry, n);
134+
let suffix = self.dataflow_for(dataflow::Exit, n);
135+
let inner_label = self.inner.node_label(n);
136+
inner_label
137+
.prefix_line(dot::LabelStr(str::Owned(prefix)))
138+
.suffix_line(dot::LabelStr(str::Owned(suffix)))
139+
}
140+
fn edge_label(&'a self, e: &Edge<'a>) -> dot::LabelText<'a> { self.inner.edge_label(e) }
141+
}
142+
143+
impl<'a> dot::GraphWalk<'a, Node<'a>, Edge<'a>> for DataflowLabeller<'a> {
144+
fn nodes(&self) -> dot::Nodes<'a, Node<'a>> { self.inner.nodes() }
145+
fn edges(&self) -> dot::Edges<'a, Edge<'a>> { self.inner.edges() }
146+
fn source(&self, edge: &Edge<'a>) -> Node<'a> { self.inner.source(edge) }
147+
fn target(&self, edge: &Edge<'a>) -> Node<'a> { self.inner.target(edge) }
148+
}

0 commit comments

Comments
 (0)