Skip to content

Commit eb57c09

Browse files
committed
cmd/go: rewrite collectDeps to only depend on imports' deps
Change-Id: I0cac9f32855e49e9899709a2f4421083aa0e75cc Reviewed-on: https://go-review.googlesource.com/c/go/+/483515 Reviewed-by: Bryan Mills <[email protected]> TryBot-Result: Gopher Robot <[email protected]> Run-TryBot: Michael Matloob <[email protected]> Reviewed-by: Michael Matloob <[email protected]>
1 parent 7830180 commit eb57c09

File tree

2 files changed

+126
-35
lines changed

2 files changed

+126
-35
lines changed

src/cmd/go/internal/list/list.go

Lines changed: 51 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -598,8 +598,6 @@ func runList(ctx context.Context, cmd *base.Command, args []string) {
598598
base.Fatalf("go list -export cannot be used with -find")
599599
}
600600

601-
suppressDeps := !listJsonFields.needAny("Deps", "DepsErrors")
602-
603601
pkgOpts := load.PackageOpts{
604602
IgnoreImports: *listFind,
605603
ModResolveTests: *listTest,
@@ -767,15 +765,28 @@ func runList(ctx context.Context, cmd *base.Command, args []string) {
767765
}
768766
}
769767

770-
if !suppressDeps {
768+
if listJsonFields.needAny("Deps", "DepsErrors") {
771769
all := pkgs
770+
// Make sure we iterate through packages in a postorder traversal,
771+
// which load.PackageList guarantees. If *listDeps, then all is
772+
// already in PackageList order. Otherwise, calling load.PackageList
773+
// provides the guarantee. In the case of an import cycle, the last package
774+
// visited in the cycle, importing the first encountered package in the cycle,
775+
// is visited first. The cycle import error will be bubbled up in the traversal
776+
// order up to the first package in the cycle, covering all the packages
777+
// in the cycle.
772778
if !*listDeps {
773-
// if *listDeps, then all is already in PackageList order.
774779
all = load.PackageList(pkgs)
775780
}
776-
// Recompute deps lists using new strings, from the leaves up.
777-
for _, p := range all {
778-
collectDeps(p)
781+
if listJsonFields.needAny("Deps") {
782+
for _, p := range all {
783+
collectDeps(p)
784+
}
785+
}
786+
if listJsonFields.needAny("DepsErrors") {
787+
for _, p := range all {
788+
collectDepsErrors(p)
789+
}
779790
}
780791
}
781792

@@ -878,29 +889,15 @@ func loadPackageList(roots []*load.Package) []*load.Package {
878889
return pkgs
879890
}
880891

881-
// collectDeps populates p.Deps and p.DepsErrors by iterating over
882-
// p.Internal.Imports.
883-
//
884-
// TODO(jayconrod): collectDeps iterates over transitive imports for every
885-
// package. We should only need to visit direct imports.
892+
// collectDeps populates p.Deps by iterating over p.Internal.Imports.
893+
// collectDeps must be called on all of p's Imports before being called on p.
886894
func collectDeps(p *load.Package) {
887-
deps := make(map[string]*load.Package)
888-
var q []*load.Package
889-
q = append(q, p.Internal.Imports...)
890-
for i := 0; i < len(q); i++ {
891-
p1 := q[i]
892-
path := p1.ImportPath
893-
// The same import path could produce an error or not,
894-
// depending on what tries to import it.
895-
// Prefer to record entries with errors, so we can report them.
896-
p0 := deps[path]
897-
if p0 == nil || p1.Error != nil && (p0.Error == nil || len(p0.Error.ImportStack) > len(p1.Error.ImportStack)) {
898-
deps[path] = p1
899-
for _, p2 := range p1.Internal.Imports {
900-
if deps[p2.ImportPath] != p2 {
901-
q = append(q, p2)
902-
}
903-
}
895+
deps := make(map[string]bool)
896+
897+
for _, p := range p.Internal.Imports {
898+
deps[p.ImportPath] = true
899+
for _, q := range p.Deps {
900+
deps[q] = true
904901
}
905902
}
906903

@@ -909,15 +906,34 @@ func collectDeps(p *load.Package) {
909906
p.Deps = append(p.Deps, dep)
910907
}
911908
sort.Strings(p.Deps)
912-
for _, dep := range p.Deps {
913-
p1 := deps[dep]
914-
if p1 == nil {
915-
panic("impossible: missing entry in package cache for " + dep + " imported by " + p.ImportPath)
909+
}
910+
911+
// collectDeps populates p.DepsErrors by iterating over p.Internal.Imports.
912+
// collectDepsErrors must be called on all of p's Imports before being called on p.
913+
func collectDepsErrors(p *load.Package) {
914+
depsErrors := make(map[*load.PackageError]bool)
915+
916+
for _, p := range p.Internal.Imports {
917+
if p.Error != nil {
918+
depsErrors[p.Error] = true
916919
}
917-
if p1.Error != nil {
918-
p.DepsErrors = append(p.DepsErrors, p1.Error)
920+
for _, q := range p.DepsErrors {
921+
depsErrors[q] = true
919922
}
920923
}
924+
925+
p.DepsErrors = make([]*load.PackageError, 0, len(depsErrors))
926+
for deperr := range depsErrors {
927+
p.DepsErrors = append(p.DepsErrors, deperr)
928+
}
929+
// Sort packages by the package on the top of the stack, which should be
930+
// the package the error was produced for. Each package can have at most
931+
// one error set on it.
932+
sort.Slice(p.DepsErrors, func(i, j int) bool {
933+
stki, stkj := p.DepsErrors[i].ImportStack, p.DepsErrors[j].ImportStack
934+
pathi, pathj := stki[len(stki)-1], stkj[len(stkj)-1]
935+
return pathi < pathj
936+
})
921937
}
922938

923939
// TrackingWriter tracks the last byte written on every write so
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
go list -e -deps -json=ImportPath,Error,DepsErrors m/a
2+
cmp stdout want
3+
4+
-- want --
5+
{
6+
"ImportPath": "m/c",
7+
"DepsErrors": [
8+
{
9+
"ImportStack": [
10+
"m/a",
11+
"m/b",
12+
"m/c",
13+
"m/a"
14+
],
15+
"Pos": "",
16+
"Err": "import cycle not allowed"
17+
}
18+
]
19+
}
20+
{
21+
"ImportPath": "m/b",
22+
"DepsErrors": [
23+
{
24+
"ImportStack": [
25+
"m/a",
26+
"m/b",
27+
"m/c",
28+
"m/a"
29+
],
30+
"Pos": "",
31+
"Err": "import cycle not allowed"
32+
}
33+
]
34+
}
35+
{
36+
"ImportPath": "m/a",
37+
"Error": {
38+
"ImportStack": [
39+
"m/a",
40+
"m/b",
41+
"m/c",
42+
"m/a"
43+
],
44+
"Pos": "",
45+
"Err": "import cycle not allowed"
46+
},
47+
"DepsErrors": [
48+
{
49+
"ImportStack": [
50+
"m/a",
51+
"m/b",
52+
"m/c",
53+
"m/a"
54+
],
55+
"Pos": "",
56+
"Err": "import cycle not allowed"
57+
}
58+
]
59+
}
60+
-- go.mod --
61+
module m
62+
63+
go 1.21
64+
-- a/a.go --
65+
package a
66+
67+
import _ "m/b"
68+
-- b/b.go --
69+
package b
70+
71+
import _ "m/c"
72+
-- c/c.go --
73+
package c
74+
75+
import _ "m/a"

0 commit comments

Comments
 (0)