rustc_data_structures/obligation_forest/
graphviz.rs

1use std::env::var_os;
2use std::fs::File;
3use std::path::Path;
4use std::sync::atomic::{AtomicUsize, Ordering};
5
6use rustc_graphviz as dot;
7
8use crate::obligation_forest::{ForestObligation, ObligationForest};
9
10impl<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!("{counter:010}_{description}.gv"));
34
35        let mut gv_file = File::create_buffered(file_path).unwrap();
36
37        dot::render(&self, &mut gv_file).unwrap();
38    }
39}
40
41impl<'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
65impl<'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}