From b4a618c43f3e483151a4963e1a3ed40721327f75 Mon Sep 17 00:00:00 2001 From: Grant Zvolsky Date: Thu, 29 May 2025 03:52:50 -0400 Subject: [PATCH] fix(query.deps): memoize provided labels rather than declared targets The `plz query deps` graph traversal previously memoized targets directly from DeclaredDependencies. This was problematic because a single declared dependency can provide different actual build labels (and therefore different actual dependencies) depending on the context of the target that requires it (via `dep.ProvideFor(target)`). If a declared dependency target `D` was processed for a pareent `P1`, `D` would be marked as done. If another parent `P2` also declared `D` as a dependency, and `D.ProvideFor(P2)` would have yielded additional build labels, these provisions for `P2` would be skipped because `D` was already memoized. --- src/query/deps.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/query/deps.go b/src/query/deps.go index 23bf9832c8..a67d884d61 100644 --- a/src/query/deps.go +++ b/src/query/deps.go @@ -17,7 +17,7 @@ func Deps(out io.Writer, state *core.BuildState, labels []core.BuildLabel, hidde fmt.Fprintf(out, " edge [fontname=\"Helvetica,Arial,sans-serif\"]\n") fmt.Fprintf(out, " rankdir=\"LR\"\n") } - done := map[*core.BuildTarget]bool{} + done := map[core.BuildLabel]bool{} for _, label := range labels { deps(out, state, state.Graph.TargetOrDie(label), done, targetLevel, 0, hidden, formatdot) } @@ -27,17 +27,17 @@ func Deps(out io.Writer, state *core.BuildState, labels []core.BuildLabel, hidde } // deps looks at all the deps of the given target & recurses into them, printing as appropriate. -func deps(out io.Writer, state *core.BuildState, target *core.BuildTarget, done map[*core.BuildTarget]bool, targetLevel, currentLevel int, hidden, formatdot bool) { +func deps(out io.Writer, state *core.BuildState, target *core.BuildTarget, done map[core.BuildLabel]bool, targetLevel, currentLevel int, hidden, formatdot bool) { if currentLevel == targetLevel { return } for _, l := range target.DeclaredDependencies() { dep := state.Graph.TargetOrDie(l) - if !state.ShouldInclude(dep) || done[dep] { - continue // target is filtered out - } - done[dep] = true for _, l := range dep.ProvideFor(target) { + if !state.ShouldInclude(dep) || done[l] { + continue // target is filtered out + } + done[l] = true if dep := state.Graph.TargetOrDie(l); hidden || !dep.HasParent() { // dep is to be printed; either we're printing hidden deps or it has no parent (i.e. is not hidden) if formatdot {