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::SetParamValue { expression, .. } => {
507            walk_expr(visitor, expression);
508        }
509        Statement::ReplaceModuleExpr { expression, .. } => {
510            walk_expr(visitor, expression);
511        }
512        Statement::ReplaceBodyExpr { expression, .. } => {
513            walk_expr(visitor, expression);
514        }
515        Statement::ReplaceBody { body, .. } => {
516            for stmt in body {
517                walk_stmt(visitor, stmt);
518            }
519        }
520    }
521
522    visitor.leave_stmt(stmt);
523}
524
525/// Walk an expression - ALL VARIANTS HANDLED EXPLICITLY
526///
527/// Visit order:
528/// 1. `visit_expr(expr)` — return `false` to skip entirely
529/// 2. `visit_expr_<variant>(expr, span)` — return `false` to skip children
530/// 3. Walk children recursively
531/// 4. `leave_expr(expr)`
532pub fn walk_expr<V: Visitor>(visitor: &mut V, expr: &Expr) {
533    if !visitor.visit_expr(expr) {
534        return;
535    }
536
537    match expr {
538        // Leaf nodes (no children)
539        Expr::Literal(lit, span) => {
540            if visitor.visit_expr_literal(expr, *span) {
541                visitor.visit_literal(lit);
542                visitor.leave_literal(lit);
543            }
544        }
545        Expr::Identifier(_, span) => {
546            visitor.visit_expr_identifier(expr, *span);
547        }
548        Expr::DataRef(data_ref, span) => {
549            if visitor.visit_expr_data_ref(expr, *span) {
550                match &data_ref.index {
551                    DataIndex::Expression(e) => walk_expr(visitor, e),
552                    DataIndex::ExpressionRange(start, end) => {
553                        walk_expr(visitor, start);
554                        walk_expr(visitor, end);
555                    }
556                    DataIndex::Single(_) | DataIndex::Range(_, _) => {}
557                }
558            }
559        }
560        Expr::DataDateTimeRef(_, span) => {
561            visitor.visit_expr_data_datetime_ref(expr, *span);
562        }
563        Expr::DataRelativeAccess {
564            reference,
565            index,
566            span,
567        } => {
568            if visitor.visit_expr_data_relative_access(expr, *span) {
569                walk_expr(visitor, reference);
570                match index {
571                    DataIndex::Expression(e) => walk_expr(visitor, e),
572                    DataIndex::ExpressionRange(start, end) => {
573                        walk_expr(visitor, start);
574                        walk_expr(visitor, end);
575                    }
576                    DataIndex::Single(_) | DataIndex::Range(_, _) => {}
577                }
578            }
579        }
580        Expr::PropertyAccess { object, span, .. } => {
581            if visitor.visit_expr_property_access(expr, *span) {
582                walk_expr(visitor, object);
583            }
584        }
585        Expr::IndexAccess {
586            object,
587            index,
588            end_index,
589            span,
590        } => {
591            if visitor.visit_expr_index_access(expr, *span) {
592                walk_expr(visitor, object);
593                walk_expr(visitor, index);
594                if let Some(end) = end_index {
595                    walk_expr(visitor, end);
596                }
597            }
598        }
599        Expr::BinaryOp {
600            left, right, span, ..
601        } => {
602            if visitor.visit_expr_binary_op(expr, *span) {
603                walk_expr(visitor, left);
604                walk_expr(visitor, right);
605            }
606        }
607        Expr::FuzzyComparison {
608            left, right, span, ..
609        } => {
610            if visitor.visit_expr_fuzzy_comparison(expr, *span) {
611                walk_expr(visitor, left);
612                walk_expr(visitor, right);
613            }
614        }
615        Expr::UnaryOp { operand, span, .. } => {
616            if visitor.visit_expr_unary_op(expr, *span) {
617                walk_expr(visitor, operand);
618            }
619        }
620        Expr::FunctionCall {
621            args,
622            named_args,
623            span,
624            ..
625        } => {
626            if visitor.visit_expr_function_call(expr, *span) {
627                for arg in args {
628                    walk_expr(visitor, arg);
629                }
630                for (_, value) in named_args {
631                    walk_expr(visitor, value);
632                }
633            }
634        }
635        Expr::EnumConstructor { payload, span, .. } => {
636            if visitor.visit_expr_enum_constructor(expr, *span) {
637                match payload {
638                    EnumConstructorPayload::Unit => {}
639                    EnumConstructorPayload::Tuple(values) => {
640                        for value in values {
641                            walk_expr(visitor, value);
642                        }
643                    }
644                    EnumConstructorPayload::Struct(fields) => {
645                        for (_, value) in fields {
646                            walk_expr(visitor, value);
647                        }
648                    }
649                }
650            }
651        }
652        Expr::TimeRef(_, span) => {
653            visitor.visit_expr_time_ref(expr, *span);
654        }
655        Expr::DateTime(_, span) => {
656            visitor.visit_expr_datetime(expr, *span);
657        }
658        Expr::PatternRef(_, span) => {
659            visitor.visit_expr_pattern_ref(expr, *span);
660        }
661        Expr::Conditional {
662            condition,
663            then_expr,
664            else_expr,
665            span,
666        } => {
667            if visitor.visit_expr_conditional(expr, *span) {
668                walk_expr(visitor, condition);
669                walk_expr(visitor, then_expr);
670                if let Some(else_e) = else_expr {
671                    walk_expr(visitor, else_e);
672                }
673            }
674        }
675        Expr::Object(entries, span) => {
676            if visitor.visit_expr_object(expr, *span) {
677                for entry in entries {
678                    match entry {
679                        ObjectEntry::Field { value, .. } => walk_expr(visitor, value),
680                        ObjectEntry::Spread(spread_expr) => walk_expr(visitor, spread_expr),
681                    }
682                }
683            }
684        }
685        Expr::Array(elements, span) => {
686            if visitor.visit_expr_array(expr, *span) {
687                for elem in elements {
688                    walk_expr(visitor, elem);
689                }
690            }
691        }
692        Expr::TableRows(rows, _span) => {
693            for row in rows {
694                for elem in row {
695                    walk_expr(visitor, elem);
696                }
697            }
698        }
699        Expr::ListComprehension(comp, span) => {
700            if visitor.visit_expr_list_comprehension(expr, *span) {
701                walk_expr(visitor, &comp.element);
702                for clause in &comp.clauses {
703                    walk_expr(visitor, &clause.iterable);
704                    if let Some(filter) = &clause.filter {
705                        walk_expr(visitor, filter);
706                    }
707                }
708            }
709        }
710        Expr::Block(block, span) => {
711            if visitor.visit_expr_block(expr, *span) {
712                if visitor.visit_block(block) {
713                    for item in &block.items {
714                        match item {
715                            BlockItem::VariableDecl(decl) => {
716                                if let Some(value) = &decl.value {
717                                    walk_expr(visitor, value);
718                                }
719                            }
720                            BlockItem::Assignment(assign) => {
721                                walk_expr(visitor, &assign.value);
722                            }
723                            BlockItem::Statement(stmt) => {
724                                walk_stmt(visitor, stmt);
725                            }
726                            BlockItem::Expression(e) => walk_expr(visitor, e),
727                        }
728                    }
729                    visitor.leave_block(block);
730                }
731            }
732        }
733        Expr::TypeAssertion {
734            expr: inner, span, ..
735        } => {
736            if visitor.visit_expr_type_assertion(expr, *span) {
737                walk_expr(visitor, inner);
738            }
739        }
740        Expr::InstanceOf {
741            expr: inner, span, ..
742        } => {
743            if visitor.visit_expr_instance_of(expr, *span) {
744                walk_expr(visitor, inner);
745            }
746        }
747        Expr::FunctionExpr {
748            params, body, span, ..
749        } => {
750            if visitor.visit_expr_function_expr(expr, *span) {
751                for param in params {
752                    if let Some(default) = &param.default_value {
753                        walk_expr(visitor, default);
754                    }
755                }
756                for stmt in body {
757                    walk_stmt(visitor, stmt);
758                }
759            }
760        }
761        Expr::Duration(_, span) => {
762            visitor.visit_expr_duration(expr, *span);
763        }
764        Expr::Spread(inner, span) => {
765            if visitor.visit_expr_spread(expr, *span) {
766                walk_expr(visitor, inner);
767            }
768        }
769        Expr::If(if_expr, span) => {
770            if visitor.visit_expr_if(expr, *span) {
771                walk_expr(visitor, &if_expr.condition);
772                walk_expr(visitor, &if_expr.then_branch);
773                if let Some(else_branch) = &if_expr.else_branch {
774                    walk_expr(visitor, else_branch);
775                }
776            }
777        }
778        Expr::While(while_expr, span) => {
779            if visitor.visit_expr_while(expr, *span) {
780                walk_expr(visitor, &while_expr.condition);
781                walk_expr(visitor, &while_expr.body);
782            }
783        }
784        Expr::For(for_expr, span) => {
785            if visitor.visit_expr_for(expr, *span) {
786                walk_expr(visitor, &for_expr.iterable);
787                walk_expr(visitor, &for_expr.body);
788            }
789        }
790        Expr::Loop(loop_expr, span) => {
791            if visitor.visit_expr_loop(expr, *span) {
792                walk_expr(visitor, &loop_expr.body);
793            }
794        }
795        Expr::Let(let_expr, span) => {
796            if visitor.visit_expr_let(expr, *span) {
797                if let Some(value) = &let_expr.value {
798                    walk_expr(visitor, value);
799                }
800                walk_expr(visitor, &let_expr.body);
801            }
802        }
803        Expr::Assign(assign, span) => {
804            if visitor.visit_expr_assign(expr, *span) {
805                walk_expr(visitor, &assign.target);
806                walk_expr(visitor, &assign.value);
807            }
808        }
809        Expr::Break(inner, span) => {
810            if visitor.visit_expr_break(expr, *span) {
811                if let Some(e) = inner {
812                    walk_expr(visitor, e);
813                }
814            }
815        }
816        Expr::Continue(span) => {
817            visitor.visit_expr_continue(expr, *span);
818        }
819        Expr::Return(inner, span) => {
820            if visitor.visit_expr_return(expr, *span) {
821                if let Some(e) = inner {
822                    walk_expr(visitor, e);
823                }
824            }
825        }
826        Expr::MethodCall {
827            receiver,
828            args,
829            named_args,
830            span,
831            ..
832        } => {
833            if visitor.visit_expr_method_call(expr, *span) {
834                walk_expr(visitor, receiver);
835                for arg in args {
836                    walk_expr(visitor, arg);
837                }
838                for (_, value) in named_args {
839                    walk_expr(visitor, value);
840                }
841            }
842        }
843        Expr::Match(match_expr, span) => {
844            if visitor.visit_expr_match(expr, *span) {
845                walk_expr(visitor, &match_expr.scrutinee);
846                for arm in &match_expr.arms {
847                    if let Some(guard) = &arm.guard {
848                        walk_expr(visitor, guard);
849                    }
850                    walk_expr(visitor, &arm.body);
851                }
852            }
853        }
854        Expr::Unit(span) => {
855            visitor.visit_expr_unit(expr, *span);
856        }
857        Expr::Range {
858            start, end, span, ..
859        } => {
860            if visitor.visit_expr_range(expr, *span) {
861                if let Some(s) = start {
862                    walk_expr(visitor, s);
863                }
864                if let Some(e) = end {
865                    walk_expr(visitor, e);
866                }
867            }
868        }
869        Expr::TimeframeContext {
870            expr: inner, span, ..
871        } => {
872            if visitor.visit_expr_timeframe_context(expr, *span) {
873                walk_expr(visitor, inner);
874            }
875        }
876        Expr::TryOperator(inner, span) => {
877            if visitor.visit_expr_try_operator(expr, *span) {
878                walk_expr(visitor, inner);
879            }
880        }
881        Expr::UsingImpl {
882            expr: inner, span, ..
883        } => {
884            if visitor.visit_expr_using_impl(expr, *span) {
885                walk_expr(visitor, inner);
886            }
887        }
888        Expr::SimulationCall { params, span, .. } => {
889            if visitor.visit_expr_simulation_call(expr, *span) {
890                for (_, value) in params {
891                    walk_expr(visitor, value);
892                }
893            }
894        }
895        Expr::WindowExpr(window_expr, span) => {
896            if visitor.visit_expr_window_expr(expr, *span) {
897                // Walk function argument expressions
898                match &window_expr.function {
899                    WindowFunction::Lead { expr, default, .. }
900                    | WindowFunction::Lag { expr, default, .. } => {
901                        walk_expr(visitor, expr);
902                        if let Some(d) = default {
903                            walk_expr(visitor, d);
904                        }
905                    }
906                    WindowFunction::FirstValue(e)
907                    | WindowFunction::LastValue(e)
908                    | WindowFunction::Sum(e)
909                    | WindowFunction::Avg(e)
910                    | WindowFunction::Min(e)
911                    | WindowFunction::Max(e) => {
912                        walk_expr(visitor, e);
913                    }
914                    WindowFunction::NthValue(e, _) => {
915                        walk_expr(visitor, e);
916                    }
917                    WindowFunction::Count(opt_e) => {
918                        if let Some(e) = opt_e {
919                            walk_expr(visitor, e);
920                        }
921                    }
922                    WindowFunction::RowNumber
923                    | WindowFunction::Rank
924                    | WindowFunction::DenseRank
925                    | WindowFunction::Ntile(_) => {}
926                }
927                // Walk partition_by expressions
928                for e in &window_expr.over.partition_by {
929                    walk_expr(visitor, e);
930                }
931                // Walk order_by expressions
932                if let Some(order_by) = &window_expr.over.order_by {
933                    for (e, _) in &order_by.columns {
934                        walk_expr(visitor, e);
935                    }
936                }
937            }
938        }
939        Expr::FromQuery(from_query, span) => {
940            if visitor.visit_expr_from_query(expr, *span) {
941                // Walk source expression
942                walk_expr(visitor, &from_query.source);
943                // Walk each clause
944                for clause in &from_query.clauses {
945                    match clause {
946                        QueryClause::Where(pred) => {
947                            walk_expr(visitor, pred);
948                        }
949                        QueryClause::OrderBy(specs) => {
950                            for spec in specs {
951                                walk_expr(visitor, &spec.key);
952                            }
953                        }
954                        QueryClause::GroupBy { element, key, .. } => {
955                            walk_expr(visitor, element);
956                            walk_expr(visitor, key);
957                        }
958                        QueryClause::Join {
959                            source,
960                            left_key,
961                            right_key,
962                            ..
963                        } => {
964                            walk_expr(visitor, source);
965                            walk_expr(visitor, left_key);
966                            walk_expr(visitor, right_key);
967                        }
968                        QueryClause::Let { value, .. } => {
969                            walk_expr(visitor, value);
970                        }
971                    }
972                }
973                // Walk select expression
974                walk_expr(visitor, &from_query.select);
975            }
976        }
977        Expr::StructLiteral { fields, span, .. } => {
978            if visitor.visit_expr_struct_literal(expr, *span) {
979                for (_, value_expr) in fields {
980                    walk_expr(visitor, value_expr);
981                }
982            }
983        }
984        Expr::Await(inner, span) => {
985            if visitor.visit_expr_await(expr, *span) {
986                walk_expr(visitor, inner);
987            }
988        }
989        Expr::Join(join_expr, span) => {
990            if visitor.visit_expr_join(expr, *span) {
991                for branch in &join_expr.branches {
992                    walk_expr(visitor, &branch.expr);
993                }
994            }
995        }
996        Expr::Annotated { target, span, .. } => {
997            if visitor.visit_expr_annotated(expr, *span) {
998                walk_expr(visitor, target);
999            }
1000        }
1001        Expr::AsyncLet(async_let, span) => {
1002            if visitor.visit_expr_async_let(expr, *span) {
1003                walk_expr(visitor, &async_let.expr);
1004            }
1005        }
1006        Expr::AsyncScope(inner, span) => {
1007            if visitor.visit_expr_async_scope(expr, *span) {
1008                walk_expr(visitor, inner);
1009            }
1010        }
1011        Expr::Comptime(stmts, span) => {
1012            if visitor.visit_expr_comptime(expr, *span) {
1013                for stmt in stmts {
1014                    walk_stmt(visitor, stmt);
1015                }
1016            }
1017        }
1018        Expr::ComptimeFor(cf, span) => {
1019            if visitor.visit_expr_comptime_for(expr, *span) {
1020                walk_expr(visitor, &cf.iterable);
1021                for stmt in &cf.body {
1022                    walk_stmt(visitor, stmt);
1023                }
1024            }
1025        }
1026        Expr::Reference {
1027            expr: inner, span, ..
1028        } => {
1029            if visitor.visit_expr_reference(expr, *span) {
1030                walk_expr(visitor, inner);
1031            }
1032        }
1033    }
1034
1035    visitor.leave_expr(expr);
1036}
1037
1038/// Walk a test statement
1039fn walk_test_statement<V: Visitor>(visitor: &mut V, test_stmt: &TestStatement) {
1040    match test_stmt {
1041        TestStatement::Statement(stmt) => walk_stmt(visitor, stmt),
1042        TestStatement::Assert(assert) => {
1043            walk_expr(visitor, &assert.condition);
1044        }
1045        TestStatement::Expect(expect) => {
1046            walk_expr(visitor, &expect.actual);
1047            match &expect.matcher {
1048                ExpectationMatcher::ToBe(e) => walk_expr(visitor, e),
1049                ExpectationMatcher::ToEqual(e) => walk_expr(visitor, e),
1050                ExpectationMatcher::ToBeCloseTo { expected, .. } => walk_expr(visitor, expected),
1051                ExpectationMatcher::ToBeGreaterThan(e) => walk_expr(visitor, e),
1052                ExpectationMatcher::ToBeLessThan(e) => walk_expr(visitor, e),
1053                ExpectationMatcher::ToContain(e) => walk_expr(visitor, e),
1054                ExpectationMatcher::ToBeTruthy => {}
1055                ExpectationMatcher::ToBeFalsy => {}
1056                ExpectationMatcher::ToThrow(_) => {}
1057                ExpectationMatcher::ToMatchPattern { .. } => {}
1058            }
1059        }
1060        TestStatement::Should(should) => {
1061            walk_expr(visitor, &should.subject);
1062            match &should.matcher {
1063                ShouldMatcher::Be(e) => walk_expr(visitor, e),
1064                ShouldMatcher::Equal(e) => walk_expr(visitor, e),
1065                ShouldMatcher::Contain(e) => walk_expr(visitor, e),
1066                ShouldMatcher::Match(_) => {}
1067                ShouldMatcher::BeCloseTo { expected, .. } => walk_expr(visitor, expected),
1068            }
1069        }
1070        TestStatement::Fixture(fixture) => match fixture {
1071            TestFixture::WithData { data, body } => {
1072                walk_expr(visitor, data);
1073                for stmt in body {
1074                    walk_stmt(visitor, stmt);
1075                }
1076            }
1077            TestFixture::WithMock {
1078                mock_value, body, ..
1079            } => {
1080                if let Some(value) = mock_value {
1081                    walk_expr(visitor, value);
1082                }
1083                for stmt in body {
1084                    walk_stmt(visitor, stmt);
1085                }
1086            }
1087        },
1088    }
1089}
1090
1091#[cfg(test)]
1092mod tests {
1093    use super::*;
1094
1095    /// Simple visitor that counts expressions
1096    struct ExprCounter {
1097        count: usize,
1098    }
1099
1100    impl Visitor for ExprCounter {
1101        fn visit_expr(&mut self, _expr: &Expr) -> bool {
1102            self.count += 1;
1103            true
1104        }
1105    }
1106
1107    #[test]
1108    fn test_visitor_counts_expressions() {
1109        let program = Program {
1110            items: vec![Item::Expression(
1111                Expr::BinaryOp {
1112                    left: Box::new(Expr::Identifier("x".to_string(), Span::DUMMY)),
1113                    op: BinaryOp::Add,
1114                    right: Box::new(Expr::Literal(Literal::Number(1.0), Span::DUMMY)),
1115                    span: Span::DUMMY,
1116                },
1117                Span::DUMMY,
1118            )],
1119            docs: shape_ast::ast::ProgramDocs::default(),
1120        };
1121
1122        let mut counter = ExprCounter { count: 0 };
1123        walk_program(&mut counter, &program);
1124
1125        // Should count: BinaryOp, Identifier, Literal = 3
1126        assert_eq!(counter.count, 3);
1127    }
1128
1129    #[test]
1130    fn test_visitor_handles_try_operator() {
1131        let program = Program {
1132            items: vec![Item::Expression(
1133                Expr::TryOperator(
1134                    Box::new(Expr::FunctionCall {
1135                        name: "some_function".to_string(),
1136                        args: vec![Expr::Literal(
1137                            Literal::String("arg".to_string()),
1138                            Span::DUMMY,
1139                        )],
1140                        named_args: vec![],
1141                        span: Span::DUMMY,
1142                    }),
1143                    Span::DUMMY,
1144                ),
1145                Span::DUMMY,
1146            )],
1147            docs: shape_ast::ast::ProgramDocs::default(),
1148        };
1149
1150        let mut counter = ExprCounter { count: 0 };
1151        walk_program(&mut counter, &program);
1152
1153        // Should count: TryOperator, FunctionCall, Literal = 3
1154        assert_eq!(counter.count, 3);
1155    }
1156
1157    /// Test that per-variant visitor methods work
1158    struct IdentifierCollector {
1159        names: Vec<String>,
1160    }
1161
1162    impl Visitor for IdentifierCollector {
1163        fn visit_expr_identifier(&mut self, expr: &Expr, _span: Span) -> bool {
1164            if let Expr::Identifier(name, _) = expr {
1165                self.names.push(name.clone());
1166            }
1167            true
1168        }
1169    }
1170
1171    #[test]
1172    fn test_per_variant_visitor_identifier() {
1173        let program = Program {
1174            items: vec![Item::Expression(
1175                Expr::BinaryOp {
1176                    left: Box::new(Expr::Identifier("x".to_string(), Span::DUMMY)),
1177                    op: BinaryOp::Add,
1178                    right: Box::new(Expr::Identifier("y".to_string(), Span::DUMMY)),
1179                    span: Span::DUMMY,
1180                },
1181                Span::DUMMY,
1182            )],
1183            docs: shape_ast::ast::ProgramDocs::default(),
1184        };
1185
1186        let mut collector = IdentifierCollector { names: vec![] };
1187        walk_program(&mut collector, &program);
1188
1189        assert_eq!(collector.names, vec!["x", "y"]);
1190    }
1191
1192    /// Test that per-variant method can skip children
1193    struct SkippingVisitor {
1194        count: usize,
1195    }
1196
1197    impl Visitor for SkippingVisitor {
1198        fn visit_expr(&mut self, _expr: &Expr) -> bool {
1199            self.count += 1;
1200            true
1201        }
1202        // Skip children of BinaryOp
1203        fn visit_expr_binary_op(&mut self, _expr: &Expr, _span: Span) -> bool {
1204            false
1205        }
1206    }
1207
1208    #[test]
1209    fn test_per_variant_skip_children() {
1210        let program = Program {
1211            items: vec![Item::Expression(
1212                Expr::BinaryOp {
1213                    left: Box::new(Expr::Identifier("x".to_string(), Span::DUMMY)),
1214                    op: BinaryOp::Add,
1215                    right: Box::new(Expr::Literal(Literal::Number(1.0), Span::DUMMY)),
1216                    span: Span::DUMMY,
1217                },
1218                Span::DUMMY,
1219            )],
1220            docs: shape_ast::ast::ProgramDocs::default(),
1221        };
1222
1223        let mut v = SkippingVisitor { count: 0 };
1224        walk_program(&mut v, &program);
1225
1226        // Only BinaryOp counted, children skipped
1227        assert_eq!(v.count, 1);
1228    }
1229
1230    /// Test combined coarse + per-variant
1231    struct MatchCollector {
1232        match_count: usize,
1233        total_expr_count: usize,
1234    }
1235
1236    impl Visitor for MatchCollector {
1237        fn visit_expr(&mut self, _expr: &Expr) -> bool {
1238            self.total_expr_count += 1;
1239            true
1240        }
1241        fn visit_expr_match(&mut self, _expr: &Expr, _span: Span) -> bool {
1242            self.match_count += 1;
1243            true
1244        }
1245    }
1246
1247    #[test]
1248    fn test_coarse_and_per_variant_combined() {
1249        let program = Program {
1250            items: vec![Item::Expression(
1251                Expr::BinaryOp {
1252                    left: Box::new(Expr::Identifier("x".to_string(), Span::DUMMY)),
1253                    op: BinaryOp::Add,
1254                    right: Box::new(Expr::Identifier("y".to_string(), Span::DUMMY)),
1255                    span: Span::DUMMY,
1256                },
1257                Span::DUMMY,
1258            )],
1259            docs: shape_ast::ast::ProgramDocs::default(),
1260        };
1261
1262        let mut mc = MatchCollector {
1263            match_count: 0,
1264            total_expr_count: 0,
1265        };
1266        walk_program(&mut mc, &program);
1267
1268        assert_eq!(mc.total_expr_count, 3); // BinaryOp + x + y
1269        assert_eq!(mc.match_count, 0); // No Match expressions
1270    }
1271}