@@ -32144,6 +32144,7 @@ const EnvironmentConfigSchema = zod.z.object({
3214432144 lowerContextAccess: ExternalFunctionSchema.nullable().default(null),
3214532145 validateNoVoidUseMemo: zod.z.boolean().default(false),
3214632146 validateNoDynamicallyCreatedComponentsOrHooks: zod.z.boolean().default(false),
32147+ enableAllowSetStateFromRefsInEffects: zod.z.boolean().default(true),
3214732148});
3214832149class Environment {
3214932150 constructor(scope, fnType, compilerMode, config, contextIdentifiers, parentFunction, logger, filename, code, programContext) {
@@ -49447,12 +49448,46 @@ function validateNoRefAccessInRenderImpl(fn, env) {
4944749448 case 'StartMemoize':
4944849449 case 'FinishMemoize':
4944949450 break;
49451+ case 'LoadGlobal': {
49452+ if (instr.value.binding.name === 'undefined') {
49453+ env.set(instr.lvalue.identifier.id, { kind: 'Nullable' });
49454+ }
49455+ break;
49456+ }
4945049457 case 'Primitive': {
4945149458 if (instr.value.value == null) {
4945249459 env.set(instr.lvalue.identifier.id, { kind: 'Nullable' });
4945349460 }
4945449461 break;
4945549462 }
49463+ case 'UnaryExpression': {
49464+ if (instr.value.operator === '!') {
49465+ const value = env.get(instr.value.value.identifier.id);
49466+ const refId = (value === null || value === void 0 ? void 0 : value.kind) === 'RefValue' && value.refId != null
49467+ ? value.refId
49468+ : null;
49469+ if (refId !== null) {
49470+ env.set(instr.lvalue.identifier.id, { kind: 'Guard', refId });
49471+ errors.pushDiagnostic(CompilerDiagnostic.create({
49472+ category: ErrorCategory.Refs,
49473+ reason: 'Cannot access refs during render',
49474+ description: ERROR_DESCRIPTION,
49475+ })
49476+ .withDetails({
49477+ kind: 'error',
49478+ loc: instr.value.value.loc,
49479+ message: `Cannot access ref value during render`,
49480+ })
49481+ .withDetails({
49482+ kind: 'hint',
49483+ message: 'To initialize a ref only once, check that the ref is null with the pattern `if (ref.current == null) { ref.current = ... }`',
49484+ }));
49485+ break;
49486+ }
49487+ }
49488+ validateNoRefValueAccess(errors, env, instr.value.value);
49489+ break;
49490+ }
4945649491 case 'BinaryExpression': {
4945749492 const left = env.get(instr.value.left.identifier.id);
4945849493 const right = env.get(instr.value.right.identifier.id);
@@ -50579,7 +50614,7 @@ function emitArrayInstr(elements, env) {
5057950614 return arrayInstr;
5058050615}
5058150616
50582- function validateNoSetStateInEffects(fn) {
50617+ function validateNoSetStateInEffects(fn, env ) {
5058350618 const setStateFunctions = new Map();
5058450619 const errors = new CompilerError();
5058550620 for (const [, block] of fn.body.blocks) {
@@ -50601,7 +50636,7 @@ function validateNoSetStateInEffects(fn) {
5060150636 case 'FunctionExpression': {
5060250637 if ([...eachInstructionValueOperand(instr.value)].some(operand => isSetStateType(operand.identifier) ||
5060350638 setStateFunctions.has(operand.identifier.id))) {
50604- const callee = getSetStateCall(instr.value.loweredFunc.func, setStateFunctions);
50639+ const callee = getSetStateCall(instr.value.loweredFunc.func, setStateFunctions, env );
5060550640 if (callee !== null) {
5060650641 setStateFunctions.set(instr.lvalue.identifier.id, callee);
5060750642 }
@@ -50645,9 +50680,29 @@ function validateNoSetStateInEffects(fn) {
5064550680 }
5064650681 return errors.asResult();
5064750682}
50648- function getSetStateCall(fn, setStateFunctions) {
50683+ function getSetStateCall(fn, setStateFunctions, env) {
50684+ const refDerivedValues = new Set();
50685+ const isDerivedFromRef = (place) => {
50686+ return (refDerivedValues.has(place.identifier.id) ||
50687+ isUseRefType(place.identifier) ||
50688+ isRefValueType(place.identifier));
50689+ };
5064950690 for (const [, block] of fn.body.blocks) {
5065050691 for (const instr of block.instructions) {
50692+ if (env.config.enableAllowSetStateFromRefsInEffects) {
50693+ const hasRefOperand = Iterable_some(eachInstructionValueOperand(instr.value), isDerivedFromRef);
50694+ if (hasRefOperand) {
50695+ for (const lvalue of eachInstructionLValue(instr)) {
50696+ refDerivedValues.add(lvalue.identifier.id);
50697+ }
50698+ }
50699+ if (instr.value.kind === 'PropertyLoad' &&
50700+ instr.value.property === 'current' &&
50701+ (isUseRefType(instr.value.object.identifier) ||
50702+ isRefValueType(instr.value.object.identifier))) {
50703+ refDerivedValues.add(instr.lvalue.identifier.id);
50704+ }
50705+ }
5065150706 switch (instr.value.kind) {
5065250707 case 'LoadLocal': {
5065350708 if (setStateFunctions.has(instr.value.place.identifier.id)) {
@@ -50666,6 +50721,14 @@ function getSetStateCall(fn, setStateFunctions) {
5066650721 const callee = instr.value.callee;
5066750722 if (isSetStateType(callee.identifier) ||
5066850723 setStateFunctions.has(callee.identifier.id)) {
50724+ if (env.config.enableAllowSetStateFromRefsInEffects) {
50725+ const arg = instr.value.args.at(0);
50726+ if (arg !== undefined &&
50727+ arg.kind === 'Identifier' &&
50728+ refDerivedValues.has(arg.identifier.id)) {
50729+ return null;
50730+ }
50731+ }
5066950732 return callee;
5067050733 }
5067150734 }
@@ -52129,7 +52192,7 @@ function runWithEnvironment(func, env) {
5212952192 validateNoDerivedComputationsInEffects(hir);
5213052193 }
5213152194 if (env.config.validateNoSetStateInEffects) {
52132- env.logErrors(validateNoSetStateInEffects(hir));
52195+ env.logErrors(validateNoSetStateInEffects(hir, env ));
5213352196 }
5213452197 if (env.config.validateNoJSXInTryStatements) {
5213552198 env.logErrors(validateNoJSXInTryStatement(hir));
0 commit comments