rapx/analysis/path_analysis/
default.rs

1use crate::analysis::{
2    Analysis,
3    path_analysis::graph::{PathEnumerator, PathGraph},
4};
5use crate::compat::FxHashMap;
6use rustc_hir::{def::DefKind, def_id::DefId};
7use rustc_middle::ty::TyCtxt;
8
9use super::PathTree;
10
11/// PathAnalyzer is responsible only for extracting path-sensitive CFG paths.
12/// Downstream analyses can reuse these paths without depending on alias logic.
13pub struct PathAnalyzer<'tcx> {
14    pub tcx: TyCtxt<'tcx>,
15    pub debug: bool,
16    pub paths: FxHashMap<DefId, PathTree>,
17    pub graphs: FxHashMap<DefId, PathGraph<'tcx>>,
18}
19
20impl<'tcx> PathAnalyzer<'tcx> {
21    pub fn new(tcx: TyCtxt<'tcx>, debug: bool) -> Self {
22        Self {
23            tcx,
24            debug,
25            paths: FxHashMap::default(),
26            graphs: FxHashMap::default(),
27        }
28    }
29
30    /// Analyze a single function, returning all whole-CFG paths for it.
31    /// Results are cached — subsequent calls for the same `def_id` return
32    /// the cached tree.
33    pub fn analyze(&mut self, def_id: DefId) -> Option<PathTree> {
34        self.analyze_repeat(def_id, 0)
35    }
36
37    /// Analyze a single function allowing each SCC postfix segment to
38    /// repeat up to `postfix_repeat` additional times.
39    pub fn analyze_repeat(&mut self, def_id: DefId, postfix_repeat: usize) -> Option<PathTree> {
40        if let Some(paths) = self.paths.get(&def_id) {
41            return Some(paths.clone());
42        }
43
44        if !self.tcx.is_mir_available(def_id) {
45            return None;
46        }
47
48        let mut graph = PathGraph::new(self.tcx, def_id);
49        graph.find_scc();
50        let mut enumerator = PathEnumerator::new(&graph);
51        let paths = enumerator.enumerate_paths_repeat(postfix_repeat);
52
53        self.graphs.insert(def_id, graph);
54        self.paths.insert(def_id, paths.clone());
55        Some(paths)
56    }
57
58    pub fn get_fn_paths(&self, def_id: DefId) -> Option<PathTree> {
59        self.paths.get(&def_id).cloned()
60    }
61
62    pub fn get_all_paths(&self) -> FxHashMap<DefId, PathTree> {
63        self.paths.clone()
64    }
65
66    /// Analyze all functions in the local crate.
67    pub fn analyze_all(&mut self) {
68        self.analyze_all_repeat(0);
69    }
70
71    /// Analyze all functions with the given postfix-repeat count.
72    pub fn analyze_all_repeat(&mut self, postfix_repeat: usize) {
73        for local_def_id in self.tcx.iter_local_def_id() {
74            if matches!(self.tcx.def_kind(local_def_id), DefKind::Fn) {
75                let def_id = local_def_id.to_def_id();
76                let _ = self.analyze_repeat(def_id, postfix_repeat);
77            }
78        }
79    }
80
81    pub fn run_with_repeat(&mut self, postfix_repeat: usize) {
82        self.analyze_all_repeat(postfix_repeat);
83    }
84}
85
86impl<'tcx> Analysis for PathAnalyzer<'tcx> {
87    fn name(&self) -> &'static str {
88        "Path Analysis"
89    }
90
91    fn run(&mut self) {
92        self.analyze_all();
93    }
94
95    fn reset(&mut self) {
96        self.paths.clear();
97        self.graphs.clear();
98    }
99}