depyler_core/codegen/
scope_tracker.rs1use std::collections::HashSet;
7
8#[derive(Debug, Clone)]
13pub struct ScopeTracker {
14 declared_vars: Vec<HashSet<String>>,
15}
16
17impl Default for ScopeTracker {
18 fn default() -> Self {
19 Self::new()
20 }
21}
22
23impl ScopeTracker {
24 pub fn new() -> Self {
26 Self {
27 declared_vars: vec![HashSet::new()],
28 }
29 }
30
31 pub fn enter_scope(&mut self) {
35 self.declared_vars.push(HashSet::new());
36 }
37
38 pub fn exit_scope(&mut self) -> bool {
42 if self.declared_vars.len() > 1 {
43 self.declared_vars.pop();
44 true
45 } else {
46 false
47 }
48 }
49
50 pub fn is_declared(&self, var_name: &str) -> bool {
52 self.declared_vars
53 .iter()
54 .any(|scope| scope.contains(var_name))
55 }
56
57 pub fn is_declared_in_current_scope(&self, var_name: &str) -> bool {
59 self.declared_vars
60 .last()
61 .is_some_and(|scope| scope.contains(var_name))
62 }
63
64 pub fn declare_var(&mut self, var_name: &str) -> bool {
68 if let Some(current_scope) = self.declared_vars.last_mut() {
69 current_scope.insert(var_name.to_string())
70 } else {
71 false
72 }
73 }
74
75 pub fn scope_depth(&self) -> usize {
77 self.declared_vars.len()
78 }
79
80 pub fn all_declared_vars(&self) -> HashSet<String> {
82 self.declared_vars
83 .iter()
84 .flat_map(|scope| scope.iter().cloned())
85 .collect()
86 }
87
88 pub fn current_scope_vars(&self) -> Option<&HashSet<String>> {
90 self.declared_vars.last()
91 }
92
93 pub fn declare_vars<I>(&mut self, var_names: I)
97 where
98 I: IntoIterator,
99 I::Item: AsRef<str>,
100 {
101 for var in var_names {
102 self.declare_var(var.as_ref());
103 }
104 }
105}
106
107#[cfg(test)]
108mod tests {
109 use super::*;
110
111 #[test]
114 fn test_new_creates_single_scope() {
115 let tracker = ScopeTracker::new();
116 assert_eq!(tracker.scope_depth(), 1);
117 }
118
119 #[test]
120 fn test_default_creates_single_scope() {
121 let tracker = ScopeTracker::default();
122 assert_eq!(tracker.scope_depth(), 1);
123 }
124
125 #[test]
126 fn test_new_scope_is_empty() {
127 let tracker = ScopeTracker::new();
128 assert!(!tracker.is_declared("x"));
129 }
130
131 #[test]
134 fn test_declare_var_returns_true_for_new() {
135 let mut tracker = ScopeTracker::new();
136 assert!(tracker.declare_var("x"));
137 }
138
139 #[test]
140 fn test_declare_var_returns_false_for_existing() {
141 let mut tracker = ScopeTracker::new();
142 tracker.declare_var("x");
143 assert!(!tracker.declare_var("x"));
144 }
145
146 #[test]
147 fn test_declared_var_is_found() {
148 let mut tracker = ScopeTracker::new();
149 tracker.declare_var("x");
150 assert!(tracker.is_declared("x"));
151 }
152
153 #[test]
154 fn test_undeclared_var_is_not_found() {
155 let tracker = ScopeTracker::new();
156 assert!(!tracker.is_declared("y"));
157 }
158
159 #[test]
162 fn test_enter_scope_increases_depth() {
163 let mut tracker = ScopeTracker::new();
164 tracker.enter_scope();
165 assert_eq!(tracker.scope_depth(), 2);
166 }
167
168 #[test]
169 fn test_exit_scope_decreases_depth() {
170 let mut tracker = ScopeTracker::new();
171 tracker.enter_scope();
172 tracker.exit_scope();
173 assert_eq!(tracker.scope_depth(), 1);
174 }
175
176 #[test]
177 fn test_exit_scope_returns_true_when_nested() {
178 let mut tracker = ScopeTracker::new();
179 tracker.enter_scope();
180 assert!(tracker.exit_scope());
181 }
182
183 #[test]
184 fn test_exit_scope_returns_false_at_outermost() {
185 let mut tracker = ScopeTracker::new();
186 assert!(!tracker.exit_scope());
187 }
188
189 #[test]
190 fn test_exit_scope_removes_vars() {
191 let mut tracker = ScopeTracker::new();
192 tracker.enter_scope();
193 tracker.declare_var("inner");
194 assert!(tracker.is_declared("inner"));
195 tracker.exit_scope();
196 assert!(!tracker.is_declared("inner"));
197 }
198
199 #[test]
200 fn test_parent_scope_vars_visible_in_child() {
201 let mut tracker = ScopeTracker::new();
202 tracker.declare_var("outer");
203 tracker.enter_scope();
204 assert!(tracker.is_declared("outer"));
205 }
206
207 #[test]
208 fn test_parent_scope_vars_survive_exit() {
209 let mut tracker = ScopeTracker::new();
210 tracker.declare_var("outer");
211 tracker.enter_scope();
212 tracker.declare_var("inner");
213 tracker.exit_scope();
214 assert!(tracker.is_declared("outer"));
215 }
216
217 #[test]
220 fn test_is_declared_in_current_scope_true() {
221 let mut tracker = ScopeTracker::new();
222 tracker.declare_var("x");
223 assert!(tracker.is_declared_in_current_scope("x"));
224 }
225
226 #[test]
227 fn test_is_declared_in_current_scope_false_for_parent() {
228 let mut tracker = ScopeTracker::new();
229 tracker.declare_var("outer");
230 tracker.enter_scope();
231 assert!(!tracker.is_declared_in_current_scope("outer"));
232 assert!(tracker.is_declared("outer")); }
234
235 #[test]
236 fn test_is_declared_in_current_scope_false_for_undeclared() {
237 let tracker = ScopeTracker::new();
238 assert!(!tracker.is_declared_in_current_scope("x"));
239 }
240
241 #[test]
244 fn test_all_declared_vars_empty() {
245 let tracker = ScopeTracker::new();
246 assert!(tracker.all_declared_vars().is_empty());
247 }
248
249 #[test]
250 fn test_all_declared_vars_single_scope() {
251 let mut tracker = ScopeTracker::new();
252 tracker.declare_var("a");
253 tracker.declare_var("b");
254 let vars = tracker.all_declared_vars();
255 assert!(vars.contains("a"));
256 assert!(vars.contains("b"));
257 assert_eq!(vars.len(), 2);
258 }
259
260 #[test]
261 fn test_all_declared_vars_multiple_scopes() {
262 let mut tracker = ScopeTracker::new();
263 tracker.declare_var("outer");
264 tracker.enter_scope();
265 tracker.declare_var("inner");
266 let vars = tracker.all_declared_vars();
267 assert!(vars.contains("outer"));
268 assert!(vars.contains("inner"));
269 assert_eq!(vars.len(), 2);
270 }
271
272 #[test]
275 fn test_current_scope_vars_returns_some() {
276 let tracker = ScopeTracker::new();
277 assert!(tracker.current_scope_vars().is_some());
278 }
279
280 #[test]
281 fn test_current_scope_vars_contains_declared() {
282 let mut tracker = ScopeTracker::new();
283 tracker.declare_var("x");
284 let vars = tracker.current_scope_vars().unwrap();
285 assert!(vars.contains("x"));
286 }
287
288 #[test]
289 fn test_current_scope_vars_excludes_parent() {
290 let mut tracker = ScopeTracker::new();
291 tracker.declare_var("outer");
292 tracker.enter_scope();
293 tracker.declare_var("inner");
294 let vars = tracker.current_scope_vars().unwrap();
295 assert!(!vars.contains("outer"));
296 assert!(vars.contains("inner"));
297 }
298
299 #[test]
302 fn test_declare_vars_multiple() {
303 let mut tracker = ScopeTracker::new();
304 tracker.declare_vars(["a", "b", "c"]);
305 assert!(tracker.is_declared("a"));
306 assert!(tracker.is_declared("b"));
307 assert!(tracker.is_declared("c"));
308 }
309
310 #[test]
311 fn test_declare_vars_empty() {
312 let mut tracker = ScopeTracker::new();
313 tracker.declare_vars(Vec::<&str>::new());
314 assert!(tracker.all_declared_vars().is_empty());
315 }
316
317 #[test]
318 fn test_declare_vars_with_strings() {
319 let mut tracker = ScopeTracker::new();
320 let vars = vec!["x".to_string(), "y".to_string()];
321 tracker.declare_vars(vars);
322 assert!(tracker.is_declared("x"));
323 assert!(tracker.is_declared("y"));
324 }
325
326 #[test]
329 fn test_scope_depth_nested() {
330 let mut tracker = ScopeTracker::new();
331 assert_eq!(tracker.scope_depth(), 1);
332 tracker.enter_scope();
333 assert_eq!(tracker.scope_depth(), 2);
334 tracker.enter_scope();
335 assert_eq!(tracker.scope_depth(), 3);
336 tracker.exit_scope();
337 assert_eq!(tracker.scope_depth(), 2);
338 tracker.exit_scope();
339 assert_eq!(tracker.scope_depth(), 1);
340 }
341
342 #[test]
345 fn test_redeclare_in_nested_scope() {
346 let mut tracker = ScopeTracker::new();
347 tracker.declare_var("x");
348 tracker.enter_scope();
349 assert!(tracker.declare_var("x"));
351 assert!(tracker.is_declared_in_current_scope("x"));
352 }
353
354 #[test]
355 fn test_shadow_persists_only_in_scope() {
356 let mut tracker = ScopeTracker::new();
357 tracker.declare_var("x");
358 tracker.enter_scope();
359 tracker.declare_var("x"); tracker.exit_scope();
361 assert!(tracker.is_declared("x"));
363 }
364
365 #[test]
368 fn test_deeply_nested_scopes() {
369 let mut tracker = ScopeTracker::new();
370 tracker.declare_var("a");
371 tracker.enter_scope();
372 tracker.declare_var("b");
373 tracker.enter_scope();
374 tracker.declare_var("c");
375 tracker.enter_scope();
376 tracker.declare_var("d");
377
378 assert!(tracker.is_declared("a"));
380 assert!(tracker.is_declared("b"));
381 assert!(tracker.is_declared("c"));
382 assert!(tracker.is_declared("d"));
383
384 assert!(tracker.is_declared_in_current_scope("d"));
386 assert!(!tracker.is_declared_in_current_scope("a"));
387
388 tracker.exit_scope();
390 assert!(!tracker.is_declared("d"));
391 tracker.exit_scope();
392 assert!(!tracker.is_declared("c"));
393 tracker.exit_scope();
394 assert!(!tracker.is_declared("b"));
395 assert!(tracker.is_declared("a"));
396 }
397
398 #[test]
399 fn test_function_like_usage() {
400 let mut tracker = ScopeTracker::new();
402
403 tracker.declare_vars(["x", "y"]);
405
406 tracker.declare_var("z");
408
409 tracker.enter_scope();
411 tracker.declare_var("w");
412 assert!(tracker.is_declared("w"));
413 assert!(tracker.is_declared("x")); tracker.exit_scope();
415
416 assert!(!tracker.is_declared("w"));
418 assert!(tracker.is_declared("x")); assert!(tracker.is_declared("z")); }
421}