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