Skip to main content

react_compiler/entrypoint/
validate_source_locations.rs

1// Copyright (c) Meta Platforms, Inc. and affiliates.
2//
3// This source code is licensed under the MIT license found in the
4// LICENSE file in the root directory of this source tree.
5
6//! Validates that important source locations from the original code are preserved
7//! in the generated AST. This ensures that Istanbul coverage instrumentation can
8//! properly map back to the original source code.
9//!
10//! This validation is test-only, enabled via `@validateSourceLocations` pragma.
11//!
12//! Analogous to TS `ValidateSourceLocations.ts`.
13
14use std::collections::{HashMap, HashSet};
15
16use react_compiler_ast::common::SourceLocation as AstSourceLocation;
17use react_compiler_ast::expressions::{
18    ArrowFunctionBody, ArrowFunctionExpression, Expression, FunctionExpression,
19    ObjectExpressionProperty,
20};
21use react_compiler_ast::patterns::PatternLike;
22use react_compiler_ast::statements::{ForInit, ForInOfLeft, Statement, VariableDeclaration};
23use react_compiler_diagnostics::{
24    CompilerDiagnostic, CompilerDiagnosticDetail, ErrorCategory,
25    SourceLocation as DiagSourceLocation, Position as DiagPosition,
26};
27use react_compiler_hir::environment::Environment;
28use react_compiler_lowering::FunctionNode;
29use react_compiler_reactive_scopes::codegen_reactive_function::CodegenFunction;
30
31/// Validate that important source locations are preserved in the generated AST.
32pub fn validate_source_locations(
33    func: &FunctionNode<'_>,
34    codegen: &CodegenFunction,
35    env: &mut Environment,
36) {
37    // Step 1: Collect important locations from the original source
38    let important_original = collect_important_original_locations(func);
39
40    // Step 2: Collect all locations from the generated AST
41    let mut generated = HashMap::<String, HashSet<String>>::new();
42    collect_generated_from_block(&codegen.body.body, &mut generated);
43    for outlined in &codegen.outlined {
44        collect_generated_from_block(&outlined.func.body.body, &mut generated);
45    }
46
47    // Step 3: Validate that all important locations are preserved
48    let strict_node_types: HashSet<&str> =
49        ["VariableDeclaration", "VariableDeclarator", "Identifier"]
50            .into_iter()
51            .collect();
52
53    // Sort entries by source position to match TS output order
54    // (JS Map preserves insertion order, which is AST traversal order = source order)
55    let mut sorted_entries: Vec<&ImportantLocation> = important_original.values().collect();
56    sorted_entries.sort_by(|a, b| {
57        a.loc.start.line.cmp(&b.loc.start.line)
58            .then(a.loc.start.column.cmp(&b.loc.start.column))
59            // Outer nodes (larger spans) before inner nodes, matching depth-first traversal
60            .then(b.loc.end.line.cmp(&a.loc.end.line))
61            .then(b.loc.end.column.cmp(&a.loc.end.column))
62    });
63
64    for entry in &sorted_entries {
65        let generated_node_types = generated.get(&entry.key);
66
67        if generated_node_types.is_none() {
68            // Location is completely missing
69            let mut node_types_str: Vec<&str> = entry.node_types.iter().copied().collect();
70            node_types_str.sort();
71            report_missing_location(env, &entry.loc, &node_types_str.join(", "));
72        } else {
73            let generated_node_types = generated_node_types.unwrap();
74            // Location exists, check each strict node type
75            for &node_type in &entry.node_types {
76                if strict_node_types.contains(node_type)
77                    && !generated_node_types.contains(node_type)
78                {
79                    // For strict node types, the specific node type must be present.
80                    // Check if any generated node type is also an important original node type.
81                    let has_valid_node_type = generated_node_types
82                        .iter()
83                        .any(|gen_type| entry.node_types.contains(gen_type.as_str()));
84
85                    if has_valid_node_type {
86                        report_missing_location(env, &entry.loc, node_type);
87                    } else {
88                        report_wrong_node_type(
89                            env,
90                            &entry.loc,
91                            node_type,
92                            generated_node_types,
93                        );
94                    }
95                }
96            }
97        }
98    }
99}
100
101// ---- Types ----
102
103struct ImportantLocation {
104    key: String,
105    loc: AstSourceLocation,
106    node_types: HashSet<&'static str>,
107}
108
109// ---- Location key ----
110
111fn location_key(loc: &AstSourceLocation) -> String {
112    format!(
113        "{}:{}-{}:{}",
114        loc.start.line, loc.start.column, loc.end.line, loc.end.column
115    )
116}
117
118// ---- AST to diagnostics SourceLocation conversion ----
119
120fn ast_to_diag_loc(loc: &AstSourceLocation) -> DiagSourceLocation {
121    DiagSourceLocation {
122        start: DiagPosition {
123            line: loc.start.line,
124            column: loc.start.column,
125            index: loc.start.index,
126        },
127        end: DiagPosition {
128            line: loc.end.line,
129            column: loc.end.column,
130            index: loc.end.index,
131        },
132    }
133}
134
135// ---- Error reporting ----
136
137fn report_missing_location(env: &mut Environment, loc: &AstSourceLocation, node_type: &str) {
138    let diag_loc = ast_to_diag_loc(loc);
139    env.record_diagnostic(
140        CompilerDiagnostic::new(
141            ErrorCategory::Todo,
142            "Important source location missing in generated code",
143            Some(format!(
144                "Source location for {} is missing in the generated output. \
145                 This can cause coverage instrumentation to fail to track this \
146                 code properly, resulting in inaccurate coverage reports.",
147                node_type
148            )),
149        )
150        .with_detail(CompilerDiagnosticDetail::Error {
151            loc: Some(diag_loc),
152            message: None,
153            identifier_name: None,
154        }),
155    );
156}
157
158fn report_wrong_node_type(
159    env: &mut Environment,
160    loc: &AstSourceLocation,
161    expected_type: &str,
162    actual_types: &HashSet<String>,
163) {
164    let diag_loc = ast_to_diag_loc(loc);
165    let mut actual: Vec<&str> = actual_types.iter().map(|s| s.as_str()).collect();
166    actual.sort();
167    env.record_diagnostic(
168        CompilerDiagnostic::new(
169            ErrorCategory::Todo,
170            "Important source location has wrong node type in generated code",
171            Some(format!(
172                "Source location for {} exists in the generated output but with wrong \
173                 node type(s): {}. This can cause coverage instrumentation to fail to \
174                 track this code properly, resulting in inaccurate coverage reports.",
175                expected_type,
176                actual.join(", ")
177            )),
178        )
179        .with_detail(CompilerDiagnosticDetail::Error {
180            loc: Some(diag_loc),
181            message: None,
182            identifier_name: None,
183        }),
184    );
185}
186
187// ---- Important type checking ----
188
189/// Returns the Babel type name if this statement variant is an "important instrumented type".
190fn important_statement_type(stmt: &Statement) -> Option<&'static str> {
191    match stmt {
192        Statement::ExpressionStatement(_) => Some("ExpressionStatement"),
193        Statement::BreakStatement(_) => Some("BreakStatement"),
194        Statement::ContinueStatement(_) => Some("ContinueStatement"),
195        Statement::ReturnStatement(_) => Some("ReturnStatement"),
196        Statement::ThrowStatement(_) => Some("ThrowStatement"),
197        Statement::TryStatement(_) => Some("TryStatement"),
198        Statement::IfStatement(_) => Some("IfStatement"),
199        Statement::ForStatement(_) => Some("ForStatement"),
200        Statement::ForInStatement(_) => Some("ForInStatement"),
201        Statement::ForOfStatement(_) => Some("ForOfStatement"),
202        Statement::WhileStatement(_) => Some("WhileStatement"),
203        Statement::DoWhileStatement(_) => Some("DoWhileStatement"),
204        Statement::SwitchStatement(_) => Some("SwitchStatement"),
205        Statement::WithStatement(_) => Some("WithStatement"),
206        Statement::FunctionDeclaration(_) => Some("FunctionDeclaration"),
207        Statement::LabeledStatement(_) => Some("LabeledStatement"),
208        Statement::VariableDeclaration(_) => Some("VariableDeclaration"),
209        _ => None,
210    }
211}
212
213/// Returns the Babel type name if this expression variant is an "important instrumented type".
214fn important_expression_type(expr: &Expression) -> Option<&'static str> {
215    match expr {
216        Expression::ArrowFunctionExpression(_) => Some("ArrowFunctionExpression"),
217        Expression::FunctionExpression(_) => Some("FunctionExpression"),
218        Expression::ConditionalExpression(_) => Some("ConditionalExpression"),
219        Expression::LogicalExpression(_) => Some("LogicalExpression"),
220        Expression::Identifier(_) => Some("Identifier"),
221        Expression::AssignmentPattern(_) => Some("AssignmentPattern"),
222        _ => None,
223    }
224}
225
226// ---- Manual memoization check ----
227
228fn is_manual_memoization(expr: &Expression) -> bool {
229    if let Expression::CallExpression(call) = expr {
230        match call.callee.as_ref() {
231            Expression::Identifier(id) => {
232                id.name == "useMemo" || id.name == "useCallback"
233            }
234            Expression::MemberExpression(mem) => {
235                if let (Expression::Identifier(obj), Expression::Identifier(prop)) =
236                    (mem.object.as_ref(), &*mem.property)
237                {
238                    obj.name == "React"
239                        && (prop.name == "useMemo" || prop.name == "useCallback")
240                } else {
241                    false
242                }
243            }
244            _ => false,
245        }
246    } else {
247        false
248    }
249}
250
251// ============================================================================
252// Step 1: Collect important original locations
253// ============================================================================
254
255fn collect_important_original_locations(
256    func: &FunctionNode<'_>,
257) -> HashMap<String, ImportantLocation> {
258    let mut locations = HashMap::new();
259
260    // Note: TS uses func.traverse() which visits DESCENDANTS only, not the root
261    // function node itself. So we don't record the root function as important.
262    match func {
263        FunctionNode::FunctionDeclaration(f) => {
264            if let Some(id) = &f.id {
265                record_important("Identifier", &id.base.loc, &mut locations);
266            }
267            for param in &f.params {
268                collect_original_pattern(param, &mut locations);
269            }
270            collect_original_block(&f.body.body, false, &mut locations);
271        }
272        FunctionNode::FunctionExpression(f) => {
273            if let Some(id) = &f.id {
274                record_important("Identifier", &id.base.loc, &mut locations);
275            }
276            for param in &f.params {
277                collect_original_pattern(param, &mut locations);
278            }
279            collect_original_block(&f.body.body, false, &mut locations);
280        }
281        FunctionNode::ArrowFunctionExpression(f) => {
282            for param in &f.params {
283                collect_original_pattern(param, &mut locations);
284            }
285            match f.body.as_ref() {
286                ArrowFunctionBody::BlockStatement(block) => {
287                    collect_original_block(&block.body, false, &mut locations);
288                }
289                ArrowFunctionBody::Expression(expr) => {
290                    collect_original_expression(expr, &mut locations);
291                }
292            }
293        }
294    }
295
296    locations
297}
298
299fn record_important(
300    node_type: &'static str,
301    loc: &Option<AstSourceLocation>,
302    locations: &mut HashMap<String, ImportantLocation>,
303) {
304    if let Some(loc) = loc {
305        let key = location_key(loc);
306        if let Some(existing) = locations.get_mut(&key) {
307            existing.node_types.insert(node_type);
308        } else {
309            let mut node_types = HashSet::new();
310            node_types.insert(node_type);
311            locations.insert(
312                key.clone(),
313                ImportantLocation {
314                    key,
315                    loc: loc.clone(),
316                    node_types,
317                },
318            );
319        }
320    }
321}
322
323fn collect_original_block(
324    stmts: &[Statement],
325    in_single_return_arrow: bool,
326    locations: &mut HashMap<String, ImportantLocation>,
327) {
328    for stmt in stmts {
329        collect_original_statement(stmt, in_single_return_arrow, locations);
330    }
331}
332
333fn collect_original_statement(
334    stmt: &Statement,
335    in_single_return_arrow: bool,
336    locations: &mut HashMap<String, ImportantLocation>,
337) {
338    // Record this statement if it's an important type
339    if let Some(type_name) = important_statement_type(stmt) {
340        // Skip return statements inside arrow functions that will be simplified
341        // to expression body: () => { return expr } -> () => expr
342        if type_name == "ReturnStatement" && in_single_return_arrow {
343            if let Statement::ReturnStatement(ret) = stmt {
344                if ret.argument.is_some() {
345                    // Skip recording, but still recurse into children
346                    if let Some(arg) = &ret.argument {
347                        collect_original_expression(arg, locations);
348                    }
349                    return;
350                }
351            }
352        }
353
354        // Skip manual memoization
355        if type_name == "ExpressionStatement" {
356            if let Statement::ExpressionStatement(expr_stmt) = stmt {
357                if is_manual_memoization(&expr_stmt.expression) {
358                    // Still recurse into children
359                    collect_original_expression(&expr_stmt.expression, locations);
360                    return;
361                }
362            }
363        }
364
365        let base_loc = statement_loc(stmt);
366        record_important(type_name, base_loc, locations);
367    }
368
369    // Recurse into children
370    match stmt {
371        Statement::BlockStatement(node) => {
372            collect_original_block(&node.body, false, locations);
373        }
374        Statement::ReturnStatement(node) => {
375            if let Some(arg) = &node.argument {
376                collect_original_expression(arg, locations);
377            }
378        }
379        Statement::ExpressionStatement(node) => {
380            collect_original_expression(&node.expression, locations);
381        }
382        Statement::IfStatement(node) => {
383            collect_original_expression(&node.test, locations);
384            collect_original_statement(&node.consequent, false, locations);
385            if let Some(alt) = &node.alternate {
386                collect_original_statement(alt, false, locations);
387            }
388        }
389        Statement::ForStatement(node) => {
390            if let Some(init) = &node.init {
391                match init.as_ref() {
392                    ForInit::VariableDeclaration(decl) => {
393                        collect_original_var_declaration(decl, locations);
394                    }
395                    ForInit::Expression(expr) => {
396                        collect_original_expression(expr, locations);
397                    }
398                }
399            }
400            if let Some(test) = &node.test {
401                collect_original_expression(test, locations);
402            }
403            if let Some(update) = &node.update {
404                collect_original_expression(update, locations);
405            }
406            collect_original_statement(&node.body, false, locations);
407        }
408        Statement::WhileStatement(node) => {
409            collect_original_expression(&node.test, locations);
410            collect_original_statement(&node.body, false, locations);
411        }
412        Statement::DoWhileStatement(node) => {
413            collect_original_statement(&node.body, false, locations);
414            collect_original_expression(&node.test, locations);
415        }
416        Statement::ForInStatement(node) => {
417            if let ForInOfLeft::Pattern(pat) = node.left.as_ref() {
418                collect_original_pattern(pat, locations);
419            }
420            collect_original_expression(&node.right, locations);
421            collect_original_statement(&node.body, false, locations);
422        }
423        Statement::ForOfStatement(node) => {
424            if let ForInOfLeft::Pattern(pat) = node.left.as_ref() {
425                collect_original_pattern(pat, locations);
426            }
427            collect_original_expression(&node.right, locations);
428            collect_original_statement(&node.body, false, locations);
429        }
430        Statement::SwitchStatement(node) => {
431            collect_original_expression(&node.discriminant, locations);
432            for case in &node.cases {
433                // SwitchCase is an important type
434                record_important("SwitchCase", &case.base.loc, locations);
435                if let Some(test) = &case.test {
436                    collect_original_expression(test, locations);
437                }
438                collect_original_block(&case.consequent, false, locations);
439            }
440        }
441        Statement::ThrowStatement(node) => {
442            collect_original_expression(&node.argument, locations);
443        }
444        Statement::TryStatement(node) => {
445            collect_original_block(&node.block.body, false, locations);
446            if let Some(handler) = &node.handler {
447                if let Some(param) = &handler.param {
448                    collect_original_pattern(param, locations);
449                }
450                collect_original_block(&handler.body.body, false, locations);
451            }
452            if let Some(finalizer) = &node.finalizer {
453                collect_original_block(&finalizer.body, false, locations);
454            }
455        }
456        Statement::LabeledStatement(node) => {
457            // Label identifier
458            record_important("Identifier", &node.label.base.loc, locations);
459            collect_original_statement(&node.body, false, locations);
460        }
461        Statement::VariableDeclaration(node) => {
462            collect_original_var_declaration(node, locations);
463        }
464        Statement::FunctionDeclaration(node) => {
465            if let Some(id) = &node.id {
466                record_important("Identifier", &id.base.loc, locations);
467            }
468            for param in &node.params {
469                collect_original_pattern(param, locations);
470            }
471            collect_original_block(&node.body.body, false, locations);
472        }
473        Statement::WithStatement(node) => {
474            collect_original_expression(&node.object, locations);
475            collect_original_statement(&node.body, false, locations);
476        }
477        // Non-runtime statements: no children to recurse into
478        _ => {}
479    }
480}
481
482fn collect_original_var_declaration(
483    decl: &VariableDeclaration,
484    locations: &mut HashMap<String, ImportantLocation>,
485) {
486    for declarator in &decl.declarations {
487        // VariableDeclarator is an important type
488        record_important("VariableDeclarator", &declarator.base.loc, locations);
489        collect_original_pattern(&declarator.id, locations);
490        if let Some(init) = &declarator.init {
491            collect_original_expression(init, locations);
492        }
493    }
494}
495
496fn collect_original_expression(
497    expr: &Expression,
498    locations: &mut HashMap<String, ImportantLocation>,
499) {
500    // Record this expression if it's an important type
501    if let Some(type_name) = important_expression_type(expr) {
502        // Skip manual memoization
503        if !is_manual_memoization(expr) {
504            let base_loc = expression_loc(expr);
505            record_important(type_name, base_loc, locations);
506        }
507    }
508
509    // Recurse into children
510    match expr {
511        Expression::Identifier(_) => {
512            // Already recorded above if important. No children.
513        }
514        Expression::CallExpression(node) => {
515            collect_original_expression(&node.callee, locations);
516            for arg in &node.arguments {
517                collect_original_expression(arg, locations);
518            }
519        }
520        Expression::MemberExpression(node) => {
521            collect_original_expression(&node.object, locations);
522            if node.computed {
523                collect_original_expression(&node.property, locations);
524            } else {
525                // Non-computed property is an Identifier - record it
526                if let Expression::Identifier(id) = node.property.as_ref() {
527                    record_important("Identifier", &id.base.loc, locations);
528                }
529            }
530        }
531        Expression::OptionalCallExpression(node) => {
532            collect_original_expression(&node.callee, locations);
533            for arg in &node.arguments {
534                collect_original_expression(arg, locations);
535            }
536        }
537        Expression::OptionalMemberExpression(node) => {
538            collect_original_expression(&node.object, locations);
539            if node.computed {
540                collect_original_expression(&node.property, locations);
541            } else if let Expression::Identifier(id) = node.property.as_ref() {
542                record_important("Identifier", &id.base.loc, locations);
543            }
544        }
545        Expression::BinaryExpression(node) => {
546            collect_original_expression(&node.left, locations);
547            collect_original_expression(&node.right, locations);
548        }
549        Expression::LogicalExpression(node) => {
550            collect_original_expression(&node.left, locations);
551            collect_original_expression(&node.right, locations);
552        }
553        Expression::UnaryExpression(node) => {
554            collect_original_expression(&node.argument, locations);
555        }
556        Expression::UpdateExpression(node) => {
557            collect_original_expression(&node.argument, locations);
558        }
559        Expression::ConditionalExpression(node) => {
560            collect_original_expression(&node.test, locations);
561            collect_original_expression(&node.consequent, locations);
562            collect_original_expression(&node.alternate, locations);
563        }
564        Expression::AssignmentExpression(node) => {
565            collect_original_pattern(&node.left, locations);
566            collect_original_expression(&node.right, locations);
567        }
568        Expression::SequenceExpression(node) => {
569            for e in &node.expressions {
570                collect_original_expression(e, locations);
571            }
572        }
573        Expression::ArrowFunctionExpression(node) => {
574            collect_original_arrow_children(node, locations);
575        }
576        Expression::FunctionExpression(node) => {
577            collect_original_fn_expr_children(node, locations);
578        }
579        Expression::ObjectExpression(node) => {
580            for prop in &node.properties {
581                match prop {
582                    ObjectExpressionProperty::ObjectProperty(p) => {
583                        if p.computed {
584                            collect_original_expression(&p.key, locations);
585                        } else if let Expression::Identifier(id) = p.key.as_ref() {
586                            record_important("Identifier", &id.base.loc, locations);
587                        }
588                        collect_original_expression(&p.value, locations);
589                    }
590                    ObjectExpressionProperty::ObjectMethod(m) => {
591                        // ObjectMethod is an important type
592                        record_important("ObjectMethod", &m.base.loc, locations);
593                        for param in &m.params {
594                            collect_original_pattern(param, locations);
595                        }
596                        collect_original_block(&m.body.body, false, locations);
597                    }
598                    ObjectExpressionProperty::SpreadElement(s) => {
599                        collect_original_expression(&s.argument, locations);
600                    }
601                }
602            }
603        }
604        Expression::ArrayExpression(node) => {
605            for elem in node.elements.iter().flatten() {
606                collect_original_expression(elem, locations);
607            }
608        }
609        Expression::NewExpression(node) => {
610            collect_original_expression(&node.callee, locations);
611            for arg in &node.arguments {
612                collect_original_expression(arg, locations);
613            }
614        }
615        Expression::TemplateLiteral(node) => {
616            for e in &node.expressions {
617                collect_original_expression(e, locations);
618            }
619        }
620        Expression::TaggedTemplateExpression(node) => {
621            collect_original_expression(&node.tag, locations);
622            for e in &node.quasi.expressions {
623                collect_original_expression(e, locations);
624            }
625        }
626        Expression::AwaitExpression(node) => {
627            collect_original_expression(&node.argument, locations);
628        }
629        Expression::YieldExpression(node) => {
630            if let Some(arg) = &node.argument {
631                collect_original_expression(arg, locations);
632            }
633        }
634        Expression::SpreadElement(node) => {
635            collect_original_expression(&node.argument, locations);
636        }
637        Expression::ParenthesizedExpression(node) => {
638            collect_original_expression(&node.expression, locations);
639        }
640        Expression::AssignmentPattern(node) => {
641            collect_original_pattern(&node.left, locations);
642            collect_original_expression(&node.right, locations);
643        }
644        Expression::ClassExpression(node) => {
645            if let Some(sc) = &node.super_class {
646                collect_original_expression(sc, locations);
647            }
648        }
649        // TS/Flow wrappers — traverse inner expression
650        Expression::TSAsExpression(node) => {
651            collect_original_expression(&node.expression, locations);
652        }
653        Expression::TSSatisfiesExpression(node) => {
654            collect_original_expression(&node.expression, locations);
655        }
656        Expression::TSNonNullExpression(node) => {
657            collect_original_expression(&node.expression, locations);
658        }
659        Expression::TSTypeAssertion(node) => {
660            collect_original_expression(&node.expression, locations);
661        }
662        Expression::TSInstantiationExpression(node) => {
663            collect_original_expression(&node.expression, locations);
664        }
665        Expression::TypeCastExpression(node) => {
666            collect_original_expression(&node.expression, locations);
667        }
668        // Leaf nodes and JSX
669        _ => {}
670    }
671}
672
673fn collect_original_arrow_children(
674    arrow: &ArrowFunctionExpression,
675    locations: &mut HashMap<String, ImportantLocation>,
676) {
677    for param in &arrow.params {
678        collect_original_pattern(param, locations);
679    }
680    match arrow.body.as_ref() {
681        ArrowFunctionBody::BlockStatement(block) => {
682            let is_single_return =
683                block.body.len() == 1 && block.directives.is_empty();
684            collect_original_block(&block.body, is_single_return, locations);
685        }
686        ArrowFunctionBody::Expression(expr) => {
687            collect_original_expression(expr, locations);
688        }
689    }
690}
691
692fn collect_original_fn_expr_children(
693    func: &FunctionExpression,
694    locations: &mut HashMap<String, ImportantLocation>,
695) {
696    if let Some(id) = &func.id {
697        record_important("Identifier", &id.base.loc, locations);
698    }
699    for param in &func.params {
700        collect_original_pattern(param, locations);
701    }
702    collect_original_block(&func.body.body, false, locations);
703}
704
705fn collect_original_pattern(
706    pattern: &PatternLike,
707    locations: &mut HashMap<String, ImportantLocation>,
708) {
709    match pattern {
710        PatternLike::Identifier(id) => {
711            record_important("Identifier", &id.base.loc, locations);
712        }
713        PatternLike::AssignmentPattern(ap) => {
714            record_important("AssignmentPattern", &ap.base.loc, locations);
715            collect_original_pattern(&ap.left, locations);
716            collect_original_expression(&ap.right, locations);
717        }
718        PatternLike::ObjectPattern(op) => {
719            for prop in &op.properties {
720                match prop {
721                    react_compiler_ast::patterns::ObjectPatternProperty::ObjectProperty(p) => {
722                        if p.computed {
723                            collect_original_expression(&p.key, locations);
724                        } else if let Expression::Identifier(id) = p.key.as_ref() {
725                            record_important("Identifier", &id.base.loc, locations);
726                        }
727                        collect_original_pattern(&p.value, locations);
728                    }
729                    react_compiler_ast::patterns::ObjectPatternProperty::RestElement(r) => {
730                        collect_original_pattern(&r.argument, locations);
731                    }
732                }
733            }
734        }
735        PatternLike::ArrayPattern(ap) => {
736            for elem in ap.elements.iter().flatten() {
737                collect_original_pattern(elem, locations);
738            }
739        }
740        PatternLike::RestElement(r) => {
741            collect_original_pattern(&r.argument, locations);
742        }
743        PatternLike::MemberExpression(m) => {
744            collect_original_expression(
745                &Expression::MemberExpression(m.clone()),
746                locations,
747            );
748        }
749        PatternLike::TSAsExpression(_)
750        | PatternLike::TSSatisfiesExpression(_)
751        | PatternLike::TSNonNullExpression(_)
752        | PatternLike::TSTypeAssertion(_)
753        | PatternLike::TypeCastExpression(_) => {}
754    }
755}
756
757// ---- Helpers to get loc from statement/expression ----
758
759fn statement_loc(stmt: &Statement) -> &Option<AstSourceLocation> {
760    match stmt {
761        Statement::BlockStatement(n) => &n.base.loc,
762        Statement::ReturnStatement(n) => &n.base.loc,
763        Statement::IfStatement(n) => &n.base.loc,
764        Statement::ForStatement(n) => &n.base.loc,
765        Statement::WhileStatement(n) => &n.base.loc,
766        Statement::DoWhileStatement(n) => &n.base.loc,
767        Statement::ForInStatement(n) => &n.base.loc,
768        Statement::ForOfStatement(n) => &n.base.loc,
769        Statement::SwitchStatement(n) => &n.base.loc,
770        Statement::ThrowStatement(n) => &n.base.loc,
771        Statement::TryStatement(n) => &n.base.loc,
772        Statement::BreakStatement(n) => &n.base.loc,
773        Statement::ContinueStatement(n) => &n.base.loc,
774        Statement::LabeledStatement(n) => &n.base.loc,
775        Statement::ExpressionStatement(n) => &n.base.loc,
776        Statement::EmptyStatement(n) => &n.base.loc,
777        Statement::DebuggerStatement(n) => &n.base.loc,
778        Statement::WithStatement(n) => &n.base.loc,
779        Statement::VariableDeclaration(n) => &n.base.loc,
780        Statement::FunctionDeclaration(n) => &n.base.loc,
781        Statement::ClassDeclaration(n) => &n.base.loc,
782        Statement::ImportDeclaration(n) => &n.base.loc,
783        Statement::ExportNamedDeclaration(n) => &n.base.loc,
784        Statement::ExportDefaultDeclaration(n) => &n.base.loc,
785        Statement::ExportAllDeclaration(n) => &n.base.loc,
786        Statement::TSTypeAliasDeclaration(n) => &n.base.loc,
787        Statement::TSInterfaceDeclaration(n) => &n.base.loc,
788        Statement::TSEnumDeclaration(n) => &n.base.loc,
789        Statement::TSModuleDeclaration(n) => &n.base.loc,
790        Statement::TSDeclareFunction(n) => &n.base.loc,
791        Statement::TypeAlias(n) => &n.base.loc,
792        Statement::OpaqueType(n) => &n.base.loc,
793        Statement::InterfaceDeclaration(n) => &n.base.loc,
794        Statement::DeclareVariable(n) => &n.base.loc,
795        Statement::DeclareFunction(n) => &n.base.loc,
796        Statement::DeclareClass(n) => &n.base.loc,
797        Statement::DeclareModule(n) => &n.base.loc,
798        Statement::DeclareModuleExports(n) => &n.base.loc,
799        Statement::DeclareExportDeclaration(n) => &n.base.loc,
800        Statement::DeclareExportAllDeclaration(n) => &n.base.loc,
801        Statement::DeclareInterface(n) => &n.base.loc,
802        Statement::DeclareTypeAlias(n) => &n.base.loc,
803        Statement::DeclareOpaqueType(n) => &n.base.loc,
804        Statement::EnumDeclaration(n) => &n.base.loc,
805        Statement::Unknown(n) => &n.base().loc,
806    }
807}
808
809fn expression_loc(expr: &Expression) -> &Option<AstSourceLocation> {
810    match expr {
811        Expression::Identifier(n) => &n.base.loc,
812        Expression::StringLiteral(n) => &n.base.loc,
813        Expression::NumericLiteral(n) => &n.base.loc,
814        Expression::BooleanLiteral(n) => &n.base.loc,
815        Expression::NullLiteral(n) => &n.base.loc,
816        Expression::BigIntLiteral(n) => &n.base.loc,
817        Expression::RegExpLiteral(n) => &n.base.loc,
818        Expression::CallExpression(n) => &n.base.loc,
819        Expression::MemberExpression(n) => &n.base.loc,
820        Expression::OptionalCallExpression(n) => &n.base.loc,
821        Expression::OptionalMemberExpression(n) => &n.base.loc,
822        Expression::BinaryExpression(n) => &n.base.loc,
823        Expression::LogicalExpression(n) => &n.base.loc,
824        Expression::UnaryExpression(n) => &n.base.loc,
825        Expression::UpdateExpression(n) => &n.base.loc,
826        Expression::ConditionalExpression(n) => &n.base.loc,
827        Expression::AssignmentExpression(n) => &n.base.loc,
828        Expression::SequenceExpression(n) => &n.base.loc,
829        Expression::ArrowFunctionExpression(n) => &n.base.loc,
830        Expression::FunctionExpression(n) => &n.base.loc,
831        Expression::ObjectExpression(n) => &n.base.loc,
832        Expression::ArrayExpression(n) => &n.base.loc,
833        Expression::NewExpression(n) => &n.base.loc,
834        Expression::TemplateLiteral(n) => &n.base.loc,
835        Expression::TaggedTemplateExpression(n) => &n.base.loc,
836        Expression::AwaitExpression(n) => &n.base.loc,
837        Expression::YieldExpression(n) => &n.base.loc,
838        Expression::SpreadElement(n) => &n.base.loc,
839        Expression::MetaProperty(n) => &n.base.loc,
840        Expression::ClassExpression(n) => &n.base.loc,
841        Expression::PrivateName(n) => &n.base.loc,
842        Expression::Super(n) => &n.base.loc,
843        Expression::Import(n) => &n.base.loc,
844        Expression::ThisExpression(n) => &n.base.loc,
845        Expression::ParenthesizedExpression(n) => &n.base.loc,
846        Expression::AssignmentPattern(n) => &n.base.loc,
847        Expression::JSXElement(n) => &n.base.loc,
848        Expression::JSXFragment(n) => &n.base.loc,
849        Expression::TSAsExpression(n) => &n.base.loc,
850        Expression::TSSatisfiesExpression(n) => &n.base.loc,
851        Expression::TSNonNullExpression(n) => &n.base.loc,
852        Expression::TSTypeAssertion(n) => &n.base.loc,
853        Expression::TSInstantiationExpression(n) => &n.base.loc,
854        Expression::TypeCastExpression(n) => &n.base.loc,
855    }
856}
857
858// ============================================================================
859// Step 2: Collect generated locations (ALL node types, not just important ones)
860// ============================================================================
861
862fn collect_generated_from_block(
863    stmts: &[Statement],
864    locations: &mut HashMap<String, HashSet<String>>,
865) {
866    for stmt in stmts {
867        collect_generated_statement(stmt, locations);
868    }
869}
870
871fn record_generated(
872    type_name: &str,
873    loc: &Option<AstSourceLocation>,
874    locations: &mut HashMap<String, HashSet<String>>,
875) {
876    if let Some(loc) = loc {
877        let key = location_key(loc);
878        locations
879            .entry(key)
880            .or_default()
881            .insert(type_name.to_string());
882    }
883}
884
885fn collect_generated_statement(
886    stmt: &Statement,
887    locations: &mut HashMap<String, HashSet<String>>,
888) {
889    // Record this statement's location
890    let type_name = statement_type_name(stmt);
891    record_generated(type_name, statement_loc(stmt), locations);
892
893    // Recurse into children (same structure as original, but record ALL types)
894    match stmt {
895        Statement::BlockStatement(node) => {
896            collect_generated_from_block(&node.body, locations);
897        }
898        Statement::ReturnStatement(node) => {
899            if let Some(arg) = &node.argument {
900                collect_generated_expression(arg, locations);
901            }
902        }
903        Statement::ExpressionStatement(node) => {
904            collect_generated_expression(&node.expression, locations);
905        }
906        Statement::IfStatement(node) => {
907            collect_generated_expression(&node.test, locations);
908            collect_generated_statement(&node.consequent, locations);
909            if let Some(alt) = &node.alternate {
910                collect_generated_statement(alt, locations);
911            }
912        }
913        Statement::ForStatement(node) => {
914            if let Some(init) = &node.init {
915                match init.as_ref() {
916                    ForInit::VariableDeclaration(decl) => {
917                        collect_generated_var_declaration(decl, locations);
918                    }
919                    ForInit::Expression(expr) => {
920                        collect_generated_expression(expr, locations);
921                    }
922                }
923            }
924            if let Some(test) = &node.test {
925                collect_generated_expression(test, locations);
926            }
927            if let Some(update) = &node.update {
928                collect_generated_expression(update, locations);
929            }
930            collect_generated_statement(&node.body, locations);
931        }
932        Statement::WhileStatement(node) => {
933            collect_generated_expression(&node.test, locations);
934            collect_generated_statement(&node.body, locations);
935        }
936        Statement::DoWhileStatement(node) => {
937            collect_generated_statement(&node.body, locations);
938            collect_generated_expression(&node.test, locations);
939        }
940        Statement::ForInStatement(node) => {
941            match node.left.as_ref() {
942                ForInOfLeft::VariableDeclaration(decl) => {
943                    collect_generated_var_declaration(decl, locations);
944                }
945                ForInOfLeft::Pattern(pat) => {
946                    collect_generated_pattern(pat, locations);
947                }
948            }
949            collect_generated_expression(&node.right, locations);
950            collect_generated_statement(&node.body, locations);
951        }
952        Statement::ForOfStatement(node) => {
953            match node.left.as_ref() {
954                ForInOfLeft::VariableDeclaration(decl) => {
955                    collect_generated_var_declaration(decl, locations);
956                }
957                ForInOfLeft::Pattern(pat) => {
958                    collect_generated_pattern(pat, locations);
959                }
960            }
961            collect_generated_expression(&node.right, locations);
962            collect_generated_statement(&node.body, locations);
963        }
964        Statement::SwitchStatement(node) => {
965            collect_generated_expression(&node.discriminant, locations);
966            for case in &node.cases {
967                record_generated("SwitchCase", &case.base.loc, locations);
968                if let Some(test) = &case.test {
969                    collect_generated_expression(test, locations);
970                }
971                collect_generated_from_block(&case.consequent, locations);
972            }
973        }
974        Statement::ThrowStatement(node) => {
975            collect_generated_expression(&node.argument, locations);
976        }
977        Statement::TryStatement(node) => {
978            collect_generated_from_block(&node.block.body, locations);
979            if let Some(handler) = &node.handler {
980                if let Some(param) = &handler.param {
981                    collect_generated_pattern(param, locations);
982                }
983                collect_generated_from_block(&handler.body.body, locations);
984            }
985            if let Some(finalizer) = &node.finalizer {
986                collect_generated_from_block(&finalizer.body, locations);
987            }
988        }
989        Statement::LabeledStatement(node) => {
990            record_generated("Identifier", &node.label.base.loc, locations);
991            collect_generated_statement(&node.body, locations);
992        }
993        Statement::VariableDeclaration(node) => {
994            collect_generated_var_declaration(node, locations);
995        }
996        Statement::FunctionDeclaration(node) => {
997            if let Some(id) = &node.id {
998                record_generated("Identifier", &id.base.loc, locations);
999            }
1000            for param in &node.params {
1001                collect_generated_pattern(param, locations);
1002            }
1003            collect_generated_from_block(&node.body.body, locations);
1004        }
1005        Statement::WithStatement(node) => {
1006            collect_generated_expression(&node.object, locations);
1007            collect_generated_statement(&node.body, locations);
1008        }
1009        Statement::ClassDeclaration(node) => {
1010            if let Some(id) = &node.id {
1011                record_generated("Identifier", &id.base.loc, locations);
1012            }
1013            if let Some(sc) = &node.super_class {
1014                collect_generated_expression(sc, locations);
1015            }
1016        }
1017        _ => {}
1018    }
1019}
1020
1021fn collect_generated_var_declaration(
1022    decl: &VariableDeclaration,
1023    locations: &mut HashMap<String, HashSet<String>>,
1024) {
1025    for declarator in &decl.declarations {
1026        record_generated("VariableDeclarator", &declarator.base.loc, locations);
1027        collect_generated_pattern(&declarator.id, locations);
1028        if let Some(init) = &declarator.init {
1029            collect_generated_expression(init, locations);
1030        }
1031    }
1032}
1033
1034fn collect_generated_expression(
1035    expr: &Expression,
1036    locations: &mut HashMap<String, HashSet<String>>,
1037) {
1038    let type_name = expression_type_name(expr);
1039    record_generated(type_name, expression_loc(expr), locations);
1040
1041    match expr {
1042        Expression::Identifier(_) => {}
1043        Expression::CallExpression(node) => {
1044            collect_generated_expression(&node.callee, locations);
1045            for arg in &node.arguments {
1046                collect_generated_expression(arg, locations);
1047            }
1048        }
1049        Expression::MemberExpression(node) => {
1050            collect_generated_expression(&node.object, locations);
1051            collect_generated_expression(&node.property, locations);
1052        }
1053        Expression::OptionalCallExpression(node) => {
1054            collect_generated_expression(&node.callee, locations);
1055            for arg in &node.arguments {
1056                collect_generated_expression(arg, locations);
1057            }
1058        }
1059        Expression::OptionalMemberExpression(node) => {
1060            collect_generated_expression(&node.object, locations);
1061            collect_generated_expression(&node.property, locations);
1062        }
1063        Expression::BinaryExpression(node) => {
1064            collect_generated_expression(&node.left, locations);
1065            collect_generated_expression(&node.right, locations);
1066        }
1067        Expression::LogicalExpression(node) => {
1068            collect_generated_expression(&node.left, locations);
1069            collect_generated_expression(&node.right, locations);
1070        }
1071        Expression::UnaryExpression(node) => {
1072            collect_generated_expression(&node.argument, locations);
1073        }
1074        Expression::UpdateExpression(node) => {
1075            collect_generated_expression(&node.argument, locations);
1076        }
1077        Expression::ConditionalExpression(node) => {
1078            collect_generated_expression(&node.test, locations);
1079            collect_generated_expression(&node.consequent, locations);
1080            collect_generated_expression(&node.alternate, locations);
1081        }
1082        Expression::AssignmentExpression(node) => {
1083            collect_generated_pattern(&node.left, locations);
1084            collect_generated_expression(&node.right, locations);
1085        }
1086        Expression::SequenceExpression(node) => {
1087            for e in &node.expressions {
1088                collect_generated_expression(e, locations);
1089            }
1090        }
1091        Expression::ArrowFunctionExpression(node) => {
1092            for param in &node.params {
1093                collect_generated_pattern(param, locations);
1094            }
1095            match node.body.as_ref() {
1096                ArrowFunctionBody::BlockStatement(block) => {
1097                    collect_generated_from_block(&block.body, locations);
1098                }
1099                ArrowFunctionBody::Expression(e) => {
1100                    collect_generated_expression(e, locations);
1101                }
1102            }
1103        }
1104        Expression::FunctionExpression(node) => {
1105            if let Some(id) = &node.id {
1106                record_generated("Identifier", &id.base.loc, locations);
1107            }
1108            for param in &node.params {
1109                collect_generated_pattern(param, locations);
1110            }
1111            collect_generated_from_block(&node.body.body, locations);
1112        }
1113        Expression::ObjectExpression(node) => {
1114            for prop in &node.properties {
1115                match prop {
1116                    ObjectExpressionProperty::ObjectProperty(p) => {
1117                        collect_generated_expression(&p.key, locations);
1118                        collect_generated_expression(&p.value, locations);
1119                    }
1120                    ObjectExpressionProperty::ObjectMethod(m) => {
1121                        record_generated("ObjectMethod", &m.base.loc, locations);
1122                        for param in &m.params {
1123                            collect_generated_pattern(param, locations);
1124                        }
1125                        collect_generated_from_block(&m.body.body, locations);
1126                    }
1127                    ObjectExpressionProperty::SpreadElement(s) => {
1128                        collect_generated_expression(&s.argument, locations);
1129                    }
1130                }
1131            }
1132        }
1133        Expression::ArrayExpression(node) => {
1134            for elem in node.elements.iter().flatten() {
1135                collect_generated_expression(elem, locations);
1136            }
1137        }
1138        Expression::NewExpression(node) => {
1139            collect_generated_expression(&node.callee, locations);
1140            for arg in &node.arguments {
1141                collect_generated_expression(arg, locations);
1142            }
1143        }
1144        Expression::TemplateLiteral(node) => {
1145            for e in &node.expressions {
1146                collect_generated_expression(e, locations);
1147            }
1148        }
1149        Expression::TaggedTemplateExpression(node) => {
1150            collect_generated_expression(&node.tag, locations);
1151            for e in &node.quasi.expressions {
1152                collect_generated_expression(e, locations);
1153            }
1154        }
1155        Expression::AwaitExpression(node) => {
1156            collect_generated_expression(&node.argument, locations);
1157        }
1158        Expression::YieldExpression(node) => {
1159            if let Some(arg) = &node.argument {
1160                collect_generated_expression(arg, locations);
1161            }
1162        }
1163        Expression::SpreadElement(node) => {
1164            collect_generated_expression(&node.argument, locations);
1165        }
1166        Expression::ParenthesizedExpression(node) => {
1167            collect_generated_expression(&node.expression, locations);
1168        }
1169        Expression::AssignmentPattern(node) => {
1170            collect_generated_pattern(&node.left, locations);
1171            collect_generated_expression(&node.right, locations);
1172        }
1173        Expression::ClassExpression(node) => {
1174            if let Some(sc) = &node.super_class {
1175                collect_generated_expression(sc, locations);
1176            }
1177        }
1178        Expression::TSAsExpression(node) => {
1179            collect_generated_expression(&node.expression, locations);
1180        }
1181        Expression::TSSatisfiesExpression(node) => {
1182            collect_generated_expression(&node.expression, locations);
1183        }
1184        Expression::TSNonNullExpression(node) => {
1185            collect_generated_expression(&node.expression, locations);
1186        }
1187        Expression::TSTypeAssertion(node) => {
1188            collect_generated_expression(&node.expression, locations);
1189        }
1190        Expression::TSInstantiationExpression(node) => {
1191            collect_generated_expression(&node.expression, locations);
1192        }
1193        Expression::TypeCastExpression(node) => {
1194            collect_generated_expression(&node.expression, locations);
1195        }
1196        // Leaf nodes and JSX
1197        _ => {}
1198    }
1199}
1200
1201fn collect_generated_pattern(
1202    pattern: &PatternLike,
1203    locations: &mut HashMap<String, HashSet<String>>,
1204) {
1205    match pattern {
1206        PatternLike::Identifier(id) => {
1207            record_generated("Identifier", &id.base.loc, locations);
1208        }
1209        PatternLike::AssignmentPattern(ap) => {
1210            record_generated("AssignmentPattern", &ap.base.loc, locations);
1211            collect_generated_pattern(&ap.left, locations);
1212            collect_generated_expression(&ap.right, locations);
1213        }
1214        PatternLike::ObjectPattern(op) => {
1215            record_generated("ObjectPattern", &op.base.loc, locations);
1216            for prop in &op.properties {
1217                match prop {
1218                    react_compiler_ast::patterns::ObjectPatternProperty::ObjectProperty(p) => {
1219                        record_generated("ObjectProperty", &p.base.loc, locations);
1220                        collect_generated_expression(&p.key, locations);
1221                        collect_generated_pattern(&p.value, locations);
1222                    }
1223                    react_compiler_ast::patterns::ObjectPatternProperty::RestElement(r) => {
1224                        record_generated("RestElement", &r.base.loc, locations);
1225                        collect_generated_pattern(&r.argument, locations);
1226                    }
1227                }
1228            }
1229        }
1230        PatternLike::ArrayPattern(ap) => {
1231            record_generated("ArrayPattern", &ap.base.loc, locations);
1232            for elem in ap.elements.iter().flatten() {
1233                collect_generated_pattern(elem, locations);
1234            }
1235        }
1236        PatternLike::RestElement(r) => {
1237            record_generated("RestElement", &r.base.loc, locations);
1238            collect_generated_pattern(&r.argument, locations);
1239        }
1240        PatternLike::MemberExpression(m) => {
1241            record_generated("MemberExpression", &m.base.loc, locations);
1242            collect_generated_expression(&m.object, locations);
1243            collect_generated_expression(&m.property, locations);
1244        }
1245        PatternLike::TSAsExpression(_)
1246        | PatternLike::TSSatisfiesExpression(_)
1247        | PatternLike::TSNonNullExpression(_)
1248        | PatternLike::TSTypeAssertion(_)
1249        | PatternLike::TypeCastExpression(_) => {}
1250    }
1251}
1252
1253// ---- Type name helpers ----
1254
1255fn statement_type_name(stmt: &Statement) -> &'static str {
1256    match stmt {
1257        Statement::BlockStatement(_) => "BlockStatement",
1258        Statement::ReturnStatement(_) => "ReturnStatement",
1259        Statement::IfStatement(_) => "IfStatement",
1260        Statement::ForStatement(_) => "ForStatement",
1261        Statement::WhileStatement(_) => "WhileStatement",
1262        Statement::DoWhileStatement(_) => "DoWhileStatement",
1263        Statement::ForInStatement(_) => "ForInStatement",
1264        Statement::ForOfStatement(_) => "ForOfStatement",
1265        Statement::SwitchStatement(_) => "SwitchStatement",
1266        Statement::ThrowStatement(_) => "ThrowStatement",
1267        Statement::TryStatement(_) => "TryStatement",
1268        Statement::BreakStatement(_) => "BreakStatement",
1269        Statement::ContinueStatement(_) => "ContinueStatement",
1270        Statement::LabeledStatement(_) => "LabeledStatement",
1271        Statement::ExpressionStatement(_) => "ExpressionStatement",
1272        Statement::EmptyStatement(_) => "EmptyStatement",
1273        Statement::DebuggerStatement(_) => "DebuggerStatement",
1274        Statement::WithStatement(_) => "WithStatement",
1275        Statement::VariableDeclaration(_) => "VariableDeclaration",
1276        Statement::FunctionDeclaration(_) => "FunctionDeclaration",
1277        Statement::ClassDeclaration(_) => "ClassDeclaration",
1278        Statement::ImportDeclaration(_) => "ImportDeclaration",
1279        Statement::ExportNamedDeclaration(_) => "ExportNamedDeclaration",
1280        Statement::ExportDefaultDeclaration(_) => "ExportDefaultDeclaration",
1281        Statement::ExportAllDeclaration(_) => "ExportAllDeclaration",
1282        Statement::TSTypeAliasDeclaration(_) => "TSTypeAliasDeclaration",
1283        Statement::TSInterfaceDeclaration(_) => "TSInterfaceDeclaration",
1284        Statement::TSEnumDeclaration(_) => "TSEnumDeclaration",
1285        Statement::TSModuleDeclaration(_) => "TSModuleDeclaration",
1286        Statement::TSDeclareFunction(_) => "TSDeclareFunction",
1287        Statement::TypeAlias(_) => "TypeAlias",
1288        Statement::OpaqueType(_) => "OpaqueType",
1289        Statement::InterfaceDeclaration(_) => "InterfaceDeclaration",
1290        Statement::DeclareVariable(_) => "DeclareVariable",
1291        Statement::DeclareFunction(_) => "DeclareFunction",
1292        Statement::DeclareClass(_) => "DeclareClass",
1293        Statement::DeclareModule(_) => "DeclareModule",
1294        Statement::DeclareModuleExports(_) => "DeclareModuleExports",
1295        Statement::DeclareExportDeclaration(_) => "DeclareExportDeclaration",
1296        Statement::DeclareExportAllDeclaration(_) => "DeclareExportAllDeclaration",
1297        Statement::DeclareInterface(_) => "DeclareInterface",
1298        Statement::DeclareTypeAlias(_) => "DeclareTypeAlias",
1299        Statement::DeclareOpaqueType(_) => "DeclareOpaqueType",
1300        Statement::EnumDeclaration(_) => "EnumDeclaration",
1301        // The real Babel `type` lives in the raw node, but this function
1302        // returns &'static str; "Unknown" is the static stand-in.
1303        Statement::Unknown(_) => "Unknown",
1304    }
1305}
1306
1307fn expression_type_name(expr: &Expression) -> &'static str {
1308    match expr {
1309        Expression::Identifier(_) => "Identifier",
1310        Expression::StringLiteral(_) => "StringLiteral",
1311        Expression::NumericLiteral(_) => "NumericLiteral",
1312        Expression::BooleanLiteral(_) => "BooleanLiteral",
1313        Expression::NullLiteral(_) => "NullLiteral",
1314        Expression::BigIntLiteral(_) => "BigIntLiteral",
1315        Expression::RegExpLiteral(_) => "RegExpLiteral",
1316        Expression::CallExpression(_) => "CallExpression",
1317        Expression::MemberExpression(_) => "MemberExpression",
1318        Expression::OptionalCallExpression(_) => "OptionalCallExpression",
1319        Expression::OptionalMemberExpression(_) => "OptionalMemberExpression",
1320        Expression::BinaryExpression(_) => "BinaryExpression",
1321        Expression::LogicalExpression(_) => "LogicalExpression",
1322        Expression::UnaryExpression(_) => "UnaryExpression",
1323        Expression::UpdateExpression(_) => "UpdateExpression",
1324        Expression::ConditionalExpression(_) => "ConditionalExpression",
1325        Expression::AssignmentExpression(_) => "AssignmentExpression",
1326        Expression::SequenceExpression(_) => "SequenceExpression",
1327        Expression::ArrowFunctionExpression(_) => "ArrowFunctionExpression",
1328        Expression::FunctionExpression(_) => "FunctionExpression",
1329        Expression::ObjectExpression(_) => "ObjectExpression",
1330        Expression::ArrayExpression(_) => "ArrayExpression",
1331        Expression::NewExpression(_) => "NewExpression",
1332        Expression::TemplateLiteral(_) => "TemplateLiteral",
1333        Expression::TaggedTemplateExpression(_) => "TaggedTemplateExpression",
1334        Expression::AwaitExpression(_) => "AwaitExpression",
1335        Expression::YieldExpression(_) => "YieldExpression",
1336        Expression::SpreadElement(_) => "SpreadElement",
1337        Expression::MetaProperty(_) => "MetaProperty",
1338        Expression::ClassExpression(_) => "ClassExpression",
1339        Expression::PrivateName(_) => "PrivateName",
1340        Expression::Super(_) => "Super",
1341        Expression::Import(_) => "Import",
1342        Expression::ThisExpression(_) => "ThisExpression",
1343        Expression::ParenthesizedExpression(_) => "ParenthesizedExpression",
1344        Expression::AssignmentPattern(_) => "AssignmentPattern",
1345        Expression::JSXElement(_) => "JSXElement",
1346        Expression::JSXFragment(_) => "JSXFragment",
1347        Expression::TSAsExpression(_) => "TSAsExpression",
1348        Expression::TSSatisfiesExpression(_) => "TSSatisfiesExpression",
1349        Expression::TSNonNullExpression(_) => "TSNonNullExpression",
1350        Expression::TSTypeAssertion(_) => "TSTypeAssertion",
1351        Expression::TSInstantiationExpression(_) => "TSInstantiationExpression",
1352        Expression::TypeCastExpression(_) => "TypeCastExpression",
1353    }
1354}