Skip to content

Commit e3c0642

Browse files
committed
[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: 418b5f5 Pull Request resolved: facebook#32688
1 parent 5f4c5c9 commit e3c0642

25 files changed

+331
-200
lines changed

compiler/packages/babel-plugin-react-compiler/src/CompilerError.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
*/
77

88
import type {SourceLocation} from './HIR';
9+
import {Err, Ok, Result} from './Utils/Result';
910
import {assertExhaustive} from './Utils/utils';
1011

1112
export enum ErrorSeverity {
@@ -224,6 +225,10 @@ export class CompilerError extends Error {
224225
return this.details.length > 0;
225226
}
226227

228+
asResult(): Result<void, CompilerError> {
229+
return this.hasErrors() ? Err(this) : Ok(undefined);
230+
}
231+
227232
/*
228233
* An error is critical if it means the compiler has entered into a broken state and cannot
229234
* 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

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ import {propagateScopeDependenciesHIR} from '../HIR/PropagateScopeDependenciesHI
100100
import {outlineJSX} from '../Optimization/OutlineJsx';
101101
import {optimizePropsMethodCalls} from '../Optimization/OptimizePropsMethodCalls';
102102
import {transformFire} from '../Transform';
103-
import {validateNoImpureFunctionsInRender} from '../Validation/ValiateNoImpureFunctionsInRender';
103+
import {validateNoImpureFunctionsInRender} from '../Validation/ValidateNoImpureFunctionsInRender';
104104
import {CompilerError} from '..';
105105
import {validateStaticComponents} from '../Validation/ValidateStaticComponents';
106106

@@ -162,7 +162,7 @@ function runWithEnvironment(
162162
log({kind: 'hir', name: 'PruneMaybeThrows', value: hir});
163163

164164
validateContextVariableLValues(hir);
165-
validateUseMemo(hir);
165+
validateUseMemo(hir).unwrap();
166166

167167
if (
168168
env.isInferredMemoEnabled &&
@@ -203,10 +203,10 @@ function runWithEnvironment(
203203

204204
if (env.isInferredMemoEnabled) {
205205
if (env.config.validateHooksUsage) {
206-
validateHooksUsage(hir);
206+
validateHooksUsage(hir).unwrap();
207207
}
208208
if (env.config.validateNoCapitalizedCalls) {
209-
validateNoCapitalizedCalls(hir);
209+
validateNoCapitalizedCalls(hir).unwrap();
210210
}
211211
}
212212

@@ -256,23 +256,23 @@ function runWithEnvironment(
256256
}
257257

258258
if (env.config.validateRefAccessDuringRender) {
259-
validateNoRefAccessInRender(hir);
259+
validateNoRefAccessInRender(hir).unwrap();
260260
}
261261

262262
if (env.config.validateNoSetStateInRender) {
263-
validateNoSetStateInRender(hir);
263+
validateNoSetStateInRender(hir).unwrap();
264264
}
265265

266266
if (env.config.validateNoSetStateInPassiveEffects) {
267-
validateNoSetStateInPassiveEffects(hir);
267+
env.logErrors(validateNoSetStateInPassiveEffects(hir));
268268
}
269269

270270
if (env.config.validateNoJSXInTryStatements) {
271-
validateNoJSXInTryStatement(hir);
271+
env.logErrors(validateNoJSXInTryStatement(hir));
272272
}
273273

274274
if (env.config.validateNoImpureFunctionsInRender) {
275-
validateNoImpureFunctionsInRender(hir);
275+
validateNoImpureFunctionsInRender(hir).unwrap();
276276
}
277277
}
278278

@@ -514,14 +514,14 @@ function runWithEnvironment(
514514
});
515515

516516
if (env.config.validateMemoizedEffectDependencies) {
517-
validateMemoizedEffectDependencies(reactiveFunction);
517+
validateMemoizedEffectDependencies(reactiveFunction).unwrap();
518518
}
519519

520520
if (
521521
env.config.enablePreserveExistingMemoizationGuarantees ||
522522
env.config.validatePreserveExistingMemoizationGuarantees
523523
) {
524-
validatePreservedManualMemoization(reactiveFunction);
524+
validatePreservedManualMemoization(reactiveFunction).unwrap();
525525
}
526526

527527
const ast = codegenFunction(reactiveFunction, {

compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateHooksUsage.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import {
2626
eachTerminalOperand,
2727
} from '../HIR/visitors';
2828
import {assertExhaustive} from '../Utils/utils';
29+
import {Result} from '../Utils/Result';
2930

3031
/**
3132
* 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 {
8788
* may not appear as the callee of a conditional call.
8889
* See the note for Kind.PotentialHook for sources of potential hooks
8990
*/
90-
export function validateHooksUsage(fn: HIRFunction): void {
91+
export function validateHooksUsage(
92+
fn: HIRFunction,
93+
): Result<void, CompilerError> {
9194
const unconditionalBlocks = computeUnconditionalBlocks(fn);
9295

9396
const errors = new CompilerError();
@@ -423,9 +426,7 @@ export function validateHooksUsage(fn: HIRFunction): void {
423426
for (const [, error] of errorsByPlace) {
424427
errors.push(error);
425428
}
426-
if (errors.hasErrors()) {
427-
throw errors;
428-
}
429+
return errors.asResult();
429430
}
430431

431432
function visitFunctionExpression(errors: CompilerError, fn: HIRFunction): void {

compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateMemoizedEffectDependencies.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import {
2222
ReactiveFunctionVisitor,
2323
visitReactiveFunction,
2424
} from '../ReactiveScopes/visitors';
25+
import {Result} from '../Utils/Result';
2526

2627
/**
2728
* Validates that all known effect dependencies are memoized. The algorithm checks two things:
@@ -47,12 +48,12 @@ import {
4748
* mutate(object); // ... mutable range ends here after this mutation
4849
* ```
4950
*/
50-
export function validateMemoizedEffectDependencies(fn: ReactiveFunction): void {
51+
export function validateMemoizedEffectDependencies(
52+
fn: ReactiveFunction,
53+
): Result<void, CompilerError> {
5154
const errors = new CompilerError();
5255
visitReactiveFunction(fn, new Visitor(), errors);
53-
if (errors.hasErrors()) {
54-
throw errors;
55-
}
56+
return errors.asResult();
5657
}
5758

5859
class Visitor extends ReactiveFunctionVisitor<CompilerError> {

compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoCapitalizedCalls.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,14 @@
44
* This source code is licensed under the MIT license found in the
55
* LICENSE file in the root directory of this source tree.
66
*/
7-
import {CompilerError, EnvironmentConfig} from '..';
7+
import {CompilerError, EnvironmentConfig, ErrorSeverity} from '..';
88
import {HIRFunction, IdentifierId} from '../HIR';
99
import {DEFAULT_GLOBALS} from '../HIR/Globals';
10+
import {Result} from '../Utils/Result';
1011

11-
export function validateNoCapitalizedCalls(fn: HIRFunction): void {
12+
export function validateNoCapitalizedCalls(
13+
fn: HIRFunction,
14+
): Result<void, CompilerError> {
1215
const envConfig: EnvironmentConfig = fn.env.config;
1316
const ALLOW_LIST = new Set([
1417
...DEFAULT_GLOBALS.keys(),
@@ -26,6 +29,7 @@ export function validateNoCapitalizedCalls(fn: HIRFunction): void {
2629
);
2730
};
2831

32+
const errors = new CompilerError();
2933
const capitalLoadGlobals = new Map<IdentifierId, string>();
3034
const capitalizedProperties = new Map<IdentifierId, string>();
3135
const reason =
@@ -73,7 +77,8 @@ export function validateNoCapitalizedCalls(fn: HIRFunction): void {
7377
const propertyIdentifier = value.property.identifier.id;
7478
const propertyName = capitalizedProperties.get(propertyIdentifier);
7579
if (propertyName != null) {
76-
CompilerError.throwInvalidReact({
80+
errors.push({
81+
severity: ErrorSeverity.InvalidReact,
7782
reason,
7883
description: `${propertyName} may be a component.`,
7984
loc: value.loc,
@@ -85,4 +90,5 @@ export function validateNoCapitalizedCalls(fn: HIRFunction): void {
8590
}
8691
}
8792
}
93+
return errors.asResult();
8894
}
Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import {CompilerError, ErrorSeverity} from '..';
99
import {HIRFunction} from '../HIR';
1010
import {getFunctionCallSignature} from '../Inference/InferReferenceEffects';
11+
import {Result} from '../Utils/Result';
1112

1213
/**
1314
* Checks that known-impure functions are not called during render. Examples of invalid functions to
@@ -18,7 +19,9 @@ import {getFunctionCallSignature} from '../Inference/InferReferenceEffects';
1819
* this in several of our validation passes and should unify those analyses into a reusable helper
1920
* and use it here.
2021
*/
21-
export function validateNoImpureFunctionsInRender(fn: HIRFunction): void {
22+
export function validateNoImpureFunctionsInRender(
23+
fn: HIRFunction,
24+
): Result<void, CompilerError> {
2225
const errors = new CompilerError();
2326
for (const [, block] of fn.body.blocks) {
2427
for (const instr of block.instructions) {
@@ -46,7 +49,5 @@ export function validateNoImpureFunctionsInRender(fn: HIRFunction): void {
4649
}
4750
}
4851
}
49-
if (errors.hasErrors()) {
50-
throw errors;
51-
}
52+
return errors.asResult();
5253
}

compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoJSXInTryStatement.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import {CompilerError, ErrorSeverity} from '..';
99
import {BlockId, HIRFunction} from '../HIR';
10+
import {Result} from '../Utils/Result';
1011
import {retainWhere} from '../Utils/utils';
1112

1213
/**
@@ -19,7 +20,9 @@ import {retainWhere} from '../Utils/utils';
1920
* created within a try block. JSX is allowed within a catch statement, unless that catch
2021
* is itself nested inside an outer try.
2122
*/
22-
export function validateNoJSXInTryStatement(fn: HIRFunction): void {
23+
export function validateNoJSXInTryStatement(
24+
fn: HIRFunction,
25+
): Result<void, CompilerError> {
2326
const activeTryBlocks: Array<BlockId> = [];
2427
const errors = new CompilerError();
2528
for (const [, block] of fn.body.blocks) {
@@ -46,7 +49,5 @@ export function validateNoJSXInTryStatement(fn: HIRFunction): void {
4649
activeTryBlocks.push(block.terminal.handler);
4750
}
4851
}
49-
if (errors.hasErrors()) {
50-
throw errors;
51-
}
52+
return errors.asResult();
5253
}

compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoRefAccesInRender.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,9 +99,11 @@ class Env extends Map<IdentifierId, RefAccessType> {
9999
}
100100
}
101101

102-
export function validateNoRefAccessInRender(fn: HIRFunction): void {
102+
export function validateNoRefAccessInRender(
103+
fn: HIRFunction,
104+
): Result<void, CompilerError> {
103105
const env = new Env();
104-
validateNoRefAccessInRenderImpl(fn, env).unwrap();
106+
return validateNoRefAccessInRenderImpl(fn, env).map(_ => undefined);
105107
}
106108

107109
function refTypeOfType(place: Place): RefAccessType {

compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoSetStateInPassiveEffects.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
Place,
1515
} from '../HIR';
1616
import {eachInstructionValueOperand} from '../HIR/visitors';
17+
import {Result} from '../Utils/Result';
1718

1819
/**
1920
* Validates against calling setState in the body of a *passive* effect (useEffect),
@@ -23,7 +24,9 @@ import {eachInstructionValueOperand} from '../HIR/visitors';
2324
* often bad for performance and frequently has more efficient and straightforward
2425
* alternatives. See https://react.dev/learn/you-might-not-need-an-effect for examples.
2526
*/
26-
export function validateNoSetStateInPassiveEffects(fn: HIRFunction): void {
27+
export function validateNoSetStateInPassiveEffects(
28+
fn: HIRFunction,
29+
): Result<void, CompilerError> {
2730
const setStateFunctions: Map<IdentifierId, Place> = new Map();
2831
const errors = new CompilerError();
2932
for (const [, block] of fn.body.blocks) {
@@ -98,9 +101,7 @@ export function validateNoSetStateInPassiveEffects(fn: HIRFunction): void {
98101
}
99102
}
100103

101-
if (errors.hasErrors()) {
102-
throw errors;
103-
}
104+
return errors.asResult();
104105
}
105106

106107
function getSetStateCall(

compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoSetStateInRender.ts

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import {CompilerError, ErrorSeverity} from '../CompilerError';
99
import {HIRFunction, IdentifierId, isSetStateType} from '../HIR';
1010
import {computeUnconditionalBlocks} from '../HIR/ComputeUnconditionalBlocks';
1111
import {eachInstructionValueOperand} from '../HIR/visitors';
12-
import {Err, Ok, Result} from '../Utils/Result';
12+
import {Result} from '../Utils/Result';
1313

1414
/**
1515
* Validates that the given function does not have an infinite update loop
@@ -39,9 +39,11 @@ import {Err, Ok, Result} from '../Utils/Result';
3939
* y();
4040
* ```
4141
*/
42-
export function validateNoSetStateInRender(fn: HIRFunction): void {
42+
export function validateNoSetStateInRender(
43+
fn: HIRFunction,
44+
): Result<void, CompilerError> {
4345
const unconditionalSetStateFunctions: Set<IdentifierId> = new Set();
44-
validateNoSetStateInRenderImpl(fn, unconditionalSetStateFunctions).unwrap();
46+
return validateNoSetStateInRenderImpl(fn, unconditionalSetStateFunctions);
4547
}
4648

4749
function validateNoSetStateInRenderImpl(
@@ -145,9 +147,5 @@ function validateNoSetStateInRenderImpl(
145147
}
146148
}
147149

148-
if (errors.hasErrors()) {
149-
return Err(errors);
150-
} else {
151-
return Ok(undefined);
152-
}
150+
return errors.asResult();
153151
}

0 commit comments

Comments
 (0)