Skip to content

Commit 00ddf28

Browse files
committed
[compiler] First functional disambiguated single line validation of no derived computations in effects
1 parent e0e356a commit 00ddf28

File tree

1 file changed

+31
-33
lines changed

1 file changed

+31
-33
lines changed

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

Lines changed: 31 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,11 @@
55
* LICENSE file in the root directory of this source tree.
66
*/
77

8-
import {effect} from 'zod';
98
import {CompilerError, Effect, ErrorSeverity, SourceLocation} from '..';
109
import {
1110
ArrayExpression,
1211
BasicBlock,
1312
BlockId,
14-
Identifier,
1513
FunctionExpression,
1614
HIRFunction,
1715
IdentifierId,
@@ -20,15 +18,12 @@ import {
2018
isSetStateType,
2119
isUseEffectHookType,
2220
isUseStateType,
23-
IdentifierName,
2421
GeneratedSource,
2522
} from '../HIR';
26-
import {printInstruction} from '../HIR/PrintHIR';
2723
import {
2824
eachInstructionOperand,
2925
eachTerminalOperand,
3026
eachInstructionLValue,
31-
eachPatternOperand,
3227
} from '../HIR/visitors';
3328
import {isMutable} from '../ReactiveScopes/InferReactiveScopeVariables';
3429
import {assertExhaustive} from '../Utils/utils';
@@ -46,12 +41,10 @@ type SetStateName = string | undefined | null;
4641

4742
type DerivationMetadata = {
4843
typeOfValue: TypeOfValue;
49-
// TODO: Rename to place
50-
identifierPlace: Place;
51-
sources: Place[];
44+
place: Place;
45+
sources: Array<Place>;
5246
};
5347

54-
// TODO: This needs refining
5548
type ErrorMetadata = {
5649
errorType: TypeOfValue;
5750
invalidDepInfo: string | undefined;
@@ -71,20 +64,22 @@ function joinValue(
7164

7265
function updateDerivationMetadata(
7366
target: Place,
74-
sources: DerivationMetadata[],
67+
sources: Array<DerivationMetadata>,
7568
typeOfValue: TypeOfValue,
7669
derivedTuple: Map<IdentifierId, DerivationMetadata>,
7770
): void {
7871
let newValue: DerivationMetadata = {
79-
identifierPlace: target,
72+
place: target,
8073
sources: [],
8174
typeOfValue: typeOfValue,
8275
};
8376

8477
for (const source of sources) {
85-
// If the identifier of the source is a promoted identifier, then
86-
// we should set the target as the source.
87-
if (source.identifierPlace.identifier.name?.kind === 'promoted') {
78+
/*
79+
* If the identifier of the source is a promoted identifier, then
80+
* we should set the target as the source.
81+
*/
82+
if (source.place.identifier.name?.kind === 'promoted') {
8883
newValue.sources.push(target);
8984
} else {
9085
newValue.sources.push(...source.sources);
@@ -96,10 +91,8 @@ function updateDerivationMetadata(
9691
function parseInstr(
9792
instr: Instruction,
9893
derivedTuple: Map<IdentifierId, DerivationMetadata>,
99-
setStateCalls: Map<SetStateName, Place[]>,
100-
) {
101-
// console.log(printInstruction(instr));
102-
// console.log(instr);
94+
setStateCalls: Map<SetStateName, Array<Place>>,
95+
): void {
10396
let typeOfValue: TypeOfValue = 'ignored';
10497

10598
// TODO: Not sure if this will catch every time we create a new useState
@@ -111,7 +104,7 @@ function parseInstr(
111104
const value = instr.value.lvalue.pattern.items[0];
112105
if (value.kind === 'Identifier') {
113106
derivedTuple.set(value.identifier.id, {
114-
identifierPlace: value,
107+
place: value,
115108
sources: [value],
116109
typeOfValue: 'fromState',
117110
});
@@ -136,7 +129,7 @@ function parseInstr(
136129
}
137130
}
138131

139-
let sources: DerivationMetadata[] = [];
132+
let sources: Array<DerivationMetadata> = [];
140133
for (const operand of eachInstructionOperand(instr)) {
141134
const opSource = derivedTuple.get(operand.identifier.id);
142135
if (opSource === undefined) {
@@ -196,23 +189,23 @@ function parseInstr(
196189
function parseBlockPhi(
197190
block: BasicBlock,
198191
derivedTuple: Map<IdentifierId, DerivationMetadata>,
199-
) {
192+
): void {
200193
for (const phi of block.phis) {
201194
for (const operand of phi.operands.values()) {
202195
const source = derivedTuple.get(operand.identifier.id);
203196
if (source !== undefined && source.typeOfValue === 'fromProps') {
204197
if (
205-
source.identifierPlace.identifier.name === null ||
206-
source.identifierPlace.identifier.name?.kind === 'promoted'
198+
source.place.identifier.name === null ||
199+
source.place.identifier.name?.kind === 'promoted'
207200
) {
208201
derivedTuple.set(phi.place.identifier.id, {
209-
identifierPlace: phi.place,
202+
place: phi.place,
210203
sources: [phi.place],
211204
typeOfValue: 'fromProps',
212205
});
213206
} else {
214207
derivedTuple.set(phi.place.identifier.id, {
215-
identifierPlace: phi.place,
208+
place: phi.place,
216209
sources: source.sources,
217210
typeOfValue: 'fromProps',
218211
});
@@ -251,16 +244,16 @@ export function validateNoDerivedComputationsInEffects(fn: HIRFunction): void {
251244
const locals: Map<IdentifierId, IdentifierId> = new Map();
252245
const derivedTuple: Map<IdentifierId, DerivationMetadata> = new Map();
253246

254-
const effectSetStates: Map<SetStateName, Place[]> = new Map();
255-
const setStateCalls: Map<SetStateName, Place[]> = new Map();
247+
const effectSetStates: Map<SetStateName, Array<Place>> = new Map();
248+
const setStateCalls: Map<SetStateName, Array<Place>> = new Map();
256249

257-
const errors: ErrorMetadata[] = [];
250+
const errors: Array<ErrorMetadata> = [];
258251

259252
if (fn.fnType === 'Hook') {
260253
for (const param of fn.params) {
261254
if (param.kind === 'Identifier') {
262255
derivedTuple.set(param.identifier.id, {
263-
identifierPlace: param,
256+
place: param,
264257
sources: [param],
265258
typeOfValue: 'fromProps',
266259
});
@@ -270,7 +263,7 @@ export function validateNoDerivedComputationsInEffects(fn: HIRFunction): void {
270263
const props = fn.params[0];
271264
if (props != null && props.kind === 'Identifier') {
272265
derivedTuple.set(props.identifier.id, {
273-
identifierPlace: props,
266+
place: props,
274267
sources: [props],
275268
typeOfValue: 'fromProps',
276269
});
@@ -347,7 +340,6 @@ export function validateNoDerivedComputationsInEffects(fn: HIRFunction): void {
347340
const throwableErrors = new CompilerError();
348341
for (const error of errors) {
349342
let reason;
350-
let description = '';
351343
// TODO: Not sure if this is robust enough.
352344
/*
353345
* If we use a setState from an invalid useEffect elsewhere then we probably have to
@@ -382,8 +374,8 @@ function validateEffect(
382374
effectFunction: HIRFunction,
383375
effectDeps: Array<IdentifierId>,
384376
derivedTuple: Map<IdentifierId, DerivationMetadata>,
385-
effectSetStates: Map<SetStateName, Place[]>,
386-
errors: ErrorMetadata[],
377+
effectSetStates: Map<SetStateName, Array<Place>>,
378+
errors: Array<ErrorMetadata>,
387379
): void {
388380
/*
389381
* TODO: This makes it so we only capture single line useEffects.
@@ -553,6 +545,12 @@ function validateEffect(
553545
invalidDepInfo = sourceNames
554546
? `Invalid deps from local state: ${sourceNames}`
555547
: '';
548+
} else {
549+
sourceNames += `[${placeNames}], `;
550+
sourceNames = sourceNames.slice(0, -2);
551+
invalidDepInfo = sourceNames
552+
? `Invalid deps from both props and local state: ${sourceNames}`
553+
: '';
556554
}
557555

558556
errors.push({

0 commit comments

Comments
 (0)