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