@@ -21,7 +21,6 @@ import {
2121 isUseStateType ,
2222 BasicBlock ,
2323 isUseRefType ,
24- GeneratedSource ,
2524 SourceLocation ,
2625} from '../HIR' ;
2726import { eachInstructionLValue , eachInstructionOperand } from '../HIR/visitors' ;
@@ -41,8 +40,8 @@ type ValidationContext = {
4140 readonly errors : CompilerError ;
4241 readonly derivationCache : DerivationCache ;
4342 readonly effects : Set < HIRFunction > ;
44- readonly setStateCache : Map < string | undefined | null , Array < Place > > ;
45- readonly effectSetStateCache : Map < string | undefined | null , Array < Place > > ;
43+ readonly setStateLoads : Map < IdentifierId , IdentifierId | null > ;
44+ readonly setStateUsages : Map < IdentifierId , Set < SourceLocation > > ;
4645} ;
4746
4847class DerivationCache {
@@ -182,19 +181,16 @@ export function validateNoDerivedComputationsInEffects_exp(
182181 const errors = new CompilerError ( ) ;
183182 const effects : Set < HIRFunction > = new Set ( ) ;
184183
185- const setStateCache : Map < string | undefined | null , Array < Place > > = new Map ( ) ;
186- const effectSetStateCache : Map <
187- string | undefined | null ,
188- Array < Place >
189- > = new Map ( ) ;
184+ const setStateLoads : Map < IdentifierId , IdentifierId > = new Map ( ) ;
185+ const setStateUsages : Map < IdentifierId , Set < SourceLocation > > = new Map ( ) ;
190186
191187 const context : ValidationContext = {
192188 functions,
193189 errors,
194190 derivationCache,
195191 effects,
196- setStateCache ,
197- effectSetStateCache ,
192+ setStateLoads ,
193+ setStateUsages ,
198194 } ;
199195
200196 if ( fn . fnType === 'Hook' ) {
@@ -284,11 +280,60 @@ function joinValue(
284280 return 'fromPropsAndState' ;
285281}
286282
283+ function getRootSetState (
284+ key : IdentifierId ,
285+ loads : Map < IdentifierId , IdentifierId | null > ,
286+ visited : Set < IdentifierId > = new Set ( ) ,
287+ ) : IdentifierId | null {
288+ if ( visited . has ( key ) ) {
289+ return null ;
290+ }
291+ visited . add ( key ) ;
292+
293+ const parentId = loads . get ( key ) ;
294+
295+ if ( parentId === undefined ) {
296+ return null ;
297+ }
298+
299+ if ( parentId === null ) {
300+ return key ;
301+ }
302+
303+ return getRootSetState ( parentId , loads , visited ) ;
304+ }
305+
306+ function maybeRecordSetState (
307+ instr : Instruction ,
308+ loads : Map < IdentifierId , IdentifierId | null > ,
309+ usages : Map < IdentifierId , Set < SourceLocation > > ,
310+ ) : void {
311+ for ( const operand of eachInstructionLValue ( instr ) ) {
312+ if ( isSetStateType ( operand . identifier ) ) {
313+ if ( instr . value . kind === 'LoadLocal' ) {
314+ loads . set ( operand . identifier . id , instr . value . place . identifier . id ) ;
315+ } else {
316+ // this is a root setState
317+ loads . set ( operand . identifier . id , null ) ;
318+ }
319+
320+ const rootSetState = getRootSetState ( operand . identifier . id , loads ) ;
321+ if ( rootSetState !== null && usages . get ( rootSetState ) === undefined ) {
322+ usages . set ( rootSetState , new Set ( [ operand . loc ] ) ) ;
323+ }
324+ }
325+ }
326+ }
327+
287328function recordInstructionDerivations (
288329 instr : Instruction ,
289330 context : ValidationContext ,
290331 isFirstPass : boolean ,
291332) : void {
333+ if ( isFirstPass ) {
334+ maybeRecordSetState ( instr , context . setStateLoads , context . setStateUsages ) ;
335+ }
336+
292337 let typeOfValue : TypeOfValue = 'ignored' ;
293338 const sources : Set < IdentifierId > = new Set ( ) ;
294339 const { lvalue, value} = instr ;
@@ -323,15 +368,13 @@ function recordInstructionDerivations(
323368 }
324369
325370 for ( const operand of eachInstructionOperand ( instr ) ) {
326- if (
327- isSetStateType ( operand . identifier ) &&
328- operand . loc !== GeneratedSource &&
329- isFirstPass
330- ) {
331- if ( context . setStateCache . has ( operand . loc . identifierName ) ) {
332- context . setStateCache . get ( operand . loc . identifierName ) ! . push ( operand ) ;
333- } else {
334- context . setStateCache . set ( operand . loc . identifierName , [ operand ] ) ;
371+ if ( isSetStateType ( operand . identifier ) && isFirstPass ) {
372+ const rootSetStateId = getRootSetState (
373+ operand . identifier . id ,
374+ context . setStateLoads ,
375+ ) ;
376+ if ( rootSetStateId !== null ) {
377+ context . setStateUsages . get ( rootSetStateId ) ?. add ( operand . loc ) ;
335378 }
336379 }
337380
@@ -477,11 +520,16 @@ function validateEffect(
477520
478521 const effectDerivedSetStateCalls : Array < {
479522 value : CallExpression ;
480- loc : SourceLocation ;
523+ id : IdentifierId ;
481524 sourceIds : Set < IdentifierId > ;
482525 typeOfValue : TypeOfValue ;
483526 } > = [ ] ;
484527
528+ const effectSetStateUsages : Map <
529+ IdentifierId ,
530+ Set < SourceLocation >
531+ > = new Map ( ) ;
532+
485533 const globals : Set < IdentifierId > = new Set ( ) ;
486534 for ( const block of effectFunction . body . blocks . values ( ) ) {
487535 for ( const pred of block . preds ) {
@@ -497,19 +545,16 @@ function validateEffect(
497545 return ;
498546 }
499547
548+ maybeRecordSetState ( instr , context . setStateLoads , effectSetStateUsages ) ;
549+
500550 for ( const operand of eachInstructionOperand ( instr ) ) {
501- if (
502- isSetStateType ( operand . identifier ) &&
503- operand . loc !== GeneratedSource
504- ) {
505- if ( context . effectSetStateCache . has ( operand . loc . identifierName ) ) {
506- context . effectSetStateCache
507- . get ( operand . loc . identifierName ) !
508- . push ( operand ) ;
509- } else {
510- context . effectSetStateCache . set ( operand . loc . identifierName , [
511- operand ,
512- ] ) ;
551+ if ( isSetStateType ( operand . identifier ) ) {
552+ const rootSetStateId = getRootSetState (
553+ operand . identifier . id ,
554+ context . setStateLoads ,
555+ ) ;
556+ if ( rootSetStateId !== null ) {
557+ effectSetStateUsages . get ( rootSetStateId ) ?. add ( operand . loc ) ;
513558 }
514559 }
515560 }
@@ -527,7 +572,7 @@ function validateEffect(
527572 if ( argMetadata !== undefined ) {
528573 effectDerivedSetStateCalls . push ( {
529574 value : instr . value ,
530- loc : instr . value . callee . loc ,
575+ id : instr . value . callee . identifier . id ,
531576 sourceIds : argMetadata . sourcesIds ,
532577 typeOfValue : argMetadata . typeOfValue ,
533578 } ) ;
@@ -561,15 +606,17 @@ function validateEffect(
561606 }
562607
563608 for ( const derivedSetStateCall of effectDerivedSetStateCalls ) {
609+ const rootSetStateCall = getRootSetState (
610+ derivedSetStateCall . id ,
611+ context . setStateLoads ,
612+ ) ;
613+
564614 if (
565- derivedSetStateCall . loc !== GeneratedSource &&
566- context . effectSetStateCache . has ( derivedSetStateCall . loc . identifierName ) &&
567- context . setStateCache . has ( derivedSetStateCall . loc . identifierName ) &&
568- context . effectSetStateCache . get ( derivedSetStateCall . loc . identifierName ) !
569- . length ===
570- context . setStateCache . get ( derivedSetStateCall . loc . identifierName ) !
571- . length -
572- 1
615+ rootSetStateCall !== null &&
616+ effectSetStateUsages . has ( rootSetStateCall ) &&
617+ context . setStateUsages . has ( rootSetStateCall ) &&
618+ effectSetStateUsages . get ( rootSetStateCall ) ! . size ===
619+ context . setStateUsages . get ( rootSetStateCall ) ! . size - 1
573620 ) {
574621 const allSourceIds = Array . from ( derivedSetStateCall . sourceIds ) ;
575622 const propsSet = new Set < string > ( ) ;
0 commit comments