From 773cefd87b4494ac365decfa8cd9180c6d2661a1 Mon Sep 17 00:00:00 2001 From: Jialun Zhang Date: Thu, 20 Jun 2024 18:26:52 +0800 Subject: [PATCH] feat: library entry funcs --- Cargo.toml | 3 +++ src/mir/analysis_context.rs | 33 ++++++++++++++++++--------------- src/mir/mod.rs | 1 + src/mir/visibility.rs | 30 ++++++++++++++++++++++++++++++ src/pta/andersen.rs | 9 ++++++--- src/pta/context_sensitive.rs | 9 ++++++--- 6 files changed, 64 insertions(+), 21 deletions(-) create mode 100644 src/mir/visibility.rs diff --git a/Cargo.toml b/Cargo.toml index 76da675..f490cd3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,3 +26,6 @@ rpds = { version = "*", features = ["serde"] } rustc_tools_util = "*" petgraph = "*" shellwords = "*" + +[package.metadata.rust-analyzer] +rustc_private = true \ No newline at end of file diff --git a/src/mir/analysis_context.rs b/src/mir/analysis_context.rs index 9f015e1..b188704 100644 --- a/src/mir/analysis_context.rs +++ b/src/mir/analysis_context.rs @@ -17,6 +17,7 @@ use std::rc::Rc; use crate::mir::function::{FuncId, FunctionReference, GenericArgE}; use crate::mir::known_names::{KnownNames, KnownNamesCache}; +use crate::mir::visibility::lib_entry_funcs; use crate::mir::path::Path; use crate::util; use crate::util::options::AnalysisOptions; @@ -33,7 +34,7 @@ pub struct AnalysisContext<'tcx, 'compilation> { pub session: &'compilation Session, /// The entry function of the analysis. - pub entry_point: DefId, + pub entry_points: HashSet, /// Options of the analysis. pub analysis_options: AnalysisOptions, @@ -80,7 +81,7 @@ impl<'tcx, 'compilation> AnalysisContext<'tcx, 'compilation> { analysis_options: AnalysisOptions, ) -> Option { info!("Initializing AnalysisContext"); - let mut entry_fn_def_id: Option = None; + let mut entry_fn_def_ids = HashSet::new(); // Find the DefId for the entry point according to the function name if !analysis_options.entry_func.is_empty() { @@ -90,33 +91,37 @@ impl<'tcx, 'compilation> AnalysisContext<'tcx, 'compilation> { if def_kind == DefKind::Fn || def_kind == DefKind::AssocFn { let item_name = tcx.item_name(local_def_id.to_def_id()); if item_name.to_string() == *entr_func { - entry_fn_def_id = Some(local_def_id.to_def_id()); + entry_fn_def_ids.insert(local_def_id.to_def_id()); } } } + } else { + // If `entry_func` is not specified and the local crate is a library, + // then we add all effectively public (i.e. can be called by library users) + // functions to the `entry_funcs`. + entry_fn_def_ids.extend(&lib_entry_funcs(tcx)); } - if entry_fn_def_id.is_none() { + if entry_fn_def_ids.is_empty() { // If `entry_def_id` flag is provided, find entry point according to the index - entry_fn_def_id = if let Some(entry_def_id) = analysis_options.entry_def_id { - Some(DefId::local(DefIndex::from_u32(entry_def_id))) + if let Some(entry_def_id) = analysis_options.entry_def_id { + entry_fn_def_ids.insert(DefId::local(DefIndex::from_u32(entry_def_id))); } else { // If no entry point specified, use the default entry if let Some((def_id, _)) = tcx.entry_fn(()) { - Some(def_id) - } else { - None + entry_fn_def_ids.insert(def_id); } } } - if let Some(entry_def_id) = entry_fn_def_id { - let entry_name = tcx.item_name(entry_def_id); - info!("Entry Point: {:?}, DefId: {:?}", entry_name, entry_def_id); + if !entry_fn_def_ids.is_empty() { + entry_fn_def_ids + .iter() + .for_each(|def_id| info!("Entry Point: {:?}, DefId: {:?}", tcx.item_name(*def_id), def_id)); Some(Self { tcx, session, - entry_point: entry_def_id, + entry_points: entry_fn_def_ids, analysis_options, functions: IndexVec::new(), func_id_map: HashMap::new(), @@ -323,6 +328,4 @@ impl<'tcx, 'compilation> AnalysisContext<'tcx, 'compilation> { self.aux_local_indexer.insert(func_id, aux_local_index + 1); aux } - } - diff --git a/src/mir/mod.rs b/src/mir/mod.rs index 4e45361..4076707 100644 --- a/src/mir/mod.rs +++ b/src/mir/mod.rs @@ -3,4 +3,5 @@ pub mod context; pub mod function; pub mod analysis_context; pub mod known_names; +mod visibility; pub mod path; diff --git a/src/mir/visibility.rs b/src/mir/visibility.rs new file mode 100644 index 0000000..561422b --- /dev/null +++ b/src/mir/visibility.rs @@ -0,0 +1,30 @@ +//! Utilities of resolving visibility issues. + +use rustc_hir::def::DefKind; +use rustc_hir::def_id::DefId; +use rustc_middle::ty::TyCtxt; +use std::collections::HashSet; + +pub(crate) fn is_reachable(tcx: TyCtxt<'_>, def_id: DefId) -> bool { + match def_id.as_local() { + Some(def_id) => tcx.effective_visibilities(()).is_reachable(def_id), + None => false, + } +} + +pub(crate) fn lib_entry_funcs<'tcx>(tcx: TyCtxt<'tcx>) -> HashSet { + let mut set = HashSet::new(); + for item in tcx.hir_crate_items(()).items() { + let def_id = item.owner_id.def_id.to_def_id(); + match tcx.def_kind(def_id) { + // XXX: make sure those cover all possible entries for call graph construction + DefKind::AssocFn | DefKind::Fn => { + if is_reachable(tcx, def_id) { + set.insert(def_id); + } + } + _ => {} + } + } + set +} diff --git a/src/pta/andersen.rs b/src/pta/andersen.rs index c299fd7..16a7a38 100644 --- a/src/pta/andersen.rs +++ b/src/pta/andersen.rs @@ -84,9 +84,12 @@ impl<'pta, 'tcx, 'compilation> AndersenPTA<'pta, 'tcx, 'compilation> { /// Initialize the analysis. pub fn initialize(&mut self) { // add the entry point to the call graph - let entry_point = self.acx.entry_point; - let entry_func_id = self.acx.get_func_id(entry_point, self.tcx().mk_args(&[])); - self.call_graph.add_node(entry_func_id); + let entry_points = self.acx.entry_points.clone(); + + for entry_point in entry_points { + let entry_func_id = self.acx.get_func_id(entry_point, self.tcx().mk_args(&[])); + self.call_graph.add_node(entry_func_id); + } // process statements of reachable functions self.process_reach_funcs(); diff --git a/src/pta/context_sensitive.rs b/src/pta/context_sensitive.rs index 42fa562..142c6c8 100644 --- a/src/pta/context_sensitive.rs +++ b/src/pta/context_sensitive.rs @@ -110,10 +110,13 @@ impl<'pta, 'tcx, 'compilation, S: ContextStrategy> ContextSensitivePTA<'pta, 'tc /// Initialize the analysis. pub fn initialize(&mut self) { // add the entry point to the call graph - let entry_point = self.acx.entry_point; let empty_context_id = self.get_empty_context_id(); - let entry_func_id = self.acx.get_func_id(entry_point, self.tcx().mk_args(&[])); - self.call_graph.add_node(CSFuncId::new(empty_context_id, entry_func_id)); + let entry_points = self.acx.entry_points.clone(); + for entry_point in entry_points { + let entry_func_id = self.acx.get_func_id(entry_point, self.tcx().mk_args(&[])); + self.call_graph + .add_node(CSFuncId::new(empty_context_id, entry_func_id)); + } // process statements of reachable functions self.process_reach_funcs();