Skip to main content

react_compiler_ast/
visitor.rs

1//! AST visitor with automatic scope tracking.
2//!
3//! Provides a [`Visitor`] trait with enter/leave hooks for specific node types,
4//! and an [`AstWalker`] that traverses the AST while tracking the active scope
5//! via the scope tree's `node_to_scope` map.
6
7use crate::Program;
8use crate::declarations::*;
9use crate::expressions::*;
10use crate::jsx::*;
11use crate::patterns::*;
12use crate::scope::ScopeId;
13use crate::scope::ScopeInfo;
14use crate::statements::*;
15
16/// Trait for visiting Babel AST nodes. All methods default to no-ops.
17/// Override specific methods to intercept nodes of interest.
18///
19/// The `'ast` lifetime ties visitor hooks to the AST being walked, allowing
20/// visitors to store references into the AST (e.g., for deferred processing).
21///
22/// The `scope_stack` parameter provides the current scope context during traversal.
23/// The active scope is `scope_stack.last()`.
24pub trait Visitor<'ast> {
25    /// Controls whether the walker recurses into function/arrow/method bodies.
26    /// Returns `true` by default. Override to `false` to skip function bodies
27    /// (similar to Babel's `path.skip()` in traverse visitors).
28    ///
29    /// When `false`, the walker still calls `enter_*` / `leave_*` for functions
30    /// but does not walk their params or body.
31    fn traverse_function_bodies(&self) -> bool {
32        true
33    }
34
35    fn enter_function_declaration(
36        &mut self,
37        _node: &'ast FunctionDeclaration,
38        _scope_stack: &[ScopeId],
39    ) {
40    }
41    fn leave_function_declaration(
42        &mut self,
43        _node: &'ast FunctionDeclaration,
44        _scope_stack: &[ScopeId],
45    ) {
46    }
47    fn enter_function_expression(
48        &mut self,
49        _node: &'ast FunctionExpression,
50        _scope_stack: &[ScopeId],
51    ) {
52    }
53    fn leave_function_expression(
54        &mut self,
55        _node: &'ast FunctionExpression,
56        _scope_stack: &[ScopeId],
57    ) {
58    }
59    fn enter_arrow_function_expression(
60        &mut self,
61        _node: &'ast ArrowFunctionExpression,
62        _scope_stack: &[ScopeId],
63    ) {
64    }
65    fn leave_arrow_function_expression(
66        &mut self,
67        _node: &'ast ArrowFunctionExpression,
68        _scope_stack: &[ScopeId],
69    ) {
70    }
71    fn enter_class_declaration(
72        &mut self,
73        _node: &'ast crate::statements::ClassDeclaration,
74        _scope_stack: &[ScopeId],
75    ) {
76    }
77    fn enter_class_expression(&mut self, _node: &'ast ClassExpression, _scope_stack: &[ScopeId]) {}
78    fn enter_object_method(&mut self, _node: &'ast ObjectMethod, _scope_stack: &[ScopeId]) {}
79    fn leave_object_method(&mut self, _node: &'ast ObjectMethod, _scope_stack: &[ScopeId]) {}
80    fn enter_assignment_expression(
81        &mut self,
82        _node: &'ast AssignmentExpression,
83        _scope_stack: &[ScopeId],
84    ) {
85    }
86    fn enter_update_expression(&mut self, _node: &'ast UpdateExpression, _scope_stack: &[ScopeId]) {
87    }
88    fn enter_identifier(&mut self, _node: &'ast Identifier, _scope_stack: &[ScopeId]) {}
89    fn enter_jsx_identifier(&mut self, _node: &'ast JSXIdentifier, _scope_stack: &[ScopeId]) {}
90    fn enter_jsx_opening_element(
91        &mut self,
92        _node: &'ast JSXOpeningElement,
93        _scope_stack: &[ScopeId],
94    ) {
95    }
96    fn leave_jsx_opening_element(
97        &mut self,
98        _node: &'ast JSXOpeningElement,
99        _scope_stack: &[ScopeId],
100    ) {
101    }
102
103    fn enter_variable_declarator(
104        &mut self,
105        _node: &'ast VariableDeclarator,
106        _scope_stack: &[ScopeId],
107    ) {
108    }
109    fn leave_variable_declarator(
110        &mut self,
111        _node: &'ast VariableDeclarator,
112        _scope_stack: &[ScopeId],
113    ) {
114    }
115
116    fn enter_call_expression(&mut self, _node: &'ast CallExpression, _scope_stack: &[ScopeId]) {}
117    fn leave_call_expression(&mut self, _node: &'ast CallExpression, _scope_stack: &[ScopeId]) {}
118
119    /// Called when the walker enters a loop expression context (while.test,
120    /// do-while.test, for-in.right, for-of.right). Functions found in these
121    /// positions are treated as non-program-scope by Babel, even though the
122    /// walker doesn't push a scope for them.
123    fn enter_loop_expression(&mut self) {}
124    fn leave_loop_expression(&mut self) {}
125}
126
127/// Walks the AST while tracking scope context via `node_to_scope`.
128pub struct AstWalker<'a> {
129    scope_info: &'a ScopeInfo,
130    scope_stack: Vec<ScopeId>,
131    /// Depth counter for loop/iteration expression positions (while.test,
132    /// do-while.test, for-in.right, for-of.right). These positions are
133    /// NOT inside a scope in the walker's model, but Babel's scope analysis
134    /// treats them as non-program-scope. Visitors can check this via
135    /// `in_loop_expression_depth()` to implement Babel-compatible scope checks.
136    loop_expression_depth: usize,
137}
138
139impl<'a> AstWalker<'a> {
140    pub fn new(scope_info: &'a ScopeInfo) -> Self {
141        AstWalker {
142            scope_info,
143            scope_stack: Vec::new(),
144            loop_expression_depth: 0,
145        }
146    }
147
148    /// Create a walker with an initial scope already on the stack.
149    pub fn with_initial_scope(scope_info: &'a ScopeInfo, initial_scope: ScopeId) -> Self {
150        AstWalker {
151            scope_info,
152            scope_stack: vec![initial_scope],
153            loop_expression_depth: 0,
154        }
155    }
156
157    pub fn scope_stack(&self) -> &[ScopeId] {
158        &self.scope_stack
159    }
160
161    /// Returns the current loop-expression depth. Non-zero when the walker is
162    /// inside a loop's test/right expression (while.test, do-while.test,
163    /// for-in.right, for-of.right). Visitors can use this to implement
164    /// Babel-compatible scope checks in 'all' compilation mode.
165    pub fn loop_expression_depth(&self) -> usize {
166        self.loop_expression_depth
167    }
168
169    /// Try to push a scope for a node. Returns true if a scope was pushed.
170    fn try_push_scope(&mut self, _start: Option<u32>, node_id: Option<u32>) -> bool {
171        let scope = self.scope_info.resolve_scope_for_node(node_id);
172        if let Some(scope_id) = scope {
173            self.scope_stack.push(scope_id);
174            return true;
175        }
176        false
177    }
178
179    // ---- Public walk methods ----
180
181    pub fn walk_program<'ast>(&mut self, v: &mut impl Visitor<'ast>, node: &'ast Program) {
182        let pushed = self.try_push_scope(node.base.start, node.base.node_id);
183        for stmt in &node.body {
184            self.walk_statement(v, stmt);
185        }
186        if pushed {
187            self.scope_stack.pop();
188        }
189    }
190
191    pub fn walk_block_statement<'ast>(
192        &mut self,
193        v: &mut impl Visitor<'ast>,
194        node: &'ast BlockStatement,
195    ) {
196        let pushed = self.try_push_scope(node.base.start, node.base.node_id);
197        for stmt in &node.body {
198            self.walk_statement(v, stmt);
199        }
200        if pushed {
201            self.scope_stack.pop();
202        }
203    }
204
205    pub fn walk_statement<'ast>(&mut self, v: &mut impl Visitor<'ast>, stmt: &'ast Statement) {
206        match stmt {
207            Statement::BlockStatement(node) => self.walk_block_statement(v, node),
208            Statement::ReturnStatement(node) => {
209                if let Some(arg) = &node.argument {
210                    self.walk_expression(v, arg);
211                }
212            }
213            Statement::ExpressionStatement(node) => {
214                self.walk_expression(v, &node.expression);
215            }
216            Statement::IfStatement(node) => {
217                self.walk_expression(v, &node.test);
218                self.walk_statement(v, &node.consequent);
219                if let Some(alt) = &node.alternate {
220                    self.walk_statement(v, alt);
221                }
222            }
223            Statement::ForStatement(node) => {
224                let pushed = self.try_push_scope(node.base.start, node.base.node_id);
225                if let Some(init) = &node.init {
226                    match init.as_ref() {
227                        ForInit::VariableDeclaration(decl) => {
228                            self.walk_variable_declaration(v, decl)
229                        }
230                        ForInit::Expression(expr) => self.walk_expression(v, expr),
231                    }
232                }
233                if let Some(test) = &node.test {
234                    self.walk_expression(v, test);
235                }
236                if let Some(update) = &node.update {
237                    self.walk_expression(v, update);
238                }
239                self.walk_statement(v, &node.body);
240                if pushed {
241                    self.scope_stack.pop();
242                }
243            }
244            Statement::WhileStatement(node) => {
245                self.loop_expression_depth += 1;
246                v.enter_loop_expression();
247                self.walk_expression(v, &node.test);
248                v.leave_loop_expression();
249                self.loop_expression_depth -= 1;
250                self.walk_statement(v, &node.body);
251            }
252            Statement::DoWhileStatement(node) => {
253                self.walk_statement(v, &node.body);
254                self.loop_expression_depth += 1;
255                v.enter_loop_expression();
256                self.walk_expression(v, &node.test);
257                v.leave_loop_expression();
258                self.loop_expression_depth -= 1;
259            }
260            Statement::ForInStatement(node) => {
261                let pushed = self.try_push_scope(node.base.start, node.base.node_id);
262                self.walk_for_in_of_left(v, &node.left);
263                self.loop_expression_depth += 1;
264                v.enter_loop_expression();
265                self.walk_expression(v, &node.right);
266                v.leave_loop_expression();
267                self.loop_expression_depth -= 1;
268                self.walk_statement(v, &node.body);
269                if pushed {
270                    self.scope_stack.pop();
271                }
272            }
273            Statement::ForOfStatement(node) => {
274                let pushed = self.try_push_scope(node.base.start, node.base.node_id);
275                self.walk_for_in_of_left(v, &node.left);
276                self.loop_expression_depth += 1;
277                v.enter_loop_expression();
278                self.walk_expression(v, &node.right);
279                v.leave_loop_expression();
280                self.loop_expression_depth -= 1;
281                self.walk_statement(v, &node.body);
282                if pushed {
283                    self.scope_stack.pop();
284                }
285            }
286            Statement::SwitchStatement(node) => {
287                let pushed = self.try_push_scope(node.base.start, node.base.node_id);
288                self.walk_expression(v, &node.discriminant);
289                for case in &node.cases {
290                    if let Some(test) = &case.test {
291                        self.walk_expression(v, test);
292                    }
293                    for consequent in &case.consequent {
294                        self.walk_statement(v, consequent);
295                    }
296                }
297                if pushed {
298                    self.scope_stack.pop();
299                }
300            }
301            Statement::ThrowStatement(node) => {
302                self.walk_expression(v, &node.argument);
303            }
304            Statement::TryStatement(node) => {
305                self.walk_block_statement(v, &node.block);
306                if let Some(handler) = &node.handler {
307                    let pushed = self.try_push_scope(handler.base.start, handler.base.node_id);
308                    if let Some(param) = &handler.param {
309                        self.walk_pattern(v, param);
310                    }
311                    self.walk_block_statement(v, &handler.body);
312                    if pushed {
313                        self.scope_stack.pop();
314                    }
315                }
316                if let Some(finalizer) = &node.finalizer {
317                    self.walk_block_statement(v, finalizer);
318                }
319            }
320            Statement::LabeledStatement(node) => {
321                self.walk_statement(v, &node.body);
322            }
323            Statement::VariableDeclaration(node) => {
324                self.walk_variable_declaration(v, node);
325            }
326            Statement::FunctionDeclaration(node) => {
327                self.walk_function_declaration_inner(v, node);
328            }
329            Statement::ClassDeclaration(node) => {
330                // Call the visitor hook so consumers can index the class name,
331                // but skip walking the class body (no compilable functions inside)
332                v.enter_class_declaration(node, &self.scope_stack);
333            }
334            Statement::WithStatement(node) => {
335                self.walk_expression(v, &node.object);
336                self.walk_statement(v, &node.body);
337            }
338            Statement::ExportNamedDeclaration(node) => {
339                if let Some(decl) = &node.declaration {
340                    self.walk_declaration(v, decl);
341                }
342            }
343            Statement::ExportDefaultDeclaration(node) => {
344                self.walk_export_default_decl(v, &node.declaration);
345            }
346            // No runtime expressions to traverse
347            Statement::BreakStatement(_)
348            | Statement::ContinueStatement(_)
349            | Statement::EmptyStatement(_)
350            | Statement::DebuggerStatement(_)
351            | Statement::ImportDeclaration(_)
352            | Statement::ExportAllDeclaration(_)
353            | Statement::TSTypeAliasDeclaration(_)
354            | Statement::TSInterfaceDeclaration(_)
355            | Statement::TSEnumDeclaration(_)
356            | Statement::TSModuleDeclaration(_)
357            | Statement::TSDeclareFunction(_)
358            | Statement::TypeAlias(_)
359            | Statement::OpaqueType(_)
360            | Statement::InterfaceDeclaration(_)
361            | Statement::DeclareVariable(_)
362            | Statement::DeclareFunction(_)
363            | Statement::DeclareClass(_)
364            | Statement::DeclareModule(_)
365            | Statement::DeclareModuleExports(_)
366            | Statement::DeclareExportDeclaration(_)
367            | Statement::DeclareExportAllDeclaration(_)
368            | Statement::DeclareInterface(_)
369            | Statement::DeclareTypeAlias(_)
370            | Statement::DeclareOpaqueType(_)
371            | Statement::EnumDeclaration(_) => {}
372        }
373    }
374
375    pub fn walk_expression<'ast>(&mut self, v: &mut impl Visitor<'ast>, expr: &'ast Expression) {
376        match expr {
377            Expression::Identifier(node) => {
378                v.enter_identifier(node, &self.scope_stack);
379            }
380            Expression::CallExpression(node) => {
381                v.enter_call_expression(node, &self.scope_stack);
382                self.walk_expression(v, &node.callee);
383                for arg in &node.arguments {
384                    self.walk_expression(v, arg);
385                }
386                v.leave_call_expression(node, &self.scope_stack);
387            }
388            Expression::MemberExpression(node) => {
389                self.walk_expression(v, &node.object);
390                if node.computed {
391                    self.walk_expression(v, &node.property);
392                }
393            }
394            Expression::OptionalCallExpression(node) => {
395                self.walk_expression(v, &node.callee);
396                for arg in &node.arguments {
397                    self.walk_expression(v, arg);
398                }
399            }
400            Expression::OptionalMemberExpression(node) => {
401                self.walk_expression(v, &node.object);
402                if node.computed {
403                    self.walk_expression(v, &node.property);
404                }
405            }
406            Expression::BinaryExpression(node) => {
407                self.walk_expression(v, &node.left);
408                self.walk_expression(v, &node.right);
409            }
410            Expression::LogicalExpression(node) => {
411                self.walk_expression(v, &node.left);
412                self.walk_expression(v, &node.right);
413            }
414            Expression::UnaryExpression(node) => {
415                self.walk_expression(v, &node.argument);
416            }
417            Expression::UpdateExpression(node) => {
418                v.enter_update_expression(node, &self.scope_stack);
419                self.walk_expression(v, &node.argument);
420            }
421            Expression::ConditionalExpression(node) => {
422                self.walk_expression(v, &node.test);
423                self.walk_expression(v, &node.consequent);
424                self.walk_expression(v, &node.alternate);
425            }
426            Expression::AssignmentExpression(node) => {
427                v.enter_assignment_expression(node, &self.scope_stack);
428                self.walk_pattern(v, &node.left);
429                self.walk_expression(v, &node.right);
430            }
431            Expression::SequenceExpression(node) => {
432                for expr in &node.expressions {
433                    self.walk_expression(v, expr);
434                }
435            }
436            Expression::ArrowFunctionExpression(node) => {
437                let pushed = self.try_push_scope(node.base.start, node.base.node_id);
438                v.enter_arrow_function_expression(node, &self.scope_stack);
439                if v.traverse_function_bodies() {
440                    for param in &node.params {
441                        self.walk_pattern(v, param);
442                    }
443                    match node.body.as_ref() {
444                        ArrowFunctionBody::BlockStatement(block) => {
445                            self.walk_block_statement(v, block);
446                        }
447                        ArrowFunctionBody::Expression(expr) => {
448                            self.walk_expression(v, expr);
449                        }
450                    }
451                }
452                v.leave_arrow_function_expression(node, &self.scope_stack);
453                if pushed {
454                    self.scope_stack.pop();
455                }
456            }
457            Expression::FunctionExpression(node) => {
458                let pushed = self.try_push_scope(node.base.start, node.base.node_id);
459                v.enter_function_expression(node, &self.scope_stack);
460                if v.traverse_function_bodies() {
461                    for param in &node.params {
462                        self.walk_pattern(v, param);
463                    }
464                    self.walk_block_statement(v, &node.body);
465                }
466                v.leave_function_expression(node, &self.scope_stack);
467                if pushed {
468                    self.scope_stack.pop();
469                }
470            }
471            Expression::ObjectExpression(node) => {
472                for prop in &node.properties {
473                    self.walk_object_expression_property(v, prop);
474                }
475            }
476            Expression::ArrayExpression(node) => {
477                for element in &node.elements {
478                    if let Some(el) = element {
479                        self.walk_expression(v, el);
480                    }
481                }
482            }
483            Expression::NewExpression(node) => {
484                self.walk_expression(v, &node.callee);
485                for arg in &node.arguments {
486                    self.walk_expression(v, arg);
487                }
488            }
489            Expression::TemplateLiteral(node) => {
490                for expr in &node.expressions {
491                    self.walk_expression(v, expr);
492                }
493            }
494            Expression::TaggedTemplateExpression(node) => {
495                self.walk_expression(v, &node.tag);
496                for expr in &node.quasi.expressions {
497                    self.walk_expression(v, expr);
498                }
499            }
500            Expression::AwaitExpression(node) => {
501                self.walk_expression(v, &node.argument);
502            }
503            Expression::YieldExpression(node) => {
504                if let Some(arg) = &node.argument {
505                    self.walk_expression(v, arg);
506                }
507            }
508            Expression::SpreadElement(node) => {
509                self.walk_expression(v, &node.argument);
510            }
511            Expression::ParenthesizedExpression(node) => {
512                self.walk_expression(v, &node.expression);
513            }
514            Expression::AssignmentPattern(node) => {
515                self.walk_pattern(v, &node.left);
516                self.walk_expression(v, &node.right);
517            }
518            Expression::ClassExpression(node) => {
519                // Call the visitor hook so consumers can index the class name,
520                // but skip walking the class body
521                v.enter_class_expression(node, &self.scope_stack);
522            }
523            // JSX
524            Expression::JSXElement(node) => self.walk_jsx_element(v, node),
525            Expression::JSXFragment(node) => self.walk_jsx_fragment(v, node),
526            // TS/Flow wrappers - traverse inner expression
527            Expression::TSAsExpression(node) => self.walk_expression(v, &node.expression),
528            Expression::TSSatisfiesExpression(node) => self.walk_expression(v, &node.expression),
529            Expression::TSNonNullExpression(node) => self.walk_expression(v, &node.expression),
530            Expression::TSTypeAssertion(node) => self.walk_expression(v, &node.expression),
531            Expression::TSInstantiationExpression(node) => {
532                self.walk_expression(v, &node.expression)
533            }
534            Expression::TypeCastExpression(node) => self.walk_expression(v, &node.expression),
535            // Leaf nodes
536            Expression::StringLiteral(_)
537            | Expression::NumericLiteral(_)
538            | Expression::BooleanLiteral(_)
539            | Expression::NullLiteral(_)
540            | Expression::BigIntLiteral(_)
541            | Expression::RegExpLiteral(_)
542            | Expression::MetaProperty(_)
543            | Expression::PrivateName(_)
544            | Expression::Super(_)
545            | Expression::Import(_)
546            | Expression::ThisExpression(_) => {}
547        }
548    }
549
550    pub fn walk_pattern<'ast>(&mut self, v: &mut impl Visitor<'ast>, pat: &'ast PatternLike) {
551        match pat {
552            PatternLike::Identifier(node) => {
553                v.enter_identifier(node, &self.scope_stack);
554            }
555            PatternLike::ObjectPattern(node) => {
556                for prop in &node.properties {
557                    match prop {
558                        ObjectPatternProperty::ObjectProperty(p) => {
559                            if p.computed {
560                                self.walk_expression(v, &p.key);
561                            }
562                            self.walk_pattern(v, &p.value);
563                        }
564                        ObjectPatternProperty::RestElement(p) => {
565                            self.walk_pattern(v, &p.argument);
566                        }
567                    }
568                }
569            }
570            PatternLike::ArrayPattern(node) => {
571                for element in &node.elements {
572                    if let Some(el) = element {
573                        self.walk_pattern(v, el);
574                    }
575                }
576            }
577            PatternLike::AssignmentPattern(node) => {
578                self.walk_pattern(v, &node.left);
579                self.walk_expression(v, &node.right);
580            }
581            PatternLike::RestElement(node) => {
582                self.walk_pattern(v, &node.argument);
583            }
584            PatternLike::MemberExpression(node) => {
585                self.walk_expression(v, &node.object);
586                if node.computed {
587                    self.walk_expression(v, &node.property);
588                }
589            }
590            PatternLike::TSAsExpression(node) => self.walk_expression(v, &node.expression),
591            PatternLike::TSSatisfiesExpression(node) => {
592                self.walk_expression(v, &node.expression)
593            }
594            PatternLike::TSNonNullExpression(node) => {
595                self.walk_expression(v, &node.expression)
596            }
597            PatternLike::TSTypeAssertion(node) => self.walk_expression(v, &node.expression),
598        }
599    }
600
601    // ---- Private helper walk methods ----
602
603    fn walk_for_in_of_left<'ast>(&mut self, v: &mut impl Visitor<'ast>, left: &'ast ForInOfLeft) {
604        match left {
605            ForInOfLeft::VariableDeclaration(decl) => self.walk_variable_declaration(v, decl),
606            ForInOfLeft::Pattern(pat) => self.walk_pattern(v, pat),
607        }
608    }
609
610    fn walk_variable_declaration<'ast>(
611        &mut self,
612        v: &mut impl Visitor<'ast>,
613        decl: &'ast VariableDeclaration,
614    ) {
615        for declarator in &decl.declarations {
616            v.enter_variable_declarator(declarator, &self.scope_stack);
617            self.walk_pattern(v, &declarator.id);
618            if let Some(init) = &declarator.init {
619                self.walk_expression(v, init);
620            }
621            v.leave_variable_declarator(declarator, &self.scope_stack);
622        }
623    }
624
625    fn walk_function_declaration_inner<'ast>(
626        &mut self,
627        v: &mut impl Visitor<'ast>,
628        node: &'ast FunctionDeclaration,
629    ) {
630        let pushed = self.try_push_scope(node.base.start, node.base.node_id);
631        v.enter_function_declaration(node, &self.scope_stack);
632        if v.traverse_function_bodies() {
633            for param in &node.params {
634                self.walk_pattern(v, param);
635            }
636            self.walk_block_statement(v, &node.body);
637        }
638        v.leave_function_declaration(node, &self.scope_stack);
639        if pushed {
640            self.scope_stack.pop();
641        }
642    }
643
644    fn walk_object_expression_property<'ast>(
645        &mut self,
646        v: &mut impl Visitor<'ast>,
647        prop: &'ast ObjectExpressionProperty,
648    ) {
649        match prop {
650            ObjectExpressionProperty::ObjectProperty(p) => {
651                if p.computed {
652                    self.walk_expression(v, &p.key);
653                }
654                self.walk_expression(v, &p.value);
655            }
656            ObjectExpressionProperty::ObjectMethod(node) => {
657                let pushed = self.try_push_scope(node.base.start, node.base.node_id);
658                v.enter_object_method(node, &self.scope_stack);
659                if v.traverse_function_bodies() {
660                    if node.computed {
661                        self.walk_expression(v, &node.key);
662                    }
663                    for param in &node.params {
664                        self.walk_pattern(v, param);
665                    }
666                    self.walk_block_statement(v, &node.body);
667                }
668                v.leave_object_method(node, &self.scope_stack);
669                if pushed {
670                    self.scope_stack.pop();
671                }
672            }
673            ObjectExpressionProperty::SpreadElement(p) => {
674                self.walk_expression(v, &p.argument);
675            }
676        }
677    }
678
679    fn walk_declaration<'ast>(&mut self, v: &mut impl Visitor<'ast>, decl: &'ast Declaration) {
680        match decl {
681            Declaration::FunctionDeclaration(node) => {
682                self.walk_function_declaration_inner(v, node);
683            }
684            Declaration::VariableDeclaration(node) => {
685                self.walk_variable_declaration(v, node);
686            }
687            // TS/Flow declarations - no runtime expressions
688            _ => {}
689        }
690    }
691
692    fn walk_export_default_decl<'ast>(
693        &mut self,
694        v: &mut impl Visitor<'ast>,
695        decl: &'ast ExportDefaultDecl,
696    ) {
697        match decl {
698            ExportDefaultDecl::FunctionDeclaration(node) => {
699                self.walk_function_declaration_inner(v, node);
700            }
701            ExportDefaultDecl::ClassDeclaration(node) => {
702                // Call the visitor hook, but skip the class body
703                v.enter_class_declaration(node, &self.scope_stack);
704            }
705            ExportDefaultDecl::EnumDeclaration(_) => {
706                // Flow enum declarations are opaque — no visitor hooks needed
707            }
708            ExportDefaultDecl::Expression(expr) => {
709                self.walk_expression(v, expr);
710            }
711        }
712    }
713
714    fn walk_jsx_element<'ast>(&mut self, v: &mut impl Visitor<'ast>, node: &'ast JSXElement) {
715        v.enter_jsx_opening_element(&node.opening_element, &self.scope_stack);
716        self.walk_jsx_element_name(v, &node.opening_element.name);
717        v.leave_jsx_opening_element(&node.opening_element, &self.scope_stack);
718        for attr in &node.opening_element.attributes {
719            match attr {
720                JSXAttributeItem::JSXAttribute(a) => {
721                    if let Some(value) = &a.value {
722                        match value {
723                            JSXAttributeValue::JSXExpressionContainer(c) => {
724                                self.walk_jsx_expr_container(v, c);
725                            }
726                            JSXAttributeValue::JSXElement(el) => {
727                                self.walk_jsx_element(v, el);
728                            }
729                            JSXAttributeValue::JSXFragment(f) => {
730                                self.walk_jsx_fragment(v, f);
731                            }
732                            JSXAttributeValue::StringLiteral(_) => {}
733                        }
734                    }
735                }
736                JSXAttributeItem::JSXSpreadAttribute(a) => {
737                    self.walk_expression(v, &a.argument);
738                }
739            }
740        }
741        for child in &node.children {
742            self.walk_jsx_child(v, child);
743        }
744    }
745
746    fn walk_jsx_fragment<'ast>(&mut self, v: &mut impl Visitor<'ast>, node: &'ast JSXFragment) {
747        for child in &node.children {
748            self.walk_jsx_child(v, child);
749        }
750    }
751
752    fn walk_jsx_child<'ast>(&mut self, v: &mut impl Visitor<'ast>, child: &'ast JSXChild) {
753        match child {
754            JSXChild::JSXElement(el) => self.walk_jsx_element(v, el),
755            JSXChild::JSXFragment(f) => self.walk_jsx_fragment(v, f),
756            JSXChild::JSXExpressionContainer(c) => self.walk_jsx_expr_container(v, c),
757            JSXChild::JSXSpreadChild(s) => self.walk_expression(v, &s.expression),
758            JSXChild::JSXText(_) => {}
759        }
760    }
761
762    fn walk_jsx_expr_container<'ast>(
763        &mut self,
764        v: &mut impl Visitor<'ast>,
765        node: &'ast JSXExpressionContainer,
766    ) {
767        match &node.expression {
768            JSXExpressionContainerExpr::Expression(expr) => self.walk_expression(v, expr),
769            JSXExpressionContainerExpr::JSXEmptyExpression(_) => {}
770        }
771    }
772
773    fn walk_jsx_element_name<'ast>(
774        &mut self,
775        v: &mut impl Visitor<'ast>,
776        name: &'ast JSXElementName,
777    ) {
778        match name {
779            JSXElementName::JSXIdentifier(id) => {
780                v.enter_jsx_identifier(id, &self.scope_stack);
781            }
782            JSXElementName::JSXMemberExpression(expr) => {
783                self.walk_jsx_member_expression(v, expr);
784            }
785            JSXElementName::JSXNamespacedName(_) => {}
786        }
787    }
788
789    fn walk_jsx_member_expression<'ast>(
790        &mut self,
791        v: &mut impl Visitor<'ast>,
792        expr: &'ast JSXMemberExpression,
793    ) {
794        match &*expr.object {
795            JSXMemberExprObject::JSXIdentifier(id) => {
796                v.enter_jsx_identifier(id, &self.scope_stack);
797            }
798            JSXMemberExprObject::JSXMemberExpression(inner) => {
799                self.walk_jsx_member_expression(v, inner);
800            }
801        }
802        v.enter_jsx_identifier(&expr.property, &self.scope_stack);
803    }
804}
805
806// =============================================================================
807// Mutable visitor
808// =============================================================================
809
810/// Result from a mutable visitor hook.
811#[derive(Debug, Clone, Copy, PartialEq, Eq)]
812pub enum VisitResult {
813    /// Continue traversal to children.
814    Continue,
815    /// Stop traversal immediately.
816    Stop,
817}
818
819impl VisitResult {
820    pub fn is_stop(self) -> bool {
821        self == VisitResult::Stop
822    }
823}
824
825/// Trait for mutating Babel AST nodes during traversal.
826///
827/// Override hooks to intercept and mutate specific node types.
828/// Return [`VisitResult::Stop`] from any hook to halt the walk.
829/// Hooks are called *before* the walker recurses into children,
830/// so returning `Stop` prevents child traversal.
831pub trait MutVisitor {
832    /// Called for every statement before recursing into its children.
833    fn visit_statement(&mut self, _stmt: &mut Statement) -> VisitResult {
834        VisitResult::Continue
835    }
836
837    /// Called for every expression before recursing into its children.
838    fn visit_expression(&mut self, _expr: &mut Expression) -> VisitResult {
839        VisitResult::Continue
840    }
841
842    /// Called for identifiers in expression position.
843    fn visit_identifier(&mut self, _node: &mut Identifier) -> VisitResult {
844        VisitResult::Continue
845    }
846}
847
848/// Walk a program's body mutably, calling visitor hooks for each node.
849pub fn walk_program_mut(v: &mut impl MutVisitor, program: &mut Program) -> VisitResult {
850    for stmt in program.body.iter_mut() {
851        if walk_statement_mut(v, stmt).is_stop() {
852            return VisitResult::Stop;
853        }
854    }
855    VisitResult::Continue
856}
857
858/// Walk a single statement mutably, calling visitor hooks and recursing into children.
859pub fn walk_statement_mut(v: &mut impl MutVisitor, stmt: &mut Statement) -> VisitResult {
860    if v.visit_statement(stmt).is_stop() {
861        return VisitResult::Stop;
862    }
863    match stmt {
864        Statement::BlockStatement(node) => {
865            for s in node.body.iter_mut() {
866                if walk_statement_mut(v, s).is_stop() {
867                    return VisitResult::Stop;
868                }
869            }
870        }
871        Statement::ReturnStatement(node) => {
872            if let Some(ref mut arg) = node.argument {
873                if walk_expression_mut(v, arg).is_stop() {
874                    return VisitResult::Stop;
875                }
876            }
877        }
878        Statement::ExpressionStatement(node) => {
879            if walk_expression_mut(v, &mut node.expression).is_stop() {
880                return VisitResult::Stop;
881            }
882        }
883        Statement::IfStatement(node) => {
884            if walk_expression_mut(v, &mut node.test).is_stop() {
885                return VisitResult::Stop;
886            }
887            if walk_statement_mut(v, &mut node.consequent).is_stop() {
888                return VisitResult::Stop;
889            }
890            if let Some(ref mut alt) = node.alternate {
891                if walk_statement_mut(v, alt).is_stop() {
892                    return VisitResult::Stop;
893                }
894            }
895        }
896        Statement::ForStatement(node) => {
897            if let Some(ref mut init) = node.init {
898                match init.as_mut() {
899                    ForInit::VariableDeclaration(decl) => {
900                        if walk_variable_declaration_mut(v, decl).is_stop() {
901                            return VisitResult::Stop;
902                        }
903                    }
904                    ForInit::Expression(expr) => {
905                        if walk_expression_mut(v, expr).is_stop() {
906                            return VisitResult::Stop;
907                        }
908                    }
909                }
910            }
911            if let Some(ref mut test) = node.test {
912                if walk_expression_mut(v, test).is_stop() {
913                    return VisitResult::Stop;
914                }
915            }
916            if let Some(ref mut update) = node.update {
917                if walk_expression_mut(v, update).is_stop() {
918                    return VisitResult::Stop;
919                }
920            }
921            if walk_statement_mut(v, &mut node.body).is_stop() {
922                return VisitResult::Stop;
923            }
924        }
925        Statement::WhileStatement(node) => {
926            if walk_expression_mut(v, &mut node.test).is_stop() {
927                return VisitResult::Stop;
928            }
929            if walk_statement_mut(v, &mut node.body).is_stop() {
930                return VisitResult::Stop;
931            }
932        }
933        Statement::DoWhileStatement(node) => {
934            if walk_statement_mut(v, &mut node.body).is_stop() {
935                return VisitResult::Stop;
936            }
937            if walk_expression_mut(v, &mut node.test).is_stop() {
938                return VisitResult::Stop;
939            }
940        }
941        Statement::ForInStatement(node) => {
942            if walk_expression_mut(v, &mut node.right).is_stop() {
943                return VisitResult::Stop;
944            }
945            if walk_statement_mut(v, &mut node.body).is_stop() {
946                return VisitResult::Stop;
947            }
948        }
949        Statement::ForOfStatement(node) => {
950            if walk_expression_mut(v, &mut node.right).is_stop() {
951                return VisitResult::Stop;
952            }
953            if walk_statement_mut(v, &mut node.body).is_stop() {
954                return VisitResult::Stop;
955            }
956        }
957        Statement::SwitchStatement(node) => {
958            if walk_expression_mut(v, &mut node.discriminant).is_stop() {
959                return VisitResult::Stop;
960            }
961            for case in node.cases.iter_mut() {
962                if let Some(ref mut test) = case.test {
963                    if walk_expression_mut(v, test).is_stop() {
964                        return VisitResult::Stop;
965                    }
966                }
967                for s in case.consequent.iter_mut() {
968                    if walk_statement_mut(v, s).is_stop() {
969                        return VisitResult::Stop;
970                    }
971                }
972            }
973        }
974        Statement::ThrowStatement(node) => {
975            if walk_expression_mut(v, &mut node.argument).is_stop() {
976                return VisitResult::Stop;
977            }
978        }
979        Statement::TryStatement(node) => {
980            for s in node.block.body.iter_mut() {
981                if walk_statement_mut(v, s).is_stop() {
982                    return VisitResult::Stop;
983                }
984            }
985            if let Some(ref mut handler) = node.handler {
986                for s in handler.body.body.iter_mut() {
987                    if walk_statement_mut(v, s).is_stop() {
988                        return VisitResult::Stop;
989                    }
990                }
991            }
992            if let Some(ref mut finalizer) = node.finalizer {
993                for s in finalizer.body.iter_mut() {
994                    if walk_statement_mut(v, s).is_stop() {
995                        return VisitResult::Stop;
996                    }
997                }
998            }
999        }
1000        Statement::LabeledStatement(node) => {
1001            if walk_statement_mut(v, &mut node.body).is_stop() {
1002                return VisitResult::Stop;
1003            }
1004        }
1005        Statement::VariableDeclaration(node) => {
1006            if walk_variable_declaration_mut(v, node).is_stop() {
1007                return VisitResult::Stop;
1008            }
1009        }
1010        Statement::FunctionDeclaration(node) => {
1011            for s in node.body.body.iter_mut() {
1012                if walk_statement_mut(v, s).is_stop() {
1013                    return VisitResult::Stop;
1014                }
1015            }
1016        }
1017        Statement::ClassDeclaration(node) => {
1018            if let Some(ref mut sc) = node.super_class {
1019                if walk_expression_mut(v, sc).is_stop() {
1020                    return VisitResult::Stop;
1021                }
1022            }
1023        }
1024        Statement::WithStatement(node) => {
1025            if walk_expression_mut(v, &mut node.object).is_stop() {
1026                return VisitResult::Stop;
1027            }
1028            if walk_statement_mut(v, &mut node.body).is_stop() {
1029                return VisitResult::Stop;
1030            }
1031        }
1032        Statement::ExportNamedDeclaration(node) => {
1033            if let Some(ref mut decl) = node.declaration {
1034                if walk_declaration_mut(v, decl).is_stop() {
1035                    return VisitResult::Stop;
1036                }
1037            }
1038        }
1039        Statement::ExportDefaultDeclaration(node) => {
1040            if walk_export_default_decl_mut(v, &mut node.declaration).is_stop() {
1041                return VisitResult::Stop;
1042            }
1043        }
1044        // No runtime expressions to traverse
1045        Statement::BreakStatement(_)
1046        | Statement::ContinueStatement(_)
1047        | Statement::EmptyStatement(_)
1048        | Statement::DebuggerStatement(_)
1049        | Statement::ImportDeclaration(_)
1050        | Statement::ExportAllDeclaration(_)
1051        | Statement::TSTypeAliasDeclaration(_)
1052        | Statement::TSInterfaceDeclaration(_)
1053        | Statement::TSEnumDeclaration(_)
1054        | Statement::TSModuleDeclaration(_)
1055        | Statement::TSDeclareFunction(_)
1056        | Statement::TypeAlias(_)
1057        | Statement::OpaqueType(_)
1058        | Statement::InterfaceDeclaration(_)
1059        | Statement::DeclareVariable(_)
1060        | Statement::DeclareFunction(_)
1061        | Statement::DeclareClass(_)
1062        | Statement::DeclareModule(_)
1063        | Statement::DeclareModuleExports(_)
1064        | Statement::DeclareExportDeclaration(_)
1065        | Statement::DeclareExportAllDeclaration(_)
1066        | Statement::DeclareInterface(_)
1067        | Statement::DeclareTypeAlias(_)
1068        | Statement::DeclareOpaqueType(_)
1069        | Statement::EnumDeclaration(_) => {}
1070    }
1071    VisitResult::Continue
1072}
1073
1074/// Walk an expression mutably, calling visitor hooks and recursing into children.
1075pub fn walk_expression_mut(v: &mut impl MutVisitor, expr: &mut Expression) -> VisitResult {
1076    if v.visit_expression(expr).is_stop() {
1077        return VisitResult::Stop;
1078    }
1079    match expr {
1080        Expression::Identifier(node) => {
1081            if v.visit_identifier(node).is_stop() {
1082                return VisitResult::Stop;
1083            }
1084        }
1085        Expression::CallExpression(node) => {
1086            if walk_expression_mut(v, &mut node.callee).is_stop() {
1087                return VisitResult::Stop;
1088            }
1089            for arg in node.arguments.iter_mut() {
1090                if walk_expression_mut(v, arg).is_stop() {
1091                    return VisitResult::Stop;
1092                }
1093            }
1094        }
1095        Expression::MemberExpression(node) => {
1096            if walk_expression_mut(v, &mut node.object).is_stop() {
1097                return VisitResult::Stop;
1098            }
1099            if node.computed {
1100                if walk_expression_mut(v, &mut node.property).is_stop() {
1101                    return VisitResult::Stop;
1102                }
1103            }
1104        }
1105        Expression::OptionalCallExpression(node) => {
1106            if walk_expression_mut(v, &mut node.callee).is_stop() {
1107                return VisitResult::Stop;
1108            }
1109            for arg in node.arguments.iter_mut() {
1110                if walk_expression_mut(v, arg).is_stop() {
1111                    return VisitResult::Stop;
1112                }
1113            }
1114        }
1115        Expression::OptionalMemberExpression(node) => {
1116            if walk_expression_mut(v, &mut node.object).is_stop() {
1117                return VisitResult::Stop;
1118            }
1119            if node.computed {
1120                if walk_expression_mut(v, &mut node.property).is_stop() {
1121                    return VisitResult::Stop;
1122                }
1123            }
1124        }
1125        Expression::BinaryExpression(node) => {
1126            if walk_expression_mut(v, &mut node.left).is_stop() {
1127                return VisitResult::Stop;
1128            }
1129            if walk_expression_mut(v, &mut node.right).is_stop() {
1130                return VisitResult::Stop;
1131            }
1132        }
1133        Expression::LogicalExpression(node) => {
1134            if walk_expression_mut(v, &mut node.left).is_stop() {
1135                return VisitResult::Stop;
1136            }
1137            if walk_expression_mut(v, &mut node.right).is_stop() {
1138                return VisitResult::Stop;
1139            }
1140        }
1141        Expression::UnaryExpression(node) => {
1142            if walk_expression_mut(v, &mut node.argument).is_stop() {
1143                return VisitResult::Stop;
1144            }
1145        }
1146        Expression::UpdateExpression(node) => {
1147            if walk_expression_mut(v, &mut node.argument).is_stop() {
1148                return VisitResult::Stop;
1149            }
1150        }
1151        Expression::ConditionalExpression(node) => {
1152            if walk_expression_mut(v, &mut node.test).is_stop() {
1153                return VisitResult::Stop;
1154            }
1155            if walk_expression_mut(v, &mut node.consequent).is_stop() {
1156                return VisitResult::Stop;
1157            }
1158            if walk_expression_mut(v, &mut node.alternate).is_stop() {
1159                return VisitResult::Stop;
1160            }
1161        }
1162        Expression::AssignmentExpression(node) => {
1163            if walk_expression_mut(v, &mut node.right).is_stop() {
1164                return VisitResult::Stop;
1165            }
1166        }
1167        Expression::SequenceExpression(node) => {
1168            for e in node.expressions.iter_mut() {
1169                if walk_expression_mut(v, e).is_stop() {
1170                    return VisitResult::Stop;
1171                }
1172            }
1173        }
1174        Expression::ArrowFunctionExpression(node) => match node.body.as_mut() {
1175            ArrowFunctionBody::BlockStatement(block) => {
1176                for s in block.body.iter_mut() {
1177                    if walk_statement_mut(v, s).is_stop() {
1178                        return VisitResult::Stop;
1179                    }
1180                }
1181            }
1182            ArrowFunctionBody::Expression(e) => {
1183                if walk_expression_mut(v, e).is_stop() {
1184                    return VisitResult::Stop;
1185                }
1186            }
1187        },
1188        Expression::FunctionExpression(node) => {
1189            for s in node.body.body.iter_mut() {
1190                if walk_statement_mut(v, s).is_stop() {
1191                    return VisitResult::Stop;
1192                }
1193            }
1194        }
1195        Expression::ObjectExpression(node) => {
1196            for prop in node.properties.iter_mut() {
1197                match prop {
1198                    ObjectExpressionProperty::ObjectProperty(p) => {
1199                        if p.computed {
1200                            if walk_expression_mut(v, &mut p.key).is_stop() {
1201                                return VisitResult::Stop;
1202                            }
1203                        }
1204                        if walk_expression_mut(v, &mut p.value).is_stop() {
1205                            return VisitResult::Stop;
1206                        }
1207                    }
1208                    ObjectExpressionProperty::ObjectMethod(m) => {
1209                        for s in m.body.body.iter_mut() {
1210                            if walk_statement_mut(v, s).is_stop() {
1211                                return VisitResult::Stop;
1212                            }
1213                        }
1214                    }
1215                    ObjectExpressionProperty::SpreadElement(s) => {
1216                        if walk_expression_mut(v, &mut s.argument).is_stop() {
1217                            return VisitResult::Stop;
1218                        }
1219                    }
1220                }
1221            }
1222        }
1223        Expression::ArrayExpression(node) => {
1224            for elem in node.elements.iter_mut().flatten() {
1225                if walk_expression_mut(v, elem).is_stop() {
1226                    return VisitResult::Stop;
1227                }
1228            }
1229        }
1230        Expression::NewExpression(node) => {
1231            if walk_expression_mut(v, &mut node.callee).is_stop() {
1232                return VisitResult::Stop;
1233            }
1234            for arg in node.arguments.iter_mut() {
1235                if walk_expression_mut(v, arg).is_stop() {
1236                    return VisitResult::Stop;
1237                }
1238            }
1239        }
1240        Expression::TemplateLiteral(node) => {
1241            for e in node.expressions.iter_mut() {
1242                if walk_expression_mut(v, e).is_stop() {
1243                    return VisitResult::Stop;
1244                }
1245            }
1246        }
1247        Expression::TaggedTemplateExpression(node) => {
1248            if walk_expression_mut(v, &mut node.tag).is_stop() {
1249                return VisitResult::Stop;
1250            }
1251            for e in node.quasi.expressions.iter_mut() {
1252                if walk_expression_mut(v, e).is_stop() {
1253                    return VisitResult::Stop;
1254                }
1255            }
1256        }
1257        Expression::AwaitExpression(node) => {
1258            if walk_expression_mut(v, &mut node.argument).is_stop() {
1259                return VisitResult::Stop;
1260            }
1261        }
1262        Expression::YieldExpression(node) => {
1263            if let Some(ref mut arg) = node.argument {
1264                if walk_expression_mut(v, arg).is_stop() {
1265                    return VisitResult::Stop;
1266                }
1267            }
1268        }
1269        Expression::SpreadElement(node) => {
1270            if walk_expression_mut(v, &mut node.argument).is_stop() {
1271                return VisitResult::Stop;
1272            }
1273        }
1274        Expression::ParenthesizedExpression(node) => {
1275            if walk_expression_mut(v, &mut node.expression).is_stop() {
1276                return VisitResult::Stop;
1277            }
1278        }
1279        Expression::AssignmentPattern(node) => {
1280            if walk_expression_mut(v, &mut node.right).is_stop() {
1281                return VisitResult::Stop;
1282            }
1283        }
1284        Expression::ClassExpression(node) => {
1285            if let Some(ref mut sc) = node.super_class {
1286                if walk_expression_mut(v, sc).is_stop() {
1287                    return VisitResult::Stop;
1288                }
1289            }
1290        }
1291        Expression::JSXElement(node) => {
1292            if walk_jsx_mut(v, &mut node.opening_element.attributes, &mut node.children).is_stop() {
1293                return VisitResult::Stop;
1294            }
1295        }
1296        Expression::JSXFragment(node) => {
1297            if walk_jsx_children_mut(v, &mut node.children).is_stop() {
1298                return VisitResult::Stop;
1299            }
1300        }
1301        // TS/Flow wrappers — traverse inner expression
1302        Expression::TSAsExpression(node) => {
1303            if walk_expression_mut(v, &mut node.expression).is_stop() {
1304                return VisitResult::Stop;
1305            }
1306        }
1307        Expression::TSSatisfiesExpression(node) => {
1308            if walk_expression_mut(v, &mut node.expression).is_stop() {
1309                return VisitResult::Stop;
1310            }
1311        }
1312        Expression::TSNonNullExpression(node) => {
1313            if walk_expression_mut(v, &mut node.expression).is_stop() {
1314                return VisitResult::Stop;
1315            }
1316        }
1317        Expression::TSTypeAssertion(node) => {
1318            if walk_expression_mut(v, &mut node.expression).is_stop() {
1319                return VisitResult::Stop;
1320            }
1321        }
1322        Expression::TSInstantiationExpression(node) => {
1323            if walk_expression_mut(v, &mut node.expression).is_stop() {
1324                return VisitResult::Stop;
1325            }
1326        }
1327        Expression::TypeCastExpression(node) => {
1328            if walk_expression_mut(v, &mut node.expression).is_stop() {
1329                return VisitResult::Stop;
1330            }
1331        }
1332        // Leaf nodes
1333        Expression::StringLiteral(_)
1334        | Expression::NumericLiteral(_)
1335        | Expression::BooleanLiteral(_)
1336        | Expression::NullLiteral(_)
1337        | Expression::BigIntLiteral(_)
1338        | Expression::RegExpLiteral(_)
1339        | Expression::MetaProperty(_)
1340        | Expression::PrivateName(_)
1341        | Expression::Super(_)
1342        | Expression::Import(_)
1343        | Expression::ThisExpression(_) => {}
1344    }
1345    VisitResult::Continue
1346}
1347
1348fn walk_jsx_mut(
1349    v: &mut impl MutVisitor,
1350    attrs: &mut [crate::jsx::JSXAttributeItem],
1351    children: &mut [crate::jsx::JSXChild],
1352) -> VisitResult {
1353    for attr in attrs.iter_mut() {
1354        match attr {
1355            crate::jsx::JSXAttributeItem::JSXAttribute(a) => {
1356                if let Some(ref mut val) = a.value {
1357                    match val {
1358                        crate::jsx::JSXAttributeValue::JSXExpressionContainer(c) => {
1359                            if let crate::jsx::JSXExpressionContainerExpr::Expression(ref mut e) =
1360                                c.expression
1361                            {
1362                                if walk_expression_mut(v, e).is_stop() {
1363                                    return VisitResult::Stop;
1364                                }
1365                            }
1366                        }
1367                        _ => {}
1368                    }
1369                }
1370            }
1371            crate::jsx::JSXAttributeItem::JSXSpreadAttribute(s) => {
1372                if walk_expression_mut(v, &mut s.argument).is_stop() {
1373                    return VisitResult::Stop;
1374                }
1375            }
1376        }
1377    }
1378    walk_jsx_children_mut(v, children)
1379}
1380
1381fn walk_jsx_children_mut(
1382    v: &mut impl MutVisitor,
1383    children: &mut [crate::jsx::JSXChild],
1384) -> VisitResult {
1385    for child in children.iter_mut() {
1386        match child {
1387            crate::jsx::JSXChild::JSXElement(el) => {
1388                if walk_jsx_mut(v, &mut el.opening_element.attributes, &mut el.children).is_stop() {
1389                    return VisitResult::Stop;
1390                }
1391            }
1392            crate::jsx::JSXChild::JSXFragment(f) => {
1393                if walk_jsx_children_mut(v, &mut f.children).is_stop() {
1394                    return VisitResult::Stop;
1395                }
1396            }
1397            crate::jsx::JSXChild::JSXExpressionContainer(c) => {
1398                if let crate::jsx::JSXExpressionContainerExpr::Expression(ref mut e) = c.expression
1399                {
1400                    if walk_expression_mut(v, e).is_stop() {
1401                        return VisitResult::Stop;
1402                    }
1403                }
1404            }
1405            crate::jsx::JSXChild::JSXSpreadChild(s) => {
1406                if walk_expression_mut(v, &mut s.expression).is_stop() {
1407                    return VisitResult::Stop;
1408                }
1409            }
1410            _ => {}
1411        }
1412    }
1413    VisitResult::Continue
1414}
1415
1416// ---- Private helper walk-mut functions ----
1417
1418fn walk_variable_declaration_mut(
1419    v: &mut impl MutVisitor,
1420    decl: &mut VariableDeclaration,
1421) -> VisitResult {
1422    for declarator in decl.declarations.iter_mut() {
1423        if let Some(ref mut init) = declarator.init {
1424            if walk_expression_mut(v, init).is_stop() {
1425                return VisitResult::Stop;
1426            }
1427        }
1428    }
1429    VisitResult::Continue
1430}
1431
1432fn walk_declaration_mut(v: &mut impl MutVisitor, decl: &mut Declaration) -> VisitResult {
1433    match decl {
1434        Declaration::FunctionDeclaration(node) => {
1435            for s in node.body.body.iter_mut() {
1436                if walk_statement_mut(v, s).is_stop() {
1437                    return VisitResult::Stop;
1438                }
1439            }
1440        }
1441        Declaration::VariableDeclaration(node) => {
1442            if walk_variable_declaration_mut(v, node).is_stop() {
1443                return VisitResult::Stop;
1444            }
1445        }
1446        Declaration::ClassDeclaration(node) => {
1447            if let Some(ref mut sc) = node.super_class {
1448                if walk_expression_mut(v, sc).is_stop() {
1449                    return VisitResult::Stop;
1450                }
1451            }
1452        }
1453        _ => {}
1454    }
1455    VisitResult::Continue
1456}
1457
1458fn walk_export_default_decl_mut(
1459    v: &mut impl MutVisitor,
1460    decl: &mut ExportDefaultDecl,
1461) -> VisitResult {
1462    match decl {
1463        ExportDefaultDecl::FunctionDeclaration(node) => {
1464            for s in node.body.body.iter_mut() {
1465                if walk_statement_mut(v, s).is_stop() {
1466                    return VisitResult::Stop;
1467                }
1468            }
1469        }
1470        ExportDefaultDecl::Expression(expr) => {
1471            if walk_expression_mut(v, expr).is_stop() {
1472                return VisitResult::Stop;
1473            }
1474        }
1475        ExportDefaultDecl::ClassDeclaration(node) => {
1476            if let Some(ref mut sc) = node.super_class {
1477                if walk_expression_mut(v, sc).is_stop() {
1478                    return VisitResult::Stop;
1479                }
1480            }
1481        }
1482        ExportDefaultDecl::EnumDeclaration(_) => {
1483            // Flow enum declarations are opaque — nothing to walk
1484        }
1485    }
1486    VisitResult::Continue
1487}