eslint-plugin-react-hooks 7.1.0-canary-257b033f-20251113 → 7.1.0-canary-0972e239-20251118

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -18957,9 +18957,18 @@ function isRefOrRefLikeMutableType(type) {
18957
18957
  function isSetStateType(id) {
18958
18958
  return id.type.kind === 'Function' && id.type.shapeId === 'BuiltInSetState';
18959
18959
  }
18960
+ function isUseActionStateType(id) {
18961
+ return (id.type.kind === 'Object' && id.type.shapeId === 'BuiltInUseActionState');
18962
+ }
18960
18963
  function isStartTransitionType(id) {
18961
18964
  return (id.type.kind === 'Function' && id.type.shapeId === 'BuiltInStartTransition');
18962
18965
  }
18966
+ function isUseOptimisticType(id) {
18967
+ return (id.type.kind === 'Object' && id.type.shapeId === 'BuiltInUseOptimistic');
18968
+ }
18969
+ function isSetOptimisticType(id) {
18970
+ return (id.type.kind === 'Function' && id.type.shapeId === 'BuiltInSetOptimistic');
18971
+ }
18963
18972
  function isSetActionStateType(id) {
18964
18973
  return (id.type.kind === 'Function' && id.type.shapeId === 'BuiltInSetActionState');
18965
18974
  }
@@ -18981,7 +18990,8 @@ function isStableType(id) {
18981
18990
  isSetActionStateType(id) ||
18982
18991
  isDispatcherType(id) ||
18983
18992
  isUseRefType(id) ||
18984
- isStartTransitionType(id));
18993
+ isStartTransitionType(id) ||
18994
+ isSetOptimisticType(id));
18985
18995
  }
18986
18996
  function isStableTypeContainer(id) {
18987
18997
  const type_ = id.type;
@@ -18989,8 +18999,9 @@ function isStableTypeContainer(id) {
18989
18999
  return false;
18990
19000
  }
18991
19001
  return (isUseStateType(id) ||
18992
- type_.shapeId === 'BuiltInUseActionState' ||
19002
+ isUseActionStateType(id) ||
18993
19003
  isUseReducerType(id) ||
19004
+ isUseOptimisticType(id) ||
18994
19005
  type_.shapeId === 'BuiltInUseTransition');
18995
19006
  }
18996
19007
  function evaluatesToStableTypeOrContainer(env, { value }) {
@@ -19003,6 +19014,7 @@ function evaluatesToStableTypeOrContainer(env, { value }) {
19003
19014
  case 'useActionState':
19004
19015
  case 'useRef':
19005
19016
  case 'useTransition':
19017
+ case 'useOptimistic':
19006
19018
  return true;
19007
19019
  }
19008
19020
  }
@@ -19851,6 +19863,29 @@ function* eachInstructionLValue(instr) {
19851
19863
  }
19852
19864
  yield* eachInstructionValueLValue(instr.value);
19853
19865
  }
