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 scope5//! via the scope tree's `node_to_scope` map.67use 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::*;1516/// 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, allowing20/// 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 bodies27 /// (similar to Babel's `path.skip()` in traverse visitors).28 ///29 /// When `false`, the walker still calls `enter_*` / `leave_*` for functions30 /// but does not walk their params or body.31 fn traverse_function_bodies(&self) -> bool {32 true33 }3435 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 }102103 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 }115116 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]) {}118119 /// Called when the walker enters a loop expression context (while.test,120 /// do-while.test, for-in.right, for-of.right). Functions found in these121 /// positions are treated as non-program-scope by Babel, even though the122 /// walker doesn't push a scope for them.123 fn enter_loop_expression(&mut self) {}124 fn leave_loop_expression(&mut self) {}125}126127/// 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 are133 /// NOT inside a scope in the walker's model, but Babel's scope analysis134 /// treats them as non-program-scope. Visitors can check this via135 /// `in_loop_expression_depth()` to implement Babel-compatible scope checks.136 loop_expression_depth: usize,137}138139impl<'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 }147148 /// 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 }156157 pub fn scope_stack(&self) -> &[ScopeId] {158 &self.scope_stack159 }160161 /// Returns the current loop-expression depth. Non-zero when the walker is162 /// inside a loop's test/right expression (while.test, do-while.test,163 /// for-in.right, for-of.right). Visitors can use this to implement164 /// Babel-compatible scope checks in 'all' compilation mode.165 pub fn loop_expression_depth(&self) -> usize {166 self.loop_expression_depth167 }168169 /// 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 false177 }178179 // ---- Public walk methods ----180181 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 }190191 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 }204205 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 traverse347 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 // Unmodeled raw node: opaque, no compilable children to traverse.373 | Statement::Unknown(_) => {}374 }375 }376377 pub fn walk_expression<'ast>(&mut self, v: &mut impl Visitor<'ast>, expr: &'ast Expression) {378 match expr {379 Expression::Identifier(node) => {380 v.enter_identifier(node, &self.scope_stack);381 }382 Expression::CallExpression(node) => {383 v.enter_call_expression(node, &self.scope_stack);384 self.walk_expression(v, &node.callee);385 for arg in &node.arguments {386 self.walk_expression(v, arg);387 }388 v.leave_call_expression(node, &self.scope_stack);389 }390 Expression::MemberExpression(node) => {391 self.walk_expression(v, &node.object);392 if node.computed {393 self.walk_expression(v, &node.property);394 }395 }396 Expression::OptionalCallExpression(node) => {397 self.walk_expression(v, &node.callee);398 for arg in &node.arguments {399 self.walk_expression(v, arg);400 }401 }402 Expression::OptionalMemberExpression(node) => {403 self.walk_expression(v, &node.object);404 if node.computed {405 self.walk_expression(v, &node.property);406 }407 }408 Expression::BinaryExpression(node) => {409 self.walk_expression(v, &node.left);410 self.walk_expression(v, &node.right);411 }412 Expression::LogicalExpression(node) => {413 self.walk_expression(v, &node.left);414 self.walk_expression(v, &node.right);415 }416 Expression::UnaryExpression(node) => {417 self.walk_expression(v, &node.argument);418 }419 Expression::UpdateExpression(node) => {420 v.enter_update_expression(node, &self.scope_stack);421 self.walk_expression(v, &node.argument);422 }423 Expression::ConditionalExpression(node) => {424 self.walk_expression(v, &node.test);425 self.walk_expression(v, &node.consequent);426 self.walk_expression(v, &node.alternate);427 }428 Expression::AssignmentExpression(node) => {429 v.enter_assignment_expression(node, &self.scope_stack);430 self.walk_pattern(v, &node.left);431 self.walk_expression(v, &node.right);432 }433 Expression::SequenceExpression(node) => {434 for expr in &node.expressions {435 self.walk_expression(v, expr);436 }437 }438 Expression::ArrowFunctionExpression(node) => {439 let pushed = self.try_push_scope(node.base.start, node.base.node_id);440 v.enter_arrow_function_expression(node, &self.scope_stack);441 if v.traverse_function_bodies() {442 for param in &node.params {443 self.walk_pattern(v, param);444 }445 match node.body.as_ref() {446 ArrowFunctionBody::BlockStatement(block) => {447 self.walk_block_statement(v, block);448 }449 ArrowFunctionBody::Expression(expr) => {450 self.walk_expression(v, expr);451 }452 }453 }454 v.leave_arrow_function_expression(node, &self.scope_stack);455 if pushed {456 self.scope_stack.pop();457 }458 }459 Expression::FunctionExpression(node) => {460 let pushed = self.try_push_scope(node.base.start, node.base.node_id);461 v.enter_function_expression(node, &self.scope_stack);462 if v.traverse_function_bodies() {463 for param in &node.params {464 self.walk_pattern(v, param);465 }466 self.walk_block_statement(v, &node.body);467 }468 v.leave_function_expression(node, &self.scope_stack);469 if pushed {470 self.scope_stack.pop();471 }472 }473 Expression::ObjectExpression(node) => {474 for prop in &node.properties {475 self.walk_object_expression_property(v, prop);476 }477 }478 Expression::ArrayExpression(node) => {479 for element in &node.elements {480 if let Some(el) = element {481 self.walk_expression(v, el);482 }483 }484 }485 Expression::NewExpression(node) => {486 self.walk_expression(v, &node.callee);487 for arg in &node.arguments {488 self.walk_expression(v, arg);489 }490 }491 Expression::TemplateLiteral(node) => {492 for expr in &node.expressions {493 self.walk_expression(v, expr);494 }495 }496 Expression::TaggedTemplateExpression(node) => {497 self.walk_expression(v, &node.tag);498 for expr in &node.quasi.expressions {499 self.walk_expression(v, expr);500 }501 }502 Expression::AwaitExpression(node) => {503 self.walk_expression(v, &node.argument);504 }505 Expression::YieldExpression(node) => {506 if let Some(arg) = &node.argument {507 self.walk_expression(v, arg);508 }509 }510 Expression::SpreadElement(node) => {511 self.walk_expression(v, &node.argument);512 }513 Expression::ParenthesizedExpression(node) => {514 self.walk_expression(v, &node.expression);515 }516 Expression::AssignmentPattern(node) => {517 self.walk_pattern(v, &node.left);518 self.walk_expression(v, &node.right);519 }520 Expression::ClassExpression(node) => {521 // Call the visitor hook so consumers can index the class name,522 // but skip walking the class body523 v.enter_class_expression(node, &self.scope_stack);524 }525 // JSX526 Expression::JSXElement(node) => self.walk_jsx_element(v, node),527 Expression::JSXFragment(node) => self.walk_jsx_fragment(v, node),528 // TS/Flow wrappers - traverse inner expression529 Expression::TSAsExpression(node) => self.walk_expression(v, &node.expression),530 Expression::TSSatisfiesExpression(node) => self.walk_expression(v, &node.expression),531 Expression::TSNonNullExpression(node) => self.walk_expression(v, &node.expression),532 Expression::TSTypeAssertion(node) => self.walk_expression(v, &node.expression),533 Expression::TSInstantiationExpression(node) => {534 self.walk_expression(v, &node.expression)535 }536 Expression::TypeCastExpression(node) => self.walk_expression(v, &node.expression),537 // Leaf nodes538 Expression::StringLiteral(_)539 | Expression::NumericLiteral(_)540 | Expression::BooleanLiteral(_)541 | Expression::NullLiteral(_)542 | Expression::BigIntLiteral(_)543 | Expression::RegExpLiteral(_)544 | Expression::MetaProperty(_)545 | Expression::PrivateName(_)546 | Expression::Super(_)547 | Expression::Import(_)548 | Expression::ThisExpression(_) => {}549 }550 }551552 pub fn walk_pattern<'ast>(&mut self, v: &mut impl Visitor<'ast>, pat: &'ast PatternLike) {553 match pat {554 PatternLike::Identifier(node) => {555 v.enter_identifier(node, &self.scope_stack);556 }557 PatternLike::ObjectPattern(node) => {558 for prop in &node.properties {559 match prop {560 ObjectPatternProperty::ObjectProperty(p) => {561 if p.computed {562 self.walk_expression(v, &p.key);563 }564 self.walk_pattern(v, &p.value);565 }566 ObjectPatternProperty::RestElement(p) => {567 self.walk_pattern(v, &p.argument);568 }569 }570 }571 }572 PatternLike::ArrayPattern(node) => {573 for element in &node.elements {574 if let Some(el) = element {575 self.walk_pattern(v, el);576 }577 }578 }579 PatternLike::AssignmentPattern(node) => {580 self.walk_pattern(v, &node.left);581 self.walk_expression(v, &node.right);582 }583 PatternLike::RestElement(node) => {584 self.walk_pattern(v, &node.argument);585 }586 PatternLike::MemberExpression(node) => {587 self.walk_expression(v, &node.object);588 if node.computed {589 self.walk_expression(v, &node.property);590 }591 }592 PatternLike::TSAsExpression(node) => self.walk_expression(v, &node.expression),593 PatternLike::TSSatisfiesExpression(node) => self.walk_expression(v, &node.expression),594 PatternLike::TSNonNullExpression(node) => self.walk_expression(v, &node.expression),595 PatternLike::TSTypeAssertion(node) => self.walk_expression(v, &node.expression),596 PatternLike::TypeCastExpression(node) => self.walk_expression(v, &node.expression),597 }598 }599600 // ---- Private helper walk methods ----601602 fn walk_for_in_of_left<'ast>(&mut self, v: &mut impl Visitor<'ast>, left: &'ast ForInOfLeft) {603 match left {604 ForInOfLeft::VariableDeclaration(decl) => self.walk_variable_declaration(v, decl),605 ForInOfLeft::Pattern(pat) => self.walk_pattern(v, pat),606 }607 }608609 fn walk_variable_declaration<'ast>(610 &mut self,611 v: &mut impl Visitor<'ast>,612 decl: &'ast VariableDeclaration,613 ) {614 for declarator in &decl.declarations {615 v.enter_variable_declarator(declarator, &self.scope_stack);616 self.walk_pattern(v, &declarator.id);617 if let Some(init) = &declarator.init {618 self.walk_expression(v, init);619 }620 v.leave_variable_declarator(declarator, &self.scope_stack);621 }622 }623624 fn walk_function_declaration_inner<'ast>(625 &mut self,626 v: &mut impl Visitor<'ast>,627 node: &'ast FunctionDeclaration,628 ) {629 let pushed = self.try_push_scope(node.base.start, node.base.node_id);630 v.enter_function_declaration(node, &self.scope_stack);631 if v.traverse_function_bodies() {632 for param in &node.params {633 self.walk_pattern(v, param);634 }635 self.walk_block_statement(v, &node.body);636 }637 v.leave_function_declaration(node, &self.scope_stack);638 if pushed {639 self.scope_stack.pop();640 }641 }642643 fn walk_object_expression_property<'ast>(644 &mut self,645 v: &mut impl Visitor<'ast>,646 prop: &'ast ObjectExpressionProperty,647 ) {648 match prop {649 ObjectExpressionProperty::ObjectProperty(p) => {650 if p.computed {651 self.walk_expression(v, &p.key);652 }653 self.walk_expression(v, &p.value);654 }655 ObjectExpressionProperty::ObjectMethod(node) => {656 let pushed = self.try_push_scope(node.base.start, node.base.node_id);657 v.enter_object_method(node, &self.scope_stack);658 if v.traverse_function_bodies() {659 if node.computed {660 self.walk_expression(v, &node.key);661 }662 for param in &node.params {663 self.walk_pattern(v, param);664 }665 self.walk_block_statement(v, &node.body);666 }667 v.leave_object_method(node, &self.scope_stack);668 if pushed {669 self.scope_stack.pop();670 }671 }672 ObjectExpressionProperty::SpreadElement(p) => {673 self.walk_expression(v, &p.argument);674 }675 }676 }677678 fn walk_declaration<'ast>(&mut self, v: &mut impl Visitor<'ast>, decl: &'ast Declaration) {679 match decl {680 Declaration::FunctionDeclaration(node) => {681 self.walk_function_declaration_inner(v, node);682 }683 Declaration::VariableDeclaration(node) => {684 self.walk_variable_declaration(v, node);685 }686 // TS/Flow declarations - no runtime expressions687 _ => {}688 }689 }690691 fn walk_export_default_decl<'ast>(692 &mut self,693 v: &mut impl Visitor<'ast>,694 decl: &'ast ExportDefaultDecl,695 ) {696 match decl {697 ExportDefaultDecl::FunctionDeclaration(node) => {698 self.walk_function_declaration_inner(v, node);699 }700 ExportDefaultDecl::ClassDeclaration(node) => {701 // Call the visitor hook, but skip the class body702 v.enter_class_declaration(node, &self.scope_stack);703 }704 ExportDefaultDecl::EnumDeclaration(_) => {705 // Flow enum declarations are opaque — no visitor hooks needed706 }707 ExportDefaultDecl::Expression(expr) => {708 self.walk_expression(v, expr);709 }710 }711 }712713 fn walk_jsx_element<'ast>(&mut self, v: &mut impl Visitor<'ast>, node: &'ast JSXElement) {714 v.enter_jsx_opening_element(&node.opening_element, &self.scope_stack);715 self.walk_jsx_element_name(v, &node.opening_element.name);716 v.leave_jsx_opening_element(&node.opening_element, &self.scope_stack);717 for attr in &node.opening_element.attributes {718 match attr {719 JSXAttributeItem::JSXAttribute(a) => {720 if let Some(value) = &a.value {721 match value {722 JSXAttributeValue::JSXExpressionContainer(c) => {723 self.walk_jsx_expr_container(v, c);724 }725 JSXAttributeValue::JSXElement(el) => {726 self.walk_jsx_element(v, el);727 }728 JSXAttributeValue::JSXFragment(f) => {729 self.walk_jsx_fragment(v, f);730 }731 JSXAttributeValue::StringLiteral(_) => {}732 }733 }734 }735 JSXAttributeItem::JSXSpreadAttribute(a) => {736 self.walk_expression(v, &a.argument);737 }738 }739 }740 for child in &node.children {741 self.walk_jsx_child(v, child);742 }743 }744745 fn walk_jsx_fragment<'ast>(&mut self, v: &mut impl Visitor<'ast>, node: &'ast JSXFragment) {746 for child in &node.children {747 self.walk_jsx_child(v, child);748 }749 }750751 fn walk_jsx_child<'ast>(&mut self, v: &mut impl Visitor<'ast>, child: &'ast JSXChild) {752 match child {753 JSXChild::JSXElement(el) => self.walk_jsx_element(v, el),754 JSXChild::JSXFragment(f) => self.walk_jsx_fragment(v, f),755 JSXChild::JSXExpressionContainer(c) => self.walk_jsx_expr_container(v, c),756 JSXChild::JSXSpreadChild(s) => self.walk_expression(v, &s.expression),757 JSXChild::JSXText(_) => {}758 }759 }760761 fn walk_jsx_expr_container<'ast>(762 &mut self,763 v: &mut impl Visitor<'ast>,764 node: &'ast JSXExpressionContainer,765 ) {766 match &node.expression {767 JSXExpressionContainerExpr::Expression(expr) => self.walk_expression(v, expr),768 JSXExpressionContainerExpr::JSXEmptyExpression(_) => {}769 }770 }771772 fn walk_jsx_element_name<'ast>(773 &mut self,774 v: &mut impl Visitor<'ast>,775 name: &'ast JSXElementName,776 ) {777 match name {778 JSXElementName::JSXIdentifier(id) => {779 v.enter_jsx_identifier(id, &self.scope_stack);780 }781 JSXElementName::JSXMemberExpression(expr) => {782 self.walk_jsx_member_expression(v, expr);783 }784 JSXElementName::JSXNamespacedName(_) => {}785 }786 }787788 fn walk_jsx_member_expression<'ast>(789 &mut self,790 v: &mut impl Visitor<'ast>,791 expr: &'ast JSXMemberExpression,792 ) {793 match &*expr.object {794 JSXMemberExprObject::JSXIdentifier(id) => {795 v.enter_jsx_identifier(id, &self.scope_stack);796 }797 JSXMemberExprObject::JSXMemberExpression(inner) => {798 self.walk_jsx_member_expression(v, inner);799 }800 }801 v.enter_jsx_identifier(&expr.property, &self.scope_stack);802 }803}804805// =============================================================================806// Mutable visitor807// =============================================================================808809/// Result from a mutable visitor hook.810#[derive(Debug, Clone, Copy, PartialEq, Eq)]811pub enum VisitResult {812 /// Continue traversal to children.813 Continue,814 /// Stop traversal immediately.815 Stop,816}817818impl VisitResult {819 pub fn is_stop(self) -> bool {820 self == VisitResult::Stop821 }822}823824/// Trait for mutating Babel AST nodes during traversal.825///826/// Override hooks to intercept and mutate specific node types.827/// Return [`VisitResult::Stop`] from any hook to halt the walk.828/// Hooks are called *before* the walker recurses into children,829/// so returning `Stop` prevents child traversal.830pub trait MutVisitor {831 /// Called for every statement before recursing into its children.832 fn visit_statement(&mut self, _stmt: &mut Statement) -> VisitResult {833 VisitResult::Continue834 }835836 /// Called for every expression before recursing into its children.837 fn visit_expression(&mut self, _expr: &mut Expression) -> VisitResult {838 VisitResult::Continue839 }840841 /// Called for identifiers in expression position.842 fn visit_identifier(&mut self, _node: &mut Identifier) -> VisitResult {843 VisitResult::Continue844 }845}846847/// Walk a program's body mutably, calling visitor hooks for each node.848pub fn walk_program_mut(v: &mut impl MutVisitor, program: &mut Program) -> VisitResult {849 for stmt in program.body.iter_mut() {850 if walk_statement_mut(v, stmt).is_stop() {851 return VisitResult::Stop;852 }853 }854 VisitResult::Continue855}856857/// Walk a single statement mutably, calling visitor hooks and recursing into children.858pub fn walk_statement_mut(v: &mut impl MutVisitor, stmt: &mut Statement) -> VisitResult {859 if v.visit_statement(stmt).is_stop() {860 return VisitResult::Stop;861 }862 match stmt {863 Statement::BlockStatement(node) => {864 for s in node.body.iter_mut() {865 if walk_statement_mut(v, s).is_stop() {866 return VisitResult::Stop;867 }868 }869 }870 Statement::ReturnStatement(node) => {871 if let Some(ref mut arg) = node.argument {872 if walk_expression_mut(v, arg).is_stop() {873 return VisitResult::Stop;874 }875 }876 }877 Statement::ExpressionStatement(node) => {878 if walk_expression_mut(v, &mut node.expression).is_stop() {879 return VisitResult::Stop;880 }881 }882 Statement::IfStatement(node) => {883 if walk_expression_mut(v, &mut node.test).is_stop() {884 return VisitResult::Stop;885 }886 if walk_statement_mut(v, &mut node.consequent).is_stop() {887 return VisitResult::Stop;888 }889 if let Some(ref mut alt) = node.alternate {890 if walk_statement_mut(v, alt).is_stop() {891 return VisitResult::Stop;892 }893 }894 }895 Statement::ForStatement(node) => {896 if let Some(ref mut init) = node.init {897 match init.as_mut() {898 ForInit::VariableDeclaration(decl) => {899 if walk_variable_declaration_mut(v, decl).is_stop() {900 return VisitResult::Stop;901 }902 }903 ForInit::Expression(expr) => {904 if walk_expression_mut(v, expr).is_stop() {905 return VisitResult::Stop;906 }907 }908 }909 }910 if let Some(ref mut test) = node.test {911 if walk_expression_mut(v, test).is_stop() {912 return VisitResult::Stop;913 }914 }915 if let Some(ref mut update) = node.update {916 if walk_expression_mut(v, update).is_stop() {917 return VisitResult::Stop;918 }919 }920 if walk_statement_mut(v, &mut node.body).is_stop() {921 return VisitResult::Stop;922 }923 }924 Statement::WhileStatement(node) => {925 if walk_expression_mut(v, &mut node.test).is_stop() {926 return VisitResult::Stop;927 }928 if walk_statement_mut(v, &mut node.body).is_stop() {929 return VisitResult::Stop;930 }931 }932 Statement::DoWhileStatement(node) => {933 if walk_statement_mut(v, &mut node.body).is_stop() {934 return VisitResult::Stop;935 }936 if walk_expression_mut(v, &mut node.test).is_stop() {937 return VisitResult::Stop;938 }939 }940 Statement::ForInStatement(node) => {941 if walk_expression_mut(v, &mut node.right).is_stop() {942 return VisitResult::Stop;943 }944 if walk_statement_mut(v, &mut node.body).is_stop() {945 return VisitResult::Stop;946 }947 }948 Statement::ForOfStatement(node) => {949 if walk_expression_mut(v, &mut node.right).is_stop() {950 return VisitResult::Stop;951 }952 if walk_statement_mut(v, &mut node.body).is_stop() {953 return VisitResult::Stop;954 }955 }956 Statement::SwitchStatement(node) => {957 if walk_expression_mut(v, &mut node.discriminant).is_stop() {958 return VisitResult::Stop;959 }960 for case in node.cases.iter_mut() {961 if let Some(ref mut test) = case.test {962 if walk_expression_mut(v, test).is_stop() {963 return VisitResult::Stop;964 }965 }966 for s in case.consequent.iter_mut() {967 if walk_statement_mut(v, s).is_stop() {968 return VisitResult::Stop;969 }970 }971 }972 }973 Statement::ThrowStatement(node) => {974 if walk_expression_mut(v, &mut node.argument).is_stop() {975 return VisitResult::Stop;976 }977 }978 Statement::TryStatement(node) => {979 for s in node.block.body.iter_mut() {980 if walk_statement_mut(v, s).is_stop() {981 return VisitResult::Stop;982 }983 }984 if let Some(ref mut handler) = node.handler {985 for s in handler.body.body.iter_mut() {986 if walk_statement_mut(v, s).is_stop() {987 return VisitResult::Stop;988 }989 }990 }991 if let Some(ref mut finalizer) = node.finalizer {992 for s in finalizer.body.iter_mut() {993 if walk_statement_mut(v, s).is_stop() {994 return VisitResult::Stop;995 }996 }997 }998 }999 Statement::LabeledStatement(node) => {1000 if walk_statement_mut(v, &mut node.body).is_stop() {1001 return VisitResult::Stop;1002 }1003 }1004 Statement::VariableDeclaration(node) => {1005 if walk_variable_declaration_mut(v, node).is_stop() {1006 return VisitResult::Stop;1007 }1008 }1009 Statement::FunctionDeclaration(node) => {1010 for s in node.body.body.iter_mut() {1011 if walk_statement_mut(v, s).is_stop() {1012 return VisitResult::Stop;1013 }1014 }1015 }1016 Statement::ClassDeclaration(node) => {1017 if let Some(ref mut sc) = node.super_class {1018 if walk_expression_mut(v, sc).is_stop() {1019 return VisitResult::Stop;1020 }1021 }1022 }1023 Statement::WithStatement(node) => {1024 if walk_expression_mut(v, &mut node.object).is_stop() {1025 return VisitResult::Stop;1026 }1027 if walk_statement_mut(v, &mut node.body).is_stop() {1028 return VisitResult::Stop;1029 }1030 }1031 Statement::ExportNamedDeclaration(node) => {1032 if let Some(ref mut decl) = node.declaration {1033 if walk_declaration_mut(v, decl).is_stop() {1034 return VisitResult::Stop;1035 }1036 }1037 }1038 Statement::ExportDefaultDeclaration(node) => {1039 if walk_export_default_decl_mut(v, &mut node.declaration).is_stop() {1040 return VisitResult::Stop;1041 }1042 }1043 // No runtime expressions to traverse1044 Statement::BreakStatement(_)1045 | Statement::ContinueStatement(_)1046 | Statement::EmptyStatement(_)1047 | Statement::DebuggerStatement(_)1048 | Statement::ImportDeclaration(_)1049 | Statement::ExportAllDeclaration(_)1050 | Statement::TSTypeAliasDeclaration(_)1051 | Statement::TSInterfaceDeclaration(_)1052 | Statement::TSEnumDeclaration(_)1053 | Statement::TSModuleDeclaration(_)1054 | Statement::TSDeclareFunction(_)1055 | Statement::TypeAlias(_)1056 | Statement::OpaqueType(_)1057 | Statement::InterfaceDeclaration(_)1058 | Statement::DeclareVariable(_)1059 | Statement::DeclareFunction(_)1060 | Statement::DeclareClass(_)1061 | Statement::DeclareModule(_)1062 | Statement::DeclareModuleExports(_)1063 | Statement::DeclareExportDeclaration(_)1064 | Statement::DeclareExportAllDeclaration(_)1065 | Statement::DeclareInterface(_)1066 | Statement::DeclareTypeAlias(_)1067 | Statement::DeclareOpaqueType(_)1068 | Statement::EnumDeclaration(_)1069 // Unmodeled raw node: opaque, no compilable children to traverse.1070 | Statement::Unknown(_) => {}1071 }1072 VisitResult::Continue1073}10741075/// 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 expression1303 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 nodes1334 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::Continue1347}13481349fn 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.expression1362 {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}13811382fn 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.expression1400 {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::Continue1415}14161417// ---- Private helper walk-mut functions ----14181419fn 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::Continue1431}14321433fn 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::Continue1457}14581459fn 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 walk1485 }1486 }1487 VisitResult::Continue1488}