]>
Commit | Line | Data |
---|---|---|
f20569fa XL |
1 | use crate::obligation_forest::{ForestObligation, ObligationForest}; |
2 | use rustc_graphviz as dot; | |
3 | use std::env::var_os; | |
4 | use std::fs::File; | |
5 | use std::io::BufWriter; | |
6 | use std::path::Path; | |
7 | use std::sync::atomic::AtomicUsize; | |
8 | use std::sync::atomic::Ordering; | |
9 | ||
10 | impl<O: ForestObligation> ObligationForest<O> { | |
11 | /// Creates a graphviz representation of the obligation forest. Given a directory this will | |
12 | /// create files with name of the format `<counter>_<description>.gv`. The counter is | |
13 | /// global and is maintained internally. | |
14 | /// | |
15 | /// Calling this will do nothing unless the environment variable | |
16 | /// `DUMP_OBLIGATION_FOREST_GRAPHVIZ` is defined. | |
17 | /// | |
18 | /// A few post-processing that you might want to do make the forest easier to visualize: | |
19 | /// | |
20 | /// * `sed 's,std::[a-z]*::,,g'` — Deletes the `std::<package>::` prefix of paths. | |
21 | /// * `sed 's,"Binder(TraitPredicate(<\(.*\)>)) (\([^)]*\))","\1 (\2)",'` — Transforms | |
22 | /// `Binder(TraitPredicate(<predicate>))` into just `<predicate>`. | |
23 | #[allow(dead_code)] | |
24 | pub fn dump_graphviz<P: AsRef<Path>>(&self, dir: P, description: &str) { | |
25 | static COUNTER: AtomicUsize = AtomicUsize::new(0); | |
26 | ||
27 | if var_os("DUMP_OBLIGATION_FOREST_GRAPHVIZ").is_none() { | |
28 | return; | |
29 | } | |
30 | ||
31 | let counter = COUNTER.fetch_add(1, Ordering::AcqRel); | |
32 | ||
33 | let file_path = dir.as_ref().join(format!("{:010}_{}.gv", counter, description)); | |
34 | ||
35 | let mut gv_file = BufWriter::new(File::create(file_path).unwrap()); | |
36 | ||
37 | dot::render(&self, &mut gv_file).unwrap(); | |
38 | } | |
39 | } | |
40 | ||
41 | impl<'a, O: ForestObligation + 'a> dot::Labeller<'a> for &'a ObligationForest<O> { | |
42 | type Node = usize; | |
43 | type Edge = (usize, usize); | |
44 | ||
45 | fn graph_id(&self) -> dot::Id<'_> { | |
46 | dot::Id::new("trait_obligation_forest").unwrap() | |
47 | } | |
48 | ||
49 | fn node_id(&self, index: &Self::Node) -> dot::Id<'_> { | |
50 | dot::Id::new(format!("obligation_{}", index)).unwrap() | |
51 | } | |
52 | ||
53 | fn node_label(&self, index: &Self::Node) -> dot::LabelText<'_> { | |
54 | let node = &self.nodes[*index]; | |
55 | let label = format!("{:?} ({:?})", node.obligation.as_cache_key(), node.state.get()); | |
56 | ||
57 | dot::LabelText::LabelStr(label.into()) | |
58 | } | |
59 | ||
60 | fn edge_label(&self, (_index_source, _index_target): &Self::Edge) -> dot::LabelText<'_> { | |
61 | dot::LabelText::LabelStr("".into()) | |
62 | } | |
63 | } | |
64 | ||
65 | impl<'a, O: ForestObligation + 'a> dot::GraphWalk<'a> for &'a ObligationForest<O> { | |
66 | type Node = usize; | |
67 | type Edge = (usize, usize); | |
68 | ||
69 | fn nodes(&self) -> dot::Nodes<'_, Self::Node> { | |
70 | (0..self.nodes.len()).collect() | |
71 | } | |
72 | ||
73 | fn edges(&self) -> dot::Edges<'_, Self::Edge> { | |
74 | (0..self.nodes.len()) | |
75 | .flat_map(|i| { | |
76 | let node = &self.nodes[i]; | |
77 | ||
78 | node.dependents.iter().map(move |&d| (d, i)) | |
79 | }) | |
80 | .collect() | |
81 | } | |
82 | ||
83 | fn source(&self, (s, _): &Self::Edge) -> Self::Node { | |
84 | *s | |
85 | } | |
86 | ||
87 | fn target(&self, (_, t): &Self::Edge) -> Self::Node { | |
88 | *t | |
89 | } | |
90 | } |