summaryrefslogtreecommitdiffstats
path: root/third_party/rust/bindgen/ir/dot.rs
blob: f7d07f19e200fa4913562497f74a7208f4e3533a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
//! Generating Graphviz `dot` files from our IR.

use super::context::{BindgenContext, ItemId};
use super::traversal::Trace;
use std::fs::File;
use std::io::{self, Write};
use std::path::Path;

/// A trait for anything that can write attributes as `<table>` rows to a dot
/// file.
pub trait DotAttributes {
    /// Write this thing's attributes to the given output. Each attribute must
    /// be its own `<tr>...</tr>`.
    fn dot_attributes<W>(
        &self,
        ctx: &BindgenContext,
        out: &mut W,
    ) -> io::Result<()>
    where
        W: io::Write;
}

/// Write a graphviz dot file containing our IR.
pub fn write_dot_file<P>(ctx: &BindgenContext, path: P) -> io::Result<()>
where
    P: AsRef<Path>,
{
    let file = File::create(path)?;
    let mut dot_file = io::BufWriter::new(file);
    writeln!(&mut dot_file, "digraph {{")?;

    let mut err: Option<io::Result<_>> = None;

    for (id, item) in ctx.items() {
        let is_allowlisted = ctx.allowlisted_items().contains(&id);

        writeln!(
            &mut dot_file,
            r#"{} [fontname="courier", color={}, label=< <table border="0" align="left">"#,
            id.as_usize(),
            if is_allowlisted { "black" } else { "gray" }
        )?;
        item.dot_attributes(ctx, &mut dot_file)?;
        writeln!(&mut dot_file, r#"</table> >];"#)?;

        item.trace(
            ctx,
            &mut |sub_id: ItemId, edge_kind| {
                if err.is_some() {
                    return;
                }

                match writeln!(
                    &mut dot_file,
                    "{} -> {} [label={:?}, color={}];",
                    id.as_usize(),
                    sub_id.as_usize(),
                    edge_kind,
                    if is_allowlisted { "black" } else { "gray" }
                ) {
                    Ok(_) => {}
                    Err(e) => err = Some(Err(e)),
                }
            },
            &(),
        );

        if let Some(err) = err {
            return err;
        }

        if let Some(module) = item.as_module() {
            for child in module.children() {
                writeln!(
                    &mut dot_file,
                    "{} -> {} [style=dotted, color=gray]",
                    item.id().as_usize(),
                    child.as_usize()
                )?;
            }
        }
    }

    writeln!(&mut dot_file, "}}")?;
    Ok(())
}