rapx/analysis/core/api_dependency/
graph.rs

1use crate::{
2    analysis::core::api_dependency::{ApiDependencyGraph, Edge, InnerGraph, Node, TyWrapper},
3    utils::fs::rap_create_file,
4};
5use petgraph::{
6    dot::{Config, Dot},
7    graph::NodeIndex,
8    Graph,
9};
10
11use rustc_middle::{
12    query::IntoQueryParam,
13    ty::{Ty, TyCtxt},
14};
15
16use rustc_hir::def_id::DefId;
17
18use std::{collections::HashMap, fmt::Display, io::Write, path::Path};
19
20pub struct Statistics {
21    pub api_count: usize,
22    pub type_count: usize,
23    pub generic_param_count: usize,
24    pub edge_cnt: usize,
25}
26
27impl<'tcx> ApiDependencyGraph<'tcx> {
28    pub fn new() -> ApiDependencyGraph<'tcx> {
29        ApiDependencyGraph {
30            graph: Graph::new(),
31            node_indices: HashMap::new(),
32        }
33    }
34
35    pub fn inner_graph(&self) -> &InnerGraph<'tcx> {
36        &self.graph
37    }
38
39    pub fn statistics(&self) -> Statistics {
40        let mut api_cnt = 0;
41        let mut ty_cnt = 0;
42        let mut generic_param_cnt = 0;
43        let mut edge_cnt = 0;
44
45        for node in self.graph.node_indices() {
46            match self.graph[node] {
47                Node::Api(_) => api_cnt += 1,
48                Node::Ty(_) => ty_cnt += 1,
49                Node::GenericParamDef(..) => generic_param_cnt += 1,
50            }
51        }
52
53        for _edge in self.graph.edge_indices() {
54            edge_cnt += 1;
55        }
56
57        Statistics {
58            api_count: api_cnt,
59            type_count: ty_cnt,
60            generic_param_count: generic_param_cnt,
61            edge_cnt,
62        }
63    }
64
65    pub fn get_node(&mut self, node: Node<'tcx>) -> NodeIndex {
66        if let Some(node_index) = self.node_indices.get(&node) {
67            *node_index
68        } else {
69            let node_index = self.graph.add_node(node.clone());
70            self.node_indices.insert(node, node_index);
71            node_index
72        }
73    }
74
75    pub fn add_edge(&mut self, src: NodeIndex, dst: NodeIndex, edge: Edge) {
76        self.graph.add_edge(src, dst, edge);
77    }
78
79    pub fn add_edge_once(&mut self, src: NodeIndex, dst: NodeIndex, edge: Edge) {
80        if !self.graph.contains_edge(src, dst) {
81            self.graph.add_edge(src, dst, edge);
82        }
83    }
84
85    pub fn dump_to_dot<P: AsRef<Path>>(&self, path: P, tcx: TyCtxt<'tcx>) {
86        let get_edge_attr =
87            |_graph: &Graph<Node<'tcx>, Edge>, edge_ref: petgraph::graph::EdgeReference<Edge>| {
88                let color = match edge_ref.weight() {
89                    Edge::Arg(_) | Edge::Ret => "black",
90                    Edge::Fn2Generic => "grey",
91                };
92                format!("label=\"{}\", color = {}", edge_ref.weight(), color)
93            };
94        let get_node_attr = |_graph: &Graph<Node<'tcx>, Edge>,
95                             node_ref: (NodeIndex, &Node<'tcx>)| {
96            format!("label={:?}, ", desc_str(node_ref.1.clone(), tcx))
97                + match node_ref.1 {
98                    Node::Api(_) => "color = blue",
99                    Node::Ty(_) => "color = red",
100                    Node::GenericParamDef(..) => "color = green",
101                }
102                + ", shape=box"
103        };
104
105        let dot = Dot::with_attr_getters(
106            &self.graph,
107            &[Config::NodeNoLabel, Config::EdgeNoLabel],
108            &get_edge_attr,
109            &get_node_attr,
110        );
111        let mut file = rap_create_file(path, "can not create dot file");
112        write!(&mut file, "{:?}", dot).expect("fail when writing data to dot file");
113        // println!("{:?}", dot);
114    }
115}
116
117impl Display for Edge {
118    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
119        match self {
120            Edge::Arg(no) => write!(f, "{}", no),
121            Edge::Ret => write!(f, "r"),
122            Edge::Fn2Generic => write!(f, ""),
123        }
124    }
125}
126
127impl Edge {
128    pub fn arg(no: usize) -> Edge {
129        Edge::Arg(no)
130    }
131    pub fn ret() -> Edge {
132        Edge::Ret
133    }
134    pub fn fn2generic() -> Edge {
135        Edge::Fn2Generic
136    }
137}
138
139#[derive(Clone, Copy, Eq, PartialEq, Hash, Debug)]
140enum IntrinsicKind {
141    Borrow,
142}
143
144pub fn desc_str<'tcx>(node: Node<'tcx>, tcx: TyCtxt<'tcx>) -> String {
145    match node {
146        Node::Api(def_id) => tcx.def_path_str(def_id),
147        Node::Ty(ty) => ty.desc_str(tcx),
148        Node::GenericParamDef(_idx, index, sym, _is_lifetime) => {
149            format!("{sym}/#{index}")
150        }
151    }
152}
153
154impl<'tcx> Node<'tcx> {
155    pub fn api(id: impl IntoQueryParam<DefId>) -> Node<'tcx> {
156        Node::Api(id.into_query_param())
157    }
158    pub fn ty(ty: Ty<'tcx>) -> Node<'tcx> {
159        Node::Ty(TyWrapper::from(ty))
160    }
161    pub fn generic_param_def(
162        fn_def_id: DefId,
163        index: usize,
164        name: impl ToString,
165        is_lifetime: bool,
166    ) -> Node<'tcx> {
167        Node::GenericParamDef(fn_def_id, index, name.to_string(), is_lifetime)
168    }
169}