diff --git a/compiler/packages/babel-plugin-react-compiler/src/CompilerError.ts b/compiler/packages/babel-plugin-react-compiler/src/CompilerError.ts index 3a3010dd2f62..7285140de0a6 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/CompilerError.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/CompilerError.ts @@ -6,6 +6,7 @@ */ import type {SourceLocation} from './HIR'; +import {Err, Ok, Result} from './Utils/Result'; import {assertExhaustive} from './Utils/utils'; export enum ErrorSeverity { @@ -224,6 +225,10 @@ export class CompilerError extends Error { return this.details.length > 0; } + asResult(): Result { + return this.hasErrors() ? Err(this) : Ok(undefined); + } + /* * An error is critical if it means the compiler has entered into a broken state and cannot * continue safely. Other expected errors such as Todos mean that we can skip over that component 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 36d96d8c1bf4..ed41ce2eedc4 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Pipeline.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Pipeline.ts @@ -100,7 +100,7 @@ import {propagateScopeDependenciesHIR} from '../HIR/PropagateScopeDependenciesHI import {outlineJSX} from '../Optimization/OutlineJsx'; import {optimizePropsMethodCalls} from '../Optimization/OptimizePropsMethodCalls'; import {transformFire} from '../Transform'; -import {validateNoImpureFunctionsInRender} from '../Validation/ValiateNoImpureFunctionsInRender'; +import {validateNoImpureFunctionsInRender} from '../Validation/ValidateNoImpureFunctionsInRender'; import {CompilerError} from '..'; import {validateStaticComponents} from '../Validation/ValidateStaticComponents'; @@ -162,7 +162,7 @@ function runWithEnvironment( log({kind: 'hir', name: 'PruneMaybeThrows', value: hir}); validateContextVariableLValues(hir); - validateUseMemo(hir); + validateUseMemo(hir).unwrap(); if ( env.isInferredMemoEnabled && @@ -203,10 +203,10 @@ function runWithEnvironment( if (env.isInferredMemoEnabled) { if (env.config.validateHooksUsage) { - validateHooksUsage(hir); + validateHooksUsage(hir).unwrap(); } if (env.config.validateNoCapitalizedCalls) { - validateNoCapitalizedCalls(hir); + validateNoCapitalizedCalls(hir).unwrap(); } } @@ -256,23 +256,23 @@ function runWithEnvironment( } if (env.config.validateRefAccessDuringRender) { - validateNoRefAccessInRender(hir); + validateNoRefAccessInRender(hir).unwrap(); } if (env.config.validateNoSetStateInRender) { - validateNoSetStateInRender(hir); + validateNoSetStateInRender(hir).unwrap(); } if (env.config.validateNoSetStateInPassiveEffects) { - validateNoSetStateInPassiveEffects(hir); + env.logErrors(validateNoSetStateInPassiveEffects(hir)); } if (env.config.validateNoJSXInTryStatements) { - validateNoJSXInTryStatement(hir); + env.logErrors(validateNoJSXInTryStatement(hir)); } if (env.config.validateNoImpureFunctionsInRender) { - validateNoImpureFunctionsInRender(hir); + validateNoImpureFunctionsInRender(hir).unwrap(); } } @@ -514,14 +514,14 @@ function runWithEnvironment( }); if (env.config.validateMemoizedEffectDependencies) { - validateMemoizedEffectDependencies(reactiveFunction); + validateMemoizedEffectDependencies(reactiveFunction).unwrap(); } if ( env.config.enablePreserveExistingMemoizationGuarantees || env.config.validatePreserveExistingMemoizationGuarantees ) { - validatePreservedManualMemoization(reactiveFunction); + validatePreservedManualMemoization(reactiveFunction).unwrap(); } const ast = codegenFunction(reactiveFunction, { diff --git a/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateHooksUsage.ts b/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateHooksUsage.ts index ed23693f6143..e90f33c74076 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateHooksUsage.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateHooksUsage.ts @@ -26,6 +26,7 @@ import { eachTerminalOperand, } from '../HIR/visitors'; import {assertExhaustive} from '../Utils/utils'; +import {Result} from '../Utils/Result'; /** * Represents the possible kinds of value which may be stored at a given Place during @@ -87,7 +88,9 @@ function joinKinds(a: Kind, b: Kind): Kind { * may not appear as the callee of a conditional call. * See the note for Kind.PotentialHook for sources of potential hooks */ -export function validateHooksUsage(fn: HIRFunction): void { +export function validateHooksUsage( + fn: HIRFunction, +): Result { const unconditionalBlocks = computeUnconditionalBlocks(fn); const errors = new CompilerError(); @@ -423,9 +426,7 @@ export function validateHooksUsage(fn: HIRFunction): void { for (const [, error] of errorsByPlace) { errors.push(error); } - if (errors.hasErrors()) { - throw errors; - } + return errors.asResult(); } function visitFunctionExpression(errors: CompilerError, fn: HIRFunction): void { diff --git a/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateMemoizedEffectDependencies.ts b/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateMemoizedEffectDependencies.ts index 364e78ae6b68..b33cfb151234 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateMemoizedEffectDependencies.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateMemoizedEffectDependencies.ts @@ -22,6 +22,7 @@ import { ReactiveFunctionVisitor, visitReactiveFunction, } from '../ReactiveScopes/visitors'; +import {Result} from '../Utils/Result'; /** * Validates that all known effect dependencies are memoized. The algorithm checks two things: @@ -47,12 +48,12 @@ import { * mutate(object); // ... mutable range ends here after this mutation * ``` */ -export function validateMemoizedEffectDependencies(fn: ReactiveFunction): void { +export function validateMemoizedEffectDependencies( + fn: ReactiveFunction, +): Result { const errors = new CompilerError(); visitReactiveFunction(fn, new Visitor(), errors); - if (errors.hasErrors()) { - throw errors; - } + return errors.asResult(); } class Visitor extends ReactiveFunctionVisitor { diff --git a/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoCapitalizedCalls.ts b/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoCapitalizedCalls.ts index 2f9b0a1c7e2c..4ba70b64a8c9 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoCapitalizedCalls.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoCapitalizedCalls.ts @@ -4,11 +4,14 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ -import {CompilerError, EnvironmentConfig} from '..'; +import {CompilerError, EnvironmentConfig, ErrorSeverity} from '..'; import {HIRFunction, IdentifierId} from '../HIR'; import {DEFAULT_GLOBALS} from '../HIR/Globals'; +import {Result} from '../Utils/Result'; -export function validateNoCapitalizedCalls(fn: HIRFunction): void { +export function validateNoCapitalizedCalls( + fn: HIRFunction, +): Result { const envConfig: EnvironmentConfig = fn.env.config; const ALLOW_LIST = new Set([ ...DEFAULT_GLOBALS.keys(), @@ -26,6 +29,7 @@ export function validateNoCapitalizedCalls(fn: HIRFunction): void { ); }; + const errors = new CompilerError(); const capitalLoadGlobals = new Map(); const capitalizedProperties = new Map(); const reason = @@ -73,7 +77,8 @@ export function validateNoCapitalizedCalls(fn: HIRFunction): void { const propertyIdentifier = value.property.identifier.id; const propertyName = capitalizedProperties.get(propertyIdentifier); if (propertyName != null) { - CompilerError.throwInvalidReact({ + errors.push({ + severity: ErrorSeverity.InvalidReact, reason, description: `${propertyName} may be a component.`, loc: value.loc, @@ -85,4 +90,5 @@ export function validateNoCapitalizedCalls(fn: HIRFunction): void { } } } + return errors.asResult(); } diff --git a/compiler/packages/babel-plugin-react-compiler/src/Validation/ValiateNoImpureFunctionsInRender.ts b/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoImpureFunctionsInRender.ts similarity index 91% rename from compiler/packages/babel-plugin-react-compiler/src/Validation/ValiateNoImpureFunctionsInRender.ts rename to compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoImpureFunctionsInRender.ts index 287a0aa978b6..6e88773ecf0b 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Validation/ValiateNoImpureFunctionsInRender.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoImpureFunctionsInRender.ts @@ -8,6 +8,7 @@ import {CompilerError, ErrorSeverity} from '..'; import {HIRFunction} from '../HIR'; import {getFunctionCallSignature} from '../Inference/InferReferenceEffects'; +import {Result} from '../Utils/Result'; /** * Checks that known-impure functions are not called during render. Examples of invalid functions to @@ -18,7 +19,9 @@ import {getFunctionCallSignature} from '../Inference/InferReferenceEffects'; * this in several of our validation passes and should unify those analyses into a reusable helper * and use it here. */ -export function validateNoImpureFunctionsInRender(fn: HIRFunction): void { +export function validateNoImpureFunctionsInRender( + fn: HIRFunction, +): Result { const errors = new CompilerError(); for (const [, block] of fn.body.blocks) { for (const instr of block.instructions) { @@ -46,7 +49,5 @@ export function validateNoImpureFunctionsInRender(fn: HIRFunction): void { } } } - if (errors.hasErrors()) { - throw errors; - } + return errors.asResult(); } diff --git a/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoJSXInTryStatement.ts b/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoJSXInTryStatement.ts index b92a89d76430..505302f7d12c 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoJSXInTryStatement.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoJSXInTryStatement.ts @@ -7,6 +7,7 @@ import {CompilerError, ErrorSeverity} from '..'; import {BlockId, HIRFunction} from '../HIR'; +import {Result} from '../Utils/Result'; import {retainWhere} from '../Utils/utils'; /** @@ -19,7 +20,9 @@ import {retainWhere} from '../Utils/utils'; * created within a try block. JSX is allowed within a catch statement, unless that catch * is itself nested inside an outer try. */ -export function validateNoJSXInTryStatement(fn: HIRFunction): void { +export function validateNoJSXInTryStatement( + fn: HIRFunction, +): Result { const activeTryBlocks: Array = []; const errors = new CompilerError(); for (const [, block] of fn.body.blocks) { @@ -46,7 +49,5 @@ export function validateNoJSXInTryStatement(fn: HIRFunction): void { activeTryBlocks.push(block.terminal.handler); } } - if (errors.hasErrors()) { - throw errors; - } + return errors.asResult(); } diff --git a/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoRefAccesInRender.ts b/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoRefAccesInRender.ts index 779207b22e64..d00302559bb4 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoRefAccesInRender.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoRefAccesInRender.ts @@ -99,9 +99,11 @@ class Env extends Map { } } -export function validateNoRefAccessInRender(fn: HIRFunction): void { +export function validateNoRefAccessInRender( + fn: HIRFunction, +): Result { const env = new Env(); - validateNoRefAccessInRenderImpl(fn, env).unwrap(); + return validateNoRefAccessInRenderImpl(fn, env).map(_ => undefined); } function refTypeOfType(place: Place): RefAccessType { diff --git a/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoSetStateInPassiveEffects.ts b/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoSetStateInPassiveEffects.ts index 2c6e7d8ac67c..a36c347faa00 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoSetStateInPassiveEffects.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoSetStateInPassiveEffects.ts @@ -14,6 +14,7 @@ import { Place, } from '../HIR'; import {eachInstructionValueOperand} from '../HIR/visitors'; +import {Result} from '../Utils/Result'; /** * Validates against calling setState in the body of a *passive* effect (useEffect), @@ -23,7 +24,9 @@ import {eachInstructionValueOperand} from '../HIR/visitors'; * often bad for performance and frequently has more efficient and straightforward * alternatives. See https://react.dev/learn/you-might-not-need-an-effect for examples. */ -export function validateNoSetStateInPassiveEffects(fn: HIRFunction): void { +export function validateNoSetStateInPassiveEffects( + fn: HIRFunction, +): Result { const setStateFunctions: Map = new Map(); const errors = new CompilerError(); for (const [, block] of fn.body.blocks) { @@ -98,9 +101,7 @@ export function validateNoSetStateInPassiveEffects(fn: HIRFunction): void { } } - if (errors.hasErrors()) { - throw errors; - } + return errors.asResult(); } function getSetStateCall( diff --git a/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoSetStateInRender.ts b/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoSetStateInRender.ts index 3f378b1289e6..fc101581b30b 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoSetStateInRender.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoSetStateInRender.ts @@ -9,7 +9,7 @@ import {CompilerError, ErrorSeverity} from '../CompilerError'; import {HIRFunction, IdentifierId, isSetStateType} from '../HIR'; import {computeUnconditionalBlocks} from '../HIR/ComputeUnconditionalBlocks'; import {eachInstructionValueOperand} from '../HIR/visitors'; -import {Err, Ok, Result} from '../Utils/Result'; +import {Result} from '../Utils/Result'; /** * Validates that the given function does not have an infinite update loop @@ -39,9 +39,11 @@ import {Err, Ok, Result} from '../Utils/Result'; * y(); * ``` */ -export function validateNoSetStateInRender(fn: HIRFunction): void { +export function validateNoSetStateInRender( + fn: HIRFunction, +): Result { const unconditionalSetStateFunctions: Set = new Set(); - validateNoSetStateInRenderImpl(fn, unconditionalSetStateFunctions).unwrap(); + return validateNoSetStateInRenderImpl(fn, unconditionalSetStateFunctions); } function validateNoSetStateInRenderImpl( @@ -145,9 +147,5 @@ function validateNoSetStateInRenderImpl( } } - if (errors.hasErrors()) { - return Err(errors); - } else { - return Ok(undefined); - } + return errors.asResult(); } diff --git a/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidatePreservedManualMemoization.ts b/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidatePreservedManualMemoization.ts index 28be6c166d2e..85fec7a75333 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidatePreservedManualMemoization.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidatePreservedManualMemoization.ts @@ -5,9 +5,10 @@ * LICENSE file in the root directory of this source tree. */ -import {CompilerError, Effect, ErrorSeverity} from '..'; +import {CompilerError, ErrorSeverity} from '../CompilerError'; import { DeclarationId, + Effect, GeneratedSource, Identifier, IdentifierId, @@ -30,6 +31,7 @@ import { ReactiveFunctionVisitor, visitReactiveFunction, } from '../ReactiveScopes/visitors'; +import {Result} from '../Utils/Result'; import {getOrInsertDefault} from '../Utils/utils'; /** @@ -39,15 +41,15 @@ import {getOrInsertDefault} from '../Utils/utils'; * This can occur if a value's mutable range somehow extended to include a hook and * was pruned. */ -export function validatePreservedManualMemoization(fn: ReactiveFunction): void { +export function validatePreservedManualMemoization( + fn: ReactiveFunction, +): Result { const state = { errors: new CompilerError(), manualMemoState: null, }; visitReactiveFunction(fn, new Visitor(), state); - if (state.errors.hasErrors()) { - throw state.errors; - } + return state.errors.asResult(); } const DEBUG = false; diff --git a/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateStaticComponents.ts b/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateStaticComponents.ts index a453d484ecb6..f7adef6ca712 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateStaticComponents.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateStaticComponents.ts @@ -7,7 +7,7 @@ import {CompilerError, ErrorSeverity} from '../CompilerError'; import {HIRFunction, IdentifierId, SourceLocation} from '../HIR'; -import {Err, Ok, Result} from '../Utils/Result'; +import {Result} from '../Utils/Result'; /** * Validates against components that are created dynamically and whose identity is not guaranteed @@ -79,9 +79,5 @@ export function validateStaticComponents( } } } - if (error.hasErrors()) { - return Err(error); - } else { - return Ok(undefined); - } + return error.asResult(); } diff --git a/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateUseMemo.ts b/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateUseMemo.ts index 8fe62b122c6b..fd4d781ef3c6 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateUseMemo.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateUseMemo.ts @@ -5,10 +5,12 @@ * LICENSE file in the root directory of this source tree. */ -import {CompilerError} from '..'; +import {CompilerError, ErrorSeverity} from '..'; import {FunctionExpression, HIRFunction, IdentifierId} from '../HIR'; +import {Result} from '../Utils/Result'; -export function validateUseMemo(fn: HIRFunction): void { +export function validateUseMemo(fn: HIRFunction): Result { + const errors = new CompilerError(); const useMemos = new Set(); const react = new Set(); const functions = new Map(); @@ -61,7 +63,8 @@ export function validateUseMemo(fn: HIRFunction): void { } if (body.loweredFunc.func.params.length > 0) { - CompilerError.throwInvalidReact({ + errors.push({ + severity: ErrorSeverity.InvalidReact, reason: 'useMemo callbacks may not accept any arguments', description: null, loc: body.loc, @@ -70,7 +73,8 @@ export function validateUseMemo(fn: HIRFunction): void { } if (body.loweredFunc.func.async || body.loweredFunc.func.generator) { - CompilerError.throwInvalidReact({ + errors.push({ + severity: ErrorSeverity.InvalidReact, reason: 'useMemo callbacks may not be async or generator functions', description: null, @@ -84,4 +88,5 @@ export function validateUseMemo(fn: HIRFunction): void { } } } + return errors.asResult(); } diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-jsx-in-catch-in-outer-try-with-catch.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-jsx-in-catch-in-outer-try-with-catch.expect.md deleted file mode 100644 index 40cebff89a75..000000000000 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-jsx-in-catch-in-outer-try-with-catch.expect.md +++ /dev/null @@ -1,38 +0,0 @@ - -## Input - -```javascript -// @validateNoJSXInTryStatements -import {identity} from 'shared-runtime'; - -function Component(props) { - let el; - try { - let value; - try { - value = identity(props.foo); - } catch { - el =
; - } - } catch { - return null; - } - return el; -} - -``` - - -## Error - -``` - 9 | value = identity(props.foo); - 10 | } catch { -> 11 | el =
; - | ^^^^^^^^^^^^^^^^^^^^^ InvalidReact: Unexpected JSX element within a try statement. To catch errors in rendering a given component, wrap that component in an error boundary. (https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary) (11:11) - 12 | } - 13 | } catch { - 14 | return null; -``` - - \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-jsx-in-try-with-catch.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-jsx-in-try-with-catch.expect.md deleted file mode 100644 index ee1f5335ef62..000000000000 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-jsx-in-try-with-catch.expect.md +++ /dev/null @@ -1,31 +0,0 @@ - -## Input - -```javascript -// @validateNoJSXInTryStatements -function Component(props) { - let el; - try { - el =
; - } catch { - return null; - } - return el; -} - -``` - - -## Error - -``` - 3 | let el; - 4 | try { -> 5 | el =
; - | ^^^^^^^ InvalidReact: Unexpected JSX element within a try statement. To catch errors in rendering a given component, wrap that component in an error boundary. (https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary) (5:5) - 6 | } catch { - 7 | return null; - 8 | } -``` - - \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-setState-in-useEffect-transitive.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-setState-in-useEffect-transitive.expect.md deleted file mode 100644 index ab10aa1bae51..000000000000 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-setState-in-useEffect-transitive.expect.md +++ /dev/null @@ -1,37 +0,0 @@ - -## Input - -```javascript -// @validateNoSetStateInPassiveEffects -import {useEffect, useState} from 'react'; - -function Component() { - const [state, setState] = useState(0); - const f = () => { - setState(s => s + 1); - }; - const g = () => { - f(); - }; - useEffect(() => { - g(); - }); - return state; -} - -``` - - -## Error - -``` - 11 | }; - 12 | useEffect(() => { -> 13 | g(); - | ^ InvalidReact: Calling setState directly within a useEffect causes cascading renders and is not recommended. Consider alternatives to useEffect. (https://react.dev/learn/you-might-not-need-an-effect) (13:13) - 14 | }); - 15 | return state; - 16 | } -``` - - \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-setState-in-useEffect.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-setState-in-useEffect.expect.md deleted file mode 100644 index 83be2965b8ad..000000000000 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-setState-in-useEffect.expect.md +++ /dev/null @@ -1,31 +0,0 @@ - -## Input - -```javascript -// @validateNoSetStateInPassiveEffects -import {useEffect, useState} from 'react'; - -function Component() { - const [state, setState] = useState(0); - useEffect(() => { - setState(s => s + 1); - }); - return state; -} - -``` - - -## Error - -``` - 5 | const [state, setState] = useState(0); - 6 | useEffect(() => { -> 7 | setState(s => s + 1); - | ^^^^^^^^ InvalidReact: Calling setState directly within a useEffect causes cascading renders and is not recommended. Consider alternatives to useEffect. (https://react.dev/learn/you-might-not-need-an-effect) (7:7) - 8 | }); - 9 | return state; - 10 | } -``` - - \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-jsx-in-catch-in-outer-try-with-catch.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-jsx-in-catch-in-outer-try-with-catch.expect.md new file mode 100644 index 000000000000..2e052ecf3355 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-jsx-in-catch-in-outer-try-with-catch.expect.md @@ -0,0 +1,73 @@ + +## Input + +```javascript +// @logger @validateNoJSXInTryStatements +import {identity} from 'shared-runtime'; + +function Component(props) { + let el; + try { + let value; + try { + value = identity(props.foo); + } catch { + el =
; + } + } catch { + return null; + } + return el; +} + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; // @logger @validateNoJSXInTryStatements +import { identity } from "shared-runtime"; + +function Component(props) { + const $ = _c(4); + let el; + try { + let value; + try { + let t0; + if ($[0] !== props.foo) { + t0 = identity(props.foo); + $[0] = props.foo; + $[1] = t0; + } else { + t0 = $[1]; + } + value = t0; + } catch { + let t0; + if ($[2] !== value) { + t0 =
; + $[2] = value; + $[3] = t0; + } else { + t0 = $[3]; + } + el = t0; + } + } catch { + return null; + } + return el; +} + +``` + +## Logs + +``` +{"kind":"CompileError","detail":{"options":{"reason":"Unexpected JSX element within a try statement. To catch errors in rendering a given component, wrap that component in an error boundary. (https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary)","description":null,"severity":"InvalidReact","loc":{"start":{"line":11,"column":11,"index":214},"end":{"line":11,"column":32,"index":235},"filename":"invalid-jsx-in-catch-in-outer-try-with-catch.ts"}}},"fnLoc":null} +{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":0,"index":83},"end":{"line":17,"column":1,"index":290},"filename":"invalid-jsx-in-catch-in-outer-try-with-catch.ts"},"fnName":"Component","memoSlots":4,"memoBlocks":2,"memoValues":2,"prunedMemoBlocks":0,"prunedMemoValues":0} +``` + +### 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.invalid-jsx-in-catch-in-outer-try-with-catch.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-jsx-in-catch-in-outer-try-with-catch.js similarity index 85% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-jsx-in-catch-in-outer-try-with-catch.js rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-jsx-in-catch-in-outer-try-with-catch.js index 0935a1a63cd8..2e5a3618b406 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-jsx-in-catch-in-outer-try-with-catch.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-jsx-in-catch-in-outer-try-with-catch.js @@ -1,4 +1,4 @@ -// @validateNoJSXInTryStatements +// @logger @validateNoJSXInTryStatements import {identity} from 'shared-runtime'; function Component(props) { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-jsx-in-try-with-catch.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-jsx-in-try-with-catch.expect.md new file mode 100644 index 000000000000..702d317a4b1d --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-jsx-in-try-with-catch.expect.md @@ -0,0 +1,50 @@ + +## Input + +```javascript +// @logger @validateNoJSXInTryStatements +function Component(props) { + let el; + try { + el =
; + } catch { + return null; + } + return el; +} + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; // @logger @validateNoJSXInTryStatements +function Component(props) { + const $ = _c(1); + let el; + try { + let t0; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t0 =
; + $[0] = t0; + } else { + t0 = $[0]; + } + el = t0; + } catch { + return null; + } + return el; +} + +``` + +## Logs + +``` +{"kind":"CompileError","detail":{"options":{"reason":"Unexpected JSX element within a try statement. To catch errors in rendering a given component, wrap that component in an error boundary. (https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary)","description":null,"severity":"InvalidReact","loc":{"start":{"line":5,"column":9,"index":96},"end":{"line":5,"column":16,"index":103},"filename":"invalid-jsx-in-try-with-catch.ts"}}},"fnLoc":null} +{"kind":"CompileSuccess","fnLoc":{"start":{"line":2,"column":0,"index":41},"end":{"line":10,"column":1,"index":152},"filename":"invalid-jsx-in-try-with-catch.ts"},"fnName":"Component","memoSlots":1,"memoBlocks":1,"memoValues":1,"prunedMemoBlocks":0,"prunedMemoValues":0} +``` + +### 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.invalid-jsx-in-try-with-catch.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-jsx-in-try-with-catch.js similarity index 73% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-jsx-in-try-with-catch.js rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-jsx-in-try-with-catch.js index 3e7747c875b3..815fb4ae9e5a 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-jsx-in-try-with-catch.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-jsx-in-try-with-catch.js @@ -1,4 +1,4 @@ -// @validateNoJSXInTryStatements +// @logger @validateNoJSXInTryStatements function Component(props) { let el; try { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-setState-in-useEffect-transitive.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-setState-in-useEffect-transitive.expect.md new file mode 100644 index 000000000000..d220617dc121 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-setState-in-useEffect-transitive.expect.md @@ -0,0 +1,73 @@ + +## Input + +```javascript +// @logger @validateNoSetStateInPassiveEffects +import {useEffect, useState} from 'react'; + +function Component() { + const [state, setState] = useState(0); + const f = () => { + setState(s => s + 1); + }; + const g = () => { + f(); + }; + useEffect(() => { + g(); + }); + return state; +} + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; // @logger @validateNoSetStateInPassiveEffects +import { useEffect, useState } from "react"; + +function Component() { + const $ = _c(2); + const [state, setState] = useState(0); + let t0; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + const f = () => { + setState(_temp); + }; + + t0 = () => { + f(); + }; + $[0] = t0; + } else { + t0 = $[0]; + } + const g = t0; + let t1; + if ($[1] === Symbol.for("react.memo_cache_sentinel")) { + t1 = () => { + g(); + }; + $[1] = t1; + } else { + t1 = $[1]; + } + useEffect(t1); + return state; +} +function _temp(s) { + return s + 1; +} + +``` + +## Logs + +``` +{"kind":"CompileError","detail":{"options":{"reason":"Calling setState directly within a useEffect causes cascading renders and is not recommended. Consider alternatives to useEffect. (https://react.dev/learn/you-might-not-need-an-effect)","description":null,"severity":"InvalidReact","suggestions":null,"loc":{"start":{"line":13,"column":4,"index":264},"end":{"line":13,"column":5,"index":265},"filename":"invalid-setState-in-useEffect-transitive.ts","identifierName":"g"}}},"fnLoc":null} +{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":0,"index":91},"end":{"line":16,"column":1,"index":292},"filename":"invalid-setState-in-useEffect-transitive.ts"},"fnName":"Component","memoSlots":2,"memoBlocks":2,"memoValues":2,"prunedMemoBlocks":0,"prunedMemoValues":0} +``` + +### 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.invalid-setState-in-useEffect-transitive.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-setState-in-useEffect-transitive.js similarity index 83% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-setState-in-useEffect-transitive.js rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-setState-in-useEffect-transitive.js index 099f0f7079be..b03c08609a3f 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-setState-in-useEffect-transitive.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-setState-in-useEffect-transitive.js @@ -1,4 +1,4 @@ -// @validateNoSetStateInPassiveEffects +// @logger @validateNoSetStateInPassiveEffects import {useEffect, useState} from 'react'; function Component() { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-setState-in-useEffect.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-setState-in-useEffect.expect.md new file mode 100644 index 000000000000..667f57b7cc23 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-setState-in-useEffect.expect.md @@ -0,0 +1,53 @@ + +## Input + +```javascript +// @logger @validateNoSetStateInPassiveEffects +import {useEffect, useState} from 'react'; + +function Component() { + const [state, setState] = useState(0); + useEffect(() => { + setState(s => s + 1); + }); + return state; +} + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; // @logger @validateNoSetStateInPassiveEffects +import { useEffect, useState } from "react"; + +function Component() { + const $ = _c(1); + const [state, setState] = useState(0); + let t0; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t0 = () => { + setState(_temp); + }; + $[0] = t0; + } else { + t0 = $[0]; + } + useEffect(t0); + return state; +} +function _temp(s) { + return s + 1; +} + +``` + +## Logs + +``` +{"kind":"CompileError","detail":{"options":{"reason":"Calling setState directly within a useEffect causes cascading renders and is not recommended. Consider alternatives to useEffect. (https://react.dev/learn/you-might-not-need-an-effect)","description":null,"severity":"InvalidReact","suggestions":null,"loc":{"start":{"line":7,"column":4,"index":179},"end":{"line":7,"column":12,"index":187},"filename":"invalid-setState-in-useEffect.ts","identifierName":"setState"}}},"fnLoc":null} +{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":0,"index":91},"end":{"line":10,"column":1,"index":224},"filename":"invalid-setState-in-useEffect.ts"},"fnName":"Component","memoSlots":1,"memoBlocks":1,"memoValues":1,"prunedMemoBlocks":0,"prunedMemoValues":0} +``` + +### 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.invalid-setState-in-useEffect.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-setState-in-useEffect.js similarity index 79% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-setState-in-useEffect.js rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-setState-in-useEffect.js index d7d8e4e8858f..e806b359c6e4 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-setState-in-useEffect.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-setState-in-useEffect.js @@ -1,4 +1,4 @@ -// @validateNoSetStateInPassiveEffects +// @logger @validateNoSetStateInPassiveEffects import {useEffect, useState} from 'react'; function Component() {