Skip to main content

shape_runtime/
visitor.rs

1//! AST Visitor trait and walk functions for Shape.
2//!
3//! This module provides a visitor pattern for traversing the AST.
4//! All variants are explicitly handled - no wildcards.
5//!
6//! ## Per-Variant Expression Methods
7//!
8//! The `Visitor` trait provides fine-grained per-variant methods for expressions.
9//! Each method has a default implementation that returns `true` (continue into
10//! children). Override only the variants you care about.
11//!
12//! The visit order for each expression is:
13//! 1. `visit_expr(expr)` — coarse pre-visit hook; return `false` to skip entirely
14//! 2. `visit_<variant>(expr, span)` — per-variant hook; return `false` to skip children
15//! 3. Walk children recursively
16//! 4. `leave_expr(expr)` — post-visit hook
17
18use shape_ast::ast::*;
19
20/// A visitor trait for traversing Shape AST nodes.
21///
22/// All `visit_*` methods return `bool`:
23/// - `true`: continue visiting children
24/// - `false`: skip children
25///
26/// The `leave_*` methods are called after visiting all children.
27///
28/// ## Per-Variant Expression Methods
29///
30/// For finer granularity, override the per-variant expression methods
31/// (e.g., `visit_identifier`, `visit_binary_op`, `visit_method_call`).
32/// These are called from `walk_expr` after the coarse `visit_expr` hook.
33/// Each receives the full `&Expr` node and its `Span`.
34pub trait Visitor: Sized {
35    // ===== Coarse-grained visitors (called on every node) =====
36
37    /// Called before visiting any expression. Return `false` to skip entirely
38    /// (neither per-variant method nor children will be visited).
39    fn visit_expr(&mut self, _expr: &Expr) -> bool {
40        true
41    }
42    /// Called after visiting an expression and all its children.
43    fn leave_expr(&mut self, _expr: &Expr) {}
44
45    // Statement visitors
46    fn visit_stmt(&mut self, _stmt: &Statement) -> bool {
47        true
48    }
49    fn leave_stmt(&mut self, _stmt: &Statement) {}
50
51    // Item visitors
52    fn visit_item(&mut self, _item: &Item) -> bool {
53        true
54    }
55    fn leave_item(&mut self, _item: &Item) {}
56
57    // Function definition visitors
58    fn visit_function(&mut self, _func: &FunctionDef) -> bool {
59        true
60    }
61    fn leave_function(&mut self, _func: &FunctionDef) {}
62
63    // Literal visitors (kept for backward compat — also called from walk_expr)
64    fn visit_literal(&mut self, _lit: &Literal) -> bool {
65        true
66    }
67    fn leave_literal(&mut self, _lit: &Literal) {}
68
69    // Block visitors (kept for backward compat — also called from walk_expr)
70    fn visit_block(&mut self, _block: &BlockExpr) -> bool {
71        true
72    }
73    fn leave_block(&mut self, _block: &BlockExpr) {}
74
75    // ===== Per-variant expression visitors =====
76    //
77    // Each method receives the full &Expr and its Span. Return `true` to
78    // continue walking children, `false` to skip children.
79    //
80    // Default implementations return `true` (walk children).
81
82    fn visit_expr_literal(&mut self, _expr: &Expr, _span: Span) -> bool {
83        true
84    }
85    fn visit_expr_identifier(&mut self, _expr: &Expr, _span: Span) -> bool {
86        true
87    }
88    fn visit_expr_data_ref(&mut self, _expr: &Expr, _span: Span) -> bool {
89        true
90    }
91    fn visit_expr_data_datetime_ref(&mut self, _expr: &Expr, _span: Span) -> bool {
92        true
93    }
94    fn visit_expr_data_relative_access(&mut self, _expr: &Expr, _span: Span) -> bool {
95        true
96    }
97    fn visit_expr_property_access(&mut self, _expr: &Expr, _span: Span) -> bool {
98        true
99    }
100    fn visit_expr_index_access(&mut self, _expr: &Expr, _span: Span) -> bool {
101        true
102    }
103    fn visit_expr_binary_op(&mut self, _expr: &Expr, _span: Span) -> bool {
104        true
105    }
106    fn visit_expr_fuzzy_comparison(&mut self, _expr: &Expr, _span: Span) -> bool {
107        true
108    }
109    fn visit_expr_unary_op(&mut self, _expr: &Expr, _span: Span) -> bool {
110        true
111    }
112    fn visit_expr_function_call(&mut self, _expr: &Expr, _span: Span) -> bool {
113        true
114    }
115    fn visit_expr_enum_constructor(&mut self, _expr: &Expr, _span: Span) -> bool {
116        true
117    }
118    fn visit_expr_time_ref(&mut self, _expr: &Expr, _span: Span) -> bool {
119        true
120    }
121    fn visit_expr_datetime(&mut self, _expr: &Expr, _span: Span) -> bool {
122        true
123    }
124    fn visit_expr_pattern_ref(&mut self, _expr: &Expr, _span: Span) -> bool {
125        true
126    }
127    fn visit_expr_conditional(&mut self, _expr: &Expr, _span: Span) -> bool {
128        true
129    }
130    fn visit_expr_object(&mut self, _expr: &Expr, _span: Span) -> bool {
131        true
132    }
133    fn visit_expr_array(&mut self, _expr: &Expr, _span: Span) -> bool {
134        true
135    }
136    fn visit_expr_list_comprehension(&mut self, _expr: &Expr, _span: Span) -> bool {
137        true
138    }
139    fn visit_expr_block(&mut self, _expr: &Expr, _span: Span) -> bool {
140        true
141    }
142    fn visit_expr_type_assertion(&mut self, _expr: &Expr, _span: Span) -> bool {
143        true
144    }
145    fn visit_expr_instance_of(&mut self, _expr: &Expr, _span: Span) -> bool {
146        true
147    }
148    fn visit_expr_function_expr(&mut self, _expr: &Expr, _span: Span) -> bool {
149        true
150    }
151    fn visit_expr_duration(&mut self, _expr: &Expr, _span: Span) -> bool {
152        true
153    }
154    fn visit_expr_spread(&mut self, _expr: &Expr, _span: Span) -> bool {
155        true
156    }
157    fn visit_expr_if(&mut self, _expr: &Expr, _span: Span) -> bool {
158        true
159    }
160    fn visit_expr_while(&mut self, _expr: &Expr, _span: Span) -> bool {
161        true
162    }
163    fn visit_expr_for(&mut self, _expr: &Expr, _span: Span) -> bool {
164        true
165    }
166    fn visit_expr_loop(&mut self, _expr: &Expr, _span: Span) -> bool {
167        true
168    }
169    fn visit_expr_let(&mut self, _expr: &Expr, _span: Span) -> bool {
170        true
171    }
172    fn visit_expr_assign(&mut self, _expr: &Expr, _span: Span) -> bool {
173        true
174    }
175    fn visit_expr_break(&mut self, _expr: &Expr, _span: Span) -> bool {
176        true
177    }
178    fn visit_expr_continue(&mut self, _expr: &Expr, _span: Span) -> bool {
179        true
180    }
181    fn visit_expr_return(&mut self, _expr: &Expr, _span: Span) -> bool {
182        true
183    }
184    fn visit_expr_method_call(&mut self, _expr: &Expr, _span: Span) -> bool {
185        true
186    }
187    fn visit_expr_match(&mut self, _expr: &Expr, _span: Span) -> bool {
188        true
189    }
190    fn visit_expr_unit(&mut self, _expr: &Expr, _span: Span) -> bool {
191        true
192    }
193    fn visit_expr_range(&mut self, _expr: &Expr, _span: Span) -> bool {
194        true
195    }
196    fn visit_expr_timeframe_context(&mut self, _expr: &Expr, _span: Span) -> bool {
197        true
198    }
199    fn visit_expr_try_operator(&mut self, _expr: &Expr, _span: Span) -> bool {
200        true
201    }
202    fn visit_expr_using_impl(&mut self, _expr: &Expr, _span: Span) -> bool {
203        true
204    }
205    fn visit_expr_simulation_call(&mut self, _expr: &Expr, _span: Span) -> bool {
206        true
207    }
208    fn visit_expr_window_expr(&mut self, _expr: &Expr, _span: Span) -> bool {
209        true
210    }
211    fn visit_expr_from_query(&mut self, _expr: &Expr, _span: Span) -> bool {
212        true
213    }
214    fn visit_expr_struct_literal(&mut self, _expr: &Expr, _span: Span) -> bool {
215        true
216    }
217    fn visit_expr_await(&mut self, _expr: &Expr, _span: Span) -> bool {
218        true
219    }
220    fn visit_expr_join(&mut self, _expr: &Expr, _span: Span) -> bool {
221        true
222    }
223    fn visit_expr_annotated(&mut self, _expr: &Expr, _span: Span) -> bool {
224        true
225    }
226    fn visit_expr_async_let(&mut self, _expr: &Expr, _span: Span) -> bool {
227        true
228    }
229    fn visit_expr_async_scope(&mut self, _expr: &Expr, _span: Span) -> bool {
230        true
231    }
232    fn visit_expr_comptime(&mut self, _expr: &Expr, _span: Span) -> bool {
233        true
234    }
235    fn visit_expr_comptime_for(&mut self, _expr: &Expr, _span: Span) -> bool {
236        true
237    }
238    fn visit_expr_reference(&mut self, _expr: &Expr, _span: Span) -> bool {
239        true
240    }
241}
242
243// ===== Walk Functions =====
244
245/// Walk a program, visiting all items
246pub fn walk_program<V: Visitor>(visitor: &mut V, program: &Program) {
247    for item in &program.items {
248        walk_item(visitor, item);
249    }
250}
251
252/// Walk an item
253pub fn walk_item<V: Visitor>(visitor: &mut V, item: &Item) {
254    if !visitor.visit_item(item) {
255        return;
256    }
257
258    match item {
259        Item::Import(_, _) => {}
260        Item::Module(module_def, _) => {
261            for inner in &module_def.items {
262                walk_item(visitor, inner);
263            }
264        }
265        Item::Export(export, _) => match &export.item {
266            ExportItem::Function(func) => walk_function(visitor, func),
267            ExportItem::TypeAlias(_) => {}
268            ExportItem::Named(_) => {}
269            ExportItem::Enum(_) => {}
270            ExportItem::Struct(_) => {}
271            ExportItem::Interface(_) => {}
272            ExportItem::Trait(_) => {}
273            ExportItem::ForeignFunction(_) => {} // foreign bodies are opaque
274        },
275        Item::TypeAlias(_, _) => {}
276        Item::Interface(_, _) => {}
277        Item::Trait(_, _) => {}
278        Item::Enum(_, _) => {}
279        Item::Extend(extend, _) => {
280            for method in &extend.methods {
281                for stmt in &method.body {
282                    walk_stmt(visitor, stmt);
283                }
284            }
285        }
286        Item::Impl(impl_block, _) => {
287            for method in &impl_block.methods {
288                for stmt in &method.body {
289                    walk_stmt(visitor, stmt);
290                }
291            }
292        }
293        Item::Function(func, _) => walk_function(visitor, func),
294        Item::Query(query, _) => walk_query(visitor, query),
295        Item::VariableDecl(decl, _) => {
296            if let Some(value) = &decl.value {
297                walk_expr(visitor, value);
298            }
299        }
300        Item::Assignment(assign, _) => {
301            walk_expr(visitor, &assign.value);
302        }
303        Item::Expression(expr, _) => walk_expr(visitor, expr),
304        Item::Stream(stream, _) => {
305            for decl in &stream.state {
306                if let Some(value) = &decl.value {
307                    walk_expr(visitor, value);
308                }
309            }
310            if let Some(stmts) = &stream.on_connect {
311                for stmt in stmts {
312                    walk_stmt(visitor, stmt);
313                }
314            }
315            if let Some(stmts) = &stream.on_disconnect {
316                for stmt in stmts {
317                    walk_stmt(visitor, stmt);
318                }
319            }
320            if let Some(on_event) = &stream.on_event {
321                for stmt in &on_event.body {
322                    walk_stmt(visitor, stmt);
323                }
324            }
325            if let Some(on_window) = &stream.on_window {
326                for stmt in &on_window.body {
327                    walk_stmt(visitor, stmt);
328                }
329            }
330            if let Some(on_error) = &stream.on_error {
331                for stmt in &on_error.body {
332                    walk_stmt(visitor, stmt);
333                }
334            }
335        }
336        Item::Test(test, _) => {
337            if let Some(setup) = &test.setup {
338                for stmt in setup {
339                    walk_stmt(visitor, stmt);
340                }
341            }
342            if let Some(teardown) = &test.teardown {
343                for stmt in teardown {
344                    walk_stmt(visitor, stmt);
345                }
346            }
347            for case in &test.test_cases {
348                for test_stmt in &case.body {
349                    walk_test_statement(visitor, test_stmt);
350                }
351            }
352        }
353        Item::Optimize(opt, _) => {
354            walk_expr(visitor, &opt.range.0);
355            walk_expr(visitor, &opt.range.1);
356            if let OptimizationMetric::Custom(expr) = &opt.metric {
357                walk_expr(visitor, expr);
358            }
359        }
360        Item::Statement(stmt, _) => walk_stmt(visitor, stmt),
361        Item::AnnotationDef(ann_def, _) => {
362            // Walk the lifecycle handlers of the annotation definition
363            for handler in &ann_def.handlers {
364                walk_expr(visitor, &handler.body);
365            }
366        }
367        Item::StructType(_, _) => {
368            // No expressions to walk in struct type definitions
369        }
370        Item::DataSource(ds, _) => {
371            walk_expr(visitor, &ds.provider_expr);
372        }
373        Item::QueryDecl(_, _) => {
374            // Query declarations have no walkable expressions (SQL is a string literal)
375        }
376        Item::Comptime(stmts, _) => {
377            for stmt in stmts {
378                walk_stmt(visitor, stmt);
379            }
380        }
381        Item::BuiltinTypeDecl(_, _) => {
382            // Declaration-only intrinsic
383        }
384        Item::BuiltinFunctionDecl(_, _) => {
385            // Declaration-only intrinsic
386        }
387        Item::ForeignFunction(_, _) => {
388            // Foreign function bodies are opaque to the Shape visitor
389        }
390    }
391
392    visitor.leave_item(item);
393}
394
395/// Walk a function definition
396pub fn walk_function<V: Visitor>(visitor: &mut V, func: &FunctionDef) {
397    if !visitor.visit_function(func) {
398        return;
399    }
400
401    // Visit parameter default values
402    for param in &func.params {
403        if let Some(default) = &param.default_value {
404            walk_expr(visitor, default);
405        }
406    }
407
408    // Visit body statements
409    for stmt in &func.body {
410        walk_stmt(visitor, stmt);
411    }
412
413    visitor.leave_function(func);
414}
415
416/// Walk a query
417pub fn walk_query<V: Visitor>(visitor: &mut V, query: &Query) {
418    match query {
419        Query::Backtest(backtest) => {
420            for (_, expr) in &backtest.parameters {
421                walk_expr(visitor, expr);
422            }
423        }
424        Query::Alert(alert) => {
425            walk_expr(visitor, &alert.condition);
426        }
427        Query::With(with_query) => {
428            // Walk CTEs
429            for cte in &with_query.ctes {
430                walk_query(visitor, &cte.query);
431            }
432            // Walk main query
433            walk_query(visitor, &with_query.query);
434        }
435    }
436}
437
438/// Walk a statement
439pub fn walk_stmt<V: Visitor>(visitor: &mut V, stmt: &Statement) {
440    if !visitor.visit_stmt(stmt) {
441        return;
442    }
443
444    match stmt {
445        Statement::Return(expr, _) => {
446            if let Some(e) = expr {
447                walk_expr(visitor, e);
448            }
449        }
450        Statement::Break(_) => {}
451        Statement::Continue(_) => {}
452        Statement::VariableDecl(decl, _) => {
453            if let Some(value) = &decl.value {
454                walk_expr(visitor, value);
455            }
456        }
457        Statement::Assignment(assign, _) => {
458            walk_expr(visitor, &assign.value);
459        }
460        Statement::Expression(expr, _) => walk_expr(visitor, expr),
461        Statement::For(for_loop, _) => {
462            match &for_loop.init {
463                ForInit::ForIn { iter, .. } => walk_expr(visitor, iter),
464                ForInit::ForC {
465                    init,
466                    condition,
467                    update,
468                } => {
469                    walk_stmt(visitor, init);
470                    walk_expr(visitor, condition);
471                    walk_expr(visitor, update);
472                }
473            }
474            for stmt in &for_loop.body {
475                walk_stmt(visitor, stmt);
476            }
477        }
478        Statement::While(while_loop, _) => {
479            walk_expr(visitor, &while_loop.condition);
480            for stmt in &while_loop.body {
481                walk_stmt(visitor, stmt);
482            }
483        }
484        Statement::If(if_stmt, _) => {
485            walk_expr(visitor, &if_stmt.condition);
486            for stmt in &if_stmt.then_body {
487                walk_stmt(visitor, stmt);
488            }
489            if let Some(else_body) = &if_stmt.else_body {
490                for stmt in else_body {
491                    walk_stmt(visitor, stmt);
492                }
493            }
494        }
495        Statement::Extend(ext, _) => {
496            for method in &ext.methods {
497                for stmt in &method.body {
498                    walk_stmt(visitor, stmt);
499                }
500            }
501        }
502        Statement::RemoveTarget(_) => {}
503        Statement::SetParamType { .. }
504        | Statement::SetReturnType { .. }
505        | Statement::SetReturnExpr { .. } => {}
506        Statement::ReplaceModuleExpr { expression, .. } => {
507            walk_expr(visitor, expression);
508        }
509        Statement::ReplaceBodyExpr { expression, .. } => {
510            walk_expr(visitor, expression);
511        }
512        Statement::ReplaceBody { body, .. } => {
513            for stmt in body {
514                walk_stmt(visitor, stmt);
515            }
516        }
517    }
518
519    visitor.leave_stmt(stmt);
520}
521
522/// Walk an expression - ALL VARIANTS HANDLED EXPLICITLY
523///
524/// Visit order:
525/// 1. `visit_expr(expr)` — return `false` to skip entirely
526/// 2. `visit_expr_<variant>(expr, span)` — return `false` to skip children
527/// 3. Walk children recursively
528/// 4. `leave_expr(expr)`
529pub fn walk_expr<V: Visitor>(visitor: &mut V, expr: &Expr) {
530    if !visitor.visit_expr(expr) {
531        return;
532    }
533
534    match expr {
535        // Leaf nodes (no children)
536        Expr::Literal(lit, span) => {
537            if visitor.visit_expr_literal(expr, *span) {
538                visitor.visit_literal(lit);
539                visitor.leave_literal(lit);
540            }
541        }
542        Expr::Identifier(_, span) => {
543            visitor.visit_expr_identifier(expr, *span);
544        }
545        Expr::DataRef(data_ref, span) => {
546            if visitor.visit_expr_data_ref(expr, *span) {
547                match &data_ref.index {
548                    DataIndex::Expression(e) => walk_expr(visitor, e),
549                    DataIndex::ExpressionRange(start, end) => {
550                        walk_expr(visitor, start);
551                        walk_expr(visitor, end);
552                    }
553                    DataIndex::Single(_) | DataIndex::Range(_, _) => {}
554                }
555            }
556        }
557        Expr::DataDateTimeRef(_, span) => {
558            visitor.visit_expr_data_datetime_ref(expr, *span);
559        }
560        Expr::DataRelativeAccess {
561            reference,
562            index,
563            span,
564        } => {
565            if visitor.visit_expr_data_relative_access(expr, *span) {
566                walk_expr(visitor, reference);
567                match index {
568                    DataIndex::Expression(e) => walk_expr(visitor, e),
569                    DataIndex::ExpressionRange(start, end) => {
570                        walk_expr(visitor, start);
571                        walk_expr(visitor, end);
572                    }
573                    DataIndex::Single(_) | DataIndex::Range(_, _) => {}
574                }
575            }
576        }
577        Expr::PropertyAccess { object, span, .. } => {
578            if visitor.visit_expr_property_access(expr, *span) {
579                walk_expr(visitor, object);
580            }
581        }
582        Expr::IndexAccess {
583            object,
584            index,
585            end_index,
586            span,
587        } => {
588            if visitor.visit_expr_index_access(expr, *span) {
589                walk_expr(visitor, object);
590                walk_expr(visitor, index);
591                if let Some(end) = end_index {
592                    walk_expr(visitor, end);
593                }
594            }
595        }
596        Expr::BinaryOp {
597            left, right, span, ..
598        } => {
599            if visitor.visit_expr_binary_op(expr, *span) {
600                walk_expr(visitor, left);
601                walk_expr(visitor, right);
602            }
603        }
604        Expr::FuzzyComparison {
605            left, right, span, ..
606        } => {
607            if visitor.visit_expr_fuzzy_comparison(expr, *span) {
608                walk_expr(visitor, left);
609                walk_expr(visitor, right);
610            }
611        }
612        Expr::UnaryOp { operand, span, .. } => {
613            if visitor.visit_expr_unary_op(expr, *span) {
614                walk_expr(visitor, operand);
615            }
616        }
617        Expr::FunctionCall {
618            args,
619            named_args,
620            span,
621            ..
622        } => {
623            if visitor.visit_expr_function_call(expr, *span) {
624                for arg in args {
625                    walk_expr(visitor, arg);
626                }
627                for (_, value) in named_args {
628                    walk_expr(visitor, value);
629                }
630            }
631        }
632        Expr::EnumConstructor { payload, span, .. } => {
633            if visitor.visit_expr_enum_constructor(expr, *span) {
634                match payload {
635                    EnumConstructorPayload::Unit => {}
636                    EnumConstructorPayload::Tuple(values) => {
637                        for value in values {
638                            walk_expr(visitor, value);
639                        }
640                    }
641                    EnumConstructorPayload::Struct(fields) => {
642                        for (_, value) in fields {
643                            walk_expr(visitor, value);
644                        }
645                    }
646                }
647            }
648        }
649        Expr::TimeRef(_, span) => {
650            visitor.visit_expr_time_ref(expr, *span);
651        }
652        Expr::DateTime(_, span) => {
653            visitor.visit_expr_datetime(expr, *span);
654        }
655        Expr::PatternRef(_, span) => {
656            visitor.visit_expr_pattern_ref(expr, *span);
657        }
658        Expr::Conditional {
659            condition,
660            then_expr,
661            else_expr,
662            span,
663        } => {
664            if visitor.visit_expr_conditional(expr, *span) {
665                walk_expr(visitor, condition);
666                walk_expr(visitor, then_expr);
667                if let Some(else_e) = else_expr {
668                    walk_expr(visitor, else_e);
669                }
670            }
671        }
672        Expr::Object(entries, span) => {
673            if visitor.visit_expr_object(expr, *span) {
674                for entry in entries {
675                    match entry {
676                        ObjectEntry::Field { value, .. } => walk_expr(visitor, value),
677                        ObjectEntry::Spread(spread_expr) => walk_expr(visitor, spread_expr),
678                    }
679                }
680            }
681        }
682        Expr::Array(elements, span) => {
683            if visitor.visit_expr_array(expr, *span) {
684                for elem in elements {
685                    walk_expr(visitor, elem);
686                }
687            }
688        }
689        Expr::ListComprehension(comp, span) => {
690            if visitor.visit_expr_list_comprehension(expr, *span) {
691                walk_expr(visitor, &comp.element);
692                for clause in &comp.clauses {
693                    walk_expr(visitor, &clause.iterable);
694                    if let Some(filter) = &clause.filter {
695                        walk_expr(visitor, filter);
696                    }
697                }
698            }
699        }
700        Expr::Block(block, span) => {
701            if visitor.visit_expr_block(expr, *span) {
702                if visitor.visit_block(block) {
703                    for item in &block.items {
704                        match item {
705                            BlockItem::VariableDecl(decl) => {
706                                if let Some(value) = &decl.value {
707                                    walk_expr(visitor, value);
708                                }
709                            }
710                            BlockItem::Assignment(assign) => {
711                                walk_expr(visitor, &assign.value);
712                            }
713                            BlockItem::Statement(stmt) => {
714                                walk_stmt(visitor, stmt);
715                            }
716                            BlockItem::Expression(e) => walk_expr(visitor, e),
717                        }
718                    }
719                    visitor.leave_block(block);
720                }
721            }
722        }
723        Expr::TypeAssertion {
724            expr: inner, span, ..
725        } => {
726            if visitor.visit_expr_type_assertion(expr, *span) {
727                walk_expr(visitor, inner);
728            }
729        }
730        Expr::InstanceOf {
731            expr: inner, span, ..
732        } => {
733            if visitor.visit_expr_instance_of(expr, *span) {
734                walk_expr(visitor, inner);
735            }
736        }
737        Expr::FunctionExpr {
738            params, body, span, ..
739        } => {
740            if visitor.visit_expr_function_expr(expr, *span) {
741                for param in params {
742                    if let Some(default) = &param.default_value {
743                        walk_expr(visitor, default);
744                    }
745                }
746                for stmt in body {
747                    walk_stmt(visitor, stmt);
748                }
749            }
750        }
751        Expr::Duration(_, span) => {
752            visitor.visit_expr_duration(expr, *span);
753        }
754        Expr::Spread(inner, span) => {
755            if visitor.visit_expr_spread(expr, *span) {
756                walk_expr(visitor, inner);
757            }
758        }
759        Expr::If(if_expr, span) => {
760            if visitor.visit_expr_if(expr, *span) {
761                walk_expr(visitor, &if_expr.condition);
762                walk_expr(visitor, &if_expr.then_branch);
763                if let Some(else_branch) = &if_expr.else_branch {
764                    walk_expr(visitor, else_branch);
765                }
766            }
767        }
768        Expr::While(while_expr, span) => {
769            if visitor.visit_expr_while(expr, *span) {
770                walk_expr(visitor, &while_expr.condition);
771                walk_expr(visitor, &while_expr.body);
772            }
773        }
774        Expr::For(for_expr, span) => {
775            if visitor.visit_expr_for(expr, *span) {
776                walk_expr(visitor, &for_expr.iterable);
777                walk_expr(visitor, &for_expr.body);
778            }
779        }
780        Expr::Loop(loop_expr, span) => {
781            if visitor.visit_expr_loop(expr, *span) {
782                walk_expr(visitor, &loop_expr.body);
783            }
784        }
785        Expr::Let(let_expr, span) => {
786            if visitor.visit_expr_let(expr, *span) {
787                if let Some(value) = &let_expr.value {
788                    walk_expr(visitor, value);
789                }
790                walk_expr(visitor, &let_expr.body);
791            }
792        }
793        Expr::Assign(assign, span) => {
794            if visitor.visit_expr_assign(expr, *span) {
795                walk_expr(visitor, &assign.target);
796                walk_expr(visitor, &assign.value);
797            }
798        }
799        Expr::Break(inner, span) => {
800            if visitor.visit_expr_break(expr, *span) {
801                if let Some(e) = inner {
802                    walk_expr(visitor, e);
803                }
804            }
805        }
806        Expr::Continue(span) => {
807            visitor.visit_expr_continue(expr, *span);
808        }
809        Expr::Return(inner, span) => {
810            if visitor.visit_expr_return(expr, *span) {
811                if let Some(e) = inner {
812                    walk_expr(visitor, e);
813                }
814            }
815        }
816        Expr::MethodCall {
817            receiver,
818            args,
819            named_args,
820            span,
821            ..
822        } => {
823            if visitor.visit_expr_method_call(expr, *span) {
824                walk_expr(visitor, receiver);
825                for arg in args {
826                    walk_expr(visitor, arg);
827                }
828                for (_, value) in named_args {
829                    walk_expr(visitor, value);
830                }
831            }
832        }
833        Expr::Match(match_expr, span) => {
834            if visitor.visit_expr_match(expr, *span) {
835                walk_expr(visitor, &match_expr.scrutinee);
836                for arm in &match_expr.arms {
837                    if let Some(guard) = &arm.guard {
838                        walk_expr(visitor, guard);
839                    }
840                    walk_expr(visitor, &arm.body);
841                }
842            }
843        }
844        Expr::Unit(span) => {
845            visitor.visit_expr_unit(expr, *span);
846        }
847        Expr::Range {
848            start, end, span, ..
849        } => {
850            if visitor.visit_expr_range(expr, *span) {
851                if let Some(s) = start {
852                    walk_expr(visitor, s);
853                }
854                if let Some(e) = end {
855                    walk_expr(visitor, e);
856                }
857            }
858        }
859        Expr::TimeframeContext {
860            expr: inner, span, ..
861        } => {
862            if visitor.visit_expr_timeframe_context(expr, *span) {
863                walk_expr(visitor, inner);
864            }
865        }
866        Expr::TryOperator(inner, span) => {
867            if visitor.visit_expr_try_operator(expr, *span) {
868                walk_expr(visitor, inner);
869            }
870        }
871        Expr::UsingImpl {
872            expr: inner, span, ..
873        } => {
874            if visitor.visit_expr_using_impl(expr, *span) {
875                walk_expr(visitor, inner);
876            }
877        }
878        Expr::SimulationCall { params, span, .. } => {
879            if visitor.visit_expr_simulation_call(expr, *span) {
880                for (_, value) in params {
881                    walk_expr(visitor, value);
882                }
883            }
884        }
885        Expr::WindowExpr(window_expr, span) => {
886            if visitor.visit_expr_window_expr(expr, *span) {
887                // Walk function argument expressions
888                match &window_expr.function {
889                    WindowFunction::Lead { expr, default, .. }
890                    | WindowFunction::Lag { expr, default, .. } => {
891                        walk_expr(visitor, expr);
892                        if let Some(d) = default {
893                            walk_expr(visitor, d);
894                        }
895                    }
896                    WindowFunction::FirstValue(e)
897                    | WindowFunction::LastValue(e)
898                    | WindowFunction::Sum(e)
899                    | WindowFunction::Avg(e)
900                    | WindowFunction::Min(e)
901                    | WindowFunction::Max(e) => {
902                        walk_expr(visitor, e);
903                    }
904                    WindowFunction::NthValue(e, _) => {
905                        walk_expr(visitor, e);
906                    }
907                    WindowFunction::Count(opt_e) => {
908                        if let Some(e) = opt_e {
909                            walk_expr(visitor, e);
910                        }
911                    }
912                    WindowFunction::RowNumber
913                    | WindowFunction::Rank
914                    | WindowFunction::DenseRank
915                    | WindowFunction::Ntile(_) => {}
916                }
917                // Walk partition_by expressions
918                for e in &window_expr.over.partition_by {
919                    walk_expr(visitor, e);
920                }
921                // Walk order_by expressions
922                if let Some(order_by) = &window_expr.over.order_by {
923                    for (e, _) in &order_by.columns {
924                        walk_expr(visitor, e);
925                    }
926                }
927            }
928        }
929        Expr::FromQuery(from_query, span) => {
930            if visitor.visit_expr_from_query(expr, *span) {
931                // Walk source expression
932                walk_expr(visitor, &from_query.source);
933                // Walk each clause
934                for clause in &from_query.clauses {
935                    match clause {
936                        QueryClause::Where(pred) => {
937                            walk_expr(visitor, pred);
938                        }
939                        QueryClause::OrderBy(specs) => {
940                            for spec in specs {
941                                walk_expr(visitor, &spec.key);
942                            }
943                        }
944                        QueryClause::GroupBy { element, key, .. } => {
945                            walk_expr(visitor, element);
946                            walk_expr(visitor, key);
947                        }
948                        QueryClause::Join {
949                            source,
950                            left_key,
951                            right_key,
952                            ..
953                        } => {
954                            walk_expr(visitor, source);
955                            walk_expr(visitor, left_key);
956                            walk_expr(visitor, right_key);
957                        }
958                        QueryClause::Let { value, .. } => {
959                            walk_expr(visitor, value);
960                        }
961                    }
962                }
963                // Walk select expression
964                walk_expr(visitor, &from_query.select);
965            }
966        }
967        Expr::StructLiteral { fields, span, .. } => {
968            if visitor.visit_expr_struct_literal(expr, *span) {
969                for (_, value_expr) in fields {
970                    walk_expr(visitor, value_expr);
971                }
972            }
973        }
974        Expr::Await(inner, span) => {
975            if visitor.visit_expr_await(expr, *span) {
976                walk_expr(visitor, inner);
977            }
978        }
979        Expr::Join(join_expr, span) => {
980            if visitor.visit_expr_join(expr, *span) {
981                for branch in &join_expr.branches {
982                    walk_expr(visitor, &branch.expr);
983                }
984            }
985        }
986        Expr::Annotated { target, span, .. } => {
987            if visitor.visit_expr_annotated(expr, *span) {
988                walk_expr(visitor, target);
989            }
990        }
991        Expr::AsyncLet(async_let, span) => {
992            if visitor.visit_expr_async_let(expr, *span) {
993                walk_expr(visitor, &async_let.expr);
994            }
995        }
996        Expr::AsyncScope(inner, span) => {
997            if visitor.visit_expr_async_scope(expr, *span) {
998                walk_expr(visitor, inner);
999            }
1000        }
1001        Expr::Comptime(stmts, span) => {
1002            if visitor.visit_expr_comptime(expr, *span) {
1003                for stmt in stmts {
1004                    walk_stmt(visitor, stmt);
1005                }
1006            }
1007        }
1008        Expr::ComptimeFor(cf, span) => {
1009            if visitor.visit_expr_comptime_for(expr, *span) {
1010                walk_expr(visitor, &cf.iterable);
1011                for stmt in &cf.body {
1012                    walk_stmt(visitor, stmt);
1013                }
1014            }
1015        }
1016        Expr::Reference {
1017            expr: inner, span, ..
1018        } => {
1019            if visitor.visit_expr_reference(expr, *span) {
1020                walk_expr(visitor, inner);
1021            }
1022        }
1023    }
1024
1025    visitor.leave_expr(expr);
1026}
1027
1028/// Walk a test statement
1029fn walk_test_statement<V: Visitor>(visitor: &mut V, test_stmt: &TestStatement) {
1030    match test_stmt {
1031        TestStatement::Statement(stmt) => walk_stmt(visitor, stmt),
1032        TestStatement::Assert(assert) => {
1033            walk_expr(visitor, &assert.condition);
1034        }
1035        TestStatement::Expect(expect) => {
1036            walk_expr(visitor, &expect.actual);
1037            match &expect.matcher {
1038                ExpectationMatcher::ToBe(e) => walk_expr(visitor, e),
1039                ExpectationMatcher::ToEqual(e) => walk_expr(visitor, e),
1040                ExpectationMatcher::ToBeCloseTo { expected, .. } => walk_expr(visitor, expected),
1041                ExpectationMatcher::ToBeGreaterThan(e) => walk_expr(visitor, e),
1042                ExpectationMatcher::ToBeLessThan(e) => walk_expr(visitor, e),
1043                ExpectationMatcher::ToContain(e) => walk_expr(visitor, e),
1044                ExpectationMatcher::ToBeTruthy => {}
1045                ExpectationMatcher::ToBeFalsy => {}
1046                ExpectationMatcher::ToThrow(_) => {}
1047                ExpectationMatcher::ToMatchPattern { .. } => {}
1048            }
1049        }
1050        TestStatement::Should(should) => {
1051            walk_expr(visitor, &should.subject);
1052            match &should.matcher {
1053                ShouldMatcher::Be(e) => walk_expr(visitor, e),
1054                ShouldMatcher::Equal(e) => walk_expr(visitor, e),
1055                ShouldMatcher::Contain(e) => walk_expr(visitor, e),
1056                ShouldMatcher::Match(_) => {}
1057                ShouldMatcher::BeCloseTo { expected, .. } => walk_expr(visitor, expected),
1058            }
1059        }
1060        TestStatement::Fixture(fixture) => match fixture {
1061            TestFixture::WithData { data, body } => {
1062                walk_expr(visitor, data);
1063                for stmt in body {
1064                    walk_stmt(visitor, stmt);
1065                }
1066            }
1067            TestFixture::WithMock {
1068                mock_value, body, ..
1069            } => {
1070                if let Some(value) = mock_value {
1071                    walk_expr(visitor, value);
1072                }
1073                for stmt in body {
1074                    walk_stmt(visitor, stmt);
1075                }
1076            }
1077        },
1078    }
1079}
1080
1081#[cfg(test)]
1082mod tests {
1083    use super::*;
1084
1085    /// Simple visitor that counts expressions
1086    struct ExprCounter {
1087        count: usize,
1088    }
1089
1090    impl Visitor for ExprCounter {
1091        fn visit_expr(&mut self, _expr: &Expr) -> bool {
1092            self.count += 1;
1093            true
1094        }
1095    }
1096
1097    #[test]
1098    fn test_visitor_counts_expressions() {
1099        let program = Program {
1100            items: vec![Item::Expression(
1101                Expr::BinaryOp {
1102                    left: Box::new(Expr::Identifier("x".to_string(), Span::DUMMY)),
1103                    op: BinaryOp::Add,
1104                    right: Box::new(Expr::Literal(Literal::Number(1.0), Span::DUMMY)),
1105                    span: Span::DUMMY,
1106                },
1107                Span::DUMMY,
1108            )],
1109        };
1110
1111        let mut counter = ExprCounter { count: 0 };
1112        walk_program(&mut counter, &program);
1113
1114        // Should count: BinaryOp, Identifier, Literal = 3
1115        assert_eq!(counter.count, 3);
1116    }
1117
1118    #[test]
1119    fn test_visitor_handles_try_operator() {
1120        let program = Program {
1121            items: vec![Item::Expression(
1122                Expr::TryOperator(
1123                    Box::new(Expr::FunctionCall {
1124                        name: "some_function".to_string(),
1125                        args: vec![Expr::Literal(
1126                            Literal::String("arg".to_string()),
1127                            Span::DUMMY,
1128                        )],
1129                        named_args: vec![],
1130                        span: Span::DUMMY,
1131                    }),
1132                    Span::DUMMY,
1133                ),
1134                Span::DUMMY,
1135            )],
1136        };
1137
1138        let mut counter = ExprCounter { count: 0 };
1139        walk_program(&mut counter, &program);
1140
1141        // Should count: TryOperator, FunctionCall, Literal = 3
1142        assert_eq!(counter.count, 3);
1143    }
1144
1145    /// Test that per-variant visitor methods work
1146    struct IdentifierCollector {
1147        names: Vec<String>,
1148    }
1149
1150    impl Visitor for IdentifierCollector {
1151        fn visit_expr_identifier(&mut self, expr: &Expr, _span: Span) -> bool {
1152            if let Expr::Identifier(name, _) = expr {
1153                self.names.push(name.clone());
1154            }
1155            true
1156        }
1157    }
1158
1159    #[test]
1160    fn test_per_variant_visitor_identifier() {
1161        let program = Program {
1162            items: vec![Item::Expression(
1163                Expr::BinaryOp {
1164                    left: Box::new(Expr::Identifier("x".to_string(), Span::DUMMY)),
1165                    op: BinaryOp::Add,
1166                    right: Box::new(Expr::Identifier("y".to_string(), Span::DUMMY)),
1167                    span: Span::DUMMY,
1168                },
1169                Span::DUMMY,
1170            )],
1171        };
1172
1173        let mut collector = IdentifierCollector { names: vec![] };
1174        walk_program(&mut collector, &program);
1175
1176        assert_eq!(collector.names, vec!["x", "y"]);
1177    }
1178
1179    /// Test that per-variant method can skip children
1180    struct SkippingVisitor {
1181        count: usize,
1182    }
1183
1184    impl Visitor for SkippingVisitor {
1185        fn visit_expr(&mut self, _expr: &Expr) -> bool {
1186            self.count += 1;
1187            true
1188        }
1189        // Skip children of BinaryOp
1190        fn visit_expr_binary_op(&mut self, _expr: &Expr, _span: Span) -> bool {
1191            false
1192        }
1193    }
1194
1195    #[test]
1196    fn test_per_variant_skip_children() {
1197        let program = Program {
1198            items: vec![Item::Expression(
1199                Expr::BinaryOp {
1200                    left: Box::new(Expr::Identifier("x".to_string(), Span::DUMMY)),
1201                    op: BinaryOp::Add,
1202                    right: Box::new(Expr::Literal(Literal::Number(1.0), Span::DUMMY)),
1203                    span: Span::DUMMY,
1204                },
1205                Span::DUMMY,
1206            )],
1207        };
1208
1209        let mut v = SkippingVisitor { count: 0 };
1210        walk_program(&mut v, &program);
1211
1212        // Only BinaryOp counted, children skipped
1213        assert_eq!(v.count, 1);
1214    }
1215
1216    /// Test combined coarse + per-variant
1217    struct MatchCollector {
1218        match_count: usize,
1219        total_expr_count: usize,
1220    }
1221
1222    impl Visitor for MatchCollector {
1223        fn visit_expr(&mut self, _expr: &Expr) -> bool {
1224            self.total_expr_count += 1;
1225            true
1226        }
1227        fn visit_expr_match(&mut self, _expr: &Expr, _span: Span) -> bool {
1228            self.match_count += 1;
1229            true
1230        }
1231    }
1232
1233    #[test]
1234    fn test_coarse_and_per_variant_combined() {
1235        let program = Program {
1236            items: vec![Item::Expression(
1237                Expr::BinaryOp {
1238                    left: Box::new(Expr::Identifier("x".to_string(), Span::DUMMY)),
1239                    op: BinaryOp::Add,
1240                    right: Box::new(Expr::Identifier("y".to_string(), Span::DUMMY)),
1241                    span: Span::DUMMY,
1242                },
1243                Span::DUMMY,
1244            )],
1245        };
1246
1247        let mut mc = MatchCollector {
1248            match_count: 0,
1249            total_expr_count: 0,
1250        };
1251        walk_program(&mut mc, &program);
1252
1253        assert_eq!(mc.total_expr_count, 3); // BinaryOp + x + y
1254        assert_eq!(mc.match_count, 0); // No Match expressions
1255    }
1256}