rapx/analysis/core/api_dependency/
mod.rs

1/// NOTE: This analysis module is currently under development and is highly unstable.
2/// The #[allow(unused)] attribute is applied to suppress excessive lint warnings.
3/// Once the analysis stabilizes, this marker should be removed.
4
5#[allow(unused)]
6pub mod graph;
7mod mono;
8mod utils;
9#[allow(unused)]
10mod visitor;
11
12use crate::analysis::Analysis;
13use crate::{rap_debug, rap_info, rap_trace};
14pub use graph::ApiDependencyGraph;
15pub use graph::{DepEdge, DepNode};
16use rustc_hir::def_id::{DefId, LOCAL_CRATE};
17use rustc_middle::ty::TyCtxt;
18pub use utils::is_fuzzable_ty;
19
20#[derive(Debug, Clone, Copy, Eq, PartialEq, PartialOrd, Default)]
21pub struct Config {
22    pub pub_only: bool,
23    pub resolve_generic: bool,
24    pub ignore_const_generic: bool,
25}
26
27pub fn is_def_id_public(fn_def_id: impl Into<DefId>, tcx: TyCtxt<'_>) -> bool {
28    let fn_def_id: DefId = fn_def_id.into();
29    let local_id = fn_def_id.expect_local();
30    rap_trace!(
31        "vis: {:?} (path: {}) => {:?}",
32        fn_def_id,
33        tcx.def_path_str(fn_def_id),
34        tcx.effective_visibilities(()).effective_vis(local_id)
35    );
36    tcx.effective_visibilities(()).is_directly_public(local_id)
37    // || tcx.effective_visibilities(()).is_exported(local_id)
38}
39
40pub trait ApiDependencyAnalysis<'tcx> {
41    fn get_api_dependency_graph(&self) -> ApiDependencyGraph<'tcx>;
42}
43
44pub struct ApiDependencyAnalyzer<'tcx> {
45    tcx: TyCtxt<'tcx>,
46    config: Config,
47    api_graph: ApiDependencyGraph<'tcx>,
48}
49
50impl<'tcx> ApiDependencyAnalyzer<'tcx> {
51    pub fn new(tcx: TyCtxt<'tcx>, config: Config) -> ApiDependencyAnalyzer<'tcx> {
52        ApiDependencyAnalyzer {
53            tcx,
54            config,
55            api_graph: ApiDependencyGraph::new(tcx),
56        }
57    }
58}
59
60impl<'tcx> Analysis for ApiDependencyAnalyzer<'tcx> {
61    fn name(&self) -> &'static str {
62        "Default API dependency graph analysis algorithm."
63    }
64
65    fn run(&mut self) {
66        let local_crate_name = self.tcx.crate_name(LOCAL_CRATE);
67        let local_crate_type = self.tcx.crate_types()[0];
68        let config = self.config;
69        rap_debug!(
70            "Build API dependency graph on {} ({}), config = {:?}",
71            local_crate_name.as_str(),
72            local_crate_type,
73            config,
74        );
75
76        let mut api_graph = ApiDependencyGraph::new(self.tcx);
77        api_graph.build(config);
78
79        let (estimate, total) = api_graph.estimate_coverage();
80
81        let statistics = api_graph.statistics();
82        // print all statistics
83        rap_info!(
84            "API Graph contains {} API nodes, {} type nodes, {} edges",
85            statistics.api_count,
86            statistics.type_count,
87            statistics.edge_cnt
88        );
89        rap_info!(
90            "estimate coverage: {:.2} ({}/{})",
91            estimate as f64 / total as f64,
92            estimate,
93            total
94        );
95        let dot_path = format!("api_graph_{}_{}.dot", local_crate_name, local_crate_type);
96        let json_path = format!("api_graph_{}_{}.json", local_crate_name, local_crate_type);
97        rap_info!("Dump API dependency graph to {}", dot_path);
98        api_graph.dump_to_dot(dot_path, self.tcx);
99        api_graph
100            .dump_to_json(&json_path)
101            .expect("failed to dump API graph to JSON");
102        rap_info!("Dump API dependency graph to {}", json_path);
103    }
104
105    fn reset(&mut self) {
106        todo!();
107    }
108}
109
110impl<'tcx> ApiDependencyAnalysis<'tcx> for ApiDependencyAnalyzer<'tcx> {
111    fn get_api_dependency_graph(&self) -> ApiDependencyGraph<'tcx> {
112        self.api_graph.clone()
113    }
114}