[compiler] Refactor validations to return Result and log where appropriate
Updates ~all of our validations to return a Result, and then updates callers to either unwrap() if they should bailout or else just log. ghstack-source-id: 418b5f5aa2b7dd49ca76b3f98a48a35150691d7e Pull Request resolved: https://github.com/facebook/react/pull/32688
Joe Savona committed
Mar 20, 2025 at 11:02 UTC
e3c06424ae1162319d786a76371d649dee412c29
25 files changed
+331
-200
compiler/packages/babel-plugin-react-compiler/src/CompilerError.ts
+5
@@ -6,6 +6,7 @@
6
*/
7
8
import type {SourceLocation} from './HIR';
9
+import {Err, Ok, Result} from './Utils/Result';
10
import {assertExhaustive} from './Utils/utils';
11
12
export enum ErrorSeverity {
@@ -224,6 +225,10 @@ export class CompilerError extends Error {
225
return this.details.length > 0;
226
}
227
228
+ asResult(): Result<void, CompilerError> {
229
+ return this.hasErrors() ? Err(this) : Ok(undefined);
230
+ }
231
+
232
/*
233
* An error is critical if it means the compiler has entered into a broken state and cannot
234
* continue safely. Other expected errors such as Todos mean that we can skip over that component
compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Pipeline.ts
+11
-11
@@ -100,7 +100,7 @@ import {propagateScopeDependenciesHIR} from '../HIR/PropagateScopeDependenciesHI
100
import {outlineJSX} from '../Optimization/OutlineJsx';
101
import {optimizePropsMethodCalls} from '../Optimization/OptimizePropsMethodCalls';
102
import {transformFire} from '../Transform';
103
-import {validateNoImpureFunctionsInRender} from '../Validation/ValiateNoImpureFunctionsInRender';
103
+import {validateNoImpureFunctionsInRender} from '../Validation/ValidateNoImpureFunctionsInRender';
104
import {CompilerError} from '..';
105
import {validateStaticComponents} from '../Validation/ValidateStaticComponents';
106
@@ -162,7 +162,7 @@ function runWithEnvironment(
162
log({kind: 'hir', name: 'PruneMaybeThrows', value: hir});
163
164
validateContextVariableLValues(hir);
165
- validateUseMemo(hir);
165
+ validateUseMemo(hir).unwrap();
166
167
if (
168
env.isInferredMemoEnabled &&
@@ -203,10 +203,10 @@ function runWithEnvironment(
203
204
if (env.isInferredMemoEnabled) {
205
if (env.config.validateHooksUsage) {
206
- validateHooksUsage(hir);
206
+ validateHooksUsage(hir).unwrap();
207
}
208
if (env.config.validateNoCapitalizedCalls) {
209
- validateNoCapitalizedCalls(hir);
209
+ validateNoCapitalizedCalls(hir).unwrap();
210
}
211
}
212
@@ -256,23 +256,23 @@ function runWithEnvironment(
256
}
257
258
if (env.config.validateRefAccessDuringRender) {
259
- validateNoRefAccessInRender(hir);
259
+ validateNoRefAccessInRender(hir).unwrap();
260
}
261
262
if (env.config.validateNoSetStateInRender) {
263
- validateNoSetStateInRender(hir);
263
+ validateNoSetStateInRender(hir).unwrap();
264
}
265
266
if (env.config.validateNoSetStateInPassiveEffects) {
267
- validateNoSetStateInPassiveEffects(hir);
267
+ env.logErrors(validateNoSetStateInPassiveEffects(hir));
268
}
269
270
if (env.config.validateNoJSXInTryStatements) {
271
- validateNoJSXInTryStatement(hir);
271
+ env.logErrors(validateNoJSXInTryStatement(hir));
272
}
273
274
if (env.config.validateNoImpureFunctionsInRender) {
275
- validateNoImpureFunctionsInRender(hir);
275
+ validateNoImpureFunctionsInRender(hir).unwrap();
276
}
277
}
278
@@ -514,14 +514,14 @@ function runWithEnvironment(
514
});
515
516
if (env.config.validateMemoizedEffectDependencies) {
517
- validateMemoizedEffectDependencies(reactiveFunction);
517
+ validateMemoizedEffectDependencies(reactiveFunction).unwrap();
518
}
519
520
if (
521
env.config.enablePreserveExistingMemoizationGuarantees ||
522
env.config.validatePreserveExistingMemoizationGuarantees
523
) {
524
- validatePreservedManualMemoization(reactiveFunction);
524
+ validatePreservedManualMemoization(reactiveFunction).unwrap();
525
}
526
527
const ast = codegenFunction(reactiveFunction, {
compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateHooksUsage.ts
+5
-4
@@ -26,6 +26,7 @@ import {
26
eachTerminalOperand,
27
} from '../HIR/visitors';
28
import {assertExhaustive} from '../Utils/utils';
29
+import {Result} from '../Utils/Result';
30
31
/**
32
* 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 {
88
* may not appear as the callee of a conditional call.
89
* See the note for Kind.PotentialHook for sources of potential hooks
90
*/
90
-export function validateHooksUsage(fn: HIRFunction): void {
91
+export function validateHooksUsage(
92
+ fn: HIRFunction,
93
+): Result<void, CompilerError> {
94
const unconditionalBlocks = computeUnconditionalBlocks(fn);
95
96
const errors = new CompilerError();
@@ -423,9 +426,7 @@ export function validateHooksUsage(fn: HIRFunction): void {
426
for (const [, error] of errorsByPlace) {
427
errors.push(error);
428
}
426
- if (errors.hasErrors()) {
427
- throw errors;
428
- }
429
+ return errors.asResult();
430
}
431
432
function visitFunctionExpression(errors: CompilerError, fn: HIRFunction): void {
compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateMemoizedEffectDependencies.ts
+5
-4
@@ -22,6 +22,7 @@ import {
22
ReactiveFunctionVisitor,
23
visitReactiveFunction,
24
} from '../ReactiveScopes/visitors';
25
+import {Result} from '../Utils/Result';
26
27
/**
28
* Validates that all known effect dependencies are memoized. The algorithm checks two things:
@@ -47,12 +48,12 @@ import {
48
* mutate(object); // ... mutable range ends here after this mutation
49
* ```
50
*/
50
-export function validateMemoizedEffectDependencies(fn: ReactiveFunction): void {
51
+export function validateMemoizedEffectDependencies(
52
+ fn: ReactiveFunction,
53
+): Result<void, CompilerError> {
54
const errors = new CompilerError();
55
visitReactiveFunction(fn, new Visitor(), errors);
53
- if (errors.hasErrors()) {
54
- throw errors;
55
- }
56
+ return errors.asResult();
57
}
58
59
class Visitor extends ReactiveFunctionVisitor<CompilerError> {
compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoCapitalizedCalls.ts
+9
-3
@@ -4,11 +4,14 @@
4
* This source code is licensed under the MIT license found in the
5
* LICENSE file in the root directory of this source tree.
6
*/
7
-import {CompilerError, EnvironmentConfig} from '..';
7
+import {CompilerError, EnvironmentConfig, ErrorSeverity} from '..';
8
import {HIRFunction, IdentifierId} from '../HIR';
9
import {DEFAULT_GLOBALS} from '../HIR/Globals';
10
+import {Result} from '../Utils/Result';
11
11
-export function validateNoCapitalizedCalls(fn: HIRFunction): void {
12
+export function validateNoCapitalizedCalls(
13
+ fn: HIRFunction,
14
+): Result<void, CompilerError> {
15
const envConfig: EnvironmentConfig = fn.env.config;
16
const ALLOW_LIST = new Set([
17
...DEFAULT_GLOBALS.keys(),
@@ -26,6 +29,7 @@ export function validateNoCapitalizedCalls(fn: HIRFunction): void {
29
);
30
};
31
32
+ const errors = new CompilerError();
33
const capitalLoadGlobals = new Map<IdentifierId, string>();
34
const capitalizedProperties = new Map<IdentifierId, string>();
35
const reason =
@@ -73,7 +77,8 @@ export function validateNoCapitalizedCalls(fn: HIRFunction): void {
77
const propertyIdentifier = value.property.identifier.id;
78
const propertyName = capitalizedProperties.get(propertyIdentifier);
79
if (propertyName != null) {
76
- CompilerError.throwInvalidReact({
80
+ errors.push({
81
+ severity: ErrorSeverity.InvalidReact,
82
reason,
83
description: `${propertyName} may be a component.`,
84
loc: value.loc,
@@ -85,4 +90,5 @@ export function validateNoCapitalizedCalls(fn: HIRFunction): void {
90
}
91
}
92
}
93
+ return errors.asResult();
94
}
compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoImpureFunctionsInRender.ts
renamed
+5
-4
@@ -8,6 +8,7 @@
8
import {CompilerError, ErrorSeverity} from '..';
9
import {HIRFunction} from '../HIR';
10
import {getFunctionCallSignature} from '../Inference/InferReferenceEffects';
11
+import {Result} from '../Utils/Result';
12
13
/**
14
* Checks that known-impure functions are not called during render. Examples of invalid functions to
@@ -18,7 +19,9 @@ import {getFunctionCallSignature} from '../Inference/InferReferenceEffects';
19
* this in several of our validation passes and should unify those analyses into a reusable helper
20
* and use it here.
21
*/
21
-export function validateNoImpureFunctionsInRender(fn: HIRFunction): void {
22
+export function validateNoImpureFunctionsInRender(
23
+ fn: HIRFunction,
24
+): Result<void, CompilerError> {
25
const errors = new CompilerError();
26
for (const [, block] of fn.body.blocks) {
27
for (const instr of block.instructions) {
@@ -46,7 +49,5 @@ export function validateNoImpureFunctionsInRender(fn: HIRFunction): void {
49
}
50
}
51
}
49
- if (errors.hasErrors()) {
50
- throw errors;
51
- }
52
+ return errors.asResult();
53
}
compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoJSXInTryStatement.ts
+5
-4
@@ -7,6 +7,7 @@
7
8
import {CompilerError, ErrorSeverity} from '..';
9
import {BlockId, HIRFunction} from '../HIR';
10
+import {Result} from '../Utils/Result';
11
import {retainWhere} from '../Utils/utils';
12
13
/**
@@ -19,7 +20,9 @@ import {retainWhere} from '../Utils/utils';
20
* created within a try block. JSX is allowed within a catch statement, unless that catch
21
* is itself nested inside an outer try.
22
*/
22
-export function validateNoJSXInTryStatement(fn: HIRFunction): void {
23
+export function validateNoJSXInTryStatement(
24
+ fn: HIRFunction,
25
+): Result<void, CompilerError> {
26
const activeTryBlocks: Array<BlockId> = [];
27
const errors = new CompilerError();
28
for (const [, block] of fn.body.blocks) {
@@ -46,7 +49,5 @@ export function validateNoJSXInTryStatement(fn: HIRFunction): void {
49
activeTryBlocks.push(block.terminal.handler);
50
}
51
}
49
- if (errors.hasErrors()) {
50
- throw errors;
51
- }
52
+ return errors.asResult();
53
}
compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoRefAccesInRender.ts
+4
-2
@@ -99,9 +99,11 @@ class Env extends Map<IdentifierId, RefAccessType> {
99
}
100
}
101
102
-export function validateNoRefAccessInRender(fn: HIRFunction): void {
102
+export function validateNoRefAccessInRender(
103
+ fn: HIRFunction,
104
+): Result<void, CompilerError> {
105
const env = new Env();
104
- validateNoRefAccessInRenderImpl(fn, env).unwrap();
106
+ return validateNoRefAccessInRenderImpl(fn, env).map(_ => undefined);
107
}
108
109
function refTypeOfType(place: Place): RefAccessType {
compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoSetStateInPassiveEffects.ts
+5
-4
@@ -14,6 +14,7 @@ import {
14
Place,
15
} from '../HIR';
16
import {eachInstructionValueOperand} from '../HIR/visitors';
17
+import {Result} from '../Utils/Result';
18
19
/**
20
* Validates against calling setState in the body of a *passive* effect (useEffect),
@@ -23,7 +24,9 @@ import {eachInstructionValueOperand} from '../HIR/visitors';
24
* often bad for performance and frequently has more efficient and straightforward
25
* alternatives. See https://react.dev/learn/you-might-not-need-an-effect for examples.
26
*/
26
-export function validateNoSetStateInPassiveEffects(fn: HIRFunction): void {
27
+export function validateNoSetStateInPassiveEffects(
28
+ fn: HIRFunction,
29
+): Result<void, CompilerError> {
30
const setStateFunctions: Map<IdentifierId, Place> = new Map();
31
const errors = new CompilerError();
32
for (const [, block] of fn.body.blocks) {
@@ -98,9 +101,7 @@ export function validateNoSetStateInPassiveEffects(fn: HIRFunction): void {
101
}
102
}
103
101
- if (errors.hasErrors()) {
102
- throw errors;
103
- }
104
+ return errors.asResult();
105
}
106
107
function getSetStateCall(
compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoSetStateInRender.ts
+6
-8
@@ -9,7 +9,7 @@ import {CompilerError, ErrorSeverity} from '../CompilerError';
9
import {HIRFunction, IdentifierId, isSetStateType} from '../HIR';
10
import {computeUnconditionalBlocks} from '../HIR/ComputeUnconditionalBlocks';
11
import {eachInstructionValueOperand} from '../HIR/visitors';
12
-import {Err, Ok, Result} from '../Utils/Result';
12
+import {Result} from '../Utils/Result';
13
14
/**
15
* Validates that the given function does not have an infinite update loop
@@ -39,9 +39,11 @@ import {Err, Ok, Result} from '../Utils/Result';
39
* y();
40
* ```
41
*/
42
-export function validateNoSetStateInRender(fn: HIRFunction): void {
42
+export function validateNoSetStateInRender(
43
+ fn: HIRFunction,
44
+): Result<void, CompilerError> {
45
const unconditionalSetStateFunctions: Set<IdentifierId> = new Set();
44
- validateNoSetStateInRenderImpl(fn, unconditionalSetStateFunctions).unwrap();
46
+ return validateNoSetStateInRenderImpl(fn, unconditionalSetStateFunctions);
47
}
48
49
function validateNoSetStateInRenderImpl(
@@ -145,9 +147,5 @@ function validateNoSetStateInRenderImpl(
147
}
148
}
149
148
- if (errors.hasErrors()) {
149
- return Err(errors);
150
- } else {
151
- return Ok(undefined);
152
- }
150
+ return errors.asResult();
151
}
compiler/packages/babel-plugin-react-compiler/src/Validation/ValidatePreservedManualMemoization.ts
+7
-5
@@ -5,9 +5,10 @@
5
* LICENSE file in the root directory of this source tree.
6
*/
7
8
-import {CompilerError, Effect, ErrorSeverity} from '..';
8
+import {CompilerError, ErrorSeverity} from '../CompilerError';
9
import {
10
DeclarationId,
11
+ Effect,
12
GeneratedSource,
13
Identifier,
14
IdentifierId,
@@ -30,6 +31,7 @@ import {
31
ReactiveFunctionVisitor,
32
visitReactiveFunction,
33
} from '../ReactiveScopes/visitors';
34
+import {Result} from '../Utils/Result';
35
import {getOrInsertDefault} from '../Utils/utils';
36
37
/**
@@ -39,15 +41,15 @@ import {getOrInsertDefault} from '../Utils/utils';
41
* This can occur if a value's mutable range somehow extended to include a hook and
42
* was pruned.
43
*/
42
-export function validatePreservedManualMemoization(fn: ReactiveFunction): void {
44
+export function validatePreservedManualMemoization(
45
+ fn: ReactiveFunction,
46
+): Result<void, CompilerError> {
47
const state = {
48
errors: new CompilerError(),
49
manualMemoState: null,
50
};
51
visitReactiveFunction(fn, new Visitor(), state);
48
- if (state.errors.hasErrors()) {
49
- throw state.errors;
50
- }
52
+ return state.errors.asResult();
53
}
54
55
const DEBUG = false;
compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateStaticComponents.ts
+2
-6
@@ -7,7 +7,7 @@
7
8
import {CompilerError, ErrorSeverity} from '../CompilerError';
9
import {HIRFunction, IdentifierId, SourceLocation} from '../HIR';
10
-import {Err, Ok, Result} from '../Utils/Result';
10
+import {Result} from '../Utils/Result';
11
12
/**
13
* Validates against components that are created dynamically and whose identity is not guaranteed
@@ -79,9 +79,5 @@ export function validateStaticComponents(
79
}
80
}
81
}
82
- if (error.hasErrors()) {
83
- return Err(error);
84
- } else {
85
- return Ok(undefined);
86
- }
82
+ return error.asResult();
83
}
compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateUseMemo.ts
+9
-4
@@ -5,10 +5,12 @@
5
* LICENSE file in the root directory of this source tree.
6
*/
7
8
-import {CompilerError} from '..';
8
+import {CompilerError, ErrorSeverity} from '..';
9
import {FunctionExpression, HIRFunction, IdentifierId} from '../HIR';
10
+import {Result} from '../Utils/Result';
11
11
-export function validateUseMemo(fn: HIRFunction): void {
12
+export function validateUseMemo(fn: HIRFunction): Result<void, CompilerError> {
13
+ const errors = new CompilerError();
14
const useMemos = new Set<IdentifierId>();
15
const react = new Set<IdentifierId>();
16
const functions = new Map<IdentifierId, FunctionExpression>();
@@ -61,7 +63,8 @@ export function validateUseMemo(fn: HIRFunction): void {
63
}
64
65
if (body.loweredFunc.func.params.length > 0) {
64
- CompilerError.throwInvalidReact({
66
+ errors.push({
67
+ severity: ErrorSeverity.InvalidReact,
68
reason: 'useMemo callbacks may not accept any arguments',
69
description: null,
70
loc: body.loc,
@@ -70,7 +73,8 @@ export function validateUseMemo(fn: HIRFunction): void {
73
}
74
75
if (body.loweredFunc.func.async || body.loweredFunc.func.generator) {
73
- CompilerError.throwInvalidReact({
76
+ errors.push({
77
+ severity: ErrorSeverity.InvalidReact,
78
reason:
79
'useMemo callbacks may not be async or generator functions',
80
description: null,
@@ -84,4 +88,5 @@ export function validateUseMemo(fn: HIRFunction): void {
88
}
89
}
90
}
91
+ return errors.asResult();
92
}
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-jsx-in-catch-in-outer-try-with-catch.expect.md
deleted
-38
@@ -1,38 +0,0 @@
1
-
2
-## Input
3
-
4
-```javascript
5
-// @validateNoJSXInTryStatements
6
-import {identity} from 'shared-runtime';
7
-
8
-function Component(props) {
9
- let el;
10
- try {
11
- let value;
12
- try {
13
- value = identity(props.foo);
14
- } catch {
15
- el = <div value={value} />;
16
- }
17
- } catch {
18
- return null;
19
- }
20
- return el;
21
-}
22
-
23
-```
24
-
25
-
26
-## Error
27
-
28
-```
29
- 9 | value = identity(props.foo);
30
- 10 | } catch {
31
-> 11 | el = <div value={value} />;
32
- | ^^^^^^^^^^^^^^^^^^^^^ 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)
33
- 12 | }
34
- 13 | } catch {
35
- 14 | return null;
36
-```
37
-
38
-
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-jsx-in-try-with-catch.expect.md
deleted
-31
@@ -1,31 +0,0 @@
1
-
2
-## Input
3
-
4
-```javascript
5
-// @validateNoJSXInTryStatements
6
-function Component(props) {
7
- let el;
8
- try {
9
- el = <div />;
10
- } catch {
11
- return null;
12
- }
13
- return el;
14
-}
15
-
16
-```
17
-
18
-
19
-## Error
20
-
21
-```
22
- 3 | let el;
23
- 4 | try {
24
-> 5 | el = <div />;
25
- | ^^^^^^^ 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)
26
- 6 | } catch {
27
- 7 | return null;
28
- 8 | }
29
-```
30
-
31
-
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-setState-in-useEffect-transitive.expect.md
deleted
-37
@@ -1,37 +0,0 @@
1
-
2
-## Input
3
-
4
-```javascript
5
-// @validateNoSetStateInPassiveEffects
6
-import {useEffect, useState} from 'react';
7
-
8
-function Component() {
9
- const [state, setState] = useState(0);
10
- const f = () => {
11
- setState(s => s + 1);
12
- };
13
- const g = () => {
14
- f();
15
- };
16
- useEffect(() => {
17
- g();
18
- });
19
- return state;
20
-}
21
-
22
-```
23
-
24
-
25
-## Error
26
-
27
-```
28
- 11 | };
29
- 12 | useEffect(() => {
30
-> 13 | g();
31
- | ^ 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)
32
- 14 | });
33
- 15 | return state;
34
- 16 | }
35
-```
36
-
37
-
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-setState-in-useEffect.expect.md
deleted
-31
@@ -1,31 +0,0 @@
1
-
2
-## Input
3
-
4
-```javascript
5
-// @validateNoSetStateInPassiveEffects
6
-import {useEffect, useState} from 'react';
7
-
8
-function Component() {
9
- const [state, setState] = useState(0);
10
- useEffect(() => {
11
- setState(s => s + 1);
12
- });
13
- return state;
14
-}
15
-
16
-```
17
-
18
-
19
-## Error
20
-
21
-```
22
- 5 | const [state, setState] = useState(0);
23
- 6 | useEffect(() => {
24
-> 7 | setState(s => s + 1);
25
- | ^^^^^^^^ 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)
26
- 8 | });
27
- 9 | return state;
28
- 10 | }
29
-```
30
-
31
-
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-jsx-in-catch-in-outer-try-with-catch.expect.md
new
+73
@@ -0,0 +1,73 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+// @logger @validateNoJSXInTryStatements
6
+import {identity} from 'shared-runtime';
7
+
8
+function Component(props) {
9
+ let el;
10
+ try {
11
+ let value;
12
+ try {
13
+ value = identity(props.foo);
14
+ } catch {
15
+ el = <div value={value} />;
16
+ }
17
+ } catch {
18
+ return null;
19
+ }
20
+ return el;
21
+}
22
+
23
+```
24
+
25
+## Code
26
+
27
+```javascript
28
+import { c as _c } from "react/compiler-runtime"; // @logger @validateNoJSXInTryStatements
29
+import { identity } from "shared-runtime";
30
+
31
+function Component(props) {
32
+ const $ = _c(4);
33
+ let el;
34
+ try {
35
+ let value;
36
+ try {
37
+ let t0;
38
+ if ($[0] !== props.foo) {
39
+ t0 = identity(props.foo);
40
+ $[0] = props.foo;
41
+ $[1] = t0;
42
+ } else {
43
+ t0 = $[1];
44
+ }
45
+ value = t0;
46
+ } catch {
47
+ let t0;
48
+ if ($[2] !== value) {
49
+ t0 = <div value={value} />;
50
+ $[2] = value;
51
+ $[3] = t0;
52
+ } else {
53
+ t0 = $[3];
54
+ }
55
+ el = t0;
56
+ }
57
+ } catch {
58
+ return null;
59
+ }
60
+ return el;
61
+}
62
+
63
+```
64
+
65
+## Logs
66
+
67
+```
68
+{"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}
69
+{"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}
70
+```
71
+
72
+### Eval output
73
+(kind: exception) Fixture not implemented
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-jsx-in-catch-in-outer-try-with-catch.js
renamed
+1
-1
@@ -1,4 +1,4 @@
1
-// @validateNoJSXInTryStatements
1
+// @logger @validateNoJSXInTryStatements
2
import {identity} from 'shared-runtime';
3
4
function Component(props) {
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-jsx-in-try-with-catch.expect.md
new
+50
@@ -0,0 +1,50 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+// @logger @validateNoJSXInTryStatements
6
+function Component(props) {
7
+ let el;
8
+ try {
9
+ el = <div />;
10
+ } catch {
11
+ return null;
12
+ }
13
+ return el;
14
+}
15
+
16
+```
17
+
18
+## Code
19
+
20
+```javascript
21
+import { c as _c } from "react/compiler-runtime"; // @logger @validateNoJSXInTryStatements
22
+function Component(props) {
23
+ const $ = _c(1);
24
+ let el;
25
+ try {
26
+ let t0;
27
+ if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
28
+ t0 = <div />;
29
+ $[0] = t0;
30
+ } else {
31
+ t0 = $[0];
32
+ }
33
+ el = t0;
34
+ } catch {
35
+ return null;
36
+ }
37
+ return el;
38
+}
39
+
40
+```
41
+
42
+## Logs
43
+
44
+```
45
+{"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}
46
+{"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}
47
+```
48
+
49
+### Eval output
50
+(kind: exception) Fixture not implemented
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-jsx-in-try-with-catch.js
renamed
+1
-1
@@ -1,4 +1,4 @@
1
-// @validateNoJSXInTryStatements
1
+// @logger @validateNoJSXInTryStatements
2
function Component(props) {
3
let el;
4
try {
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-setState-in-useEffect-transitive.expect.md
new
+73
@@ -0,0 +1,73 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+// @logger @validateNoSetStateInPassiveEffects
6
+import {useEffect, useState} from 'react';
7
+
8
+function Component() {
9
+ const [state, setState] = useState(0);
10
+ const f = () => {
11
+ setState(s => s + 1);
12
+ };
13
+ const g = () => {
14
+ f();
15
+ };
16
+ useEffect(() => {
17
+ g();
18
+ });
19
+ return state;
20
+}
21
+
22
+```
23
+
24
+## Code
25
+
26
+```javascript
27
+import { c as _c } from "react/compiler-runtime"; // @logger @validateNoSetStateInPassiveEffects
28
+import { useEffect, useState } from "react";
29
+
30
+function Component() {
31
+ const $ = _c(2);
32
+ const [state, setState] = useState(0);
33
+ let t0;
34
+ if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
35
+ const f = () => {
36
+ setState(_temp);
37
+ };
38
+
39
+ t0 = () => {
40
+ f();
41
+ };
42
+ $[0] = t0;
43
+ } else {
44
+ t0 = $[0];
45
+ }
46
+ const g = t0;
47
+ let t1;
48
+ if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
49
+ t1 = () => {
50
+ g();
51
+ };
52
+ $[1] = t1;
53
+ } else {
54
+ t1 = $[1];
55
+ }
56
+ useEffect(t1);
57
+ return state;
58
+}
59
+function _temp(s) {
60
+ return s + 1;
61
+}
62
+
63
+```
64
+
65
+## Logs
66
+
67
+```
68
+{"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}
69
+{"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}
70
+```
71
+
72
+### Eval output
73
+(kind: exception) Fixture not implemented
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-setState-in-useEffect-transitive.js
renamed
+1
-1
@@ -1,4 +1,4 @@
1
-// @validateNoSetStateInPassiveEffects
1
+// @logger @validateNoSetStateInPassiveEffects
2
import {useEffect, useState} from 'react';
3
4
function Component() {
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-setState-in-useEffect.expect.md
new
+53
@@ -0,0 +1,53 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+// @logger @validateNoSetStateInPassiveEffects
6
+import {useEffect, useState} from 'react';
7
+
8
+function Component() {
9
+ const [state, setState] = useState(0);
10
+ useEffect(() => {
11
+ setState(s => s + 1);
12
+ });
13
+ return state;
14
+}
15
+
16
+```
17
+
18
+## Code
19
+
20
+```javascript
21
+import { c as _c } from "react/compiler-runtime"; // @logger @validateNoSetStateInPassiveEffects
22
+import { useEffect, useState } from "react";
23
+
24
+function Component() {
25
+ const $ = _c(1);
26
+ const [state, setState] = useState(0);
27
+ let t0;
28
+ if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
29
+ t0 = () => {
30
+ setState(_temp);
31
+ };
32
+ $[0] = t0;
33
+ } else {
34
+ t0 = $[0];
35
+ }
36
+ useEffect(t0);
37
+ return state;
38
+}
39
+function _temp(s) {
40
+ return s + 1;
41
+}
42
+
43
+```
44
+
45
+## Logs
46
+
47
+```
48
+{"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}
49
+{"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}
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/invalid-setState-in-useEffect.js
renamed
+1
-1
@@ -1,4 +1,4 @@
1
-// @validateNoSetStateInPassiveEffects
1
+// @logger @validateNoSetStateInPassiveEffects
2
import {useEffect, useState} from 'react';
3
4
function Component() {