[compiler] Differentiate between inferred setStates and factual setStates

Summary:
Rough proposal on how we can differentiate between the a real setState we can track and one we infer.

Particularly useful when setting `enableTreatSetIdentifiersAsStateSetters=true`

This is mainly so ValidateNoDerivedComputationsInEffects_exp doesn't introduce false positives since we rely on setState type tracking

Test Plan:
Tests
This commit is contained in:
Jorge Cabiedes Acosta
2025-11-11 14:30:38 -08:00
parent 5e94655cbb
commit 54d7abde6e
5 changed files with 17 additions and 7 deletions

View File

@@ -1875,6 +1875,14 @@ export function isSetStateType(id: Identifier): boolean {
return id.type.kind === 'Function' && id.type.shapeId === 'BuiltInSetState';
}
export function isAnySetStateType(id: Identifier): boolean {
return (
id.type.kind === 'Function' &&
(id.type.shapeId === 'InferredSetState' ||
id.type.shapeId === 'BuiltInSetState')
);
}
export function isUseActionStateType(id: Identifier): boolean {
return (
id.type.kind === 'Object' && id.type.shapeId === 'BuiltInUseActionState'

View File

@@ -386,6 +386,7 @@ export const BuiltInJsxId = 'BuiltInJsx';
export const BuiltInObjectId = 'BuiltInObject';
export const BuiltInUseStateId = 'BuiltInUseState';
export const BuiltInSetStateId = 'BuiltInSetState';
export const InferredSetState = 'InferredSetState';
export const BuiltInUseActionStateId = 'BuiltInUseActionState';
export const BuiltInSetActionStateId = 'BuiltInSetActionState';
export const BuiltInUseRefId = 'BuiltInUseRefId';

View File

@@ -25,7 +25,7 @@ import {
ReactiveScopeDependencies,
Terminal,
isUseRefType,
isSetStateType,
isAnySetStateType,
isFireFunctionType,
makeScopeId,
HIR,
@@ -223,7 +223,7 @@ export function inferEffectDependencies(fn: HIRFunction): void {
for (const maybeDep of minimalDeps) {
if (
((isUseRefType(maybeDep.identifier) ||
isSetStateType(maybeDep.identifier)) &&
isAnySetStateType(maybeDep.identifier)) &&
!reactiveIds.has(maybeDep.identifier.id)) ||
isFireFunctionType(maybeDep.identifier) ||
isEffectEventFunctionType(maybeDep.identifier)

View File

@@ -31,8 +31,8 @@ import {
BuiltInObjectId,
BuiltInPropsId,
BuiltInRefValueId,
BuiltInSetStateId,
BuiltInUseRefId,
InferredSetState,
} from '../HIR/ObjectShape';
import {eachInstructionLValue, eachInstructionOperand} from '../HIR/visitors';
import {assertExhaustive} from '../Utils/utils';
@@ -281,7 +281,7 @@ function* generateInstructionTypes(
if (env.config.enableTreatSetIdentifiersAsStateSetters) {
const name = getName(names, value.callee.identifier.id);
if (name.startsWith('set')) {
shapeId = BuiltInSetStateId;
shapeId = InferredSetState;
}
}
yield equation(value.callee.identifier.type, {

View File

@@ -5,12 +5,13 @@
* LICENSE file in the root directory of this source tree.
*/
import {Environment} from '../HIR/Environment';
import {
CompilerDiagnostic,
CompilerError,
ErrorCategory,
} from '../CompilerError';
import {HIRFunction, IdentifierId, isSetStateType} from '../HIR';
import {HIRFunction, IdentifierId, isAnySetStateType} from '../HIR';
import {computeUnconditionalBlocks} from '../HIR/ComputeUnconditionalBlocks';
import {eachInstructionValueOperand} from '../HIR/visitors';
import {Result} from '../Utils/Result';
@@ -85,7 +86,7 @@ function validateNoSetStateInRenderImpl(
// faster-path to check if the function expression references a setState
[...eachInstructionValueOperand(instr.value)].some(
operand =>
isSetStateType(operand.identifier) ||
isAnySetStateType(operand.identifier) ||
unconditionalSetStateFunctions.has(operand.identifier.id),
) &&
// if yes, does it unconditonally call it?
@@ -136,7 +137,7 @@ function validateNoSetStateInRenderImpl(
case 'CallExpression': {
const callee = instr.value.callee;
if (
isSetStateType(callee.identifier) ||
isAnySetStateType(callee.identifier) ||
unconditionalSetStateFunctions.has(callee.identifier.id)
) {
if (activeManualMemoId !== null) {