libperl_macrogen/
inline_fn.rs1use std::collections::{HashMap, HashSet};
7
8use crate::apidoc_patches::ApidocPatchSet;
9use crate::ast::FunctionDef;
10use crate::intern::{InternedStr, StringInterner};
11use crate::macro_infer::{convert_assert_calls_in_compound_stmt, MacroInferContext};
12
13#[derive(Debug, Default)]
18pub struct InlineFnDict {
19 fns: HashMap<InternedStr, FunctionDef>,
20 called_functions: HashMap<InternedStr, HashSet<InternedStr>>,
22 calls_unavailable: HashSet<InternedStr>,
24 apidoc_suppressed: HashSet<InternedStr>,
29 local_usage: HashMap<InternedStr, crate::local_usage::LocalUsageAnalysis>,
33}
34
35impl InlineFnDict {
36 pub fn new() -> Self {
38 Self::default()
39 }
40
41 pub fn insert(&mut self, name: InternedStr, func_def: FunctionDef) {
43 self.fns.insert(name, func_def);
44 }
45
46 pub fn get(&self, name: InternedStr) -> Option<&FunctionDef> {
48 self.fns.get(&name)
49 }
50
51 pub fn iter(&self) -> impl Iterator<Item = (&InternedStr, &FunctionDef)> {
53 self.fns.iter()
54 }
55
56 pub fn len(&self) -> usize {
58 self.fns.len()
59 }
60
61 pub fn is_empty(&self) -> bool {
63 self.fns.is_empty()
64 }
65
66 pub fn get_called_functions(&self, name: InternedStr) -> Option<&HashSet<InternedStr>> {
68 self.called_functions.get(&name)
69 }
70
71 pub fn is_calls_unavailable(&self, name: InternedStr) -> bool {
73 self.calls_unavailable.contains(&name)
74 }
75
76 pub fn set_calls_unavailable(&mut self, name: InternedStr) {
78 self.calls_unavailable.insert(name);
79 }
80
81 pub fn is_apidoc_suppressed(&self, name: InternedStr) -> bool {
83 self.apidoc_suppressed.contains(&name)
84 }
85
86 pub fn set_apidoc_suppressed(&mut self, name: InternedStr) {
88 self.apidoc_suppressed.insert(name);
89 }
90
91 pub fn is_unavailable_for_codegen(&self, name: InternedStr) -> bool {
97 self.is_calls_unavailable(name) || self.is_apidoc_suppressed(name)
98 }
99
100 pub fn apply_apidoc_suppressions(
107 &mut self,
108 patches: &ApidocPatchSet,
109 interner: &StringInterner,
110 ) -> usize {
111 let mut count = 0usize;
112 for name_str in patches.skip_codegen.keys() {
113 if let Some(interned) = interner.lookup(name_str) {
114 if self.fns.contains_key(&interned) {
115 self.apidoc_suppressed.insert(interned);
116 count += 1;
117 }
118 }
119 }
120 count
121 }
122
123 pub fn called_functions_iter(&self) -> impl Iterator<Item = (&InternedStr, &HashSet<InternedStr>)> {
125 self.called_functions.iter()
126 }
127
128 pub fn analyze_local_usage(&mut self) {
130 let names: Vec<InternedStr> = self.fns.keys().copied().collect();
131 for name in names {
132 if let Some(func_def) = self.fns.get(&name) {
133 let analysis = crate::local_usage::analyze_function(func_def);
134 self.local_usage.insert(name, analysis);
135 }
136 }
137 }
138
139 pub fn local_usage(&self, name: InternedStr) -> Option<&crate::local_usage::LocalUsageAnalysis> {
141 self.local_usage.get(&name)
142 }
143
144 pub fn collect_from_function_def(&mut self, func_def: &FunctionDef, interner: &StringInterner) {
156 let is_static = func_def.specs.storage == Some(crate::ast::StorageClass::Static);
157 if !func_def.specs.is_inline && !is_static {
158 return;
159 }
160
161 let name = match func_def.declarator.name {
162 Some(n) => n,
163 None => return,
164 };
165
166 let mut func_def = func_def.clone();
168 convert_assert_calls_in_compound_stmt(&mut func_def.body, interner);
169
170 let mut calls = HashSet::new();
172 MacroInferContext::collect_function_calls_from_block_items(
173 &func_def.body.items,
174 &mut calls,
175 );
176 self.called_functions.insert(name, calls);
177
178 self.insert(name, func_def);
179 }
180}
181
182#[cfg(test)]
183mod tests {
184 use super::*;
185
186 #[test]
187 fn test_inline_fn_dict_new() {
188 let dict = InlineFnDict::new();
189 assert!(dict.is_empty());
190 assert_eq!(dict.len(), 0);
191 }
192}