19866
+ function* eachInstructionLValueWithKind(instr) {
19867
+ switch (instr.value.kind) {
19868
+ case 'DeclareContext':
19869
+ case 'StoreContext':
19870
+ case 'DeclareLocal':
19871
+ case 'StoreLocal': {
19872
+ yield [instr.value.lvalue.place, instr.value.lvalue.kind];
19873
+ break;
19874
+ }
19875
+ case 'Destructure': {
19876
+ const kind = instr.value.lvalue.kind;
19877
+ for (const place of eachPatternOperand(instr.value.lvalue.pattern)) {
19878
+ yield [place, kind];
19879
+ }
19880
+ break;
19881
+ }
19882
+ case 'PostfixUpdate':
19883
+ case 'PrefixUpdate': {
19884
+ yield [instr.value.lvalue, InstructionKind.Reassign];
19885
+ break;
19886
+ }
19887
+ }
19888
+ }
19854
19889
  function* eachInstructionValueLValue(value) {
19855
19890
  switch (value.kind) {
19856
19891
  case 'DeclareContext':
@@ -22081,12 +22116,15 @@ const BuiltInUseReducerId = 'BuiltInUseReducer';
22081
22116
  const BuiltInDispatchId = 'BuiltInDispatch';
22082
22117
  const BuiltInUseContextHookId = 'BuiltInUseContextHook';
22083
22118
  const BuiltInUseTransitionId = 'BuiltInUseTransition';
22119
+ const BuiltInUseOptimisticId = 'BuiltInUseOptimistic';
22120
+ const BuiltInSetOptimisticId = 'BuiltInSetOptimistic';
22084
22121
  const BuiltInStartTransitionId = 'BuiltInStartTransition';
22085
22122
  const BuiltInFireId = 'BuiltInFire';
22086
22123
  const BuiltInFireFunctionId = 'BuiltInFireFunction';
22087
22124
  const BuiltInUseEffectEventId = 'BuiltInUseEffectEvent';
22088
- const BuiltinEffectEventId = 'BuiltInEffectEventFunction';
22125
+ const BuiltInEffectEventId = 'BuiltInEffectEventFunction';
22089
22126
  const BuiltInAutodepsId = 'BuiltInAutoDepsId';
22127
+ const BuiltInEventHandlerId = 'BuiltInEventHandlerId';
22090
22128
  const ReanimatedSharedValueId = 'ReanimatedSharedValueId';
22091
22129
  const BUILTIN_SHAPES = new Map();
22092
22130
  addObject(BUILTIN_SHAPES, BuiltInPropsId, [
@@ -22694,6 +22732,19 @@ addObject(BUILTIN_SHAPES, BuiltInUseTransitionId, [
22694
22732
  }, BuiltInStartTransitionId),
22695
22733
  ],
22696
22734
  ]);
22735
+ addObject(BUILTIN_SHAPES, BuiltInUseOptimisticId, [
22736
+ ['0', { kind: 'Poly' }],
22737
+ [
22738
+ '1',
22739
+ addFunction(BUILTIN_SHAPES, [], {
22740
+ positionalParams: [],
22741
+ restParam: Effect.Freeze,
22742
+ returnType: PRIMITIVE_TYPE,
22743
+ calleeEffect: Effect.Read,
22744
+ returnValueKind: ValueKind.Primitive,
22745
+ }, BuiltInSetOptimisticId),
22746
+ ],
22747
+ ]);
22697
22748
  addObject(BUILTIN_SHAPES, BuiltInUseActionStateId, [
22698
22749
  ['0', { kind: 'Poly' }],
22699
22750
  [
@@ -22733,7 +22784,14 @@ addFunction(BUILTIN_SHAPES, [], {
22733
22784
  returnType: { kind: 'Poly' },
22734
22785
  calleeEffect: Effect.ConditionallyMutate,
22735
22786
  returnValueKind: ValueKind.Mutable,
22736
- }, BuiltinEffectEventId);
22787
+ }, BuiltInEffectEventId);
22788
+ addFunction(BUILTIN_SHAPES, [], {
22789
+ positionalParams: [],
22790
+ restParam: Effect.ConditionallyMutate,
22791
+ returnType: { kind: 'Poly' },
22792
+ calleeEffect: Effect.ConditionallyMutate,
22793
+ returnValueKind: ValueKind.Mutable,
22794
+ }, BuiltInEventHandlerId);
22737
22795
  addObject(BUILTIN_SHAPES, BuiltInMixedReadonlyId, [
22738
22796
  [
22739
22797
  'toString',
@@ -31088,6 +31146,18 @@ const REACT_APIS = [
31088
31146
  returnValueKind: ValueKind.Frozen,
31089
31147
  }),
31090
31148
  ],
31149
+ [
31150
+ 'useOptimistic',
31151
+ addHook(DEFAULT_SHAPES, {
31152
+ positionalParams: [],
31153
+ restParam: Effect.Freeze,
31154
+ returnType: { kind: 'Object', shapeId: BuiltInUseOptimisticId },
31155
+ calleeEffect: Effect.Read,
31156
+ hookKind: 'useOptimistic',
31157
+ returnValueKind: ValueKind.Frozen,
31158
+ returnValueReason: ValueReason.State,
31159
+ }),
31160
+ ],
31091
31161
  [
31092
31162
  'use',
31093
31163
  addFunction(DEFAULT_SHAPES, [], {
@@ -31121,7 +31191,7 @@ const REACT_APIS = [
31121
31191
  returnType: {
31122
31192
  kind: 'Function',
31123
31193
  return: { kind: 'Poly' },
31124
- shapeId: BuiltinEffectEventId,
31194
+ shapeId: BuiltInEffectEventId,
31125
31195
  isConstructor: false,
31126
31196
  },
31127
31197
  calleeEffect: Effect.Read,
@@ -32144,6 +32214,7 @@ const EnvironmentConfigSchema = v4.z.object({
32144
32214
  validateNoVoidUseMemo: v4.z.boolean().default(true),
32145
32215
  validateNoDynamicallyCreatedComponentsOrHooks: v4.z.boolean().default(false),
32146
32216
  enableAllowSetStateFromRefsInEffects: v4.z.boolean().default(true),
32217
+ enableInferEventHandlers: v4.z.boolean().default(false),
32147
32218
  });
32148
32219
  class Environment {
32149
32220
  constructor(scope, fnType, compilerMode, config, contextIdentifiers, parentFunction, logger, filename, code, programContext) {
@@ -38115,33 +38186,11 @@ function codegenInstructionNullable(cx, instr) {
38115
38186
  }
38116
38187
  else {
38117
38188
  lvalue = instr.value.lvalue.pattern;
38118
- let hasReassign = false;
38119
- let hasDeclaration = false;
38120
38189
  for (const place of eachPatternOperand(lvalue)) {
38121
38190
  if (kind !== InstructionKind.Reassign &&
38122
38191
  place.identifier.name === null) {
38123
38192
  cx.temp.set(place.identifier.declarationId, null);
38124
38193
  }
38125
- const isDeclared = cx.hasDeclared(place.identifier);
38126
- hasReassign || (hasReassign = isDeclared);
38127
- hasDeclaration || (hasDeclaration = !isDeclared);
38128
- }
38129
- if (hasReassign && hasDeclaration) {
38130
- CompilerError.invariant(false, {
38131
- reason: 'Encountered a destructuring operation where some identifiers are already declared (reassignments) but others are not (declarations)',
38132
- description: null,
38133
- details: [
38134
- {
38135
- kind: 'error',
38136
- loc: instr.loc,
38137
- message: null,
38138
- },
38139
- ],
38140
- suggestions: null,
38141
- });
38142
- }
38143
- else if (hasReassign) {
38144
- kind = InstructionKind.Reassign;
38145
38194
  }
38146
38195
  value = codegenPlaceToExpression(cx, instr.value.value);
38147
38196
  }
@@ -39345,10 +39394,13 @@ let Visitor$9 = class Visitor extends ReactiveFunctionTransform {
39345
39394
  }
39346
39395
  transformInstruction(instruction, state) {
39347
39396
  this.visitInstruction(instruction, state);
39397
+ let instructionsToProcess = [instruction];
39398
+ let result = { kind: 'keep' };
39348
39399
  if (instruction.value.kind === 'Destructure') {
39349
39400
  const transformed = transformDestructuring(state, instruction, instruction.value);
39350
39401
  if (transformed) {
39351
- return {
39402
+ instructionsToProcess = transformed;
39403
+ result = {
39352
39404
  kind: 'replace-many',
39353
39405
  value: transformed.map(instruction => ({
39354
39406
  kind: 'instruction',
@@ -39357,7 +39409,14 @@ let Visitor$9 = class Visitor extends ReactiveFunctionTransform {
39357
39409
  };
39358
39410
  }
39359
39411
  }
39360
- return { kind: 'keep' };
39412
+ for (const instr of instructionsToProcess) {
39413
+ for (const [place, kind] of eachInstructionLValueWithKind(instr)) {
39414
+ if (kind !== InstructionKind.Reassign) {
39415
+ state.declared.add(place.identifier.declarationId);
39416
+ }
39417
+ }
39418
+ }
39419
+ return result;
39361
39420
  }
39362
39421
  };
39363
39422
  function transformDestructuring(state, instr, destructure) {
@@ -39368,9 +39427,12 @@ function transformDestructuring(state, instr, destructure) {
39368
39427
  if (isDeclared) {
39369
39428
  reassigned.add(place.identifier.id);
39370
39429
  }
39371
- hasDeclaration || (hasDeclaration = !isDeclared);
39430
+ else {
39431
+ hasDeclaration = true;
39432
+ }
39372
39433
  }
39373
- if (reassigned.size === 0 || !hasDeclaration) {
39434
+ if (!hasDeclaration) {
39435
+ destructure.lvalue.kind = InstructionKind.Reassign;
39374
39436
  return null;
39375
39437
  }
39376
39438
  const instructions = [];
@@ -41052,6 +41114,7 @@ function applyEffect(context, state, _effect, initialized, effects) {
41052
41114
  case ValueKind.Primitive: {
41053
41115
  break;
41054
41116
  }
41117
+ case ValueKind.MaybeFrozen:
41055
41118
  case ValueKind.Frozen: {
41056
41119
  sourceType = 'frozen';
41057
41120
  break;
@@ -44792,6 +44855,85 @@ function findOptionalPlaces(fn) {
44792
44855
  return optionals;
44793
44856
  }
44794
44857
 
44858
+ function createControlDominators(fn, isControlVariable) {
44859
+ const postDominators = computePostDominatorTree(fn, {
44860
+ includeThrowsAsExitNode: false,
44861
+ });
44862
+ const postDominatorFrontierCache = new Map();
44863
+ function isControlledBlock(id) {
44864
+ let controlBlocks = postDominatorFrontierCache.get(id);
44865
+ if (controlBlocks === undefined) {
44866
+ controlBlocks = postDominatorFrontier(fn, postDominators, id);
44867
+ postDominatorFrontierCache.set(id, controlBlocks);
44868
+ }
44869
+ for (const blockId of controlBlocks) {
44870
+ const controlBlock = fn.body.blocks.get(blockId);
44871
+ switch (controlBlock.terminal.kind) {
44872
+ case 'if':
44873
+ case 'branch': {
44874
+ if (isControlVariable(controlBlock.terminal.test)) {
44875
+ return true;
44876
+ }
44877
+ break;
44878
+ }
44879
+ case 'switch': {
44880
+ if (isControlVariable(controlBlock.terminal.test)) {
44881
+ return true;
44882
+ }
44883
+ for (const case_ of controlBlock.terminal.cases) {
44884
+ if (case_.test !== null && isControlVariable(case_.test)) {
44885
+ return true;
44886
+ }
44887
+ }
44888
+ break;
44889
+ }
44890
+ }
44891
+ }
44892
+ return false;
44893
+ }
44894
+ return isControlledBlock;
44895
+ }
44896
+ function postDominatorFrontier(fn, postDominators, targetId) {
44897
+ const visited = new Set();
44898
+ const frontier = new Set();
44899
+ const targetPostDominators = postDominatorsOf(fn, postDominators, targetId);
44900
+ for (const blockId of [...targetPostDominators, targetId]) {
44901
+ if (visited.has(blockId)) {
44902
+ continue;
44903
+ }
44904
+ visited.add(blockId);
44905
+ const block = fn.body.blocks.get(blockId);
44906
+ for (const pred of block.preds) {
44907
+ if (!targetPostDominators.has(pred)) {
44908
+ frontier.add(pred);
44909
+ }
44910
+ }
44911
+ }
44912
+ return frontier;
44913
+ }
44914
+ function postDominatorsOf(fn, postDominators, targetId) {
44915
+ var _a;
44916
+ const result = new Set();
44917
+ const visited = new Set();
44918
+ const queue = [targetId];
44919
+ while (queue.length) {
44920
+ const currentId = queue.shift();
44921
+ if (visited.has(currentId)) {
44922
+ continue;
44923
+ }
44924
+ visited.add(currentId);
44925
+ const current = fn.body.blocks.get(currentId);
44926
+ for (const pred of current.preds) {
44927
+ const predPostDominator = (_a = postDominators.get(pred)) !== null && _a !== void 0 ? _a : pred;
44928
+ if (predPostDominator === targetId || result.has(predPostDominator)) {
44929
+ result.add(pred);
44930
+ }
44931
+ queue.push(pred);
44932
+ }
44933
+ }
44934
+ return result;
44935
+ }
44936
+
44795
44937
  class StableSidemap {
44796
44938
  constructor(env) {
44797
44939
  this.map = new Map();
@@ -44867,42 +45009,7 @@ function inferReactivePlaces(fn) {
44867
45009
  const place = param.kind === 'Identifier' ? param : param.place;
44868
45010
  reactiveIdentifiers.markReactive(place);
44869
45011
  }
44870
- const postDominators = computePostDominatorTree(fn, {
44871
- includeThrowsAsExitNode: false,
44872
- });
44873
- const postDominatorFrontierCache = new Map();
44874
- function isReactiveControlledBlock(id) {
44875
- let controlBlocks = postDominatorFrontierCache.get(id);
44876
- if (controlBlocks === undefined) {
44877
- controlBlocks = postDominatorFrontier(fn, postDominators, id);
44878
- postDominatorFrontierCache.set(id, controlBlocks);
44879
- }
44880
- for (const blockId of controlBlocks) {
44881
- const controlBlock = fn.body.blocks.get(blockId);
44882
- switch (controlBlock.terminal.kind) {
44883
- case 'if':
44884
- case 'branch': {
44885
- if (reactiveIdentifiers.isReactive(controlBlock.terminal.test)) {
44886
- return true;
44887
- }
44888
- break;
44889
- }
44890
- case 'switch': {
44891
- if (reactiveIdentifiers.isReactive(controlBlock.terminal.test)) {
44892
- return true;
44893
- }
44894
- for (const case_ of controlBlock.terminal.cases) {
44895
- if (case_.test !== null &&
44896
- reactiveIdentifiers.isReactive(case_.test)) {
44897
- return true;
44898
- }
44899
- }
44900
- break;
44901
- }
44902
- }
44903
- }
44904
- return false;
44905
- }
45012
+ const isReactiveControlledBlock = createControlDominators(fn, place => reactiveIdentifiers.isReactive(place));
44906
45013
  do {
44907
45014
  for (const [, block] of fn.body.blocks) {
44908
45015
  let hasReactiveControl = isReactiveControlledBlock(block.id);
@@ -45020,46 +45127,6 @@ function inferReactivePlaces(fn) {
45020
45127
  }
45021
45128
  propagateReactivityToInnerFunctions(fn, true);
45022
45129
  }
45023
- function postDominatorFrontier(fn, postDominators, targetId) {
45024
- const visited = new Set();
45025
- const frontier = new Set();
45026
- const targetPostDominators = postDominatorsOf(fn, postDominators, targetId);
45027
- for (const blockId of [...targetPostDominators, targetId]) {
45028
- if (visited.has(blockId)) {
45029
- continue;
45030
- }
45031
- visited.add(blockId);
45032
- const block = fn.body.blocks.get(blockId);
45033
- for (const pred of block.preds) {
45034
- if (!targetPostDominators.has(pred)) {
45035
- frontier.add(pred);
45036
- }
45037
- }
45038
- }
45039
- return frontier;
45040
- }
45041
- function postDominatorsOf(fn, postDominators, targetId) {
45042
- var _a;
45043
- const result = new Set();
45044
- const visited = new Set();
45045
- const queue = [targetId];
45046
- while (queue.length) {
45047
- const currentId = queue.shift();
45048
- if (visited.has(currentId)) {
45049
- continue;
45050
- }
45051
- visited.add(currentId);
45052
- const current = fn.body.blocks.get(currentId);
45053
- for (const pred of current.preds) {
45054
- const predPostDominator = (_a = postDominators.get(pred)) !== null && _a !== void 0 ? _a : pred;
45055
- if (predPostDominator === targetId || result.has(predPostDominator)) {
45056
- result.add(pred);
45057
- }
45058
- queue.push(pred);
45059
- }
45060
- }
45061
- return result;
45062
- }
45063
45130
  class ReactivityMap {
45064
45131
  constructor(aliasedIdentifiers) {
45065
45132
  this.hasChanges = false;
@@ -48306,6 +48373,25 @@ function* generateInstructionTypes(env, names, instr) {
48306
48373
  }
48307
48374
  }
48308
48375
  }
48376
+ if (env.config.enableInferEventHandlers) {
48377
+ if (value.kind === 'JsxExpression' &&
48378
+ value.tag.kind === 'BuiltinTag' &&
48379
+ !value.tag.name.includes('-')) {
48380
+ for (const prop of value.props) {
48381
+ if (prop.kind === 'JsxAttribute' &&
48382
+ prop.name.startsWith('on') &&
48383
+ prop.name.length > 2 &&
48384
+ prop.name[2] === prop.name[2].toUpperCase()) {
48385
+ yield equation(prop.place.identifier.type, {
48386
+ kind: 'Function',
48387
+ shapeId: BuiltInEventHandlerId,
48388
+ return: makeType(),
48389
+ isConstructor: false,
48390
+ });
48391
+ }
48392
+ }
48393
+ }
48394
+ }
48309
48395
  yield equation(left, { kind: 'Object', shapeId: BuiltInJsxId });
48310
48396
  break;
48311
48397
  }
@@ -49229,6 +49315,10 @@ function refTypeOfType(place) {
49229
49315
  return { kind: 'None' };
49230
49316
  }
49231
49317
  }
49318
+ function isEventHandlerType(identifier) {
49319
+ const type = identifier.type;
49320
+ return type.kind === 'Function' && type.shapeId === BuiltInEventHandlerId;
49321
+ }
49232
49322
  function tyEqual(a, b) {
49233
49323
  if (a.kind !== b.kind) {
49234
49324
  return false;
@@ -49515,8 +49605,10 @@ function validateNoRefAccessInRenderImpl(fn, env) {
49515
49605
  }
49516
49606
  if (!didError) {
49517
49607
  const isRefLValue = isUseRefType(instr.lvalue.identifier);
49608
+ const isEventHandlerLValue = isEventHandlerType(instr.lvalue.identifier);
49518
49609
  for (const operand of eachInstructionValueOperand(instr.value)) {
49519
49610
  if (isRefLValue ||
49611
+ isEventHandlerLValue ||
49520
49612
  (hookKind != null &&
49521
49613
  hookKind !== 'useState' &&
49522
49614
  hookKind !== 'useReducer')) {
@@ -51015,20 +51107,84 @@ function validateNoSetStateInEffects(fn, env) {
51015
51107
  return errors.asResult();
51016
51108
  }
51017
51109
  function getSetStateCall(fn, setStateFunctions, env) {
51110
+ const enableAllowSetStateFromRefsInEffects = env.config.enableAllowSetStateFromRefsInEffects;
51018
51111
  const refDerivedValues = new Set();
51019
51112
  const isDerivedFromRef = (place) => {
51020
51113
  return (refDerivedValues.has(place.identifier.id) ||
51021
51114
  isUseRefType(place.identifier) ||
51022
51115
  isRefValueType(place.identifier));
51023
51116
  };
51117
+ const isRefControlledBlock = enableAllowSetStateFromRefsInEffects
51118
+ ? createControlDominators(fn, place => isDerivedFromRef(place))
51119
+ : () => false;
51024
51120
  for (const [, block] of fn.body.blocks) {
51121
+ if (enableAllowSetStateFromRefsInEffects) {
51122
+ for (const phi of block.phis) {
51123
+ if (isDerivedFromRef(phi.place)) {
51124
+ continue;
51125
+ }
51126
+ let isPhiDerivedFromRef = false;
51127
+ for (const [, operand] of phi.operands) {
51128
+ if (isDerivedFromRef(operand)) {
51129
+ isPhiDerivedFromRef = true;
51130
+ break;
51131
+ }
51132
+ }
51133
+ if (isPhiDerivedFromRef) {
51134
+ refDerivedValues.add(phi.place.identifier.id);
51135
+ }
51136
+ else {
51137
+ for (const [pred] of phi.operands) {
51138
+ if (isRefControlledBlock(pred)) {
51139
+ refDerivedValues.add(phi.place.identifier.id);
51140
+ break;
51141
+ }
51142
+ }
51143
+ }
51144
+ }
51145
+ }
51025
51146
  for (const instr of block.instructions) {
51026
- if (env.config.enableAllowSetStateFromRefsInEffects) {
51147
+ if (enableAllowSetStateFromRefsInEffects) {
51027
51148
  const hasRefOperand = Iterable_some(eachInstructionValueOperand(instr.value), isDerivedFromRef);
51028
51149
  if (hasRefOperand) {
51029
51150
  for (const lvalue of eachInstructionLValue(instr)) {
51030
51151
  refDerivedValues.add(lvalue.identifier.id);
51031
51152
  }
51153
+ for (const operand of eachInstructionValueOperand(instr.value)) {
51154
+ switch (operand.effect) {
51155
+ case Effect.Capture:
51156
+ case Effect.Store:
51157
+ case Effect.ConditionallyMutate:
51158
+ case Effect.ConditionallyMutateIterator:
51159
+ case Effect.Mutate: {
51160
+ if (isMutable(instr, operand)) {
51161
+ refDerivedValues.add(operand.identifier.id);
51162
+ }
51163
+ break;
51164
+ }
51165
+ case Effect.Freeze:
51166
+ case Effect.Read: {
51167
+ break;
51168
+ }
51169
+ case Effect.Unknown: {
51170
+ CompilerError.invariant(false, {
51171
+ reason: 'Unexpected unknown effect',
51172
+ description: null,
51173
+ details: [
51174
+ {
51175
+ kind: 'error',
51176
+ loc: operand.loc,
51177
+ message: null,
51178
+ },
51179
+ ],
51180
+ suggestions: null,
51181
+ });
51182
+ }
51183
+ default: {
51184
+ assertExhaustive$1(operand.effect, `Unexpected effect kind \`${operand.effect}\``);
51185
+ }
51186
+ }
51187
+ }
51032
51188
  }
51033
51189
  if (instr.value.kind === 'PropertyLoad' &&
51034
51190
  instr.value.property === 'current' &&
@@ -51055,13 +51211,16 @@ function getSetStateCall(fn, setStateFunctions, env) {
51055
51211
  const callee = instr.value.callee;
51056
51212
  if (isSetStateType(callee.identifier) ||
51057
51213
  setStateFunctions.has(callee.identifier.id)) {
51058
- if (env.config.enableAllowSetStateFromRefsInEffects) {
51214
+ if (enableAllowSetStateFromRefsInEffects) {
51059
51215
  const arg = instr.value.args.at(0);
51060
51216
  if (arg !== undefined &&
51061
51217
  arg.kind === 'Identifier' &&
51062
51218
  refDerivedValues.has(arg.identifier.id)) {
51063
51219
  return null;
51064
51220
  }
51221
+ else if (isRefControlledBlock(block.id)) {
51222
+ continue;
51223
+ }
51065
51224
  }
51066
51225
  return callee;
51067
51226
  }
@@ -18948,9 +18948,18 @@ function isRefOrRefLikeMutableType(type) {
18948
18948
  function isSetStateType(id) {
18949
18949
  return id.type.kind === 'Function' && id.type.shapeId === 'BuiltInSetState';
18950
18950
  }
18951
+ function isUseActionStateType(id) {
18952
+ return (id.type.kind === 'Object' && id.type.shapeId === 'BuiltInUseActionState');
18953
+ }
18951
18954
  function isStartTransitionType(id) {
18952
18955
  return (id.type.kind === 'Function' && id.type.shapeId === 'BuiltInStartTransition');
18953
18956
  }
18957
+ function isUseOptimisticType(id) {
18958
+ return (id.type.kind === 'Object' && id.type.shapeId === 'BuiltInUseOptimistic');
18959
+ }
18960
+ function isSetOptimisticType(id) {
18961
+ return (id.type.kind === 'Function' && id.type.shapeId === 'BuiltInSetOptimistic');
18962
+ }
18954
18963
  function isSetActionStateType(id) {
18955
18964
  return (id.type.kind === 'Function' && id.type.shapeId === 'BuiltInSetActionState');
18956
18965
  }
@@ -18972,7 +18981,8 @@ function isStableType(id) {
18972
18981
  isSetActionStateType(id) ||
18973
18982
  isDispatcherType(id) ||
18974
18983
  isUseRefType(id) ||
18975
- isStartTransitionType(id));
18984
+ isStartTransitionType(id) ||
18985
+ isSetOptimisticType(id));
18976
18986
  }
18977
18987
  function isStableTypeContainer(id) {
18978
18988
  const type_ = id.type;
@@ -18980,8 +18990,9 @@ function isStableTypeContainer(id) {
18980
18990
  return false;
18981
18991
  }
18982
18992
  return (isUseStateType(id) ||
18983
- type_.shapeId === 'BuiltInUseActionState' ||
18993
+ isUseActionStateType(id) ||
18984
18994
  isUseReducerType(id) ||
18995
+ isUseOptimisticType(id) ||
18985
18996
  type_.shapeId === 'BuiltInUseTransition');
18986
18997
  }
18987
18998
  function evaluatesToStableTypeOrContainer(env, { value }) {
@@ -18994,6 +19005,7 @@ function evaluatesToStableTypeOrContainer(env, { value }) {
18994
19005
  case 'useActionState':
18995
19006
  case 'useRef':
18996
19007
  case 'useTransition':
19008
+ case 'useOptimistic':
18997
19009
  return true;
18998
19010
  }
18999
19011
  }
@@ -19842,6 +19854,29 @@ function* eachInstructionLValue(instr) {
19842
19854
  }
19843
19855
  yield* eachInstructionValueLValue(instr.value);
19844
19856
  }
19857
+ function* eachInstructionLValueWithKind(instr) {
19858
+ switch (instr.value.kind) {
19859
+ case 'DeclareContext':
19860
+ case 'StoreContext':
19861
+ case 'DeclareLocal':
19862
+ case 'StoreLocal': {
19863
+ yield [instr.value.lvalue.place, instr.value.lvalue.kind];
19864
+ break;
19865
+ }
19866
+ case 'Destructure': {
19867
+ const kind = instr.value.lvalue.kind;
19868
+ for (const place of eachPatternOperand(instr.value.lvalue.pattern)) {
19869
+ yield [place, kind];
19870
+ }
19871
+ break;
19872
+ }
19873
+ case 'PostfixUpdate':
19874
+ case 'PrefixUpdate': {
19875
+ yield [instr.value.lvalue, InstructionKind.Reassign];
19876
+ break;
19877
+ }
19878
+ }
19879
+ }
19845
19880
  function* eachInstructionValueLValue(value) {
19846
19881
  switch (value.kind) {
19847
19882
  case 'DeclareContext':
@@ -22072,12 +22107,15 @@ const BuiltInUseReducerId = 'BuiltInUseReducer';
22072
22107
  const BuiltInDispatchId = 'BuiltInDispatch';
22073
22108
  const BuiltInUseContextHookId = 'BuiltInUseContextHook';
22074
22109
  const BuiltInUseTransitionId = 'BuiltInUseTransition';
22110
+ const BuiltInUseOptimisticId = 'BuiltInUseOptimistic';
22111
+ const BuiltInSetOptimisticId = 'BuiltInSetOptimistic';
22075
22112
  const BuiltInStartTransitionId = 'BuiltInStartTransition';
22076
22113
  const BuiltInFireId = 'BuiltInFire';
22077
22114
  const BuiltInFireFunctionId = 'BuiltInFireFunction';
22078
22115
  const BuiltInUseEffectEventId = 'BuiltInUseEffectEvent';
22079
- const BuiltinEffectEventId = 'BuiltInEffectEventFunction';
22116
+ const BuiltInEffectEventId = 'BuiltInEffectEventFunction';
22080
22117
  const BuiltInAutodepsId = 'BuiltInAutoDepsId';
22118
+ const BuiltInEventHandlerId = 'BuiltInEventHandlerId';
22081
22119
  const ReanimatedSharedValueId = 'ReanimatedSharedValueId';
22082
22120
  const BUILTIN_SHAPES = new Map();
22083
22121
  addObject(BUILTIN_SHAPES, BuiltInPropsId, [
@@ -22685,6 +22723,19 @@ addObject(BUILTIN_SHAPES, BuiltInUseTransitionId, [
22685
22723
  }, BuiltInStartTransitionId),
22686
22724
  ],
22687
22725
  ]);
22726
+ addObject(BUILTIN_SHAPES, BuiltInUseOptimisticId, [
22727
+ ['0', { kind: 'Poly' }],
22728
+ [
22729
+ '1',
22730
+ addFunction(BUILTIN_SHAPES, [], {
22731
+ positionalParams: [],
22732
+ restParam: Effect.Freeze,
22733
+ returnType: PRIMITIVE_TYPE,
22734
+ calleeEffect: Effect.Read,
22735
+ returnValueKind: ValueKind.Primitive,
22736
+ }, BuiltInSetOptimisticId),
22737
+ ],
22738
+ ]);
22688
22739
  addObject(BUILTIN_SHAPES, BuiltInUseActionStateId, [
22689
22740
  ['0', { kind: 'Poly' }],
22690
22741
  [
@@ -22724,7 +22775,14 @@ addFunction(BUILTIN_SHAPES, [], {
22724
22775
  returnType: { kind: 'Poly' },
22725
22776
  calleeEffect: Effect.ConditionallyMutate,
22726
22777
  returnValueKind: ValueKind.Mutable,
22727
- }, BuiltinEffectEventId);
22778
+ }, BuiltInEffectEventId);
22779
+ addFunction(BUILTIN_SHAPES, [], {
22780
+ positionalParams: [],
22781
+ restParam: Effect.ConditionallyMutate,
22782
+ returnType: { kind: 'Poly' },
22783
+ calleeEffect: Effect.ConditionallyMutate,
22784
+ returnValueKind: ValueKind.Mutable,
22785
+ }, BuiltInEventHandlerId);
22728
22786
  addObject(BUILTIN_SHAPES, BuiltInMixedReadonlyId, [
22729
22787
  [
22730
22788
  'toString',
@@ -30915,6 +30973,18 @@ const REACT_APIS = [
30915
30973
  returnValueKind: ValueKind.Frozen,
30916
30974
  }),
30917
30975
  ],
30976
+ [
30977
+ 'useOptimistic',
30978
+ addHook(DEFAULT_SHAPES, {
30979
+ positionalParams: [],
30980
+ restParam: Effect.Freeze,
30981
+ returnType: { kind: 'Object', shapeId: BuiltInUseOptimisticId },
30982
+ calleeEffect: Effect.Read,
30983
+ hookKind: 'useOptimistic',
30984
+ returnValueKind: ValueKind.Frozen,
30985
+ returnValueReason: ValueReason.State,
30986
+ }),
30987
+ ],
30918
30988
  [
30919
30989
  'use',
30920
30990
  addFunction(DEFAULT_SHAPES, [], {
@@ -30948,7 +31018,7 @@ const REACT_APIS = [
30948
31018
  returnType: {
30949
31019
  kind: 'Function',
30950
31020
  return: { kind: 'Poly' },
30951
- shapeId: BuiltinEffectEventId,
31021
+ shapeId: BuiltInEffectEventId,
30952
31022
  isConstructor: false,
30953
31023
  },
30954
31024
  calleeEffect: Effect.Read,
@@ -31971,6 +32041,7 @@ const EnvironmentConfigSchema = v4.z.object({
31971
32041
  validateNoVoidUseMemo: v4.z.boolean().default(true),
31972
32042
  validateNoDynamicallyCreatedComponentsOrHooks: v4.z.boolean().default(false),
31973
32043
  enableAllowSetStateFromRefsInEffects: v4.z.boolean().default(true),
32044
+ enableInferEventHandlers: v4.z.boolean().default(false),
31974
32045
  });
31975
32046
  class Environment {
31976
32047
  constructor(scope, fnType, compilerMode, config, contextIdentifiers, parentFunction, logger, filename, code, programContext) {
@@ -37942,33 +38013,11 @@ function codegenInstructionNullable(cx, instr) {
37942
38013
  }
37943
38014
  else {
37944
38015
  lvalue = instr.value.lvalue.pattern;
37945
- let hasReassign = false;
37946
- let hasDeclaration = false;
37947
38016
  for (const place of eachPatternOperand(lvalue)) {
37948
38017
  if (kind !== InstructionKind.Reassign &&
37949
38018
  place.identifier.name === null) {
37950
38019
  cx.temp.set(place.identifier.declarationId, null);
37951
38020
  }
37952
- const isDeclared = cx.hasDeclared(place.identifier);
37953
- hasReassign || (hasReassign = isDeclared);
37954
- hasDeclaration || (hasDeclaration = !isDeclared);
37955
- }
37956
- if (hasReassign && hasDeclaration) {
37957
- CompilerError.invariant(false, {
37958
- reason: 'Encountered a destructuring operation where some identifiers are already declared (reassignments) but others are not (declarations)',
37959
- description: null,
37960
- details: [
37961
- {
37962
- kind: 'error',
37963
- loc: instr.loc,
37964
- message: null,
37965
- },
37966
- ],
37967
- suggestions: null,
37968
- });
37969
- }
37970
- else if (hasReassign) {
37971
- kind = InstructionKind.Reassign;
37972
38021
  }
37973
38022
  value = codegenPlaceToExpression(cx, instr.value.value);
37974
38023
  }
@@ -39172,10 +39221,13 @@ let Visitor$9 = class Visitor extends ReactiveFunctionTransform {
39172
39221
  }
39173
39222
  transformInstruction(instruction, state) {
39174
39223
  this.visitInstruction(instruction, state);
39224
+ let instructionsToProcess = [instruction];
39225
+ let result = { kind: 'keep' };
39175
39226
  if (instruction.value.kind === 'Destructure') {
39176
39227
  const transformed = transformDestructuring(state, instruction, instruction.value);
39177
39228
  if (transformed) {
39178
- return {
39229
+ instructionsToProcess = transformed;
39230
+ result = {
39179
39231
  kind: 'replace-many',
39180
39232
  value: transformed.map(instruction => ({
39181
39233
  kind: 'instruction',
@@ -39184,7 +39236,14 @@ let Visitor$9 = class Visitor extends ReactiveFunctionTransform {
39184
39236
  };
39185
39237
  }
39186
39238
  }
39187
- return { kind: 'keep' };
39239
+ for (const instr of instructionsToProcess) {
39240
+ for (const [place, kind] of eachInstructionLValueWithKind(instr)) {
39241
+ if (kind !== InstructionKind.Reassign) {
39242
+ state.declared.add(place.identifier.declarationId);
39243
+ }
39244
+ }
39245
+ }
39246
+ return result;
39188
39247
  }
39189
39248
  };
39190
39249
  function transformDestructuring(state, instr, destructure) {
@@ -39195,9 +39254,12 @@ function transformDestructuring(state, instr, destructure) {
39195
39254
  if (isDeclared) {
39196
39255
  reassigned.add(place.identifier.id);
39197
39256
  }
39198
- hasDeclaration || (hasDeclaration = !isDeclared);
39257
+ else {
39258
+ hasDeclaration = true;
39259
+ }
39199
39260
  }
39200
- if (reassigned.size === 0 || !hasDeclaration) {
39261
+ if (!hasDeclaration) {
39262
+ destructure.lvalue.kind = InstructionKind.Reassign;
39201
39263
  return null;
39202
39264
  }
39203
39265
  const instructions = [];
@@ -40879,6 +40941,7 @@ function applyEffect(context, state, _effect, initialized, effects) {
40879
40941
  case ValueKind.Primitive: {
40880
40942
  break;
40881
40943
  }
40944
+ case ValueKind.MaybeFrozen:
40882
40945
  case ValueKind.Frozen: {
40883
40946
  sourceType = 'frozen';
40884
40947
  break;
@@ -44619,6 +44682,85 @@ function findOptionalPlaces(fn) {
44619
44682
  return optionals;
44620
44683
  }
44621
44684
 
44685
+ function createControlDominators(fn, isControlVariable) {
44686
+ const postDominators = computePostDominatorTree(fn, {
44687
+ includeThrowsAsExitNode: false,
44688
+ });
44689
+ const postDominatorFrontierCache = new Map();
44690
+ function isControlledBlock(id) {
44691
+ let controlBlocks = postDominatorFrontierCache.get(id);
44692
+ if (controlBlocks === undefined) {
44693
+ controlBlocks = postDominatorFrontier(fn, postDominators, id);
44694
+ postDominatorFrontierCache.set(id, controlBlocks);
44695
+ }
44696
+ for (const blockId of controlBlocks) {
44697
+ const controlBlock = fn.body.blocks.get(blockId);
44698
+ switch (controlBlock.terminal.kind) {
44699
+ case 'if':
44700
+ case 'branch': {
44701
+ if (isControlVariable(controlBlock.terminal.test)) {
44702
+ return true;
44703
+ }
44704
+ break;
44705
+ }
44706
+ case 'switch': {
44707
+ if (isControlVariable(controlBlock.terminal.test)) {
44708
+ return true;
44709
+ }
44710
+ for (const case_ of controlBlock.terminal.cases) {
44711
+ if (case_.test !== null && isControlVariable(case_.test)) {
44712
+ return true;
44713
+ }
44714
+ }
44715
+ break;
44716
+ }
44717
+ }
44718
+ }
44719
+ return false;
44720
+ }
44721
+ return isControlledBlock;
44722
+ }
44723
+ function postDominatorFrontier(fn, postDominators, targetId) {
44724
+ const visited = new Set();
44725
+ const frontier = new Set();
44726
+ const targetPostDominators = postDominatorsOf(fn, postDominators, targetId);
44727
+ for (const blockId of [...targetPostDominators, targetId]) {
44728
+ if (visited.has(blockId)) {
44729
+ continue;
44730
+ }
44731
+ visited.add(blockId);
44732
+ const block = fn.body.blocks.get(blockId);
44733
+ for (const pred of block.preds) {
44734
+ if (!targetPostDominators.has(pred)) {
44735
+ frontier.add(pred);
44736
+ }
44737
+ }
44738
+ }
44739
+ return frontier;
44740
+ }
44741
+ function postDominatorsOf(fn, postDominators, targetId) {
44742
+ var _a;
44743
+ const result = new Set();
44744
+ const visited = new Set();
44745
+ const queue = [targetId];
44746
+ while (queue.length) {
44747
+ const currentId = queue.shift();
44748
+ if (visited.has(currentId)) {
44749
+ continue;
44750
+ }
44751
+ visited.add(currentId);
44752
+ const current = fn.body.blocks.get(currentId);
44753
+ for (const pred of current.preds) {
44754
+ const predPostDominator = (_a = postDominators.get(pred)) !== null && _a !== void 0 ? _a : pred;
44755
+ if (predPostDominator === targetId || result.has(predPostDominator)) {
44756
+ result.add(pred);
44757
+ }
44758
+ queue.push(pred);
44759
+ }
44760
+ }
44761
+ return result;
44762
+ }
44763
+
44622
44764
  class StableSidemap {
44623
44765
  constructor(env) {
44624
44766
  this.map = new Map();
@@ -44694,42 +44836,7 @@ function inferReactivePlaces(fn) {
44694
44836
  const place = param.kind === 'Identifier' ? param : param.place;
44695
44837
  reactiveIdentifiers.markReactive(place);
44696
44838
  }
44697
- const postDominators = computePostDominatorTree(fn, {
44698
- includeThrowsAsExitNode: false,
44699
- });
44700
- const postDominatorFrontierCache = new Map();
44701
- function isReactiveControlledBlock(id) {
44702
- let controlBlocks = postDominatorFrontierCache.get(id);
44703
- if (controlBlocks === undefined) {
44704
- controlBlocks = postDominatorFrontier(fn, postDominators, id);
44705
- postDominatorFrontierCache.set(id, controlBlocks);
44706
- }
44707
- for (const blockId of controlBlocks) {
44708
- const controlBlock = fn.body.blocks.get(blockId);
44709
- switch (controlBlock.terminal.kind) {
44710
- case 'if':
44711
- case 'branch': {
44712
- if (reactiveIdentifiers.isReactive(controlBlock.terminal.test)) {
44713
- return true;
44714
- }
44715
- break;
44716
- }
44717
- case 'switch': {
44718
- if (reactiveIdentifiers.isReactive(controlBlock.terminal.test)) {
44719
- return true;
44720
- }
44721
- for (const case_ of controlBlock.terminal.cases) {
44722
- if (case_.test !== null &&
44723
- reactiveIdentifiers.isReactive(case_.test)) {
44724
- return true;
44725
- }
44726
- }
44727
- break;
44728
- }
44729
- }
44730
- }
44731
- return false;
44732
- }
44839
+ const isReactiveControlledBlock = createControlDominators(fn, place => reactiveIdentifiers.isReactive(place));
44733
44840
  do {
44734
44841
  for (const [, block] of fn.body.blocks) {
44735
44842
  let hasReactiveControl = isReactiveControlledBlock(block.id);
@@ -44847,46 +44954,6 @@ function inferReactivePlaces(fn) {
44847
44954
  }
44848
44955
  propagateReactivityToInnerFunctions(fn, true);
44849
44956
  }
44850
- function postDominatorFrontier(fn, postDominators, targetId) {
44851
- const visited = new Set();
44852
- const frontier = new Set();
44853
- const targetPostDominators = postDominatorsOf(fn, postDominators, targetId);
44854
- for (const blockId of [...targetPostDominators, targetId]) {
44855
- if (visited.has(blockId)) {
44856
- continue;
44857
- }
44858
- visited.add(blockId);
44859
- const block = fn.body.blocks.get(blockId);
44860
- for (const pred of block.preds) {
44861
- if (!targetPostDominators.has(pred)) {
44862
- frontier.add(pred);
44863
- }
44864
- }
44865
- }
44866
- return frontier;
44867
- }
44868
- function postDominatorsOf(fn, postDominators, targetId) {
44869
- var _a;
44870
- const result = new Set();
44871
- const visited = new Set();
44872
- const queue = [targetId];
44873
- while (queue.length) {
44874
- const currentId = queue.shift();
44875
- if (visited.has(currentId)) {
44876
- continue;
44877
- }
44878
- visited.add(currentId);
44879
- const current = fn.body.blocks.get(currentId);
44880
- for (const pred of current.preds) {
44881
- const predPostDominator = (_a = postDominators.get(pred)) !== null && _a !== void 0 ? _a : pred;
44882
- if (predPostDominator === targetId || result.has(predPostDominator)) {
44883
- result.add(pred);
44884
- }
44885
- queue.push(pred);
44886
- }
44887
- }
44888
- return result;
44889
- }
44890
44957
  class ReactivityMap {
44891
44958
  constructor(aliasedIdentifiers) {
44892
44959
  this.hasChanges = false;
@@ -48133,6 +48200,25 @@ function* generateInstructionTypes(env, names, instr) {
48133
48200
  }
48134
48201
  }
48135
48202
  }
48203
+ if (env.config.enableInferEventHandlers) {
48204
+ if (value.kind === 'JsxExpression' &&
48205
+ value.tag.kind === 'BuiltinTag' &&
48206
+ !value.tag.name.includes('-')) {
48207
+ for (const prop of value.props) {
48208
+ if (prop.kind === 'JsxAttribute' &&
48209
+ prop.name.startsWith('on') &&
48210
+ prop.name.length > 2 &&
48211
+ prop.name[2] === prop.name[2].toUpperCase()) {
48212
+ yield equation(prop.place.identifier.type, {
48213
+ kind: 'Function',
48214
+ shapeId: BuiltInEventHandlerId,
48215
+ return: makeType(),
48216
+ isConstructor: false,
48217
+ });
48218
+ }
48219
+ }
48220
+ }
48221
+ }
48136
48222
  yield equation(left, { kind: 'Object', shapeId: BuiltInJsxId });
48137
48223
  break;
48138
48224
  }
@@ -49056,6 +49142,10 @@ function refTypeOfType(place) {
49056
49142
  return { kind: 'None' };
49057
49143
  }
49058
49144
  }
49145
+ function isEventHandlerType(identifier) {
49146
+ const type = identifier.type;
49147
+ return type.kind === 'Function' && type.shapeId === BuiltInEventHandlerId;
49148
+ }
49059
49149
  function tyEqual(a, b) {
49060
49150
  if (a.kind !== b.kind) {
49061
49151
  return false;
@@ -49342,8 +49432,10 @@ function validateNoRefAccessInRenderImpl(fn, env) {
49342
49432
  }
49343
49433
  if (!didError) {
49344
49434
  const isRefLValue = isUseRefType(instr.lvalue.identifier);
49435
+ const isEventHandlerLValue = isEventHandlerType(instr.lvalue.identifier);
49345
49436
  for (const operand of eachInstructionValueOperand(instr.value)) {
49346
49437
  if (isRefLValue ||
49438
+ isEventHandlerLValue ||
49347
49439
  (hookKind != null &&
49348
49440
  hookKind !== 'useState' &&
49349
49441
  hookKind !== 'useReducer')) {
@@ -50842,20 +50934,84 @@ function validateNoSetStateInEffects(fn, env) {
50842
50934
  return errors.asResult();
50843
50935
  }
50844
50936
  function getSetStateCall(fn, setStateFunctions, env) {
50937
+ const enableAllowSetStateFromRefsInEffects = env.config.enableAllowSetStateFromRefsInEffects;
50845
50938
  const refDerivedValues = new Set();
50846
50939
  const isDerivedFromRef = (place) => {
50847
50940
  return (refDerivedValues.has(place.identifier.id) ||
50848
50941
  isUseRefType(place.identifier) ||
50849
50942
  isRefValueType(place.identifier));
50850
50943
  };
50944
+ const isRefControlledBlock = enableAllowSetStateFromRefsInEffects
50945
+ ? createControlDominators(fn, place => isDerivedFromRef(place))
50946
+ : () => false;
50851
50947
  for (const [, block] of fn.body.blocks) {
50948
+ if (enableAllowSetStateFromRefsInEffects) {
50949
+ for (const phi of block.phis) {
50950
+ if (isDerivedFromRef(phi.place)) {
50951
+ continue;
50952
+ }
50953
+ let isPhiDerivedFromRef = false;
50954
+ for (const [, operand] of phi.operands) {
50955
+ if (isDerivedFromRef(operand)) {
50956
+ isPhiDerivedFromRef = true;
50957
+ break;
50958
+ }
50959
+ }
50960
+ if (isPhiDerivedFromRef) {
50961
+ refDerivedValues.add(phi.place.identifier.id);
50962
+ }
50963
+ else {
50964
+ for (const [pred] of phi.operands) {
50965
+ if (isRefControlledBlock(pred)) {
50966
+ refDerivedValues.add(phi.place.identifier.id);
50967
+ break;
50968
+ }
50969
+ }
50970
+ }
50971
+ }
50972
+ }
50852
50973
  for (const instr of block.instructions) {
50853
- if (env.config.enableAllowSetStateFromRefsInEffects) {
50974
+ if (enableAllowSetStateFromRefsInEffects) {
50854
50975
  const hasRefOperand = Iterable_some(eachInstructionValueOperand(instr.value), isDerivedFromRef);
50855
50976
  if (hasRefOperand) {
50856
50977
  for (const lvalue of eachInstructionLValue(instr)) {
50857
50978
  refDerivedValues.add(lvalue.identifier.id);
50858
50979
  }
50980
+ for (const operand of eachInstructionValueOperand(instr.value)) {
50981
+ switch (operand.effect) {
50982
+ case Effect.Capture:
50983
+ case Effect.Store:
50984
+ case Effect.ConditionallyMutate:
50985
+ case Effect.ConditionallyMutateIterator:
50986
+ case Effect.Mutate: {
50987
+ if (isMutable(instr, operand)) {
50988
+ refDerivedValues.add(operand.identifier.id);
50989
+ }
50990
+ break;
50991
+ }
50992
+ case Effect.Freeze:
50993
+ case Effect.Read: {
50994
+ break;
50995
+ }
50996
+ case Effect.Unknown: {
50997
+ CompilerError.invariant(false, {
50998
+ reason: 'Unexpected unknown effect',
50999
+ description: null,
51000
+ details: [
51001
+ {
51002
+ kind: 'error',
51003
+ loc: operand.loc,
51004
+ message: null,
51005
+ },
51006
+ ],
51007
+ suggestions: null,
51008
+ });
51009
+ }
51010
+ default: {
51011
+ assertExhaustive$1(operand.effect, `Unexpected effect kind \`${operand.effect}\``);
51012
+ }
51013
+ }
51014
+ }
50859
51015
  }
50860
51016
  if (instr.value.kind === 'PropertyLoad' &&
50861
51017
  instr.value.property === 'current' &&
@@ -50882,13 +51038,16 @@ function getSetStateCall(fn, setStateFunctions, env) {
50882
51038
  const callee = instr.value.callee;
50883
51039
  if (isSetStateType(callee.identifier) ||
50884
51040
  setStateFunctions.has(callee.identifier.id)) {
50885
- if (env.config.enableAllowSetStateFromRefsInEffects) {
51041
+ if (enableAllowSetStateFromRefsInEffects) {
50886
51042
  const arg = instr.value.args.at(0);
50887
51043
  if (arg !== undefined &&
50888
51044
  arg.kind === 'Identifier' &&
50889
51045
  refDerivedValues.has(arg.identifier.id)) {
50890
51046
  return null;
50891
51047
  }
51048
+ else if (isRefControlledBlock(block.id)) {
51049
+ continue;
51050
+ }
50892
51051
  }
50893
51052
  return callee;
50894
51053
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "eslint-plugin-react-hooks",
3
3
  "description": "ESLint rules for React Hooks",
4
- "version": "7.1.0-canary-257b033f-20251113",
4
+ "version": "7.1.0-canary-0972e239-20251118",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "https://github.com/facebook/react.git",