use crate::{CompilerState, Pass};
use leo_ast::{Ast, Library, Location, Stub};
use leo_errors::Result;
use indexmap::IndexSet;
pub struct LibraryPruning;
impl Pass for LibraryPruning {
type Input = ();
type Output = ();
const NAME: &str = "LibraryPruning";
fn do_pass(_input: Self::Input, state: &mut CompilerState) -> Result<Self::Output> {
let has_library_stub = matches!(&state.ast, Ast::Program(p)
if p.stubs.values().any(|s| matches!(s, Stub::FromLibrary { .. })));
if !has_library_stub {
return Ok(());
}
let mut reachable: IndexSet<Location> = IndexSet::new();
let mut queue: Vec<Location> = Vec::new();
for node in state.call_graph.nodes() {
if !state.symbol_table.is_library(node.program) {
queue.extend(state.call_graph.neighbors(node).cloned());
}
}
while let Some(node) = queue.pop() {
if reachable.insert(node.clone()) {
queue.extend(state.call_graph.neighbors(&node).cloned());
}
}
let Ast::Program(program) = &mut state.ast else { unreachable!("checked above") };
for stub in program.stubs.values_mut() {
if let Stub::FromLibrary { library, .. } = stub {
prune_library(library, &reachable);
}
}
Ok(())
}
}
fn prune_library(library: &mut Library, reachable: &IndexSet<Location>) {
let declares_const = !library.consts.is_empty() || library.modules.values().any(|module| !module.consts.is_empty());
if declares_const {
return;
}
let name = library.name;
library.functions.retain(|(symbol, _)| reachable.contains(&Location::new(name, vec![*symbol])));
for (path, module) in &mut library.modules {
let program = module.unit_name;
module.functions.retain(|(symbol, _)| {
let mut full_path = Vec::with_capacity(path.len() + 1);
full_path.extend_from_slice(path);
full_path.push(*symbol);
reachable.contains(&Location::new(program, full_path))
});
}
}