Skip to content

Commit df8c8c3

Browse files
committed
[compiler] rebase #32285 (#34102)
Redo of #32285 which was created with ghstack and is tedious to rebase with sapling. DiffTrain build for [d3b26b2](d3b26b2)
1 parent 952bee4 commit df8c8c3

35 files changed

+251
-86
lines changed

compiled/eslint-plugin-react-hooks/index.js

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30871,6 +30871,7 @@ const EnvironmentConfigSchema = zod.z.object({
3087130871
validateRefAccessDuringRender: zod.z.boolean().default(true),
3087230872
validateNoSetStateInRender: zod.z.boolean().default(true),
3087330873
validateNoSetStateInEffects: zod.z.boolean().default(false),
30874+
validateNoDerivedComputationsInEffects: zod.z.boolean().default(false),
3087430875
validateNoJSXInTryStatements: zod.z.boolean().default(false),
3087530876
validateStaticComponents: zod.z.boolean().default(false),
3087630877
validateMemoizedEffectDependencies: zod.z.boolean().default(false),
@@ -51050,6 +51051,167 @@ function validateNoFreezingKnownMutableFunctions(fn) {
5105051051
return errors.asResult();
5105151052
}
5105251053

51054+
function validateNoDerivedComputationsInEffects(fn) {
51055+
const candidateDependencies = new Map();
51056+
const functions = new Map();
51057+
const locals = new Map();
51058+
const errors = new CompilerError();
51059+
for (const block of fn.body.blocks.values()) {
51060+
for (const instr of block.instructions) {
51061+
const { lvalue, value } = instr;
51062+
if (value.kind === 'LoadLocal') {
51063+
locals.set(lvalue.identifier.id, value.place.identifier.id);
51064+
}
51065+
else if (value.kind === 'ArrayExpression') {
51066+
candidateDependencies.set(lvalue.identifier.id, value);
51067+
}
51068+
else if (value.kind === 'FunctionExpression') {
51069+
functions.set(lvalue.identifier.id, value);
51070+
}
51071+
else if (value.kind === 'CallExpression' ||
51072+
value.kind === 'MethodCall') {
51073+
const callee = value.kind === 'CallExpression' ? value.callee : value.property;
51074+
if (isUseEffectHookType(callee.identifier) &&
51075+
value.args.length === 2 &&
51076+
value.args[0].kind === 'Identifier' &&
51077+
value.args[1].kind === 'Identifier') {
51078+
const effectFunction = functions.get(value.args[0].identifier.id);
51079+
const deps = candidateDependencies.get(value.args[1].identifier.id);
51080+
if (effectFunction != null &&
51081+
deps != null &&
51082+
deps.elements.length !== 0 &&
51083+
deps.elements.every(element => element.kind === 'Identifier')) {
51084+
const dependencies = deps.elements.map(dep => {
51085+
var _a;
51086+
CompilerError.invariant(dep.kind === 'Identifier', {
51087+
reason: `Dependency is checked as a place above`,
51088+
loc: value.loc,
51089+
});
51090+
return (_a = locals.get(dep.identifier.id)) !== null && _a !== void 0 ? _a : dep.identifier.id;
51091+
});
51092+
validateEffect(effectFunction.loweredFunc.func, dependencies, errors);
51093+
}
51094+
}
51095+
}
51096+
}
51097+
}
51098+
if (errors.hasErrors()) {
51099+
throw errors;
51100+
}
51101+
}
51102+
function validateEffect(effectFunction, effectDeps, errors) {
51103+
for (const operand of effectFunction.context) {
51104+
if (isSetStateType(operand.identifier)) {
51105+
continue;
51106+
}
51107+
else if (effectDeps.find(dep => dep === operand.identifier.id) != null) {
51108+
continue;
51109+
}
51110+
else {
51111+
return;
51112+
}
51113+
}
51114+
for (const dep of effectDeps) {
51115+
if (effectFunction.context.find(operand => operand.identifier.id === dep) ==
51116+
null) {
51117+
return;
51118+
}
51119+
}
51120+
const seenBlocks = new Set();
51121+
const values = new Map();
51122+
for (const dep of effectDeps) {
51123+
values.set(dep, [dep]);
51124+
}
51125+
const setStateLocations = [];
51126+
for (const block of effectFunction.body.blocks.values()) {
51127+
for (const pred of block.preds) {
51128+
if (!seenBlocks.has(pred)) {
51129+
return;
51130+
}
51131+
}
51132+
for (const phi of block.phis) {
51133+
const aggregateDeps = new Set();
51134+
for (const operand of phi.operands.values()) {
51135+
const deps = values.get(operand.identifier.id);
51136+
if (deps != null) {
51137+
for (const dep of deps) {
51138+
aggregateDeps.add(dep);
51139+
}
51140+
}
51141+
}
51142+
if (aggregateDeps.size !== 0) {
51143+
values.set(phi.place.identifier.id, Array.from(aggregateDeps));
51144+
}
51145+
}
51146+
for (const instr of block.instructions) {
51147+
switch (instr.value.kind) {
51148+
case 'Primitive':
51149+
case 'JSXText':
51150+
case 'LoadGlobal': {
51151+
break;
51152+
}
51153+
case 'LoadLocal': {
51154+
const deps = values.get(instr.value.place.identifier.id);
51155+
if (deps != null) {
51156+
values.set(instr.lvalue.identifier.id, deps);
51157+
}
51158+
break;
51159+
}
51160+
case 'ComputedLoad':
51161+
case 'PropertyLoad':
51162+
case 'BinaryExpression':
51163+
case 'TemplateLiteral':
51164+
case 'CallExpression':
51165+
case 'MethodCall': {
51166+
const aggregateDeps = new Set();
51167+
for (const operand of eachInstructionValueOperand(instr.value)) {
51168+
const deps = values.get(operand.identifier.id);
51169+
if (deps != null) {
51170+
for (const dep of deps) {
51171+
aggregateDeps.add(dep);
51172+
}
51173+
}
51174+
}
51175+
if (aggregateDeps.size !== 0) {
51176+
values.set(instr.lvalue.identifier.id, Array.from(aggregateDeps));
51177+
}
51178+
if (instr.value.kind === 'CallExpression' &&
51179+
isSetStateType(instr.value.callee.identifier) &&
51180+
instr.value.args.length === 1 &&
51181+
instr.value.args[0].kind === 'Identifier') {
51182+
const deps = values.get(instr.value.args[0].identifier.id);
51183+
if (deps != null && new Set(deps).size === effectDeps.length) {
51184+
setStateLocations.push(instr.value.callee.loc);
51185+
}
51186+
else {
51187+
return;
51188+
}
51189+
}
51190+
break;
51191+
}
51192+
default: {
51193+
return;
51194+
}
51195+
}
51196+
}
51197+
for (const operand of eachTerminalOperand(block.terminal)) {
51198+
if (values.has(operand.identifier.id)) {
51199+
return;
51200+
}
51201+
}
51202+
seenBlocks.add(block.id);
51203+
}
51204+
for (const loc of setStateLocations) {
51205+
errors.push({
51206+
reason: 'Values derived from props and state should be calculated during render, not in an effect. (https://react.dev/learn/you-might-not-need-an-effect#updating-state-based-on-props-or-state)',
51207+
description: null,
51208+
severity: ErrorSeverity.InvalidReact,
51209+
loc,
51210+
suggestions: null,
51211+
});
51212+
}
51213+
}
51214+
5105351215
function run(func, config, fnType, mode, programContext, logger, filename, code) {
5105451216
var _a, _b;
5105551217
const contextIdentifiers = findContextIdentifiers(func);
@@ -51172,6 +51334,9 @@ function runWithEnvironment(func, env) {
5117251334
if (env.config.validateNoSetStateInRender) {
5117351335
validateNoSetStateInRender(hir).unwrap();
5117451336
}
51337+
if (env.config.validateNoDerivedComputationsInEffects) {
51338+
validateNoDerivedComputationsInEffects(hir);
51339+
}
5117551340
if (env.config.validateNoSetStateInEffects) {
5117651341
env.logErrors(validateNoSetStateInEffects(hir));
5117751342
}

compiled/facebook-www/REVISION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
ddf8bc3fbac7aefbf557e2e4a3e14d8de1b80872
1+
d3b26b2953c7e780abaa49f422c53fd4cda08e47
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
ddf8bc3fbac7aefbf557e2e4a3e14d8de1b80872
1+
d3b26b2953c7e780abaa49f422c53fd4cda08e47

compiled/facebook-www/React-dev.classic.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1434,7 +1434,7 @@ __DEV__ &&
14341434
exports.useTransition = function () {
14351435
return resolveDispatcher().useTransition();
14361436
};
1437-
exports.version = "19.2.0-www-classic-ddf8bc3f-20250801";
1437+
exports.version = "19.2.0-www-classic-d3b26b29-20250804";
14381438
"undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ &&
14391439
"function" ===
14401440
typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop &&

compiled/facebook-www/React-dev.modern.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1434,7 +1434,7 @@ __DEV__ &&
14341434
exports.useTransition = function () {
14351435
return resolveDispatcher().useTransition();
14361436
};
1437-
exports.version = "19.2.0-www-modern-ddf8bc3f-20250801";
1437+
exports.version = "19.2.0-www-modern-d3b26b29-20250804";
14381438
"undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ &&
14391439
"function" ===
14401440
typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop &&

compiled/facebook-www/React-prod.classic.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -610,4 +610,4 @@ exports.useSyncExternalStore = function (
610610
exports.useTransition = function () {
611611
return ReactSharedInternals.H.useTransition();
612612
};
613-
exports.version = "19.2.0-www-classic-ddf8bc3f-20250801";
613+
exports.version = "19.2.0-www-classic-d3b26b29-20250804";

compiled/facebook-www/React-prod.modern.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -610,4 +610,4 @@ exports.useSyncExternalStore = function (
610610
exports.useTransition = function () {
611611
return ReactSharedInternals.H.useTransition();
612612
};
613-
exports.version = "19.2.0-www-modern-ddf8bc3f-20250801";
613+
exports.version = "19.2.0-www-modern-d3b26b29-20250804";

compiled/facebook-www/React-profiling.classic.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -614,7 +614,7 @@ exports.useSyncExternalStore = function (
614614
exports.useTransition = function () {
615615
return ReactSharedInternals.H.useTransition();
616616
};
617-
exports.version = "19.2.0-www-classic-ddf8bc3f-20250801";
617+
exports.version = "19.2.0-www-classic-d3b26b29-20250804";
618618
"undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ &&
619619
"function" ===
620620
typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop &&

compiled/facebook-www/React-profiling.modern.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -614,7 +614,7 @@ exports.useSyncExternalStore = function (
614614
exports.useTransition = function () {
615615
return ReactSharedInternals.H.useTransition();
616616
};
617-
exports.version = "19.2.0-www-modern-ddf8bc3f-20250801";
617+
exports.version = "19.2.0-www-modern-d3b26b29-20250804";
618618
"undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ &&
619619
"function" ===
620620
typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop &&

compiled/facebook-www/ReactART-dev.classic.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19318,10 +19318,10 @@ __DEV__ &&
1931819318
(function () {
1931919319
var internals = {
1932019320
bundleType: 1,
19321-
version: "19.2.0-www-classic-ddf8bc3f-20250801",
19321+
version: "19.2.0-www-classic-d3b26b29-20250804",
1932219322
rendererPackageName: "react-art",
1932319323
currentDispatcherRef: ReactSharedInternals,
19324-
reconcilerVersion: "19.2.0-www-classic-ddf8bc3f-20250801"
19324+
reconcilerVersion: "19.2.0-www-classic-d3b26b29-20250804"
1932519325
};
1932619326
internals.overrideHookState = overrideHookState;
1932719327
internals.overrideHookStateDeletePath = overrideHookStateDeletePath;
@@ -19355,7 +19355,7 @@ __DEV__ &&
1935519355
exports.Shape = Shape;
1935619356
exports.Surface = Surface;
1935719357
exports.Text = Text;
19358-
exports.version = "19.2.0-www-classic-ddf8bc3f-20250801";
19358+
exports.version = "19.2.0-www-classic-d3b26b29-20250804";
1935919359
"undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ &&
1936019360
"function" ===
1936119361
typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop &&

0 commit comments

Comments
 (0)