From 61633098fa458516ce5d052dce46e15369e13a94 Mon Sep 17 00:00:00 2001 From: Joe Savona Date: Fri, 20 Feb 2026 15:59:36 -0800 Subject: [PATCH] [compiler] Remove tryRecord, add catch-all error handling, fix remaining throws 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) --- .../src/Entrypoint/Options.ts | 6 + .../src/Entrypoint/Pipeline.ts | 263 +++++------------- .../src/Entrypoint/Program.ts | 29 +- .../src/HIR/BuildHIR.ts | 67 ++--- .../HIR/CollectOptionalChainDependencies.ts | 17 +- .../src/HIR/Environment.ts | 23 -- .../src/HIR/HIRBuilder.ts | 22 +- .../ReactiveScopes/BuildReactiveFunction.ts | 7 +- .../ReactiveScopes/CodegenReactiveFunction.ts | 20 +- .../ecma/error.reserved-words.expect.md | 4 +- ...rror.dont-hoist-inline-reference.expect.md | 8 +- ...-optional-call-chain-in-optional.expect.md | 39 --- .../fbt/error.todo-fbt-as-local.expect.md | 43 ++- .../error.todo-locally-require-fbt.expect.md | 14 +- ...-optional-call-chain-in-optional.expect.md | 40 --- ...-optional-call-chain-in-optional.expect.md | 54 ++++ ...> todo-optional-call-chain-in-optional.ts} | 0 ...-optional-call-chain-in-optional.expect.md | 53 ++++ ...> todo-optional-call-chain-in-optional.ts} | 0 ...ror.invalid-mix-fire-and-no-fire.expect.md | 6 +- .../error.invalid-multiple-args.expect.md | 6 +- .../error.invalid-not-call.expect.md | 6 +- .../error.invalid-spread.expect.md | 6 +- .../error.todo-method.expect.md | 6 +- compiler/packages/snap/src/compiler.ts | 11 + 25 files changed, 340 insertions(+), 410 deletions(-) delete mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-optional-call-chain-in-optional.expect.md delete mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/propagate-scope-deps-hir-fork/error.todo-optional-call-chain-in-optional.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/propagate-scope-deps-hir-fork/todo-optional-call-chain-in-optional.expect.md rename compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/propagate-scope-deps-hir-fork/{error.todo-optional-call-chain-in-optional.ts => todo-optional-call-chain-in-optional.ts} (100%) create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/todo-optional-call-chain-in-optional.expect.md rename compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/{error.todo-optional-call-chain-in-optional.ts => todo-optional-call-chain-in-optional.ts} (100%) diff --git a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Options.ts b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Options.ts index b758d7b024..5d862a17c2 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Options.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Options.ts @@ -254,6 +254,7 @@ export type LoggerEvent = | CompileErrorEvent | CompileDiagnosticEvent | CompileSkipEvent + | CompileUnexpectedThrowEvent | PipelineErrorEvent | TimingEvent | AutoDepsDecorationsEvent @@ -290,6 +291,11 @@ export type PipelineErrorEvent = { fnLoc: t.SourceLocation | null; data: string; }; +export type CompileUnexpectedThrowEvent = { + kind: 'CompileUnexpectedThrow'; + fnLoc: t.SourceLocation | null; + data: string; +}; export type TimingEvent = { kind: 'Timing'; measurement: PerformanceMeasure; diff --git a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Pipeline.ts b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Pipeline.ts index c3509fc24b..22cf8a93e3 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Pipeline.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Pipeline.ts @@ -13,7 +13,6 @@ import {CompilerError} from '../CompilerError'; import {Err, Ok, Result} from '../Utils/Result'; import { HIRFunction, - IdentifierId, ReactiveFunction, assertConsistentIdentifiers, assertTerminalPredsExist, @@ -166,17 +165,11 @@ function runWithEnvironment( const hir = lower(func, env); log({kind: 'hir', name: 'HIR', value: hir}); - env.tryRecord(() => { - pruneMaybeThrows(hir); - }); + pruneMaybeThrows(hir); log({kind: 'hir', name: 'PruneMaybeThrows', value: hir}); - env.tryRecord(() => { - validateContextVariableLValues(hir); - }); - env.tryRecord(() => { - validateUseMemo(hir); - }); + validateContextVariableLValues(hir); + validateUseMemo(hir); if ( env.enableDropManualMemoization && @@ -188,136 +181,94 @@ function runWithEnvironment( log({kind: 'hir', name: 'DropManualMemoization', value: hir}); } - env.tryRecord(() => { - inlineImmediatelyInvokedFunctionExpressions(hir); - }); + inlineImmediatelyInvokedFunctionExpressions(hir); log({ kind: 'hir', name: 'InlineImmediatelyInvokedFunctionExpressions', value: hir, }); - env.tryRecord(() => { - mergeConsecutiveBlocks(hir); - }); + mergeConsecutiveBlocks(hir); log({kind: 'hir', name: 'MergeConsecutiveBlocks', value: hir}); assertConsistentIdentifiers(hir); assertTerminalSuccessorsExist(hir); - env.tryRecord(() => { - enterSSA(hir); - }); + enterSSA(hir); log({kind: 'hir', name: 'SSA', value: hir}); - env.tryRecord(() => { - eliminateRedundantPhi(hir); - }); + eliminateRedundantPhi(hir); log({kind: 'hir', name: 'EliminateRedundantPhi', value: hir}); assertConsistentIdentifiers(hir); - env.tryRecord(() => { - constantPropagation(hir); - }); + constantPropagation(hir); log({kind: 'hir', name: 'ConstantPropagation', value: hir}); - env.tryRecord(() => { - inferTypes(hir); - }); + inferTypes(hir); log({kind: 'hir', name: 'InferTypes', value: hir}); if (env.enableValidations) { if (env.config.validateHooksUsage) { - env.tryRecord(() => { - validateHooksUsage(hir); - }); + validateHooksUsage(hir); } if (env.config.validateNoCapitalizedCalls) { - env.tryRecord(() => { - validateNoCapitalizedCalls(hir); - }); + validateNoCapitalizedCalls(hir); } } if (env.config.enableFire) { - env.tryRecord(() => { - transformFire(hir); - }); + transformFire(hir); log({kind: 'hir', name: 'TransformFire', value: hir}); } if (env.config.lowerContextAccess) { - env.tryRecord(() => { - lowerContextAccess(hir, env.config.lowerContextAccess!); - }); + lowerContextAccess(hir, env.config.lowerContextAccess!); } - env.tryRecord(() => { - optimizePropsMethodCalls(hir); - }); + optimizePropsMethodCalls(hir); log({kind: 'hir', name: 'OptimizePropsMethodCalls', value: hir}); - env.tryRecord(() => { - analyseFunctions(hir); - }); + analyseFunctions(hir); log({kind: 'hir', name: 'AnalyseFunctions', value: hir}); - env.tryRecord(() => { - inferMutationAliasingEffects(hir); - }); + inferMutationAliasingEffects(hir); log({kind: 'hir', name: 'InferMutationAliasingEffects', value: hir}); if (env.outputMode === 'ssr') { - env.tryRecord(() => { - optimizeForSSR(hir); - }); + optimizeForSSR(hir); log({kind: 'hir', name: 'OptimizeForSSR', value: hir}); } // Note: Has to come after infer reference effects because "dead" code may still affect inference - env.tryRecord(() => { - deadCodeElimination(hir); - }); + deadCodeElimination(hir); log({kind: 'hir', name: 'DeadCodeElimination', value: hir}); if (env.config.enableInstructionReordering) { - env.tryRecord(() => { - instructionReordering(hir); - }); + instructionReordering(hir); log({kind: 'hir', name: 'InstructionReordering', value: hir}); } - env.tryRecord(() => { - pruneMaybeThrows(hir); - }); + pruneMaybeThrows(hir); log({kind: 'hir', name: 'PruneMaybeThrows', value: hir}); - env.tryRecord(() => { - inferMutationAliasingRanges(hir, { - isFunctionExpression: false, - }); + inferMutationAliasingRanges(hir, { + isFunctionExpression: false, }); log({kind: 'hir', name: 'InferMutationAliasingRanges', value: hir}); if (env.enableValidations) { - env.tryRecord(() => { - validateLocalsNotReassignedAfterRender(hir); - }); + validateLocalsNotReassignedAfterRender(hir); if (env.config.assertValidMutableRanges) { assertValidMutableRanges(hir); } if (env.config.validateRefAccessDuringRender) { - env.tryRecord(() => { - validateNoRefAccessInRender(hir); - }); + validateNoRefAccessInRender(hir); } if (env.config.validateNoSetStateInRender) { - env.tryRecord(() => { - validateNoSetStateInRender(hir); - }); + validateNoSetStateInRender(hir); } if ( @@ -326,9 +277,7 @@ function runWithEnvironment( ) { env.logErrors(validateNoDerivedComputationsInEffects_exp(hir)); } else if (env.config.validateNoDerivedComputationsInEffects) { - env.tryRecord(() => { - validateNoDerivedComputationsInEffects(hir); - }); + validateNoDerivedComputationsInEffects(hir); } if (env.config.validateNoSetStateInEffects && env.outputMode === 'lint') { @@ -340,19 +289,13 @@ function runWithEnvironment( } if (env.config.validateNoImpureFunctionsInRender) { - env.tryRecord(() => { - validateNoImpureFunctionsInRender(hir); - }); + validateNoImpureFunctionsInRender(hir); } - env.tryRecord(() => { - validateNoFreezingKnownMutableFunctions(hir); - }); + validateNoFreezingKnownMutableFunctions(hir); } - env.tryRecord(() => { - inferReactivePlaces(hir); - }); + inferReactivePlaces(hir); log({kind: 'hir', name: 'InferReactivePlaces', value: hir}); if (env.enableValidations) { @@ -361,15 +304,11 @@ function runWithEnvironment( env.config.validateExhaustiveEffectDependencies ) { // NOTE: this relies on reactivity inference running first - env.tryRecord(() => { - validateExhaustiveDependencies(hir); - }); + validateExhaustiveDependencies(hir); } } - env.tryRecord(() => { - rewriteInstructionKindsBasedOnReassignment(hir); - }); + rewriteInstructionKindsBasedOnReassignment(hir); log({ kind: 'hir', name: 'RewriteInstructionKindsBasedOnReassignment', @@ -390,16 +329,11 @@ function runWithEnvironment( * if inferred memoization is enabled. This makes all later passes which * transform reactive-scope labeled instructions no-ops. */ - env.tryRecord(() => { - inferReactiveScopeVariables(hir); - }); + inferReactiveScopeVariables(hir); log({kind: 'hir', name: 'InferReactiveScopeVariables', value: hir}); } - let fbtOperands: Set = new Set(); - env.tryRecord(() => { - fbtOperands = memoizeFbtAndMacroOperandsInSameScope(hir); - }); + const fbtOperands = memoizeFbtAndMacroOperandsInSameScope(hir); log({ kind: 'hir', name: 'MemoizeFbtAndMacroOperandsInSameScope', @@ -407,15 +341,11 @@ function runWithEnvironment( }); if (env.config.enableJsxOutlining) { - env.tryRecord(() => { - outlineJSX(hir); - }); + outlineJSX(hir); } if (env.config.enableNameAnonymousFunctions) { - env.tryRecord(() => { - nameAnonymousFunctions(hir); - }); + nameAnonymousFunctions(hir); log({ kind: 'hir', name: 'NameAnonymousFunctions', @@ -424,51 +354,39 @@ function runWithEnvironment( } if (env.config.enableFunctionOutlining) { - env.tryRecord(() => { - outlineFunctions(hir, fbtOperands); - }); + outlineFunctions(hir, fbtOperands); log({kind: 'hir', name: 'OutlineFunctions', value: hir}); } - env.tryRecord(() => { - alignMethodCallScopes(hir); - }); + alignMethodCallScopes(hir); log({ kind: 'hir', name: 'AlignMethodCallScopes', value: hir, }); - env.tryRecord(() => { - alignObjectMethodScopes(hir); - }); + alignObjectMethodScopes(hir); log({ kind: 'hir', name: 'AlignObjectMethodScopes', value: hir, }); - env.tryRecord(() => { - pruneUnusedLabelsHIR(hir); - }); + pruneUnusedLabelsHIR(hir); log({ kind: 'hir', name: 'PruneUnusedLabelsHIR', value: hir, }); - env.tryRecord(() => { - alignReactiveScopesToBlockScopesHIR(hir); - }); + alignReactiveScopesToBlockScopesHIR(hir); log({ kind: 'hir', name: 'AlignReactiveScopesToBlockScopesHIR', value: hir, }); - env.tryRecord(() => { - mergeOverlappingReactiveScopesHIR(hir); - }); + mergeOverlappingReactiveScopesHIR(hir); log({ kind: 'hir', name: 'MergeOverlappingReactiveScopesHIR', @@ -476,9 +394,7 @@ function runWithEnvironment( }); assertValidBlockNesting(hir); - env.tryRecord(() => { - buildReactiveScopeTerminalsHIR(hir); - }); + buildReactiveScopeTerminalsHIR(hir); log({ kind: 'hir', name: 'BuildReactiveScopeTerminalsHIR', @@ -487,18 +403,14 @@ function runWithEnvironment( assertValidBlockNesting(hir); - env.tryRecord(() => { - flattenReactiveLoopsHIR(hir); - }); + flattenReactiveLoopsHIR(hir); log({ kind: 'hir', name: 'FlattenReactiveLoopsHIR', value: hir, }); - env.tryRecord(() => { - flattenScopesWithHooksOrUseHIR(hir); - }); + flattenScopesWithHooksOrUseHIR(hir); log({ kind: 'hir', name: 'FlattenScopesWithHooksOrUseHIR', @@ -506,9 +418,8 @@ function runWithEnvironment( }); assertTerminalSuccessorsExist(hir); assertTerminalPredsExist(hir); - env.tryRecord(() => { - propagateScopeDependenciesHIR(hir); - }); + + propagateScopeDependenciesHIR(hir); log({ kind: 'hir', name: 'PropagateScopeDependenciesHIR', @@ -516,9 +427,7 @@ function runWithEnvironment( }); if (env.config.inferEffectDependencies) { - env.tryRecord(() => { - inferEffectDependencies(hir); - }); + inferEffectDependencies(hir); log({ kind: 'hir', name: 'InferEffectDependencies', @@ -527,9 +436,7 @@ function runWithEnvironment( } if (env.config.inlineJsxTransform) { - env.tryRecord(() => { - inlineJsxTransform(hir, env.config.inlineJsxTransform!); - }); + inlineJsxTransform(hir, env.config.inlineJsxTransform!); log({ kind: 'hir', name: 'inlineJsxTransform', @@ -537,10 +444,7 @@ function runWithEnvironment( }); } - let reactiveFunction!: ReactiveFunction; - env.tryRecord(() => { - reactiveFunction = buildReactiveFunction(hir); - }); + const reactiveFunction = buildReactiveFunction(hir); log({ kind: 'reactive', name: 'BuildReactiveFunction', @@ -549,9 +453,7 @@ function runWithEnvironment( assertWellFormedBreakTargets(reactiveFunction); - env.tryRecord(() => { - pruneUnusedLabels(reactiveFunction); - }); + pruneUnusedLabels(reactiveFunction); log({ kind: 'reactive', name: 'PruneUnusedLabels', @@ -559,45 +461,35 @@ function runWithEnvironment( }); assertScopeInstructionsWithinScopes(reactiveFunction); - env.tryRecord(() => { - pruneNonEscapingScopes(reactiveFunction); - }); + pruneNonEscapingScopes(reactiveFunction); log({ kind: 'reactive', name: 'PruneNonEscapingScopes', value: reactiveFunction, }); - env.tryRecord(() => { - pruneNonReactiveDependencies(reactiveFunction); - }); + pruneNonReactiveDependencies(reactiveFunction); log({ kind: 'reactive', name: 'PruneNonReactiveDependencies', value: reactiveFunction, }); - env.tryRecord(() => { - pruneUnusedScopes(reactiveFunction); - }); + pruneUnusedScopes(reactiveFunction); log({ kind: 'reactive', name: 'PruneUnusedScopes', value: reactiveFunction, }); - env.tryRecord(() => { - mergeReactiveScopesThatInvalidateTogether(reactiveFunction); - }); + mergeReactiveScopesThatInvalidateTogether(reactiveFunction); log({ kind: 'reactive', name: 'MergeReactiveScopesThatInvalidateTogether', value: reactiveFunction, }); - env.tryRecord(() => { - pruneAlwaysInvalidatingScopes(reactiveFunction); - }); + pruneAlwaysInvalidatingScopes(reactiveFunction); log({ kind: 'reactive', name: 'PruneAlwaysInvalidatingScopes', @@ -605,9 +497,7 @@ function runWithEnvironment( }); if (env.config.enableChangeDetectionForDebugging != null) { - env.tryRecord(() => { - pruneInitializationDependencies(reactiveFunction); - }); + pruneInitializationDependencies(reactiveFunction); log({ kind: 'reactive', name: 'PruneInitializationDependencies', @@ -615,64 +505,49 @@ function runWithEnvironment( }); } - env.tryRecord(() => { - propagateEarlyReturns(reactiveFunction); - }); + propagateEarlyReturns(reactiveFunction); log({ kind: 'reactive', name: 'PropagateEarlyReturns', value: reactiveFunction, }); - env.tryRecord(() => { - pruneUnusedLValues(reactiveFunction); - }); + pruneUnusedLValues(reactiveFunction); log({ kind: 'reactive', name: 'PruneUnusedLValues', value: reactiveFunction, }); - env.tryRecord(() => { - promoteUsedTemporaries(reactiveFunction); - }); + promoteUsedTemporaries(reactiveFunction); log({ kind: 'reactive', name: 'PromoteUsedTemporaries', value: reactiveFunction, }); - env.tryRecord(() => { - extractScopeDeclarationsFromDestructuring(reactiveFunction); - }); + extractScopeDeclarationsFromDestructuring(reactiveFunction); log({ kind: 'reactive', name: 'ExtractScopeDeclarationsFromDestructuring', value: reactiveFunction, }); - env.tryRecord(() => { - stabilizeBlockIds(reactiveFunction); - }); + stabilizeBlockIds(reactiveFunction); log({ kind: 'reactive', name: 'StabilizeBlockIds', value: reactiveFunction, }); - let uniqueIdentifiers: Set = new Set(); - env.tryRecord(() => { - uniqueIdentifiers = renameVariables(reactiveFunction); - }); + const uniqueIdentifiers = renameVariables(reactiveFunction); log({ kind: 'reactive', name: 'RenameVariables', value: reactiveFunction, }); - env.tryRecord(() => { - pruneHoistedContexts(reactiveFunction); - }); + pruneHoistedContexts(reactiveFunction); log({ kind: 'reactive', name: 'PruneHoistedContexts', @@ -680,18 +555,14 @@ function runWithEnvironment( }); if (env.config.validateMemoizedEffectDependencies) { - env.tryRecord(() => { - validateMemoizedEffectDependencies(reactiveFunction); - }); + validateMemoizedEffectDependencies(reactiveFunction); } if ( env.config.enablePreserveExistingMemoizationGuarantees || env.config.validatePreserveExistingMemoizationGuarantees ) { - env.tryRecord(() => { - validatePreservedManualMemoization(reactiveFunction); - }); + validatePreservedManualMemoization(reactiveFunction); } const ast = codegenFunction(reactiveFunction, { @@ -704,9 +575,7 @@ function runWithEnvironment( } if (env.config.validateSourceLocations) { - env.tryRecord(() => { - validateSourceLocations(func, ast, env); - }); + validateSourceLocations(func, ast, env); } /** diff --git a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Program.ts b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Program.ts index d93c814913..2475956627 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Program.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Program.ts @@ -744,6 +744,20 @@ function tryCompileFunction( return {kind: 'error', error: result.unwrapErr()}; } } catch (err) { + /** + * A pass incorrectly threw instead of recording the error. + * Log for detection in development. + */ + if ( + err instanceof CompilerError && + err.details.every(detail => detail.category !== ErrorCategory.Invariant) + ) { + programContext.logEvent({ + kind: 'CompileUnexpectedThrow', + fnLoc: fn.node.loc ?? null, + data: err.toString(), + }); + } return {kind: 'error', error: err}; } } @@ -792,7 +806,20 @@ function retryCompileFunction( } return compiledFn; } catch (err) { - // TODO: we might want to log error here, but this will also result in duplicate logging + /** + * A pass incorrectly threw instead of recording the error. + * Log for detection in development. + */ + if ( + err instanceof CompilerError && + err.details.every(detail => detail.category !== ErrorCategory.Invariant) + ) { + programContext.logEvent({ + kind: 'CompileUnexpectedThrow', + fnLoc: fn.node.loc ?? null, + data: err.toString(), + }); + } if (err instanceof CompilerError) { programContext.retryErrors.push({fn, error: err}); } diff --git a/compiler/packages/babel-plugin-react-compiler/src/HIR/BuildHIR.ts b/compiler/packages/babel-plugin-react-compiler/src/HIR/BuildHIR.ts index 9702e3100e..76fb0589c8 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/BuildHIR.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/BuildHIR.ts @@ -185,47 +185,32 @@ export function lower( let directives: Array = []; const body = func.get('body'); - try { - if (body.isExpression()) { - const fallthrough = builder.reserve('block'); - const terminal: ReturnTerminal = { - kind: 'return', - returnVariant: 'Implicit', - loc: GeneratedSource, - value: lowerExpressionToTemporary(builder, body), - id: makeInstructionId(0), - effects: null, - }; - builder.terminateWithContinuation(terminal, fallthrough); - } else if (body.isBlockStatement()) { - lowerStatement(builder, body); - directives = body.get('directives').map(d => d.node.value.value); - } else { - builder.errors.pushDiagnostic( - CompilerDiagnostic.create({ - category: ErrorCategory.Syntax, - reason: `Unexpected function body kind`, - description: `Expected function body to be an expression or a block statement, got \`${body.type}\``, - }).withDetails({ - kind: 'error', - loc: body.node.loc ?? null, - message: 'Expected a block statement or expression', - }), - ); - } - } catch (err) { - if (err instanceof CompilerError) { - // Re-throw invariant errors immediately - for (const detail of err.details) { - if (detail.category === ErrorCategory.Invariant) { - throw err; - } - } - // Record non-invariant errors and continue to produce partial HIR - builder.errors.merge(err); - } else { - throw err; - } + if (body.isExpression()) { + const fallthrough = builder.reserve('block'); + const terminal: ReturnTerminal = { + kind: 'return', + returnVariant: 'Implicit', + loc: GeneratedSource, + value: lowerExpressionToTemporary(builder, body), + id: makeInstructionId(0), + effects: null, + }; + builder.terminateWithContinuation(terminal, fallthrough); + } else if (body.isBlockStatement()) { + lowerStatement(builder, body); + directives = body.get('directives').map(d => d.node.value.value); + } else { + builder.errors.pushDiagnostic( + CompilerDiagnostic.create({ + category: ErrorCategory.Syntax, + reason: `Unexpected function body kind`, + description: `Expected function body to be an expression or a block statement, got \`${body.type}\``, + }).withDetails({ + kind: 'error', + loc: body.node.loc ?? null, + message: 'Expected a block statement or expression', + }), + ); } let validatedId: HIRFunction['id'] = null; diff --git a/compiler/packages/babel-plugin-react-compiler/src/HIR/CollectOptionalChainDependencies.ts b/compiler/packages/babel-plugin-react-compiler/src/HIR/CollectOptionalChainDependencies.ts index f78598ec3c..ece62bf56a 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/CollectOptionalChainDependencies.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/CollectOptionalChainDependencies.ts @@ -310,16 +310,13 @@ function traverseOptionalBlock( * - a optional base block with a separate nested optional-chain (e.g. a(c?.d)?.d) */ const testBlock = context.blocks.get(maybeTest.terminal.fallthrough)!; - if (testBlock!.terminal.kind !== 'branch') { - /** - * Fallthrough of the inner optional should be a block with no - * instructions, terminating with Test($) - */ - CompilerError.throwTodo({ - reason: `Unexpected terminal kind \`${testBlock.terminal.kind}\` for optional fallthrough block`, - loc: maybeTest.terminal.loc, - }); + /** + * Fallthrough of the inner optional should be a block with no + * instructions, terminating with Test($) + */ + if (testBlock.terminal.kind !== 'branch') { + return null; } /** * Recurse into inner optional blocks to collect inner optional-chain diff --git a/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts b/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts index 50d7a982e2..e294333dca 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts @@ -1019,29 +1019,6 @@ export class Environment { return this.#errors; } - /** - * Wraps a callback in try/catch: if the callback throws a CompilerError - * that is NOT an invariant, the error is recorded and execution continues. - * Non-CompilerError exceptions and invariants are re-thrown. - */ - tryRecord(fn: () => void): void { - try { - fn(); - } catch (err) { - if (err instanceof CompilerError) { - // Check if any detail is an invariant — if so, re-throw - for (const detail of err.details) { - if (detail.category === ErrorCategory.Invariant) { - throw err; - } - } - this.recordErrors(err); - } else { - throw err; - } - } - } - isContextIdentifier(node: t.Identifier): boolean { return this.#contextIdentifiers.has(node); } diff --git a/compiler/packages/babel-plugin-react-compiler/src/HIR/HIRBuilder.ts b/compiler/packages/babel-plugin-react-compiler/src/HIR/HIRBuilder.ts index 738bfd0c0f..d7c65c6564 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/HIRBuilder.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/HIRBuilder.ts @@ -308,33 +308,23 @@ export default class HIRBuilder { resolveBinding(node: t.Identifier): Identifier { if (node.name === 'fbt') { - CompilerError.throwDiagnostic({ + this.errors.push({ category: ErrorCategory.Todo, reason: 'Support local variables named `fbt`', description: 'Local variables named `fbt` may conflict with the fbt plugin and are not yet supported', - details: [ - { - kind: 'error', - message: 'Rename to avoid conflict with fbt plugin', - loc: node.loc ?? GeneratedSource, - }, - ], + loc: node.loc ?? GeneratedSource, + suggestions: null, }); } if (node.name === 'this') { - CompilerError.throwDiagnostic({ + this.errors.push({ category: ErrorCategory.UnsupportedSyntax, reason: '`this` is not supported syntax', description: 'React Compiler does not support compiling functions that use `this`', - details: [ - { - kind: 'error', - message: '`this` was used here', - loc: node.loc ?? GeneratedSource, - }, - ], + loc: node.loc ?? GeneratedSource, + suggestions: null, }); } const originalName = node.name; diff --git a/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/BuildReactiveFunction.ts b/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/BuildReactiveFunction.ts index f5b2a654ec..f53f7d15e0 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/BuildReactiveFunction.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/BuildReactiveFunction.ts @@ -1007,11 +1007,10 @@ class Driver { const test = this.visitValueBlock(testBlockId, loc); const testBlock = this.cx.ir.blocks.get(test.block)!; if (testBlock.terminal.kind !== 'branch') { - CompilerError.throwTodo({ - reason: `Unexpected terminal kind \`${testBlock.terminal.kind}\` for ${terminalKind} test block`, - description: null, + CompilerError.invariant(false, { + reason: `Expected a branch terminal for ${terminalKind} test block`, + description: `Got \`${testBlock.terminal.kind}\``, loc: testBlock.terminal.loc, - suggestions: null, }); } return { diff --git a/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/CodegenReactiveFunction.ts b/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/CodegenReactiveFunction.ts index 07e3a4b91d..1e0e97d8c8 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/CodegenReactiveFunction.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/CodegenReactiveFunction.ts @@ -972,12 +972,13 @@ function codegenTerminal( loc: terminal.init.loc, }); if (terminal.init.instructions.length !== 2) { - CompilerError.throwTodo({ + cx.errors.push({ reason: 'Support non-trivial for..in inits', - description: null, + category: ErrorCategory.Todo, loc: terminal.init.loc, suggestions: null, }); + return t.emptyStatement(); } const iterableCollection = terminal.init.instructions[0]; const iterableItem = terminal.init.instructions[1]; @@ -992,12 +993,13 @@ function codegenTerminal( break; } case 'StoreContext': { - CompilerError.throwTodo({ + cx.errors.push({ reason: 'Support non-trivial for..in inits', - description: null, + category: ErrorCategory.Todo, loc: terminal.init.loc, suggestions: null, }); + return t.emptyStatement(); } default: CompilerError.invariant(false, { @@ -1067,12 +1069,13 @@ function codegenTerminal( loc: terminal.test.loc, }); if (terminal.test.instructions.length !== 2) { - CompilerError.throwTodo({ + cx.errors.push({ reason: 'Support non-trivial for..of inits', - description: null, + category: ErrorCategory.Todo, loc: terminal.init.loc, suggestions: null, }); + return t.emptyStatement(); } const iterableItem = terminal.test.instructions[1]; let lval: t.LVal; @@ -1086,12 +1089,13 @@ function codegenTerminal( break; } case 'StoreContext': { - CompilerError.throwTodo({ + cx.errors.push({ reason: 'Support non-trivial for..of inits', - description: null, + category: ErrorCategory.Todo, loc: terminal.init.loc, suggestions: null, }); + return t.emptyStatement(); } default: CompilerError.invariant(false, { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/ecma/error.reserved-words.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/ecma/error.reserved-words.expect.md index 8cc9419462..a6ee8a798b 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/ecma/error.reserved-words.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/ecma/error.reserved-words.expect.md @@ -24,9 +24,9 @@ function useThing(fn) { ``` Found 1 error: -Invariant: [HIRBuilder] Unexpected null block +Error: Expected a non-reserved identifier name -expected block 0 to exist. +`this` is a reserved word in JavaScript and cannot be used as an identifier name. ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.dont-hoist-inline-reference.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.dont-hoist-inline-reference.expect.md index bbb66dd6b5..00a68405f8 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.dont-hoist-inline-reference.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.dont-hoist-inline-reference.expect.md @@ -21,15 +21,15 @@ export const FIXTURE_ENTRYPOINT = { ``` Found 1 error: -Invariant: [InferMutationAliasingEffects] Expected value kind to be initialized +Todo: [hoisting] EnterSSA: Expected identifier to be defined before being used - x$1. +Identifier x$1 is undefined. -error.dont-hoist-inline-reference.ts:3:21 +error.dont-hoist-inline-reference.ts:3:2 1 | import {identity} from 'shared-runtime'; 2 | function useInvalid() { > 3 | const x = identity(x); - | ^ this is uninitialized + | ^^^^^^^^^^^^^^^^^^^^^^ [hoisting] EnterSSA: Expected identifier to be defined before being used 4 | return x; 5 | } 6 | diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-optional-call-chain-in-optional.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-optional-call-chain-in-optional.expect.md deleted file mode 100644 index 6551bb8d40..0000000000 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-optional-call-chain-in-optional.expect.md +++ /dev/null @@ -1,39 +0,0 @@ - -## Input - -```javascript -function useFoo(props: {value: {x: string; y: string} | null}) { - const value = props.value; - return createArray(value?.x, value?.y)?.join(', '); -} - -function createArray(...args: Array): Array { - return args; -} - -export const FIXTURE_ENTRYPONT = { - fn: useFoo, - props: [{value: null}], -}; - -``` - - -## Error - -``` -Found 1 error: - -Todo: Unexpected terminal kind `optional` for optional fallthrough block - -error.todo-optional-call-chain-in-optional.ts:3:21 - 1 | function useFoo(props: {value: {x: string; y: string} | null}) { - 2 | const value = props.value; -> 3 | return createArray(value?.x, value?.y)?.join(', '); - | ^^^^^^^^ Unexpected terminal kind `optional` for optional fallthrough block - 4 | } - 5 | - 6 | function createArray(...args: Array): Array { -``` - - \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/fbt/error.todo-fbt-as-local.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/fbt/error.todo-fbt-as-local.expect.md index bb86d3bc42..c2cc0a1950 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/fbt/error.todo-fbt-as-local.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/fbt/error.todo-fbt-as-local.expect.md @@ -50,7 +50,7 @@ export const FIXTURE_ENTRYPOINT = { ## Error ``` -Found 1 error: +Found 4 errors: Todo: Support local variables named `fbt` @@ -60,10 +60,49 @@ error.todo-fbt-as-local.ts:18:19 16 | 17 | function Foo(props) { > 18 | const getText1 = fbt => - | ^^^ Rename to avoid conflict with fbt plugin + | ^^^ Support local variables named `fbt` 19 | fbt( 20 | `Hello, ${fbt.param('(key) name', identity(props.name))}!`, 21 | '(description) Greeting' + +Todo: Support local variables named `fbt` + +Local variables named `fbt` may conflict with the fbt plugin and are not yet supported. + +error.todo-fbt-as-local.ts:18:19 + 16 | + 17 | function Foo(props) { +> 18 | const getText1 = fbt => + | ^^^ Support local variables named `fbt` + 19 | fbt( + 20 | `Hello, ${fbt.param('(key) name', identity(props.name))}!`, + 21 | '(description) Greeting' + +Todo: Support local variables named `fbt` + +Local variables named `fbt` may conflict with the fbt plugin and are not yet supported. + +error.todo-fbt-as-local.ts:18:19 + 16 | + 17 | function Foo(props) { +> 18 | const getText1 = fbt => + | ^^^ Support local variables named `fbt` + 19 | fbt( + 20 | `Hello, ${fbt.param('(key) name', identity(props.name))}!`, + 21 | '(description) Greeting' + +Todo: Support local variables named `fbt` + +Local variables named `fbt` may conflict with the fbt plugin and are not yet supported. + +error.todo-fbt-as-local.ts:24:19 + 22 | ); + 23 | +> 24 | const getText2 = fbt => + | ^^^ Support local variables named `fbt` + 25 | fbt( + 26 | `Goodbye, ${fbt.param('(key) name', identity(props.name))}!`, + 27 | '(description) Greeting2' ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/fbt/error.todo-locally-require-fbt.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/fbt/error.todo-locally-require-fbt.expect.md index 62605e5896..2847ad9d5a 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/fbt/error.todo-locally-require-fbt.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/fbt/error.todo-locally-require-fbt.expect.md @@ -16,17 +16,15 @@ function Component(props) { ``` Found 1 error: -Todo: Support local variables named `fbt` +Invariant: tags should be module-level imports -Local variables named `fbt` may conflict with the fbt plugin and are not yet supported. - -error.todo-locally-require-fbt.ts:2:8 - 1 | function Component(props) { -> 2 | const fbt = require('fbt'); - | ^^^ Rename to avoid conflict with fbt plugin +error.todo-locally-require-fbt.ts:4:10 + 2 | const fbt = require('fbt'); 3 | - 4 | return {'Text'}; +> 4 | return {'Text'}; + | ^^^ tags should be module-level imports 5 | } + 6 | ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/propagate-scope-deps-hir-fork/error.todo-optional-call-chain-in-optional.expect.md b/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 file mode 100644 index 5da7122c76..0000000000 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/propagate-scope-deps-hir-fork/error.todo-optional-call-chain-in-optional.expect.md +++ /dev/null @@ -1,40 +0,0 @@ - -## Input - -```javascript -// @enablePropagateDepsInHIR -function useFoo(props: {value: {x: string; y: string} | null}) { - const value = props.value; - return createArray(value?.x, value?.y)?.join(', '); -} - -function createArray(...args: Array): Array { - return args; -} - -export const FIXTURE_ENTRYPONT = { - fn: useFoo, - props: [{value: null}], -}; - -``` - - -## Error - -``` -Found 1 error: - -Todo: Unexpected terminal kind `optional` for optional fallthrough block - -error.todo-optional-call-chain-in-optional.ts:4:21 - 2 | function useFoo(props: {value: {x: string; y: string} | null}) { - 3 | const value = props.value; -> 4 | return createArray(value?.x, value?.y)?.join(', '); - | ^^^^^^^^ Unexpected terminal kind `optional` for optional fallthrough block - 5 | } - 6 | - 7 | function createArray(...args: Array): Array { -``` - - \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/propagate-scope-deps-hir-fork/todo-optional-call-chain-in-optional.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/propagate-scope-deps-hir-fork/todo-optional-call-chain-in-optional.expect.md new file mode 100644 index 0000000000..af046d58b7 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/propagate-scope-deps-hir-fork/todo-optional-call-chain-in-optional.expect.md @@ -0,0 +1,54 @@ + +## Input + +```javascript +// @enablePropagateDepsInHIR +function useFoo(props: {value: {x: string; y: string} | null}) { + const value = props.value; + return createArray(value?.x, value?.y)?.join(', '); +} + +function createArray(...args: Array): Array { + return args; +} + +export const FIXTURE_ENTRYPONT = { + fn: useFoo, + props: [{value: null}], +}; + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; // @enablePropagateDepsInHIR +function useFoo(props) { + const $ = _c(3); + const value = props.value; + let t0; + if ($[0] !== value?.x || $[1] !== value?.y) { + t0 = createArray(value?.x, value?.y)?.join(", "); + $[0] = value?.x; + $[1] = value?.y; + $[2] = t0; + } else { + t0 = $[2]; + } + return t0; +} + +function createArray(...t0) { + const args = t0; + return args; +} + +export const FIXTURE_ENTRYPONT = { + fn: useFoo, + props: [{ value: null }], +}; + +``` + +### Eval output +(kind: exception) Fixture not implemented \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/propagate-scope-deps-hir-fork/error.todo-optional-call-chain-in-optional.ts b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/propagate-scope-deps-hir-fork/todo-optional-call-chain-in-optional.ts similarity index 100% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/propagate-scope-deps-hir-fork/error.todo-optional-call-chain-in-optional.ts rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/propagate-scope-deps-hir-fork/todo-optional-call-chain-in-optional.ts diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/todo-optional-call-chain-in-optional.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/todo-optional-call-chain-in-optional.expect.md new file mode 100644 index 0000000000..c9d71a8c3b --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/todo-optional-call-chain-in-optional.expect.md @@ -0,0 +1,53 @@ + +## Input + +```javascript +function useFoo(props: {value: {x: string; y: string} | null}) { + const value = props.value; + return createArray(value?.x, value?.y)?.join(', '); +} + +function createArray(...args: Array): Array { + return args; +} + +export const FIXTURE_ENTRYPONT = { + fn: useFoo, + props: [{value: null}], +}; + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; +function useFoo(props) { + const $ = _c(3); + const value = props.value; + let t0; + if ($[0] !== value?.x || $[1] !== value?.y) { + t0 = createArray(value?.x, value?.y)?.join(", "); + $[0] = value?.x; + $[1] = value?.y; + $[2] = t0; + } else { + t0 = $[2]; + } + return t0; +} + +function createArray(...t0) { + const args = t0; + return args; +} + +export const FIXTURE_ENTRYPONT = { + fn: useFoo, + props: [{ value: null }], +}; + +``` + +### Eval output +(kind: exception) Fixture not implemented \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-optional-call-chain-in-optional.ts b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/todo-optional-call-chain-in-optional.ts similarity index 100% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-optional-call-chain-in-optional.ts rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/todo-optional-call-chain-in-optional.ts diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/error.invalid-mix-fire-and-no-fire.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/error.invalid-mix-fire-and-no-fire.expect.md index e4b0057ac6..1eb6bf66e9 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/error.invalid-mix-fire-and-no-fire.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/error.invalid-mix-fire-and-no-fire.expect.md @@ -29,15 +29,15 @@ function Component(props) { ``` Found 1 error: -Invariant: [InferMutationAliasingEffects] Expected value kind to be initialized +Error: Cannot compile `fire` - foo$42:TFunction(): :TPrimitive. +All uses of foo must be either used with a fire() call in this effect or not used with a fire() call at all. foo was used with fire() on line 10:10 in this effect. error.invalid-mix-fire-and-no-fire.ts:11:6 9 | function nested() { 10 | fire(foo(props)); > 11 | foo(props); - | ^^^ this is uninitialized + | ^^^ Cannot compile `fire` 12 | } 13 | 14 | nested(); diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/error.invalid-multiple-args.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/error.invalid-multiple-args.expect.md index 03172c74cc..c519d43fb4 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/error.invalid-multiple-args.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/error.invalid-multiple-args.expect.md @@ -24,15 +24,15 @@ function Component({bar, baz}) { ``` Found 1 error: -Invariant: [InferMutationAliasingEffects] Expected value kind to be initialized +Error: Cannot compile `fire` - $43:TFunction(): :TFunction(): :TPoly. +fire() can only take in a single call expression as an argument but received multiple arguments. error.invalid-multiple-args.ts:9:4 7 | }; 8 | useEffect(() => { > 9 | fire(foo(bar), baz); - | ^^^^ this is uninitialized + | ^^^^^^^^^^^^^^^^^^^ Cannot compile `fire` 10 | }); 11 | 12 | return null; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/error.invalid-not-call.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/error.invalid-not-call.expect.md index 17a27c4e63..40c4bc5394 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/error.invalid-not-call.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/error.invalid-not-call.expect.md @@ -24,15 +24,15 @@ function Component(props) { ``` Found 1 error: -Invariant: [InferMutationAliasingEffects] Expected value kind to be initialized +Error: Cannot compile `fire` - $32:TFunction(): :TFunction(): :TPoly. +`fire()` can only receive a function call such as `fire(fn(a,b)). Method calls and other expressions are not allowed. error.invalid-not-call.ts:9:4 7 | }; 8 | useEffect(() => { > 9 | fire(props); - | ^^^^ this is uninitialized + | ^^^^^^^^^^^ Cannot compile `fire` 10 | }); 11 | 12 | return null; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/error.invalid-spread.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/error.invalid-spread.expect.md index 087b0b8e72..dcd302bbe1 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/error.invalid-spread.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/error.invalid-spread.expect.md @@ -24,15 +24,15 @@ function Component(props) { ``` Found 1 error: -Invariant: [InferMutationAliasingEffects] Expected value kind to be initialized +Error: Cannot compile `fire` - $32:TFunction(): :TFunction(): :TPoly. +fire() can only take in a single call expression as an argument but received a spread argument. error.invalid-spread.ts:9:4 7 | }; 8 | useEffect(() => { > 9 | fire(...foo); - | ^^^^ this is uninitialized + | ^^^^^^^^^^^^ Cannot compile `fire` 10 | }); 11 | 12 | return null; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/error.todo-method.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/error.todo-method.expect.md index d7c56ffb33..67410297f3 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/error.todo-method.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/transform-fire/error.todo-method.expect.md @@ -24,15 +24,15 @@ function Component(props) { ``` Found 1 error: -Invariant: [InferMutationAliasingEffects] Expected value kind to be initialized +Error: Cannot compile `fire` - $34:TFunction(): :TFunction(): :TPoly. +`fire()` can only receive a function call such as `fire(fn(a,b)). Method calls and other expressions are not allowed. error.todo-method.ts:9:4 7 | }; 8 | useEffect(() => { > 9 | fire(props.foo()); - | ^^^^ this is uninitialized + | ^^^^^^^^^^^^^^^^^ Cannot compile `fire` 10 | }); 11 | 12 | return null; diff --git a/compiler/packages/snap/src/compiler.ts b/compiler/packages/snap/src/compiler.ts index 1ad2b81ef3..0ee2ee0945 100644 --- a/compiler/packages/snap/src/compiler.ts +++ b/compiler/packages/snap/src/compiler.ts @@ -378,6 +378,17 @@ export async function transformFixtureInput( msg: 'Expected nothing to be compiled (from `// @expectNothingCompiled`), but some functions compiled or errored', }; } + const unexpectedThrows = logs.filter( + log => log.event.kind === 'CompileUnexpectedThrow', + ); + if (unexpectedThrows.length > 0) { + return { + kind: 'err', + msg: + `Compiler pass(es) threw instead of recording errors:\n` + + unexpectedThrows.map(l => (l.event as any).data).join('\n'), + }; + } return { kind: 'ok', value: {