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            PatternLike::TypeCastExpression(node) => self.walk_expression(v, &node.expression),
599        }
600    }
601
602    // ---- Private helper walk methods ----
603
604    fn walk_for_in_of_left<'ast>(&mut self, v: &mut impl Visitor<'ast>, left: &'ast ForInOfLeft) {
605        match left {
606            ForInOfLeft::VariableDeclaration(decl) => self.walk_variable_declaration(v, decl),
607            ForInOfLeft::Pattern(pat) => self.walk_pattern(v, pat),
608        }
609    }
610
611    fn walk_variable_declaration<'ast>(
612        &mut self,
613        v: &mut impl Visitor<'ast>,
614        decl: &'ast VariableDeclaration,
615    ) {
616        for declarator in &decl.declarations {
617            v.enter_variable_declarator(declarator, &self.scope_stack);
618            self.walk_pattern(v, &declarator.id);
619            if let Some(init) = &declarator.init {
620                self.walk_expression(v, init);
621            }
622            v.leave_variable_declarator(declarator, &self.scope_stack);
623        }
624    }
625
626    fn walk_function_declaration_inner<'ast>(
627        &mut self,
628        v: &mut impl Visitor<'ast>,
629        node: &'ast FunctionDeclaration,
630    ) {
631        let pushed = self.try_push_scope(node.base.start, node.base.node_id);
632        v.enter_function_declaration(node, &self.scope_stack);
633        if v.traverse_function_bodies() {
634            for param in &node.params {
635                self.walk_pattern(v, param);
636            }
637            self.walk_block_statement(v, &node.body);
638        }
639        v.leave_function_declaration(node, &self.scope_stack);
640        if pushed {
641            self.scope_stack.pop();
642        }
643    }
644
645    fn walk_object_expression_property<'ast>(
646        &mut self,
647        v: &mut impl Visitor<'ast>,
648        prop: &'ast ObjectExpressionProperty,
649    ) {
650        match prop {
651            ObjectExpressionProperty::ObjectProperty(p) => {
652                if p.computed {
653                    self.walk_expression(v, &p.key);
654                }
655                self.walk_expression(v, &p.value);
656            }
657            ObjectExpressionProperty::ObjectMethod(node) => {
658                let pushed = self.try_push_scope(node.base.start, node.base.node_id);
659                v.enter_object_method(node, &self.scope_stack);
660                if v.traverse_function_bodies() {
661                    if node.computed {
662                        self.walk_expression(v, &node.key);
663                    }
664                    for param in &node.params {
665                        self.walk_pattern(v, param);
666                    }
667                    self.walk_block_statement(v, &node.body);
668                }
669                v.leave_object_method(node, &self.scope_stack);
670                if pushed {
671                    self.scope_stack.pop();
672                }
673            }
674            ObjectExpressionProperty::SpreadElement(p) => {
675                self.walk_expression(v, &p.argument);
676            }
677        }
678    }
679
680    fn walk_declaration<'ast>(&mut self, v: &mut impl Visitor<'ast>, decl: &'ast Declaration) {
681        match decl {
682            Declaration::FunctionDeclaration(node) => {
683                self.walk_function_declaration_inner(v, node);
684            }
685            Declaration::VariableDeclaration(node) => {
686                self.walk_variable_declaration(v, node);
687            }
688            // TS/Flow declarations - no runtime expressions
689            _ => {}
690        }
691    }
692
693    fn walk_export_default_decl<'ast>(
694        &mut self,
695        v: &mut impl Visitor<'ast>,
696        decl: &'ast ExportDefaultDecl,
697    ) {
698        match decl {
699            ExportDefaultDecl::FunctionDeclaration(node) => {
700                self.walk_function_declaration_inner(v, node);
701            }
702            ExportDefaultDecl::ClassDeclaration(node) => {
703                // Call the visitor hook, but skip the class body
704                v.enter_class_declaration(node, &self.scope_stack);
705            }
706            ExportDefaultDecl::EnumDeclaration(_) => {
707                // Flow enum declarations are opaque — no visitor hooks needed
708            }
709            ExportDefaultDecl::Expression(expr) => {
710                self.walk_expression(v, expr);
711            }
712        }
713    }
714
715    fn walk_jsx_element<'ast>(&mut self, v: &mut impl Visitor<'ast>, node: &'ast JSXElement) {
716        v.enter_jsx_opening_element(&node.opening_element, &self.scope_stack);
717        self.walk_jsx_element_name(v, &node.opening_element.name);
718        v.leave_jsx_opening_element(&node.opening_element, &self.scope_stack);
719        for attr in &node.opening_element.attributes {
720            match attr {
721                JSXAttributeItem::JSXAttribute(a) => {
722                    if let Some(value) = &a.value {
723                        match value {
724                            JSXAttributeValue::JSXExpressionContainer(c) => {
725                                self.walk_jsx_expr_container(v, c);
726                            }
727                            JSXAttributeValue::JSXElement(el) => {
728                                self.walk_jsx_element(v, el);
729                            }
730                            JSXAttributeValue::JSXFragment(f) => {
731                                self.walk_jsx_fragment(v, f);
732                            }
733                            JSXAttributeValue::StringLiteral(_) => {}
734                        }
735                    }
736                }
737                JSXAttributeItem::JSXSpreadAttribute(a) => {
738                    self.walk_expression(v, &a.argument);
739                }
740            }
741        }
742        for child in &node.children {
743            self.walk_jsx_child(v, child);
744        }
745    }
746
747    fn walk_jsx_fragment<'ast>(&mut self, v: &mut impl Visitor<'ast>, node: &'ast JSXFragment) {
748        for child in &node.children {
749            self.walk_jsx_child(v, child);
750        }
751    }
752
753    fn walk_jsx_child<'ast>(&mut self, v: &mut impl Visitor<'ast>, child: &'ast JSXChild) {
754        match child {
755            JSXChild::JSXElement(el) => self.walk_jsx_element(v, el),
756            JSXChild::JSXFragment(f) => self.walk_jsx_fragment(v, f),
757            JSXChild::JSXExpressionContainer(c) => self.walk_jsx_expr_container(v, c),
758            JSXChild::JSXSpreadChild(s) => self.walk_expression(v, &s.expression),
759            JSXChild::JSXText(_) => {}
760        }
761    }
762
763    fn walk_jsx_expr_container<'ast>(
764        &mut self,
765        v: &mut impl Visitor<'ast>,
766        node: &'ast JSXExpressionContainer,
767    ) {
768        match &node.expression {
769            JSXExpressionContainerExpr::Expression(expr) => self.walk_expression(v, expr),
770            JSXExpressionContainerExpr::JSXEmptyExpression(_) => {}
771        }
772    }
773
774    fn walk_jsx_element_name<'ast>(
775        &mut self,
776        v: &mut impl Visitor<'ast>,
777        name: &'ast JSXElementName,
778    ) {
779        match name {
780            JSXElementName::JSXIdentifier(id) => {
781                v.enter_jsx_identifier(id, &self.scope_stack);
782            }
783            JSXElementName::JSXMemberExpression(expr) => {
784                self.walk_jsx_member_expression(v, expr);
785            }
786            JSXElementName::JSXNamespacedName(_) => {}
787        }
788    }
789
790    fn walk_jsx_member_expression<'ast>(
791        &mut self,
792        v: &mut impl Visitor<'ast>,
793        expr: &'ast JSXMemberExpression,
794    ) {
795        match &*expr.object {
796            JSXMemberExprObject::JSXIdentifier(id) => {
797                v.enter_jsx_identifier(id, &self.scope_stack);
798            }
799            JSXMemberExprObject::JSXMemberExpression(inner) => {
800                self.walk_jsx_member_expression(v, inner);
801            }
802        }
803        v.enter_jsx_identifier(&expr.property, &self.scope_stack);
804    }
805}
806
807// =============================================================================
808// Mutable visitor
809// =============================================================================
810
811/// Result from a mutable visitor hook.
812#[derive(Debug, Clone, Copy, PartialEq, Eq)]
813pub enum VisitResult {
814    /// Continue traversal to children.
815    Continue,
816    /// Stop traversal immediately.
817    Stop,
818}
819
820impl VisitResult {
821    pub fn is_stop(self) -> bool {
822        self == VisitResult::Stop
823    }
824}
825
826/// Trait for mutating Babel AST nodes during traversal.
827///
828/// Override hooks to intercept and mutate specific node types.
829/// Return [`VisitResult::Stop`] from any hook to halt the walk.
830/// Hooks are called *before* the walker recurses into children,
831/// so returning `Stop` prevents child traversal.
832pub trait MutVisitor {
833    /// Called for every statement before recursing into its children.
834    fn visit_statement(&mut self, _stmt: &mut Statement) -> VisitResult {
835        VisitResult::Continue
836    }
837
838    /// Called for every expression before recursing into its children.
839    fn visit_expression(&mut self, _expr: &mut Expression) -> VisitResult {
840        VisitResult::Continue
841    }
842
843    /// Called for identifiers in expression position.
844    fn visit_identifier(&mut self, _node: &mut Identifier) -> VisitResult {
845        VisitResult::Continue
846    }
847}
848
849/// Walk a program's body mutably, calling visitor hooks for each node.
850pub fn walk_program_mut(v: &mut impl MutVisitor, program: &mut Program) -> VisitResult {
851    for stmt in program.body.iter_mut() {
852        if walk_statement_mut(v, stmt).is_stop() {
853            return VisitResult::Stop;
854        }
855    }
856    VisitResult::Continue
857}
858
859/// Walk a single statement mutably, calling visitor hooks and recursing into children.
860pub fn walk_statement_mut(v: &mut impl MutVisitor, stmt: &mut Statement) -> VisitResult {
861    if v.visit_statement(stmt).is_stop() {
862        return VisitResult::Stop;
863    }
864    match stmt {
865        Statement::BlockStatement(node) => {
866            for s in node.body.iter_mut() {
867                if walk_statement_mut(v, s).is_stop() {
868                    return VisitResult::Stop;
869                }
870            }
871        }
872        Statement::ReturnStatement(node) => {
873            if let Some(ref mut arg) = node.argument {
874                if walk_expression_mut(v, arg).is_stop() {
875                    return VisitResult::Stop;
876                }
877            }
878        }
879        Statement::ExpressionStatement(node) => {
880            if walk_expression_mut(v, &mut node.expression).is_stop() {
881                return VisitResult::Stop;
882            }
883        }
884        Statement::IfStatement(node) => {
885            if walk_expression_mut(v, &mut node.test).is_stop() {
886                return VisitResult::Stop;
887            }
888            if walk_statement_mut(v, &mut node.consequent).is_stop() {
889                return VisitResult::Stop;
890            }
891            if let Some(ref mut alt) = node.alternate {
892                if walk_statement_mut(v, alt).is_stop() {
893                    return VisitResult::Stop;
894                }
895            }
896        }
897        Statement::ForStatement(node) => {
898            if let Some(ref mut init) = node.init {
899                match init.as_mut() {
900                    ForInit::VariableDeclaration(decl) => {
901                        if walk_variable_declaration_mut(v, decl).is_stop() {
902                            return VisitResult::Stop;
903                        }
904                    }
905                    ForInit::Expression(expr) => {
906                        if walk_expression_mut(v, expr).is_stop() {
907                            return VisitResult::Stop;
908                        }
909                    }
910                }
911            }
912            if let Some(ref mut test) = node.test {
913                if walk_expression_mut(v, test).is_stop() {
914                    return VisitResult::Stop;
915                }
916            }
917            if let Some(ref mut update) = node.update {
918                if walk_expression_mut(v, update).is_stop() {
919                    return VisitResult::Stop;
920                }
921            }
922            if walk_statement_mut(v, &mut node.body).is_stop() {
923                return VisitResult::Stop;
924            }
925        }
926        Statement::WhileStatement(node) => {
927            if walk_expression_mut(v, &mut node.test).is_stop() {
928                return VisitResult::Stop;
929            }
930            if walk_statement_mut(v, &mut node.body).is_stop() {
931                return VisitResult::Stop;
932            }
933        }
934        Statement::DoWhileStatement(node) => {
935            if walk_statement_mut(v, &mut node.body).is_stop() {
936                return VisitResult::Stop;
937            }
938            if walk_expression_mut(v, &mut node.test).is_stop() {
939                return VisitResult::Stop;
940            }
941        }
942        Statement::ForInStatement(node) => {
943            if walk_expression_mut(v, &mut node.right).is_stop() {
944                return VisitResult::Stop;
945            }
946            if walk_statement_mut(v, &mut node.body).is_stop() {
947                return VisitResult::Stop;
948            }
949        }
950        Statement::ForOfStatement(node) => {
951            if walk_expression_mut(v, &mut node.right).is_stop() {
952                return VisitResult::Stop;
953            }
954            if walk_statement_mut(v, &mut node.body).is_stop() {
955                return VisitResult::Stop;
956            }
957        }
958        Statement::SwitchStatement(node) => {
959            if walk_expression_mut(v, &mut node.discriminant).is_stop() {
960                return VisitResult::Stop;
961            }
962            for case in node.cases.iter_mut() {
963                if let Some(ref mut test) = case.test {
964                    if walk_expression_mut(v, test).is_stop() {
965                        return VisitResult::Stop;
966                    }
967                }
968                for s in case.consequent.iter_mut() {
969                    if walk_statement_mut(v, s).is_stop() {
970                        return VisitResult::Stop;
971                    }
972                }
973            }
974        }
975        Statement::ThrowStatement(node) => {
976            if walk_expression_mut(v, &mut node.argument).is_stop() {
977                return VisitResult::Stop;
978            }
979        }
980        Statement::TryStatement(node) => {
981            for s in node.block.body.iter_mut() {
982                if walk_statement_mut(v, s).is_stop() {
983                    return VisitResult::Stop;
984                }
985            }
986            if let Some(ref mut handler) = node.handler {
987                for s in handler.body.body.iter_mut() {
988                    if walk_statement_mut(v, s).is_stop() {
989                        return VisitResult::Stop;
990                    }
991                }
992            }
993            if let Some(ref mut finalizer) = node.finalizer {
994                for s in finalizer.body.iter_mut() {
995                    if walk_statement_mut(v, s).is_stop() {
996                        return VisitResult::Stop;
997                    }
998                }
999            }
1000        }
1001        Statement::LabeledStatement(node) => {
1002            if walk_statement_mut(v, &mut node.body).is_stop() {
1003                return VisitResult::Stop;
1004            }
1005        }
1006        Statement::VariableDeclaration(node) => {
1007            if walk_variable_declaration_mut(v, node).is_stop() {
1008                return VisitResult::Stop;
1009            }
1010        }
1011        Statement::FunctionDeclaration(node) => {
1012            for s in node.body.body.iter_mut() {
1013                if walk_statement_mut(v, s).is_stop() {
1014                    return VisitResult::Stop;
1015                }
1016            }
1017        }
1018        Statement::ClassDeclaration(node) => {
1019            if let Some(ref mut sc) = node.super_class {
1020                if walk_expression_mut(v, sc).is_stop() {
1021                    return VisitResult::Stop;
1022                }
1023            }
1024        }
1025        Statement::WithStatement(node) => {
1026            if walk_expression_mut(v, &mut node.object).is_stop() {
1027                return VisitResult::Stop;
1028            }
1029            if walk_statement_mut(v, &mut node.body).is_stop() {
1030                return VisitResult::Stop;
1031            }
1032        }
1033        Statement::ExportNamedDeclaration(node) => {
1034            if let Some(ref mut decl) = node.declaration {
1035                if walk_declaration_mut(v, decl).is_stop() {
1036                    return VisitResult::Stop;
1037                }
1038            }
1039        }
1040        Statement::ExportDefaultDeclaration(node) => {
1041            if walk_export_default_decl_mut(v, &mut node.declaration).is_stop() {
1042                return VisitResult::Stop;
1043            }
1044        }
1045        // No runtime expressions to traverse
1046        Statement::BreakStatement(_)
1047        | Statement::ContinueStatement(_)
1048        | Statement::EmptyStatement(_)
1049        | Statement::DebuggerStatement(_)
1050        | Statement::ImportDeclaration(_)
1051        | Statement::ExportAllDeclaration(_)
1052        | Statement::TSTypeAliasDeclaration(_)
1053        | Statement::TSInterfaceDeclaration(_)
1054        | Statement::TSEnumDeclaration(_)
1055        | Statement::TSModuleDeclaration(_)
1056        | Statement::TSDeclareFunction(_)
1057        | Statement::TypeAlias(_)
1058        | Statement::OpaqueType(_)
1059        | Statement::InterfaceDeclaration(_)
1060        | Statement::DeclareVariable(_)
1061        | Statement::DeclareFunction(_)
1062        | Statement::DeclareClass(_)
1063        | Statement::DeclareModule(_)
1064        | Statement::DeclareModuleExports(_)
1065        | Statement::DeclareExportDeclaration(_)
1066        | Statement::DeclareExportAllDeclaration(_)
1067        | Statement::DeclareInterface(_)
1068        | Statement::DeclareTypeAlias(_)
1069        | Statement::DeclareOpaqueType(_)
1070        | Statement::EnumDeclaration(_) => {}
1071    }
1072    VisitResult::Continue
1073}
1074
1075/// Walk an expression mutably, calling visitor hooks and recursing into children.
1076pub fn walk_expression_mut(v: &mut impl MutVisitor, expr: &mut Expression) -> VisitResult {
1077    if v.visit_expression(expr).is_stop() {
1078        return VisitResult::Stop;
1079    }
1080    match expr {
1081        Expression::Identifier(node) => {
1082            if v.visit_identifier(node).is_stop() {
1083                return VisitResult::Stop;
1084            }
1085        }
1086        Expression::CallExpression(node) => {
1087            if walk_expression_mut(v, &mut node.callee).is_stop() {
1088                return VisitResult::Stop;
1089            }
1090            for arg in node.arguments.iter_mut() {
1091                if walk_expression_mut(v, arg).is_stop() {
1092                    return VisitResult::Stop;
1093                }
1094            }
1095        }
1096        Expression::MemberExpression(node) => {
1097            if walk_expression_mut(v, &mut node.object).is_stop() {
1098                return VisitResult::Stop;
1099            }
1100            if node.computed {
1101                if walk_expression_mut(v, &mut node.property).is_stop() {
1102                    return VisitResult::Stop;
1103                }
1104            }
1105        }
1106        Expression::OptionalCallExpression(node) => {
1107            if walk_expression_mut(v, &mut node.callee).is_stop() {
1108                return VisitResult::Stop;
1109            }
1110            for arg in node.arguments.iter_mut() {
1111                if walk_expression_mut(v, arg).is_stop() {
1112                    return VisitResult::Stop;
1113                }
1114            }
1115        }
1116        Expression::OptionalMemberExpression(node) => {
1117            if walk_expression_mut(v, &mut node.object).is_stop() {
1118                return VisitResult::Stop;
1119            }
1120            if node.computed {
1121                if walk_expression_mut(v, &mut node.property).is_stop() {
1122                    return VisitResult::Stop;
1123                }
1124            }
1125        }
1126        Expression::BinaryExpression(node) => {
1127            if walk_expression_mut(v, &mut node.left).is_stop() {
1128                return VisitResult::Stop;
1129            }
1130            if walk_expression_mut(v, &mut node.right).is_stop() {
1131                return VisitResult::Stop;
1132            }
1133        }
1134        Expression::LogicalExpression(node) => {
1135            if walk_expression_mut(v, &mut node.left).is_stop() {
1136                return VisitResult::Stop;
1137            }
1138            if walk_expression_mut(v, &mut node.right).is_stop() {
1139                return VisitResult::Stop;
1140            }
1141        }
1142        Expression::UnaryExpression(node) => {
1143            if walk_expression_mut(v, &mut node.argument).is_stop() {
1144                return VisitResult::Stop;
1145            }
1146        }
1147        Expression::UpdateExpression(node) => {
1148            if walk_expression_mut(v, &mut node.argument).is_stop() {
1149                return VisitResult::Stop;
1150            }
1151        }
1152        Expression::ConditionalExpression(node) => {
1153            if walk_expression_mut(v, &mut node.test).is_stop() {
1154                return VisitResult::Stop;
1155            }
1156            if walk_expression_mut(v, &mut node.consequent).is_stop() {
1157                return VisitResult::Stop;
1158            }
1159            if walk_expression_mut(v, &mut node.alternate).is_stop() {
1160                return VisitResult::Stop;
1161            }
1162        }
1163        Expression::AssignmentExpression(node) => {
1164            if walk_expression_mut(v, &mut node.right).is_stop() {
1165                return VisitResult::Stop;
1166            }
1167        }
1168        Expression::SequenceExpression(node) => {
1169            for e in node.expressions.iter_mut() {
1170                if walk_expression_mut(v, e).is_stop() {
1171                    return VisitResult::Stop;
1172                }
1173            }
1174        }
1175        Expression::ArrowFunctionExpression(node) => match node.body.as_mut() {
1176            ArrowFunctionBody::BlockStatement(block) => {
1177                for s in block.body.iter_mut() {
1178                    if walk_statement_mut(v, s).is_stop() {
1179                        return VisitResult::Stop;
1180                    }
1181                }
1182            }
1183            ArrowFunctionBody::Expression(e) => {
1184                if walk_expression_mut(v, e).is_stop() {
1185                    return VisitResult::Stop;
1186                }
1187            }
1188        },
1189        Expression::FunctionExpression(node) => {
1190            for s in node.body.body.iter_mut() {
1191                if walk_statement_mut(v, s).is_stop() {
1192                    return VisitResult::Stop;
1193                }
1194            }
1195        }
1196        Expression::ObjectExpression(node) => {
1197            for prop in node.properties.iter_mut() {
1198                match prop {
1199                    ObjectExpressionProperty::ObjectProperty(p) => {
1200                        if p.computed {
1201                            if walk_expression_mut(v, &mut p.key).is_stop() {
1202                                return VisitResult::Stop;
1203                            }
1204                        }
1205                        if walk_expression_mut(v, &mut p.value).is_stop() {
1206                            return VisitResult::Stop;
1207                        }
1208                    }
1209                    ObjectExpressionProperty::ObjectMethod(m) => {
1210                        for s in m.body.body.iter_mut() {
1211                            if walk_statement_mut(v, s).is_stop() {
1212                                return VisitResult::Stop;
1213                            }
1214                        }
1215                    }
1216                    ObjectExpressionProperty::SpreadElement(s) => {
1217                        if walk_expression_mut(v, &mut s.argument).is_stop() {
1218                            return VisitResult::Stop;
1219                        }
1220                    }
1221                }
1222            }
1223        }
1224        Expression::ArrayExpression(node) => {
1225            for elem in node.elements.iter_mut().flatten() {
1226                if walk_expression_mut(v, elem).is_stop() {
1227                    return VisitResult::Stop;
1228                }
1229            }
1230        }
1231        Expression::NewExpression(node) => {
1232            if walk_expression_mut(v, &mut node.callee).is_stop() {
1233                return VisitResult::Stop;
1234            }
1235            for arg in node.arguments.iter_mut() {
1236                if walk_expression_mut(v, arg).is_stop() {
1237                    return VisitResult::Stop;
1238                }
1239            }
1240        }
1241        Expression::TemplateLiteral(node) => {
1242            for e in node.expressions.iter_mut() {
1243                if walk_expression_mut(v, e).is_stop() {
1244                    return VisitResult::Stop;
1245                }
1246            }
1247        }
1248        Expression::TaggedTemplateExpression(node) => {
1249            if walk_expression_mut(v, &mut node.tag).is_stop() {
1250                return VisitResult::Stop;
1251            }
1252            for e in node.quasi.expressions.iter_mut() {
1253                if walk_expression_mut(v, e).is_stop() {
1254                    return VisitResult::Stop;
1255                }
1256            }
1257        }
1258        Expression::AwaitExpression(node) => {
1259            if walk_expression_mut(v, &mut node.argument).is_stop() {
1260                return VisitResult::Stop;
1261            }
1262        }
1263        Expression::YieldExpression(node) => {
1264            if let Some(ref mut arg) = node.argument {
1265                if walk_expression_mut(v, arg).is_stop() {
1266                    return VisitResult::Stop;
1267                }
1268            }
1269        }
1270        Expression::SpreadElement(node) => {
1271            if walk_expression_mut(v, &mut node.argument).is_stop() {
1272                return VisitResult::Stop;
1273            }
1274        }
1275        Expression::ParenthesizedExpression(node) => {
1276            if walk_expression_mut(v, &mut node.expression).is_stop() {
1277                return VisitResult::Stop;
1278            }
1279        }
1280        Expression::AssignmentPattern(node) => {
1281            if walk_expression_mut(v, &mut node.right).is_stop() {
1282                return VisitResult::Stop;
1283            }
1284        }
1285        Expression::ClassExpression(node) => {
1286            if let Some(ref mut sc) = node.super_class {
1287                if walk_expression_mut(v, sc).is_stop() {
1288                    return VisitResult::Stop;
1289                }
1290            }
1291        }
1292        Expression::JSXElement(node) => {
1293            if walk_jsx_mut(v, &mut node.opening_element.attributes, &mut node.children).is_stop() {
1294                return VisitResult::Stop;
1295            }
1296        }
1297        Expression::JSXFragment(node) => {
1298            if walk_jsx_children_mut(v, &mut node.children).is_stop() {
1299                return VisitResult::Stop;
1300            }
1301        }
1302        // TS/Flow wrappers — traverse inner expression
1303        Expression::TSAsExpression(node) => {
1304            if walk_expression_mut(v, &mut node.expression).is_stop() {
1305                return VisitResult::Stop;
1306            }
1307        }
1308        Expression::TSSatisfiesExpression(node) => {
1309            if walk_expression_mut(v, &mut node.expression).is_stop() {
1310                return VisitResult::Stop;
1311            }
1312        }
1313        Expression::TSNonNullExpression(node) => {
1314            if walk_expression_mut(v, &mut node.expression).is_stop() {
1315                return VisitResult::Stop;
1316            }
1317        }
1318        Expression::TSTypeAssertion(node) => {
1319            if walk_expression_mut(v, &mut node.expression).is_stop() {
1320                return VisitResult::Stop;
1321            }
1322        }
1323        Expression::TSInstantiationExpression(node) => {
1324            if walk_expression_mut(v, &mut node.expression).is_stop() {
1325                return VisitResult::Stop;
1326            }
1327        }
1328        Expression::TypeCastExpression(node) => {
1329            if walk_expression_mut(v, &mut node.expression).is_stop() {
1330                return VisitResult::Stop;
1331            }
1332        }
1333        // Leaf nodes
1334        Expression::StringLiteral(_)
1335        | Expression::NumericLiteral(_)
1336        | Expression::BooleanLiteral(_)
1337        | Expression::NullLiteral(_)
1338        | Expression::BigIntLiteral(_)
1339        | Expression::RegExpLiteral(_)
1340        | Expression::MetaProperty(_)
1341        | Expression::PrivateName(_)
1342        | Expression::Super(_)
1343        | Expression::Import(_)
1344        | Expression::ThisExpression(_) => {}
1345    }
1346    VisitResult::Continue
1347}
1348
1349fn walk_jsx_mut(
1350    v: &mut impl MutVisitor,
1351    attrs: &mut [crate::jsx::JSXAttributeItem],
1352    children: &mut [crate::jsx::JSXChild],
1353) -> VisitResult {
1354    for attr in attrs.iter_mut() {
1355        match attr {
1356            crate::jsx::JSXAttributeItem::JSXAttribute(a) => {
1357                if let Some(ref mut val) = a.value {
1358                    match val {
1359                        crate::jsx::JSXAttributeValue::JSXExpressionContainer(c) => {
1360                            if let crate::jsx::JSXExpressionContainerExpr::Expression(ref mut e) =
1361                                c.expression
1362                            {
1363                                if walk_expression_mut(v, e).is_stop() {
1364                                    return VisitResult::Stop;
1365                                }
1366                            }
1367                        }
1368                        _ => {}
1369                    }
1370                }
1371            }
1372            crate::jsx::JSXAttributeItem::JSXSpreadAttribute(s) => {
1373                if walk_expression_mut(v, &mut s.argument).is_stop() {
1374                    return VisitResult::Stop;
1375                }
1376            }
1377        }
1378    }
1379    walk_jsx_children_mut(v, children)
1380}
1381
1382fn walk_jsx_children_mut(
1383    v: &mut impl MutVisitor,
1384    children: &mut [crate::jsx::JSXChild],
1385) -> VisitResult {
1386    for child in children.iter_mut() {
1387        match child {
1388            crate::jsx::JSXChild::JSXElement(el) => {
1389                if walk_jsx_mut(v, &mut el.opening_element.attributes, &mut el.children).is_stop() {
1390                    return VisitResult::Stop;
1391                }
1392            }
1393            crate::jsx::JSXChild::JSXFragment(f) => {
1394                if walk_jsx_children_mut(v, &mut f.children).is_stop() {
1395                    return VisitResult::Stop;
1396                }
1397            }
1398            crate::jsx::JSXChild::JSXExpressionContainer(c) => {
1399                if let crate::jsx::JSXExpressionContainerExpr::Expression(ref mut e) = c.expression
1400                {
1401                    if walk_expression_mut(v, e).is_stop() {
1402                        return VisitResult::Stop;
1403                    }
1404                }
1405            }
1406            crate::jsx::JSXChild::JSXSpreadChild(s) => {
1407                if walk_expression_mut(v, &mut s.expression).is_stop() {
1408                    return VisitResult::Stop;
1409                }
1410            }
1411            _ => {}
1412        }
1413    }
1414    VisitResult::Continue
1415}
1416
1417// ---- Private helper walk-mut functions ----
1418
1419fn walk_variable_declaration_mut(
1420    v: &mut impl MutVisitor,
1421    decl: &mut VariableDeclaration,
1422) -> VisitResult {
1423    for declarator in decl.declarations.iter_mut() {
1424        if let Some(ref mut init) = declarator.init {
1425            if walk_expression_mut(v, init).is_stop() {
1426                return VisitResult::Stop;
1427            }
1428        }
1429    }
1430    VisitResult::Continue
1431}
1432
1433fn walk_declaration_mut(v: &mut impl MutVisitor, decl: &mut Declaration) -> VisitResult {
1434    match decl {
1435        Declaration::FunctionDeclaration(node) => {
1436            for s in node.body.body.iter_mut() {
1437                if walk_statement_mut(v, s).is_stop() {
1438                    return VisitResult::Stop;
1439                }
1440            }
1441        }
1442        Declaration::VariableDeclaration(node) => {
1443            if walk_variable_declaration_mut(v, node).is_stop() {
1444                return VisitResult::Stop;
1445            }
1446        }
1447        Declaration::ClassDeclaration(node) => {
1448            if let Some(ref mut sc) = node.super_class {
1449                if walk_expression_mut(v, sc).is_stop() {
1450                    return VisitResult::Stop;
1451                }
1452            }
1453        }
1454        _ => {}
1455    }
1456    VisitResult::Continue
1457}
1458
1459fn walk_export_default_decl_mut(
1460    v: &mut impl MutVisitor,
1461    decl: &mut ExportDefaultDecl,
1462) -> VisitResult {
1463    match decl {
1464        ExportDefaultDecl::FunctionDeclaration(node) => {
1465            for s in node.body.body.iter_mut() {
1466                if walk_statement_mut(v, s).is_stop() {
1467                    return VisitResult::Stop;
1468                }
1469            }
1470        }
1471        ExportDefaultDecl::Expression(expr) => {
1472            if walk_expression_mut(v, expr).is_stop() {
1473                return VisitResult::Stop;
1474            }
1475        }
1476        ExportDefaultDecl::ClassDeclaration(node) => {
1477            if let Some(ref mut sc) = node.super_class {
1478                if walk_expression_mut(v, sc).is_stop() {
1479                    return VisitResult::Stop;
1480                }
1481            }
1482        }
1483        ExportDefaultDecl::EnumDeclaration(_) => {
1484            // Flow enum declarations are opaque — nothing to walk
1485        }
1486    }
1487    VisitResult::Continue
1488}