@samitouri / QOS-React / commits / 9075330979

[compiler] Remove tryRecord, add catch-all error handling, fix remaining throws (#35881)

Remove `tryRecord()` from the compilation pipeline now that all passes record errors directly via `env.recordError()` / `env.recordErrors()`. A single catch-all try/catch in Program.ts provides the safety net for any pass that incorrectly throws instead of recording. Key changes: - Remove all ~64 `env.tryRecord()` wrappers in Pipeline.ts - Delete `tryRecord()` method from Environment.ts - Add `CompileUnexpectedThrow` logger event so thrown errors are detectable - Log `CompileUnexpectedThrow` in Program.ts catch-all for non-invariant throws - Fail snap tests on `CompileUnexpectedThrow` to surface pass bugs in dev - Convert throwTodo/throwDiagnostic calls in HIRBuilder (fbt, this), CodegenReactiveFunction (for-in/for-of), and BuildReactiveFunction to record errors or use invariants as appropriate - Remove try/catch from BuildHIR's lower() since inner throws are now recorded - CollectOptionalChainDependencies: return null instead of throwing on unsupported optional chain patterns (graceful optimization skip) --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/35881). * #35888 * #35884 * #35883 * #35882 * __->__ #35881

Joseph Savona committed Feb 23, 2026 at 16:10 UTC 9075330979bb9eeec7c29b80fbab6048154c8f1f
19 files changed +260 -243
compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Options.ts
+6
@@ -252,6 +252,7 @@ export type LoggerEvent =
252 | CompileErrorEvent
253 | CompileDiagnosticEvent
254 | CompileSkipEvent
255 + | CompileUnexpectedThrowEvent
256 | PipelineErrorEvent
257 | TimingEvent;
258
@@ -286,6 +287,11 @@ export type PipelineErrorEvent = {
287 fnLoc: t.SourceLocation | null;
288 data: string;
289 };
290 +export type CompileUnexpectedThrowEvent = {
291 + kind: 'CompileUnexpectedThrow';
292 + fnLoc: t.SourceLocation | null;
293 + data: string;
294 +};
295 export type TimingEvent = {
296 kind: 'Timing';
297 measurement: PerformanceMeasure;
compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Pipeline.ts
+19 -50
@@ -13,7 +13,6 @@ import {CompilerError} from '../CompilerError';
13 import {Err, Ok, Result} from '../Utils/Result';
14 import {
15 HIRFunction,
16 - IdentifierId,
16 ReactiveFunction,
17 assertConsistentIdentifiers,
18 assertTerminalPredsExist,
@@ -161,12 +160,8 @@ function runWithEnvironment(
160 pruneMaybeThrows(hir);
161 log({kind: 'hir', name: 'PruneMaybeThrows', value: hir});
162
164 - env.tryRecord(() => {
165 - validateContextVariableLValues(hir);
166 - });
167 - env.tryRecord(() => {
168 - validateUseMemo(hir);
169 - });
163 + validateContextVariableLValues(hir);
164 + validateUseMemo(hir);
165
166 if (env.enableDropManualMemoization) {
167 dropManualMemoization(hir);
@@ -202,14 +197,10 @@ function runWithEnvironment(
197
198 if (env.enableValidations) {
199 if (env.config.validateHooksUsage) {
205 - env.tryRecord(() => {
206 - validateHooksUsage(hir);
207 - });
200 + validateHooksUsage(hir);
201 }
202 if (env.config.validateNoCapitalizedCalls) {
210 - env.tryRecord(() => {
211 - validateNoCapitalizedCalls(hir);
212 - });
203 + validateNoCapitalizedCalls(hir);
204 }
205 }
206
@@ -219,9 +210,7 @@ function runWithEnvironment(
210 analyseFunctions(hir);
211 log({kind: 'hir', name: 'AnalyseFunctions', value: hir});
212
222 - env.tryRecord(() => {
223 - inferMutationAliasingEffects(hir);
224 - });
213 + inferMutationAliasingEffects(hir);
214 log({kind: 'hir', name: 'InferMutationAliasingEffects', value: hir});
215
216 if (env.outputMode === 'ssr') {
@@ -235,31 +224,23 @@ function runWithEnvironment(
224 pruneMaybeThrows(hir);
225 log({kind: 'hir', name: 'PruneMaybeThrows', value: hir});
226
238 - env.tryRecord(() => {
239 - inferMutationAliasingRanges(hir, {
240 - isFunctionExpression: false,
241 - });
227 + inferMutationAliasingRanges(hir, {
228 + isFunctionExpression: false,
229 });
230 log({kind: 'hir', name: 'InferMutationAliasingRanges', value: hir});
231 if (env.enableValidations) {
245 - env.tryRecord(() => {
246 - validateLocalsNotReassignedAfterRender(hir);
247 - });
232 + validateLocalsNotReassignedAfterRender(hir);
233
234 if (env.config.assertValidMutableRanges) {
235 assertValidMutableRanges(hir);
236 }
237
238 if (env.config.validateRefAccessDuringRender) {
254 - env.tryRecord(() => {
255 - validateNoRefAccessInRender(hir);
256 - });
239 + validateNoRefAccessInRender(hir);
240 }
241
242 if (env.config.validateNoSetStateInRender) {
260 - env.tryRecord(() => {
261 - validateNoSetStateInRender(hir);
262 - });
243 + validateNoSetStateInRender(hir);
244 }
245
246 if (
@@ -268,9 +249,7 @@ function runWithEnvironment(
249 ) {
250 env.logErrors(validateNoDerivedComputationsInEffects_exp(hir));
251 } else if (env.config.validateNoDerivedComputationsInEffects) {
271 - env.tryRecord(() => {
272 - validateNoDerivedComputationsInEffects(hir);
273 - });
252 + validateNoDerivedComputationsInEffects(hir);
253 }
254
255 if (env.config.validateNoSetStateInEffects && env.outputMode === 'lint') {
@@ -281,9 +260,7 @@ function runWithEnvironment(
260 env.logErrors(validateNoJSXInTryStatement(hir));
261 }
262
284 - env.tryRecord(() => {
285 - validateNoFreezingKnownMutableFunctions(hir);
286 - });
263 + validateNoFreezingKnownMutableFunctions(hir);
264 }
265
266 inferReactivePlaces(hir);
@@ -295,9 +272,7 @@ function runWithEnvironment(
272 env.config.validateExhaustiveEffectDependencies
273 ) {
274 // NOTE: this relies on reactivity inference running first
298 - env.tryRecord(() => {
299 - validateExhaustiveDependencies(hir);
300 - });
275 + validateExhaustiveDependencies(hir);
276 }
277 }
278
@@ -326,8 +301,7 @@ function runWithEnvironment(
301 log({kind: 'hir', name: 'InferReactiveScopeVariables', value: hir});
302 }
303
329 - let fbtOperands: Set<IdentifierId> = new Set();
330 - fbtOperands = memoizeFbtAndMacroOperandsInSameScope(hir);
304 + const fbtOperands = memoizeFbtAndMacroOperandsInSameScope(hir);
305 log({
306 kind: 'hir',
307 name: 'MemoizeFbtAndMacroOperandsInSameScope',
@@ -412,6 +386,7 @@ function runWithEnvironment(
386 });
387 assertTerminalSuccessorsExist(hir);
388 assertTerminalPredsExist(hir);
389 +
390 propagateScopeDependenciesHIR(hir);
391 log({
392 kind: 'hir',
@@ -419,8 +394,7 @@ function runWithEnvironment(
394 value: hir,
395 });
396
422 - let reactiveFunction!: ReactiveFunction;
423 - reactiveFunction = buildReactiveFunction(hir);
397 + const reactiveFunction = buildReactiveFunction(hir);
398 log({
399 kind: 'reactive',
400 name: 'BuildReactiveFunction',
@@ -507,8 +481,7 @@ function runWithEnvironment(
481 value: reactiveFunction,
482 });
483
510 - let uniqueIdentifiers: Set<string> = new Set();
511 - uniqueIdentifiers = renameVariables(reactiveFunction);
484 + const uniqueIdentifiers = renameVariables(reactiveFunction);
485 log({
486 kind: 'reactive',
487 name: 'RenameVariables',
@@ -526,9 +499,7 @@ function runWithEnvironment(
499 env.config.enablePreserveExistingMemoizationGuarantees ||
500 env.config.validatePreserveExistingMemoizationGuarantees
501 ) {
529 - env.tryRecord(() => {
530 - validatePreservedManualMemoization(reactiveFunction);
531 - });
502 + validatePreservedManualMemoization(reactiveFunction);
503 }
504
505 const ast = codegenFunction(reactiveFunction, {
@@ -541,9 +512,7 @@ function runWithEnvironment(
512 }
513
514 if (env.config.validateSourceLocations) {
544 - env.tryRecord(() => {
545 - validateSourceLocations(func, ast, env);
546 - });
515 + validateSourceLocations(func, ast, env);
516 }
517
518 /**
compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Program.ts
+14
@@ -713,6 +713,20 @@ function tryCompileFunction(
713 return {kind: 'error', error: result.unwrapErr()};
714 }
715 } catch (err) {
716 + /**
717 + * A pass incorrectly threw instead of recording the error.
718 + * Log for detection in development.
719 + */
720 + if (
721 + err instanceof CompilerError &&
722 + err.details.every(detail => detail.category !== ErrorCategory.Invariant)
723 + ) {
724 + programContext.logEvent({
725 + kind: 'CompileUnexpectedThrow',
726 + fnLoc: fn.node.loc ?? null,
727 + data: err.toString(),
728 + });
729 + }
730 return {kind: 'error', error: err};
731 }
732 }
compiler/packages/babel-plugin-react-compiler/src/HIR/BuildHIR.ts
+26 -41
@@ -185,47 +185,32 @@ export function lower(
185
186 let directives: Array<string> = [];
187 const body = func.get('body');
188 - try {
189 - if (body.isExpression()) {
190 - const fallthrough = builder.reserve('block');
191 - const terminal: ReturnTerminal = {
192 - kind: 'return',
193 - returnVariant: 'Implicit',
194 - loc: GeneratedSource,
195 - value: lowerExpressionToTemporary(builder, body),
196 - id: makeInstructionId(0),
197 - effects: null,
198 - };
199 - builder.terminateWithContinuation(terminal, fallthrough);
200 - } else if (body.isBlockStatement()) {
201 - lowerStatement(builder, body);
202 - directives = body.get('directives').map(d => d.node.value.value);
203 - } else {
204 - builder.errors.pushDiagnostic(
205 - CompilerDiagnostic.create({
206 - category: ErrorCategory.Syntax,
207 - reason: `Unexpected function body kind`,
208 - description: `Expected function body to be an expression or a block statement, got \`${body.type}\``,
209 - }).withDetails({
210 - kind: 'error',
211 - loc: body.node.loc ?? null,
212 - message: 'Expected a block statement or expression',
213 - }),
214 - );
215 - }
216 - } catch (err) {
217 - if (err instanceof CompilerError) {
218 - // Re-throw invariant errors immediately
219 - for (const detail of err.details) {
220 - if (detail.category === ErrorCategory.Invariant) {
221 - throw err;
222 - }
223 - }
224 - // Record non-invariant errors and continue to produce partial HIR
225 - builder.errors.merge(err);
226 - } else {
227 - throw err;
228 - }
188 + if (body.isExpression()) {
189 + const fallthrough = builder.reserve('block');
190 + const terminal: ReturnTerminal = {
191 + kind: 'return',
192 + returnVariant: 'Implicit',
193 + loc: GeneratedSource,
194 + value: lowerExpressionToTemporary(builder, body),
195 + id: makeInstructionId(0),
196 + effects: null,
197 + };
198 + builder.terminateWithContinuation(terminal, fallthrough);
199 + } else if (body.isBlockStatement()) {
200 + lowerStatement(builder, body);
201 + directives = body.get('directives').map(d => d.node.value.value);
202 + } else {
203 + builder.errors.pushDiagnostic(
204 + CompilerDiagnostic.create({
205 + category: ErrorCategory.Syntax,
206 + reason: `Unexpected function body kind`,
207 + description: `Expected function body to be an expression or a block statement, got \`${body.type}\``,
208 + }).withDetails({
209 + kind: 'error',
210 + loc: body.node.loc ?? null,
211 + message: 'Expected a block statement or expression',
212 + }),
213 + );
214 }
215
216 let validatedId: HIRFunction['id'] = null;
compiler/packages/babel-plugin-react-compiler/src/HIR/CollectOptionalChainDependencies.ts
+7 -10
@@ -310,16 +310,13 @@ function traverseOptionalBlock(
310 * - a optional base block with a separate nested optional-chain (e.g. a(c?.d)?.d)
311 */
312 const testBlock = context.blocks.get(maybeTest.terminal.fallthrough)!;
313 - if (testBlock!.terminal.kind !== 'branch') {
314 - /**
315 - * Fallthrough of the inner optional should be a block with no
316 - * instructions, terminating with Test($<temporary written to from
317 - * StoreLocal>)
318 - */
319 - CompilerError.throwTodo({
320 - reason: `Unexpected terminal kind \`${testBlock.terminal.kind}\` for optional fallthrough block`,
321 - loc: maybeTest.terminal.loc,
322 - });
313 + /**
314 + * Fallthrough of the inner optional should be a block with no
315 + * instructions, terminating with Test($<temporary written to from
316 + * StoreLocal>)
317 + */
318 + if (testBlock.terminal.kind !== 'branch') {
319 + return null;
320 }
321 /**
322 * Recurse into inner optional blocks to collect inner optional-chain
compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts
-23
@@ -759,29 +759,6 @@ export class Environment {
759 return this.#errors;
760 }
761
762 - /**
763 - * Wraps a callback in try/catch: if the callback throws a CompilerError
764 - * that is NOT an invariant, the error is recorded and execution continues.
765 - * Non-CompilerError exceptions and invariants are re-thrown.
766 - */
767 - tryRecord(fn: () => void): void {
768 - try {
769 - fn();
770 - } catch (err) {
771 - if (err instanceof CompilerError) {
772 - // Check if any detail is an invariant — if so, re-throw
773 - for (const detail of err.details) {
774 - if (detail.category === ErrorCategory.Invariant) {
775 - throw err;
776 - }
777 - }
778 - this.recordErrors(err);
779 - } else {
780 - throw err;
781 - }
782 - }
783 - }
784 -
762 isContextIdentifier(node: t.Identifier): boolean {
763 return this.#contextIdentifiers.has(node);
764 }
compiler/packages/babel-plugin-react-compiler/src/HIR/HIRBuilder.ts
+6 -16
@@ -308,33 +308,23 @@ export default class HIRBuilder {
308
309 resolveBinding(node: t.Identifier): Identifier {
310 if (node.name === 'fbt') {
311 - CompilerError.throwDiagnostic({
311 + this.errors.push({
312 category: ErrorCategory.Todo,
313 reason: 'Support local variables named `fbt`',
314 description:
315 'Local variables named `fbt` may conflict with the fbt plugin and are not yet supported',
316 - details: [
317 - {
318 - kind: 'error',
319 - message: 'Rename to avoid conflict with fbt plugin',
320 - loc: node.loc ?? GeneratedSource,
321 - },
322 - ],
316 + loc: node.loc ?? GeneratedSource,
317 + suggestions: null,
318 });
319 }
320 if (node.name === 'this') {
326 - CompilerError.throwDiagnostic({
321 + this.errors.push({
322 category: ErrorCategory.UnsupportedSyntax,
323 reason: '`this` is not supported syntax',
324 description:
325 'React Compiler does not support compiling functions that use `this`',
331 - details: [
332 - {
333 - kind: 'error',
334 - message: '`this` was used here',
335 - loc: node.loc ?? GeneratedSource,
336 - },
337 - ],
326 + loc: node.loc ?? GeneratedSource,
327 + suggestions: null,
328 });
329 }
330 const originalName = node.name;
compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/BuildReactiveFunction.ts
+3 -4
@@ -1007,11 +1007,10 @@ class Driver {
1007 const test = this.visitValueBlock(testBlockId, loc);
1008 const testBlock = this.cx.ir.blocks.get(test.block)!;
1009 if (testBlock.terminal.kind !== 'branch') {
1010 - CompilerError.throwTodo({
1011 - reason: `Unexpected terminal kind \`${testBlock.terminal.kind}\` for ${terminalKind} test block`,
1012 - description: null,
1010 + CompilerError.invariant(false, {
1011 + reason: `Expected a branch terminal for ${terminalKind} test block`,
1012 + description: `Got \`${testBlock.terminal.kind}\``,
1013 loc: testBlock.terminal.loc,
1014 - suggestions: null,
1014 });
1015 }
1016 return {
compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/CodegenReactiveFunction.ts
+12 -8
@@ -775,12 +775,13 @@ function codegenTerminal(
775 loc: terminal.init.loc,
776 });
777 if (terminal.init.instructions.length !== 2) {
778 - CompilerError.throwTodo({
778 + cx.errors.push({
779 reason: 'Support non-trivial for..in inits',
780 - description: null,
780 + category: ErrorCategory.Todo,
781 loc: terminal.init.loc,
782 suggestions: null,
783 });
784 + return t.emptyStatement();
785 }
786 const iterableCollection = terminal.init.instructions[0];
787 const iterableItem = terminal.init.instructions[1];
@@ -795,12 +796,13 @@ function codegenTerminal(
796 break;
797 }
798 case 'StoreContext': {
798 - CompilerError.throwTodo({
799 + cx.errors.push({
800 reason: 'Support non-trivial for..in inits',
800 - description: null,
801 + category: ErrorCategory.Todo,
802 loc: terminal.init.loc,
803 suggestions: null,
804 });
805 + return t.emptyStatement();
806 }
807 default:
808 CompilerError.invariant(false, {
@@ -870,12 +872,13 @@ function codegenTerminal(
872 loc: terminal.test.loc,
873 });
874 if (terminal.test.instructions.length !== 2) {
873 - CompilerError.throwTodo({
875 + cx.errors.push({
876 reason: 'Support non-trivial for..of inits',
875 - description: null,
877 + category: ErrorCategory.Todo,
878 loc: terminal.init.loc,
879 suggestions: null,
880 });
881 + return t.emptyStatement();
882 }
883 const iterableItem = terminal.test.instructions[1];
884 let lval: t.LVal;
@@ -889,12 +892,13 @@ function codegenTerminal(
892 break;
893 }
894 case 'StoreContext': {
892 - CompilerError.throwTodo({
895 + cx.errors.push({
896 reason: 'Support non-trivial for..of inits',
894 - description: null,
897 + category: ErrorCategory.Todo,
898 loc: terminal.init.loc,
899 suggestions: null,
900 });
901 + return t.emptyStatement();
902 }
903 default:
904 CompilerError.invariant(false, {
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/ecma/error.reserved-words.expect.md
+2 -2
@@ -24,9 +24,9 @@ function useThing(fn) {
24 ```
25 Found 1 error:
26
27 -Invariant: [HIRBuilder] Unexpected null block
27 +Error: Expected a non-reserved identifier name
28
29 -expected block 0 to exist.
29 +`this` is a reserved word in JavaScript and cannot be used as an identifier name.
30 ```
31
32
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-optional-call-chain-in-optional.expect.md deleted
-39
@@ -1,39 +0,0 @@
1 -
2 -## Input
3 -
4 -```javascript
5 -function useFoo(props: {value: {x: string; y: string} | null}) {
6 - const value = props.value;
7 - return createArray(value?.x, value?.y)?.join(', ');
8 -}
9 -
10 -function createArray<T>(...args: Array<T>): Array<T> {
11 - return args;
12 -}
13 -
14 -export const FIXTURE_ENTRYPONT = {
15 - fn: useFoo,
16 - props: [{value: null}],
17 -};
18 -
19 -```
20 -
21 -
22 -## Error
23 -
24 -```
25 -Found 1 error:
26 -
27 -Todo: Unexpected terminal kind `optional` for optional fallthrough block
28 -
29 -error.todo-optional-call-chain-in-optional.ts:3:21
30 - 1 | function useFoo(props: {value: {x: string; y: string} | null}) {
31 - 2 | const value = props.value;
32 -> 3 | return createArray(value?.x, value?.y)?.join(', ');
33 - | ^^^^^^^^ Unexpected terminal kind `optional` for optional fallthrough block
34 - 4 | }
35 - 5 |
36 - 6 | function createArray<T>(...args: Array<T>): Array<T> {
37 -```
38 -
39 -
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/fbt/error.todo-fbt-as-local.expect.md
+41 -2
@@ -50,7 +50,7 @@ export const FIXTURE_ENTRYPOINT = {
50 ## Error
51
52 ```
53 -Found 1 error:
53 +Found 4 errors:
54
55 Todo: Support local variables named `fbt`
56
@@ -60,10 +60,49 @@ error.todo-fbt-as-local.ts:18:19
60 16 |
61 17 | function Foo(props) {
62 > 18 | const getText1 = fbt =>
63 - | ^^^ Rename to avoid conflict with fbt plugin
63 + | ^^^ Support local variables named `fbt`
64 19 | fbt(
65 20 | `Hello, ${fbt.param('(key) name', identity(props.name))}!`,
66 21 | '(description) Greeting'
67 +
68 +Todo: Support local variables named `fbt`
69 +
70 +Local variables named `fbt` may conflict with the fbt plugin and are not yet supported.
71 +
72 +error.todo-fbt-as-local.ts:18:19
73 + 16 |
74 + 17 | function Foo(props) {
75 +> 18 | const getText1 = fbt =>
76 + | ^^^ Support local variables named `fbt`
77 + 19 | fbt(
78 + 20 | `Hello, ${fbt.param('(key) name', identity(props.name))}!`,
79 + 21 | '(description) Greeting'
80 +
81 +Todo: Support local variables named `fbt`
82 +
83 +Local variables named `fbt` may conflict with the fbt plugin and are not yet supported.
84 +
85 +error.todo-fbt-as-local.ts:18:19
86 + 16 |
87 + 17 | function Foo(props) {
88 +> 18 | const getText1 = fbt =>
89 + | ^^^ Support local variables named `fbt`
90 + 19 | fbt(
91 + 20 | `Hello, ${fbt.param('(key) name', identity(props.name))}!`,
92 + 21 | '(description) Greeting'
93 +
94 +Todo: Support local variables named `fbt`
95 +
96 +Local variables named `fbt` may conflict with the fbt plugin and are not yet supported.
97 +
98 +error.todo-fbt-as-local.ts:24:19
99 + 22 | );
100 + 23 |
101 +> 24 | const getText2 = fbt =>
102 + | ^^^ Support local variables named `fbt`
103 + 25 | fbt(
104 + 26 | `Goodbye, ${fbt.param('(key) name', identity(props.name))}!`,
105 + 27 | '(description) Greeting2'
106 ```
107
108
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/fbt/error.todo-locally-require-fbt.expect.md
+6 -8
@@ -16,17 +16,15 @@ function Component(props) {
16 ```
17 Found 1 error:
18
19 -Todo: Support local variables named `fbt`
19 +Invariant: <fbt> tags should be module-level imports
20
21 -Local variables named `fbt` may conflict with the fbt plugin and are not yet supported.
22 -
23 -error.todo-locally-require-fbt.ts:2:8
24 - 1 | function Component(props) {
25 -> 2 | const fbt = require('fbt');
26 - | ^^^ Rename to avoid conflict with fbt plugin
21 +error.todo-locally-require-fbt.ts:4:10
22 + 2 | const fbt = require('fbt');
23 3 |
28 - 4 | return <fbt desc="Description">{'Text'}</fbt>;
24 +> 4 | return <fbt desc="Description">{'Text'}</fbt>;
25 + | ^^^ <fbt> tags should be module-level imports
26 5 | }
27 + 6 |
28 ```
29
30
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/propagate-scope-deps-hir-fork/error.todo-optional-call-chain-in-optional.expect.md deleted
-40
@@ -1,40 +0,0 @@
1 -
2 -## Input
3 -
4 -```javascript
5 -// @enablePropagateDepsInHIR
6 -function useFoo(props: {value: {x: string; y: string} | null}) {
7 - const value = props.value;
8 - return createArray(value?.x, value?.y)?.join(', ');
9 -}
10 -
11 -function createArray<T>(...args: Array<T>): Array<T> {
12 - return args;
13 -}
14 -
15 -export const FIXTURE_ENTRYPONT = {
16 - fn: useFoo,
17 - props: [{value: null}],
18 -};
19 -
20 -```
21 -
22 -
23 -## Error
24 -
25 -```
26 -Found 1 error:
27 -
28 -Todo: Unexpected terminal kind `optional` for optional fallthrough block
29 -
30 -error.todo-optional-call-chain-in-optional.ts:4:21
31 - 2 | function useFoo(props: {value: {x: string; y: string} | null}) {
32 - 3 | const value = props.value;
33 -> 4 | return createArray(value?.x, value?.y)?.join(', ');
34 - | ^^^^^^^^ Unexpected terminal kind `optional` for optional fallthrough block
35 - 5 | }
36 - 6 |
37 - 7 | function createArray<T>(...args: Array<T>): Array<T> {
38 -```
39 -
40 -
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/propagate-scope-deps-hir-fork/todo-optional-call-chain-in-optional.expect.md new
+54
@@ -0,0 +1,54 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +// @enablePropagateDepsInHIR
6 +function useFoo(props: {value: {x: string; y: string} | null}) {
7 + const value = props.value;
8 + return createArray(value?.x, value?.y)?.join(', ');
9 +}
10 +
11 +function createArray<T>(...args: Array<T>): Array<T> {
12 + return args;
13 +}
14 +
15 +export const FIXTURE_ENTRYPONT = {
16 + fn: useFoo,
17 + props: [{value: null}],
18 +};
19 +
20 +```
21 +
22 +## Code
23 +
24 +```javascript
25 +import { c as _c } from "react/compiler-runtime"; // @enablePropagateDepsInHIR
26 +function useFoo(props) {
27 + const $ = _c(3);
28 + const value = props.value;
29 + let t0;
30 + if ($[0] !== value?.x || $[1] !== value?.y) {
31 + t0 = createArray(value?.x, value?.y)?.join(", ");
32 + $[0] = value?.x;
33 + $[1] = value?.y;
34 + $[2] = t0;
35 + } else {
36 + t0 = $[2];
37 + }
38 + return t0;
39 +}
40 +
41 +function createArray(...t0) {
42 + const args = t0;
43 + return args;
44 +}
45 +
46 +export const FIXTURE_ENTRYPONT = {
47 + fn: useFoo,
48 + props: [{ value: null }],
49 +};
50 +
51 +```
52 +
53 +### Eval output
54 +(kind: exception) Fixture not implemented
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/propagate-scope-deps-hir-fork/todo-optional-call-chain-in-optional.ts renamed
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/todo-optional-call-chain-in-optional.expect.md new
+53
@@ -0,0 +1,53 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +function useFoo(props: {value: {x: string; y: string} | null}) {
6 + const value = props.value;
7 + return createArray(value?.x, value?.y)?.join(', ');
8 +}
9 +
10 +function createArray<T>(...args: Array<T>): Array<T> {
11 + return args;
12 +}
13 +
14 +export const FIXTURE_ENTRYPONT = {
15 + fn: useFoo,
16 + props: [{value: null}],
17 +};
18 +
19 +```
20 +
21 +## Code
22 +
23 +```javascript
24 +import { c as _c } from "react/compiler-runtime";
25 +function useFoo(props) {
26 + const $ = _c(3);
27 + const value = props.value;
28 + let t0;
29 + if ($[0] !== value?.x || $[1] !== value?.y) {
30 + t0 = createArray(value?.x, value?.y)?.join(", ");
31 + $[0] = value?.x;
32 + $[1] = value?.y;
33 + $[2] = t0;
34 + } else {
35 + t0 = $[2];
36 + }
37 + return t0;
38 +}
39 +
40 +function createArray(...t0) {
41 + const args = t0;
42 + return args;
43 +}
44 +
45 +export const FIXTURE_ENTRYPONT = {
46 + fn: useFoo,
47 + props: [{ value: null }],
48 +};
49 +
50 +```
51 +
52 +### Eval output
53 +(kind: exception) Fixture not implemented
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/todo-optional-call-chain-in-optional.ts renamed
compiler/packages/snap/src/compiler.ts
+11
@@ -378,6 +378,17 @@ export async function transformFixtureInput(
378 msg: 'Expected nothing to be compiled (from `// @expectNothingCompiled`), but some functions compiled or errored',
379 };
380 }
381 + const unexpectedThrows = logs.filter(
382 + log => log.event.kind === 'CompileUnexpectedThrow',
383 + );
384 + if (unexpectedThrows.length > 0) {
385 + return {
386 + kind: 'err',
387 + msg:
388 + `Compiler pass(es) threw instead of recording errors:\n` +
389 + unexpectedThrows.map(l => (l.event as any).data).join('\n'),
390 + };
391 + }
392 return {
393 kind: 'ok',
394 value: {