@@ -467,53 +467,52 @@ type TreeNode = {
467467function buildTreeNode (
468468 sourceId : IdentifierId ,
469469 context : ValidationContext ,
470- ) : TreeNode | null {
470+ visited : Set < string > = new Set ( ) ,
471+ ) : Array < TreeNode > {
471472 const sourceMetadata = context . derivationCache . cache . get ( sourceId ) ;
472473 if ( ! sourceMetadata ) {
473- return null ;
474+ return [ ] ;
474475 }
475476
476- const childSourceIds = Array . from ( sourceMetadata . sourcesIds ) . filter (
477- id => id !== sourceId ,
478- ) ;
479-
480- if ( ! isNamedIdentifier ( sourceMetadata . place ) ) {
481- const childrenMap = new Map < string , TreeNode > ( ) ;
482- for ( const childId of childSourceIds ) {
483- const childNode = buildTreeNode ( childId , context ) ;
484- if ( childNode ) {
485- if ( ! childrenMap . has ( childNode . name ) ) {
486- childrenMap . set ( childNode . name , childNode ) ;
477+ const children : Array < TreeNode > = [ ] ;
478+
479+ const namedSiblings : Set < string > = new Set ( ) ;
480+ for ( const childId of sourceMetadata . sourcesIds ) {
481+ const childNodes = buildTreeNode (
482+ childId ,
483+ context ,
484+ new Set ( [
485+ ...visited ,
486+ ...( isNamedIdentifier ( sourceMetadata . place )
487+ ? [ sourceMetadata . place . identifier . name . value ]
488+ : [ ] ) ,
489+ ] ) ,
490+ ) ;
491+ if ( childNodes ) {
492+ for ( const childNode of childNodes ) {
493+ if ( ! namedSiblings . has ( childNode . name ) ) {
494+ children . push ( childNode ) ;
495+ namedSiblings . add ( childNode . name ) ;
487496 }
488497 }
489498 }
490- const children = Array . from ( childrenMap . values ( ) ) ;
491-
492- if ( children . length === 1 ) {
493- return children [ 0 ] ;
494- } else if ( children . length > 1 ) {
495- return null ;
496- }
497- return null ;
498499 }
499500
500- const childrenMap = new Map < string , TreeNode > ( ) ;
501- for ( const childId of childSourceIds ) {
502- const childNode = buildTreeNode ( childId , context ) ;
503- if ( childNode ) {
504- if ( ! childrenMap . has ( childNode . name ) ) {
505- childrenMap . set ( childNode . name , childNode ) ;
506- }
507- }
501+ if (
502+ isNamedIdentifier ( sourceMetadata . place ) &&
503+ ! visited . has ( sourceMetadata . place . identifier . name . value )
504+ ) {
505+ return [
506+ {
507+ name : sourceMetadata . place . identifier . name . value ,
508+ typeOfValue : sourceMetadata . typeOfValue ,
509+ isSource : sourceMetadata . isStateSource ,
510+ children : children ,
511+ } ,
512+ ] ;
508513 }
509- const children = Array . from ( childrenMap . values ( ) ) ;
510514
511- return {
512- name : sourceMetadata . place . identifier . name . value ,
513- typeOfValue : sourceMetadata . typeOfValue ,
514- isSource : sourceMetadata . isStateSource ,
515- children,
516- } ;
515+ return children ;
517516}
518517
519518function renderTree (
@@ -558,6 +557,23 @@ function renderTree(
558557 return result ;
559558}
560559
560+ function getFnGlobalDeps (
561+ fn : FunctionExpression | undefined ,
562+ deps : Set < IdentifierId > ,
563+ ) : void {
564+ if ( ! fn ) {
565+ return ;
566+ }
567+
568+ for ( const [ , block ] of fn . loweredFunc . func . body . blocks ) {
569+ for ( const instr of block . instructions ) {
570+ if ( instr . value . kind === 'LoadLocal' ) {
571+ deps . add ( instr . value . place . identifier . id ) ;
572+ }
573+ }
574+ }
575+ }
576+
561577function validateEffect (
562578 effectFunction : HIRFunction ,
563579 context : ValidationContext ,
@@ -576,8 +592,24 @@ function validateEffect(
576592 Set < SourceLocation >
577593 > = new Map ( ) ;
578594
595+ const cleanUpFunctionDeps : Set < IdentifierId > = new Set ( ) ;
596+
579597 const globals : Set < IdentifierId > = new Set ( ) ;
580598 for ( const block of effectFunction . body . blocks . values ( ) ) {
599+ /*
600+ * if the block is in an effect and is of type return then its an effect's cleanup function
601+ * if the cleanup function depends on a value from which effect-set state is derived then
602+ * we can't validate
603+ */
604+ if (
605+ block . terminal . kind === 'return' &&
606+ block . terminal . returnVariant === 'Explicit'
607+ ) {
608+ getFnGlobalDeps (
609+ context . functions . get ( block . terminal . value . identifier . id ) ,
610+ cleanUpFunctionDeps ,
611+ ) ;
612+ }
581613 for ( const pred of block . preds ) {
582614 if ( ! seenBlocks . has ( pred ) ) {
583615 // skip if block has a back edge
@@ -652,6 +684,8 @@ function validateEffect(
652684 }
653685
654686 for ( const derivedSetStateCall of effectDerivedSetStateCalls ) {
687+ console . log ( context . derivationCache . cache ) ;
688+ console . log ( derivedSetStateCall ) ;
655689 const rootSetStateCall = getRootSetState (
656690 derivedSetStateCall . id ,
657691 context . setStateLoads ,
@@ -667,25 +701,34 @@ function validateEffect(
667701 const allSourceIds = Array . from ( derivedSetStateCall . sourceIds ) ;
668702 const propsSet = new Set < string > ( ) ;
669703 const stateSet = new Set < string > ( ) ;
704+ const allSetStateDeps = new Set < IdentifierId > ( ) ;
670705
671- const treeNodesMap = new Map < string , TreeNode > ( ) ;
706+ const rootNodesMap = new Map < string , TreeNode > ( ) ;
672707 for ( const id of allSourceIds ) {
673- const node = buildTreeNode ( id , context ) ;
674- if ( node && ! treeNodesMap . has ( node . name ) ) {
675- treeNodesMap . set ( node . name , node ) ;
708+ const nodes = buildTreeNode ( id , context ) ;
709+ for ( const node of nodes ) {
710+ if ( ! rootNodesMap . has ( node . name ) ) {
711+ rootNodesMap . set ( node . name , node ) ;
712+ }
676713 }
677714 }
678- const treeNodes = Array . from ( treeNodesMap . values ( ) ) ;
715+ const rootNodes = Array . from ( rootNodesMap . values ( ) ) ;
679716
680- const trees = treeNodes . map ( ( node , index ) =>
681- renderTree (
717+ const trees = rootNodes . map ( ( node , index ) => {
718+ return renderTree (
682719 node ,
683720 '' ,
684- index === treeNodes . length - 1 ,
721+ index === rootNodes . length - 1 ,
685722 propsSet ,
686723 stateSet ,
687- ) ,
688- ) ;
724+ ) ;
725+ } ) ;
726+
727+ for ( const dep of allSetStateDeps ) {
728+ if ( cleanUpFunctionDeps . has ( dep ) ) {
729+ return ;
730+ }
731+ }
689732
690733 const propsArr = Array . from ( propsSet ) ;
691734 const stateArr = Array . from ( stateSet ) ;
@@ -699,7 +742,7 @@ function validateEffect(
699742 rootSources += `State: [${ stateArr . join ( ', ' ) } ]` ;
700743 }
701744
702- const description = `Using an effect triggers an additional render which can hurt performance and user experience, potentially briefly showing stale values to the user
745+ const description = `TESTUsing an effect triggers an additional render which can hurt performance and user experience, potentially briefly showing stale values to the user
703746
704747This setState call is setting a derived value that depends on the following reactive sources:
705748
0 commit comments