Skip to content

Commit 2bdbc94

Browse files
author
Jay Conrod
committed
cmd/go: print package import chains for some build list errors
When we construct the build list by loading packages (e.g., in "go build", "go list", or "go test"), we may load additional modules not mentioned in the original build list. If we encounter an error loading one of these modules, mvs.BuildList currently returns a BuildListError with a chain of requirments. Unfortunately, this is not helpful, since the graph is structured such that these missing modules are direct requirements of the main module. With this change, loader.load keeps track of the package that caused each "missing" module to be added. If an error occurs in a missing module, the chain of package imports is printed instead of the module requirements. Fixes #31475 Change-Id: Ie484814af42ceea3e85fedc38e705ba3a38cd495 Reviewed-on: https://go-review.googlesource.com/c/go/+/171859 Run-TryBot: Jay Conrod <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Bryan C. Mills <[email protected]>
1 parent f7c9672 commit 2bdbc94

File tree

4 files changed

+70
-14
lines changed

4 files changed

+70
-14
lines changed

src/cmd/go/internal/modload/load.go

+25-9
Original file line numberDiff line numberDiff line change
@@ -546,6 +546,7 @@ func (ld *loader) load(roots func() []string) {
546546
for _, m := range buildList {
547547
haveMod[m] = true
548548
}
549+
modAddedBy := make(map[module.Version]*loadPkg)
549550
for _, pkg := range ld.pkgs {
550551
if err, ok := pkg.err.(*ImportMissingError); ok && err.Module.Path != "" {
551552
if err.newMissingVersion != "" {
@@ -558,6 +559,7 @@ func (ld *loader) load(roots func() []string) {
558559
numAdded++
559560
if !haveMod[err.Module] {
560561
haveMod[err.Module] = true
562+
modAddedBy[err.Module] = pkg
561563
buildList = append(buildList, err.Module)
562564
}
563565
continue
@@ -573,6 +575,14 @@ func (ld *loader) load(roots func() []string) {
573575
reqs = Reqs()
574576
buildList, err = mvs.BuildList(Target, reqs)
575577
if err != nil {
578+
// If an error was found in a newly added module, report the package
579+
// import stack instead of the module requirement stack. Packages
580+
// are more descriptive.
581+
if err, ok := err.(*mvs.BuildListError); ok {
582+
if pkg := modAddedBy[err.Module()]; pkg != nil {
583+
base.Fatalf("go: %s: %v", pkg.stackText(), err.Err)
584+
}
585+
}
576586
base.Fatalf("go: %v", err)
577587
}
578588
}
@@ -804,27 +814,33 @@ func (ld *loader) buildStacks() {
804814
// stackText builds the import stack text to use when
805815
// reporting an error in pkg. It has the general form
806816
//
807-
// import root ->
808-
// import other ->
809-
// import other2 ->
810-
// import pkg
817+
// root imports
818+
// other imports
819+
// other2 tested by
820+
// other2.test imports
821+
// pkg
811822
//
812823
func (pkg *loadPkg) stackText() string {
813824
var stack []*loadPkg
814-
for p := pkg.stack; p != nil; p = p.stack {
825+
for p := pkg; p != nil; p = p.stack {
815826
stack = append(stack, p)
816827
}
817828

818829
var buf bytes.Buffer
819830
for i := len(stack) - 1; i >= 0; i-- {
820831
p := stack[i]
832+
fmt.Fprint(&buf, p.path)
821833
if p.testOf != nil {
822-
fmt.Fprintf(&buf, "test ->\n\t")
823-
} else {
824-
fmt.Fprintf(&buf, "import %q ->\n\t", p.path)
834+
fmt.Fprint(&buf, ".test")
835+
}
836+
if i > 0 {
837+
if stack[i-1].testOf == p {
838+
fmt.Fprint(&buf, " tested by\n\t")
839+
} else {
840+
fmt.Fprint(&buf, " imports\n\t")
841+
}
825842
}
826843
}
827-
fmt.Fprintf(&buf, "import %q", pkg.path)
828844
return buf.String()
829845
}
830846

src/cmd/go/internal/mvs/mvs.go

+12-3
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ type Reqs interface {
6565
// while constructing a build list. BuildListError prints the chain
6666
// of requirements to the module where the error occurred.
6767
type BuildListError struct {
68-
err error
68+
Err error
6969
stack []buildListErrorElem
7070
}
7171

@@ -77,9 +77,18 @@ type buildListErrorElem struct {
7777
nextReason string
7878
}
7979

80+
// Module returns the module where the error occurred. If the module stack
81+
// is empty, this returns a zero value.
82+
func (e *BuildListError) Module() module.Version {
83+
if len(e.stack) == 0 {
84+
return module.Version{}
85+
}
86+
return e.stack[0].m
87+
}
88+
8089
func (e *BuildListError) Error() string {
8190
b := &strings.Builder{}
82-
errMsg := e.err.Error()
91+
errMsg := e.Err.Error()
8392
stack := e.stack
8493

8594
// Don't print modules at the beginning of the chain without a
@@ -177,7 +186,7 @@ func buildList(target module.Version, reqs Reqs, upgrade func(module.Version) mo
177186

178187
if node.err != nil {
179188
err := &BuildListError{
180-
err: node.err,
189+
Err: node.err,
181190
stack: []buildListErrorElem{{m: node.m}},
182191
}
183192
for n, prev := neededBy[node], node; n != nil; n, prev = neededBy[n], n {

src/cmd/go/testdata/script/mod_load_badchain.txt

+32-1
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,39 @@ cmp go.mod go.mod.orig
1919
cmp stderr update-main-expected
2020
cmp go.mod go.mod.orig
2121

22-
# update manually. Listing modules should produce an error.
22+
# Update manually. Listing modules should produce an error.
2323
go mod edit -require=example.com/badchain/[email protected]
2424
! go list -m
2525
cmp stderr list-expected
2626

27+
# Try listing a package that imports a package
28+
# in a module without a requirement.
29+
go mod edit -droprequire example.com/badchain/a
30+
! go list m/use
31+
cmp stderr list-missing-expected
32+
33+
! go list -test m/testuse
34+
cmp stderr list-missing-test-expected
35+
2736
-- go.mod.orig --
2837
module m
2938

3039
require example.com/badchain/a v1.0.0
40+
-- use/use.go --
41+
package use
42+
43+
import _ "example.com/badchain/c"
44+
-- testuse/testuse.go --
45+
package testuse
46+
-- testuse/testuse_test.go --
47+
package testuse
48+
49+
import (
50+
"testing"
51+
_ "example.com/badchain/c"
52+
)
53+
54+
func Test(t *testing.T) {}
3155
-- update-main-expected --
3256
go get: example.com/badchain/[email protected] updating to
3357
example.com/badchain/[email protected]: parsing go.mod: unexpected module path "example.com/badchain/wrong"
@@ -39,3 +63,10 @@ go get: example.com/badchain/[email protected] requires
3963
go: example.com/badchain/[email protected] requires
4064
example.com/badchain/[email protected] requires
4165
example.com/badchain/[email protected]: parsing go.mod: unexpected module path "example.com/badchain/wrong"
66+
-- list-missing-expected --
67+
go: m/use imports
68+
example.com/badchain/c: example.com/badchain/[email protected]: parsing go.mod: unexpected module path "example.com/badchain/wrong"
69+
-- list-missing-test-expected --
70+
go: m/testuse tested by
71+
m/testuse.test imports
72+
example.com/badchain/c: example.com/badchain/[email protected]: parsing go.mod: unexpected module path "example.com/badchain/wrong"

src/cmd/go/testdata/script/mod_missingpkg_prerelease.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
env GO111MODULE=on
22

33
! go list use.go
4-
stderr 'import "example.com/missingpkg/deprecated": package provided by example.com/missingpkg at latest version v1.0.0 but not at required version v1.0.1-beta'
4+
stderr 'example.com/missingpkg/deprecated: package provided by example.com/missingpkg at latest version v1.0.0 but not at required version v1.0.1-beta'
55

66
-- use.go --
77
package use

0 commit comments

Comments
 (0)