rapx/analysis/core/api_dependency/
graph.rs1use 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 }
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}