1use std::fmt;
9
10use rustc_data_structures::{fx::FxIndexMap, unord::UnordMap};
11use rustc_hir::{def::DefKind, def_id::DefId};
12use rustc_macros::{TyDecodable, TyEncodable};
13use rustc_middle::{
14 mir::Location,
15 ty::{GenericArgs, Instance, InstanceKind, TyCtxt, TypeVisitableExt},
16};
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, TyEncodable, TyDecodable)]
21pub enum NodeKey<'tcx> {
22 Item(DefId),
24 Mono(Instance<'tcx>),
30}
31
32impl<'tcx> NodeKey<'tcx> {
33 pub fn from_instance(instance: Instance<'tcx>) -> Self {
35 if let InstanceKind::Item(def_id) = instance.def
36 && (instance.args.has_param() || instance.args.is_empty())
39 {
40 NodeKey::Item(def_id)
41 } else {
42 NodeKey::Mono(instance)
43 }
44 }
45
46 pub fn def_id(self) -> DefId {
47 match self {
48 NodeKey::Item(def_id) => def_id,
49 NodeKey::Mono(instance) => instance.def_id(),
50 }
51 }
52
53 pub fn instance(self, tcx: TyCtxt<'tcx>) -> Instance<'tcx> {
56 match self {
57 NodeKey::Item(def_id) => {
58 Instance::new_raw(def_id, GenericArgs::identity_for_item(tcx, def_id))
59 }
60 NodeKey::Mono(instance) => instance,
61 }
62 }
63
64 #[allow(
69 clippy::disallowed_methods,
70 reason = "is responsibility of who builds the graph to filter out dummy extern spec items."
71 )]
72 pub fn is_local_item(self) -> bool {
73 self.def_id().is_local()
74 && match self {
75 NodeKey::Item(_) => true,
76 NodeKey::Mono(instance) => matches!(instance.def, InstanceKind::Item(_)),
77 }
78 }
79}
80
81#[derive(Debug, Clone)]
83pub struct CallSite<'tcx> {
84 pub location: Location,
85 pub kind: CallSiteKind<'tcx>,
86}
87
88#[derive(Debug, Clone)]
89pub enum CallSiteKind<'tcx> {
90 Resolved { callee: NodeKey<'tcx> },
93 SynthesizedPanic,
96 Unresolved { def_id: DefId },
98 DynamicDispatch,
100}
101
102#[derive(Debug, Clone)]
106pub enum Node<'tcx> {
107 Analyzed { call_sites: Vec<CallSite<'tcx>> },
109 ExternalCrate,
111 Leaf(DefKind),
115}
116
117impl<'tcx> Node<'tcx> {
118 fn call_sites(&self) -> &[CallSite<'tcx>] {
120 match self {
121 Node::Analyzed { call_sites } => call_sites,
122 Node::ExternalCrate | Node::Leaf(_) => &[],
123 }
124 }
125
126 pub fn resolved_callees(&self) -> impl Iterator<Item = NodeKey<'tcx>> {
128 self.call_sites().iter().filter_map(|site| {
129 match site.kind {
130 CallSiteKind::Resolved { callee } => Some(callee),
131 _ => None,
132 }
133 })
134 }
135}
136
137pub struct CallGraph<'tcx> {
138 pub nodes: FxIndexMap<NodeKey<'tcx>, Node<'tcx>>,
139 pub callers: UnordMap<NodeKey<'tcx>, Vec<NodeKey<'tcx>>>,
140}
141
142impl<'tcx> CallGraph<'tcx> {
143 pub fn new(nodes: FxIndexMap<NodeKey<'tcx>, Node<'tcx>>) -> Self {
144 let mut callers: UnordMap<_, Vec<_>> = UnordMap::default();
145 for (&caller, node) in &nodes {
146 for callee in node.resolved_callees() {
147 callers.entry(callee).or_default().push(caller);
148 }
149 }
150 CallGraph { nodes, callers }
151 }
152
153 pub fn resolved_callee(&self, caller: DefId, location: Location) -> Option<NodeKey<'tcx>> {
157 let node = self.nodes.get(&NodeKey::Item(caller))?;
158 node.call_sites().iter().find_map(|site| {
159 match site.kind {
160 CallSiteKind::Resolved { callee } if site.location == location => Some(callee),
161 _ => None,
162 }
163 })
164 }
165}
166
167impl<'tcx> fmt::Display for NodeKey<'tcx> {
168 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
169 match self {
170 NodeKey::Item(def_id) => write!(f, "{def_id:?}"),
171 NodeKey::Mono(instance) => write!(f, "{instance}"),
172 }
173 }
174}
175
176impl<'tcx> fmt::Display for Node<'tcx> {
177 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
178 match self {
179 Node::Analyzed { .. } => write!(f, "Analyzed"),
180 Node::ExternalCrate => write!(f, "ExternalCrate"),
181 Node::Leaf(kind) => write!(f, "Leaf({kind:?})"),
182 }
183 }
184}
185
186impl<'tcx> fmt::Display for CallSiteKind<'tcx> {
187 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
188 match self {
189 CallSiteKind::Resolved { callee } => write!(f, "-> {callee}"),
190 CallSiteKind::SynthesizedPanic => write!(f, "-> panic"),
191 CallSiteKind::Unresolved { def_id: method } => write!(f, "-> trait {method:?}"),
192 CallSiteKind::DynamicDispatch => write!(f, "-> <dynamic>"),
193 }
194 }
195}
196
197impl<'tcx> fmt::Display for CallGraph<'tcx> {
198 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
199 writeln!(f, "call graph ({} nodes):", self.nodes.len())?;
200 let mut nodes: Vec<_> = self.nodes.iter().collect();
201 nodes.sort_by_key(|(key, _)| format!("{key}"));
202 for (key, node) in nodes {
203 writeln!(f, " {key} [{node}]:")?;
204 for site in node.call_sites() {
205 writeln!(f, " {} at {:?}", site.kind, site.location)?;
206 }
207 }
208 Ok(())
209 }
210}