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};
17use rustc_serialize::ContextOrd;
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, TyEncodable, TyDecodable)]
22pub enum NodeKey<'tcx> {
23 Item(DefId),
25 Mono(Instance<'tcx>),
31}
32
33impl<'tcx, E> ContextOrd<E> for NodeKey<'tcx> {}
35
36impl<'tcx> NodeKey<'tcx> {
37 pub fn from_instance(instance: Instance<'tcx>) -> Self {
39 if let InstanceKind::Item(def_id) = instance.def
40 && (instance.args.has_param() || instance.args.is_empty())
43 {
44 NodeKey::Item(def_id)
45 } else {
46 NodeKey::Mono(instance)
47 }
48 }
49
50 pub fn def_id(self) -> DefId {
51 match self {
52 NodeKey::Item(def_id) => def_id,
53 NodeKey::Mono(instance) => instance.def_id(),
54 }
55 }
56
57 pub fn instance(self, tcx: TyCtxt<'tcx>) -> Instance<'tcx> {
60 match self {
61 NodeKey::Item(def_id) => {
62 Instance::new_raw(def_id, GenericArgs::identity_for_item(tcx, def_id))
63 }
64 NodeKey::Mono(instance) => instance,
65 }
66 }
67
68 #[allow(
73 clippy::disallowed_methods,
74 reason = "is responsibility of who builds the graph to filter out dummy extern spec items."
75 )]
76 pub fn is_local_item(self) -> bool {
77 self.def_id().is_local()
78 && match self {
79 NodeKey::Item(_) => true,
80 NodeKey::Mono(instance) => matches!(instance.def, InstanceKind::Item(_)),
81 }
82 }
83}
84
85#[derive(Debug, Clone)]
87pub struct CallSite<'tcx> {
88 pub location: Location,
89 pub kind: CallSiteKind<'tcx>,
90}
91
92#[derive(Debug, Clone)]
93pub enum CallSiteKind<'tcx> {
94 Resolved { callee: NodeKey<'tcx> },
97 SynthesizedPanic,
100 Unresolved { def_id: DefId },
102 DynamicDispatch,
104}
105
106#[derive(Debug, Clone)]
110pub enum Node<'tcx> {
111 Analyzed { call_sites: Vec<CallSite<'tcx>> },
113 ExternalCrate,
115 Leaf(DefKind),
119}
120
121impl<'tcx> Node<'tcx> {
122 fn call_sites(&self) -> &[CallSite<'tcx>] {
124 match self {
125 Node::Analyzed { call_sites } => call_sites,
126 Node::ExternalCrate | Node::Leaf(_) => &[],
127 }
128 }
129
130 pub fn resolved_callees(&self) -> impl Iterator<Item = NodeKey<'tcx>> {
132 self.call_sites().iter().filter_map(|site| {
133 match site.kind {
134 CallSiteKind::Resolved { callee } => Some(callee),
135 _ => None,
136 }
137 })
138 }
139}
140
141pub struct CallGraph<'tcx> {
142 pub nodes: FxIndexMap<NodeKey<'tcx>, Node<'tcx>>,
143 pub callers: UnordMap<NodeKey<'tcx>, Vec<NodeKey<'tcx>>>,
144}
145
146impl<'tcx> CallGraph<'tcx> {
147 pub fn new(nodes: FxIndexMap<NodeKey<'tcx>, Node<'tcx>>) -> Self {
148 let mut callers: UnordMap<_, Vec<_>> = UnordMap::default();
149 for (&caller, node) in &nodes {
150 for callee in node.resolved_callees() {
151 callers.entry(callee).or_default().push(caller);
152 }
153 }
154 CallGraph { nodes, callers }
155 }
156
157 pub fn resolved_callee(&self, caller: DefId, location: Location) -> Option<NodeKey<'tcx>> {
161 let node = self.nodes.get(&NodeKey::Item(caller))?;
162 node.call_sites().iter().find_map(|site| {
163 match site.kind {
164 CallSiteKind::Resolved { callee } if site.location == location => Some(callee),
165 _ => None,
166 }
167 })
168 }
169}
170
171impl<'tcx> fmt::Display for NodeKey<'tcx> {
172 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
173 match self {
174 NodeKey::Item(def_id) => write!(f, "{def_id:?}"),
175 NodeKey::Mono(instance) => write!(f, "{instance}"),
176 }
177 }
178}
179
180impl<'tcx> fmt::Display for Node<'tcx> {
181 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
182 match self {
183 Node::Analyzed { .. } => write!(f, "Analyzed"),
184 Node::ExternalCrate => write!(f, "ExternalCrate"),
185 Node::Leaf(kind) => write!(f, "Leaf({kind:?})"),
186 }
187 }
188}
189
190impl<'tcx> fmt::Display for CallSiteKind<'tcx> {
191 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
192 match self {
193 CallSiteKind::Resolved { callee } => write!(f, "-> {callee}"),
194 CallSiteKind::SynthesizedPanic => write!(f, "-> panic"),
195 CallSiteKind::Unresolved { def_id: method } => write!(f, "-> trait {method:?}"),
196 CallSiteKind::DynamicDispatch => write!(f, "-> <dynamic>"),
197 }
198 }
199}
200
201impl<'tcx> fmt::Display for CallGraph<'tcx> {
202 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
203 writeln!(f, "call graph ({} nodes):", self.nodes.len())?;
204 let mut nodes: Vec<_> = self.nodes.iter().collect();
205 nodes.sort_by_key(|(key, _)| format!("{key}"));
206 for (key, node) in nodes {
207 writeln!(f, " {key} [{node}]:")?;
208 for site in node.call_sites() {
209 writeln!(f, " {} at {:?}", site.kind, site.location)?;
210 }
211 }
212 Ok(())
213 }
214}