From 03b0c4a6848ab4f0944d1e0e6ac15f37d885a2ec Mon Sep 17 00:00:00 2001 From: Sung Yoon Whang Date: Tue, 7 Jul 2026 14:44:13 -0700 Subject: [PATCH 1/2] fix(itg): propagate invalidation lookup errors --- core/itg/graph/invalidate.go | 5 ++++- core/itg/graph/invalidate_test.go | 11 +++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/core/itg/graph/invalidate.go b/core/itg/graph/invalidate.go index f4f3224..2ded12b 100644 --- a/core/itg/graph/invalidate.go +++ b/core/itg/graph/invalidate.go @@ -102,7 +102,10 @@ func (g *OptimizedGraph) invalidateHashRecursively(ids IntSet, invalidated IntSe targetID := candidates[curIdx] curIdx++ target, ok := g.OptimizedTargets[targetID] - if !ok || target.Hash == nil { + if !ok { + return fmt.Errorf("target %d not found", targetID) + } + if target.Hash == nil { continue } diff --git a/core/itg/graph/invalidate_test.go b/core/itg/graph/invalidate_test.go index fffdd02..caa8ee4 100644 --- a/core/itg/graph/invalidate_test.go +++ b/core/itg/graph/invalidate_test.go @@ -91,6 +91,17 @@ func TestInvalidateHashRecursively(t *testing.T) { // b has a nil hash and is thus skipped; invalidated stays empty assert.Empty(t, invalidated.UnsortedList()) }) + + t.Run("missing target returns an error", func(t *testing.T) { + t.Parallel() + + g := OptimizeGraph(map[string]*targethasher.Target{}) + ids := NewIntSet() + ids.Insert(42) + err := g.invalidateHashRecursively(ids, NewIntSet()) + + require.Error(t, err) + }) } // --- removeTarget --- From 0c3977ccd2696377d6637253e8b371bcc58483c4 Mon Sep 17 00:00:00 2001 From: Sung Yoon Whang Date: Tue, 7 Jul 2026 15:00:36 -0700 Subject: [PATCH 2/2] fix(itg): traverse nil-hash invalidations --- core/itg/graph/invalidate.go | 2 +- core/itg/graph/invalidate_test.go | 10 +++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/core/itg/graph/invalidate.go b/core/itg/graph/invalidate.go index 2ded12b..5723039 100644 --- a/core/itg/graph/invalidate.go +++ b/core/itg/graph/invalidate.go @@ -105,7 +105,7 @@ func (g *OptimizedGraph) invalidateHashRecursively(ids IntSet, invalidated IntSe if !ok { return fmt.Errorf("target %d not found", targetID) } - if target.Hash == nil { + if invalidated.Contains(targetID) { continue } diff --git a/core/itg/graph/invalidate_test.go b/core/itg/graph/invalidate_test.go index caa8ee4..9dc4c96 100644 --- a/core/itg/graph/invalidate_test.go +++ b/core/itg/graph/invalidate_test.go @@ -76,20 +76,24 @@ func TestInvalidateHashRecursively(t *testing.T) { assert.False(t, invalidated.Contains(aID)) }) - t.Run("targets with nil hash are skipped", func(t *testing.T) { + t.Run("targets with nil hash and their reverse deps are invalidated", func(t *testing.T) { t.Parallel() targets := map[string]*targethasher.Target{ "//pkg:a": {Name: "//pkg:a", RuleType: "go_library"}, // Hash=nil "//pkg:b": {Name: "//pkg:b", RuleType: "go_library", Deps: []string{"//pkg:a"}}, // Hash=nil + "//pkg:c": {Name: "//pkg:c", RuleType: "go_library", Hash: []byte{1}, Deps: []string{"//pkg:b"}}, } g := OptimizeGraph(targets) aID := g.TargetNameToID["//pkg:a"] + bID := g.TargetNameToID["//pkg:b"] + cID := g.TargetNameToID["//pkg:c"] invalidated := NewIntSet() require.NoError(t, g.invalidateHashRecursively(g.OptimizedTargets[aID].ReverseDeps, invalidated)) - // b has a nil hash and is thus skipped; invalidated stays empty - assert.Empty(t, invalidated.UnsortedList()) + assert.True(t, invalidated.Contains(bID)) + assert.True(t, invalidated.Contains(cID)) + assert.Nil(t, g.OptimizedTargets[cID].Hash) }) t.Run("missing target returns an error", func(t *testing.T) {