@samitouri / QOS-React-1 / commits / e77d975b16

Flag ValidateNoSetStateInRender

This needs a bit more work before we can turn it on by default, see the next PR for a failing test case.

Lauren Tan committed Jul 27, 2023 at 17:08 UTC e77d975b169a4e768e2efec2a506eb4a1bd1090d
3 files changed +24 -6
compiler/forget/packages/babel-plugin-react-forget/src/Entrypoint/Pipeline.ts
+8 -6
@@ -137,12 +137,14 @@ export function* run(
137 validateNoRefAccessInRender(hir);
138 }
139
140 - const noSetStateInRenderResult = validateNoSetStateInRender(hir).unwrap();
141 - yield log({
142 - kind: "debug",
143 - name: "ValidateNoSetStateInRender",
144 - value: noSetStateInRenderResult.debug(),
145 - });
140 + if (env.validateNoSetStateInRender) {
141 + const noSetStateInRenderResult = validateNoSetStateInRender(hir).unwrap();
142 + yield log({
143 + kind: "debug",
144 + name: "ValidateNoSetStateInRender",
145 + value: noSetStateInRenderResult.debug(),
146 + });
147 + }
148
149 leaveSSA(hir);
150 yield log({ kind: "hir", name: "LeaveSSA", value: hir });
compiler/forget/packages/babel-plugin-react-forget/src/HIR/Environment.ts
+11
@@ -83,6 +83,14 @@ export type EnvironmentConfig = Partial<{
83 */
84 validateFrozenLambdas: boolean;
85
86 + /**
87 + * Validates that setState is not unconditionally called during render, as it can lead to
88 + * infinite loops.
89 + *
90 + * Defaults to false
91 + */
92 + validateNoSetStateInRender: boolean;
93 +
94 /**
95 * Enable inlining of `useMemo()` function expressions so that they can be more optimally
96 * compiled.
@@ -176,6 +184,7 @@ export class Environment {
184 validateHooksUsage: boolean;
185 validateRefAccessDuringRender: boolean;
186 validateFrozenLambdas: boolean;
187 + validateNoSetStateInRender: boolean;
188 enableFunctionCallSignatureOptimizations: boolean;
189 enableAssumeHooksFollowRulesOfReact: boolean;
190 enableTreatHooksAsFunctions: boolean;
@@ -231,6 +240,8 @@ export class Environment {
240 this.enableOptimizeFunctionExpressions =
241 config?.enableOptimizeFunctionExpressions ?? true;
242 this.assertValidMutableRanges = config?.assertValidMutableRanges ?? false;
243 + this.validateNoSetStateInRender =
244 + config?.validateNoSetStateInRender ?? false;
245
246 this.#contextIdentifiers = contextIdentifiers;
247 }
compiler/forget/packages/snap/src/compiler-worker.ts
+5
@@ -97,6 +97,7 @@ export async function compile(
97 let enableTreatHooksAsFunctions = true;
98 let disableAllMemoization = false;
99 let validateRefAccessDuringRender = true;
100 + let validateNoSetStateInRender = true;
101 let enableEmitFreeze = null;
102 let enableOptimizeFunctionExpressions = true;
103 if (firstLine.indexOf("@forgetDirective") !== -1) {
@@ -132,6 +133,9 @@ export async function compile(
133 if (firstLine.includes("@validateRefAccessDuringRender false")) {
134 validateRefAccessDuringRender = false;
135 }
136 + if (firstLine.includes("@validateNoSetStateInRender false")) {
137 + validateNoSetStateInRender = false;
138 + }
139 if (firstLine.includes("@enableOptimizeFunctionExpressions false")) {
140 enableOptimizeFunctionExpressions = false;
141 }
@@ -165,6 +169,7 @@ export async function compile(
169 validateHooksUsage: true,
170 validateRefAccessDuringRender,
171 validateFrozenLambdas: true,
172 + validateNoSetStateInRender,
173 enableEmitFreeze,
174 enableOptimizeFunctionExpressions,
175 assertValidMutableRanges: true,