| 1 | use react_compiler_ast::scope::ScopeInfo; |
| 2 | use react_compiler_ast::statements::FunctionDeclaration; |
| 3 | use react_compiler_hir::InstructionValue; |
| 4 | use react_compiler_hir::environment::Environment; |
| 5 | use react_compiler_lowering::{FunctionNode, lower}; |
| 6 | use serde_json::json; |
| 7 | |
| 8 | /// An unknown statement inside a function body degrades like the other |
| 9 | /// unsupported-statement arms: an UnsupportedSyntax error is recorded and an |
| 10 | /// UnsupportedNode instruction carries the raw node verbatim. |
| 11 | #[test] |
| 12 | fn unknown_statement_in_function_body_records_bailout() { |
| 13 | let unknown_node = json!({ |
| 14 | "type": "TSFutureStatement", |
| 15 | "start": 40, |
| 16 | "end": 52, |
| 17 | "payload": { "type": "Identifier", "name": "x" } |
| 18 | }); |
| 19 | let func: FunctionDeclaration = serde_json::from_value(json!({ |
| 20 | "type": "FunctionDeclaration", |
| 21 | "start": 0, |
| 22 | "end": 60, |
| 23 | "id": { "type": "Identifier", "name": "useValue", "start": 9, "end": 17 }, |
| 24 | "generator": false, |
| 25 | "async": false, |
| 26 | "params": [], |
| 27 | "body": { |
| 28 | "type": "BlockStatement", |
| 29 | "start": 20, |
| 30 | "end": 60, |
| 31 | "body": [unknown_node.clone()], |
| 32 | "directives": [] |
| 33 | } |
| 34 | })) |
| 35 | .unwrap(); |
| 36 | |
| 37 | let scope_info: ScopeInfo = serde_json::from_value(json!({ |
| 38 | "scopes": [ |
| 39 | { "id": 0, "parent": null, "kind": "program", "bindings": { "useValue": 0 } }, |
| 40 | { "id": 1, "parent": 0, "kind": "function", "bindings": {} } |
| 41 | ], |
| 42 | "bindings": [ |
| 43 | { |
| 44 | "id": 0, |
| 45 | "name": "useValue", |
| 46 | "kind": "hoisted", |
| 47 | "scope": 0, |
| 48 | "declarationType": "FunctionDeclaration" |
| 49 | } |
| 50 | ], |
| 51 | "nodeToScope": { "0": 1 }, |
| 52 | "referenceToBinding": {}, |
| 53 | "programScope": 0 |
| 54 | })) |
| 55 | .unwrap(); |
| 56 | |
| 57 | let mut env = Environment::new(); |
| 58 | let result = lower( |
| 59 | &FunctionNode::FunctionDeclaration(&func), |
| 60 | None, |
| 61 | &scope_info, |
| 62 | &mut env, |
| 63 | ); |
| 64 | |
| 65 | assert!( |
| 66 | env.has_errors(), |
| 67 | "expected a recorded error, got result {result:?}" |
| 68 | ); |
| 69 | let rendered = format!("{:?}", env.errors()); |
| 70 | assert!( |
| 71 | rendered.contains("Unsupported statement kind 'TSFutureStatement'"), |
| 72 | "unexpected error payload: {rendered}" |
| 73 | ); |
| 74 | |
| 75 | let hir = result.expect("lowering degrades, it does not fail outright"); |
| 76 | let unsupported = hir |
| 77 | .instructions |
| 78 | .iter() |
| 79 | .find_map(|instr| match &instr.value { |
| 80 | InstructionValue::UnsupportedNode { |
| 81 | node_type, |
| 82 | original_node, |
| 83 | .. |
| 84 | } => Some((node_type.clone(), original_node.clone())), |
| 85 | _ => None, |
| 86 | }) |
| 87 | .expect("expected an UnsupportedNode instruction"); |
| 88 | |
| 89 | assert_eq!(unsupported.0.as_deref(), Some("TSFutureStatement")); |
| 90 | assert_eq!(unsupported.1, Some(unknown_node)); |
| 91 | } |