use std::collections::{HashMap, HashSet};
use crate::apidoc_patches::ApidocPatchSet;
use crate::ast::FunctionDef;
use crate::intern::{InternedStr, StringInterner};
use crate::macro_infer::{convert_assert_calls_in_compound_stmt, MacroInferContext};
#[derive(Debug, Default)]
pub struct InlineFnDict {
fns: HashMap<InternedStr, FunctionDef>,
called_functions: HashMap<InternedStr, HashSet<InternedStr>>,
calls_unavailable: HashSet<InternedStr>,
apidoc_suppressed: HashSet<InternedStr>,
local_usage: HashMap<InternedStr, crate::local_usage::LocalUsageAnalysis>,
}
impl InlineFnDict {
pub fn new() -> Self {
Self::default()
}
pub fn insert(&mut self, name: InternedStr, func_def: FunctionDef) {
self.fns.insert(name, func_def);
}
pub fn get(&self, name: InternedStr) -> Option<&FunctionDef> {
self.fns.get(&name)
}
pub fn iter(&self) -> impl Iterator<Item = (&InternedStr, &FunctionDef)> {
self.fns.iter()
}
pub fn len(&self) -> usize {
self.fns.len()
}
pub fn is_empty(&self) -> bool {
self.fns.is_empty()
}
pub fn get_called_functions(&self, name: InternedStr) -> Option<&HashSet<InternedStr>> {
self.called_functions.get(&name)
}
pub fn is_calls_unavailable(&self, name: InternedStr) -> bool {
self.calls_unavailable.contains(&name)
}
pub fn set_calls_unavailable(&mut self, name: InternedStr) {
self.calls_unavailable.insert(name);
}
pub fn is_apidoc_suppressed(&self, name: InternedStr) -> bool {
self.apidoc_suppressed.contains(&name)
}
pub fn set_apidoc_suppressed(&mut self, name: InternedStr) {
self.apidoc_suppressed.insert(name);
}
pub fn is_unavailable_for_codegen(&self, name: InternedStr) -> bool {
self.is_calls_unavailable(name) || self.is_apidoc_suppressed(name)
}
pub fn apply_apidoc_suppressions(
&mut self,
patches: &ApidocPatchSet,
interner: &StringInterner,
) -> usize {
let mut count = 0usize;
for name_str in patches.skip_codegen.keys() {
if let Some(interned) = interner.lookup(name_str) {
if self.fns.contains_key(&interned) {
self.apidoc_suppressed.insert(interned);
count += 1;
}
}
}
count
}
pub fn called_functions_iter(&self) -> impl Iterator<Item = (&InternedStr, &HashSet<InternedStr>)> {
self.called_functions.iter()
}
pub fn analyze_local_usage(&mut self) {
let names: Vec<InternedStr> = self.fns.keys().copied().collect();
for name in names {
if let Some(func_def) = self.fns.get(&name) {
let analysis = crate::local_usage::analyze_function(func_def);
self.local_usage.insert(name, analysis);
}
}
}
pub fn local_usage(&self, name: InternedStr) -> Option<&crate::local_usage::LocalUsageAnalysis> {
self.local_usage.get(&name)
}
pub fn collect_from_function_def(&mut self, func_def: &FunctionDef, interner: &StringInterner) {
let is_static = func_def.specs.storage == Some(crate::ast::StorageClass::Static);
if !func_def.specs.is_inline && !is_static {
return;
}
let name = match func_def.declarator.name {
Some(n) => n,
None => return,
};
let mut func_def = func_def.clone();
convert_assert_calls_in_compound_stmt(&mut func_def.body, interner);
let mut calls = HashSet::new();
MacroInferContext::collect_function_calls_from_block_items(
&func_def.body.items,
&mut calls,
);
self.called_functions.insert(name, calls);
self.insert(name, func_def);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_inline_fn_dict_new() {
let dict = InlineFnDict::new();
assert!(dict.is_empty());
assert_eq!(dict.len(), 0);
}
}