Skip to content

Commit 6a71817

Browse files
author
Bryan C. Mills
committed
cmd/go/internal/mvs: export a NewBuildListError function
Also factor out BuildListError to a separate file. For #36460 Change-Id: Ibd1143893b09a2bbef659bea1e8c5dd35184a7ef Reviewed-on: https://go-review.googlesource.com/c/go/+/247764 Reviewed-by: Jay Conrod <[email protected]>
1 parent 9bcc5d2 commit 6a71817

File tree

2 files changed

+108
-65
lines changed

2 files changed

+108
-65
lines changed

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

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
// Copyright 2020 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package mvs
6+
7+
import (
8+
"fmt"
9+
"strings"
10+
11+
"golang.org/x/mod/module"
12+
)
13+
14+
// BuildListError decorates an error that occurred gathering requirements
15+
// while constructing a build list. BuildListError prints the chain
16+
// of requirements to the module where the error occurred.
17+
type BuildListError struct {
18+
Err error
19+
stack []buildListErrorElem
20+
}
21+
22+
type buildListErrorElem struct {
23+
m module.Version
24+
25+
// nextReason is the reason this module depends on the next module in the
26+
// stack. Typically either "requires", or "updating to".
27+
nextReason string
28+
}
29+
30+
// NewBuildListError returns a new BuildListError wrapping an error that
31+
// occurred at a module found along the given path of requirements and/or
32+
// upgrades, which must be non-empty.
33+
//
34+
// The isUpgrade function reports whether a path step is due to an upgrade.
35+
// A nil isUpgrade function indicates that none of the path steps are due to upgrades.
36+
func NewBuildListError(err error, path []module.Version, isUpgrade func(from, to module.Version) bool) *BuildListError {
37+
stack := make([]buildListErrorElem, 0, len(path))
38+
for len(path) > 1 {
39+
reason := "requires"
40+
if isUpgrade != nil && isUpgrade(path[0], path[1]) {
41+
reason = "updating to"
42+
}
43+
stack = append(stack, buildListErrorElem{
44+
m: path[0],
45+
nextReason: reason,
46+
})
47+
path = path[1:]
48+
}
49+
stack = append(stack, buildListErrorElem{m: path[0]})
50+
51+
return &BuildListError{
52+
Err: err,
53+
stack: stack,
54+
}
55+
}
56+
57+
// Module returns the module where the error occurred. If the module stack
58+
// is empty, this returns a zero value.
59+
func (e *BuildListError) Module() module.Version {
60+
if len(e.stack) == 0 {
61+
return module.Version{}
62+
}
63+
return e.stack[len(e.stack)-1].m
64+
}
65+
66+
func (e *BuildListError) Error() string {
67+
b := &strings.Builder{}
68+
stack := e.stack
69+
70+
// Don't print modules at the beginning of the chain without a
71+
// version. These always seem to be the main module or a
72+
// synthetic module ("target@").
73+
for len(stack) > 0 && stack[0].m.Version == "" {
74+
stack = stack[1:]
75+
}
76+
77+
if len(stack) == 0 {
78+
b.WriteString(e.Err.Error())
79+
} else {
80+
for _, elem := range stack[:len(stack)-1] {
81+
fmt.Fprintf(b, "%s@%s %s\n\t", elem.m.Path, elem.m.Version, elem.nextReason)
82+
}
83+
// Ensure that the final module path and version are included as part of the
84+
// error message.
85+
m := stack[len(stack)-1].m
86+
if _, ok := e.Err.(*module.ModuleError); ok {
87+
// TODO(bcmills): Also ensure that the module path and version match.
88+
// (Otherwise, we may be reporting an error from a replacement without
89+
// indicating the replacement path.)
90+
fmt.Fprintf(b, "%v", e.Err)
91+
} else {
92+
fmt.Fprintf(b, "%v", module.VersionError(m, e.Err))
93+
}
94+
}
95+
return b.String()
96+
}

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

Lines changed: 12 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ package mvs
99
import (
1010
"fmt"
1111
"sort"
12-
"strings"
1312
"sync"
1413
"sync/atomic"
1514

@@ -61,59 +60,6 @@ type Reqs interface {
6160
Previous(m module.Version) (module.Version, error)
6261
}
6362

64-
// BuildListError decorates an error that occurred gathering requirements
65-
// while constructing a build list. BuildListError prints the chain
66-
// of requirements to the module where the error occurred.
67-
type BuildListError struct {
68-
Err error
69-
stack []buildListErrorElem
70-
}
71-
72-
type buildListErrorElem struct {
73-
m module.Version
74-
75-
// nextReason is the reason this module depends on the next module in the
76-
// stack. Typically either "requires", or "upgraded to".
77-
nextReason string
78-
}
79-
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[len(e.stack)-1].m
87-
}
88-
89-
func (e *BuildListError) Error() string {
90-
b := &strings.Builder{}
91-
stack := e.stack
92-
93-
// Don't print modules at the beginning of the chain without a
94-
// version. These always seem to be the main module or a
95-
// synthetic module ("target@").
96-
for len(stack) > 0 && stack[0].m.Version == "" {
97-
stack = stack[1:]
98-
}
99-
100-
if len(stack) == 0 {
101-
b.WriteString(e.Err.Error())
102-
} else {
103-
for _, elem := range stack[:len(stack)-1] {
104-
fmt.Fprintf(b, "%s@%s %s\n\t", elem.m.Path, elem.m.Version, elem.nextReason)
105-
}
106-
// Ensure that the final module path and version are included as part of the
107-
// error message.
108-
if _, ok := e.Err.(*module.ModuleError); ok {
109-
fmt.Fprintf(b, "%v", e.Err)
110-
} else {
111-
fmt.Fprintf(b, "%v", module.VersionError(stack[len(stack)-1].m, e.Err))
112-
}
113-
}
114-
return b.String()
115-
}
116-
11763
// BuildList returns the build list for the target module.
11864
//
11965
// target is the root vertex of a module requirement graph. For cmd/go, this is
@@ -202,29 +148,30 @@ func buildList(target module.Version, reqs Reqs, upgrade func(module.Version) (m
202148
q = q[1:]
203149

204150
if node.err != nil {
205-
// Construct the stack reversed (from the error to the main module),
151+
pathUpgrade := map[module.Version]module.Version{}
152+
153+
// Construct the error path reversed (from the error to the main module),
206154
// then reverse it to obtain the usual order (from the main module to
207155
// the error).
208-
stack := []buildListErrorElem{{m: node.m}}
156+
errPath := []module.Version{node.m}
209157
for n, prev := neededBy[node], node; n != nil; n, prev = neededBy[n], n {
210-
reason := "requires"
211158
if n.upgrade == prev.m {
212-
reason = "updating to"
159+
pathUpgrade[n.m] = prev.m
213160
}
214-
stack = append(stack, buildListErrorElem{m: n.m, nextReason: reason})
161+
errPath = append(errPath, n.m)
215162
}
216-
i, j := 0, len(stack)-1
163+
i, j := 0, len(errPath)-1
217164
for i < j {
218-
stack[i], stack[j] = stack[j], stack[i]
165+
errPath[i], errPath[j] = errPath[j], errPath[i]
219166
i++
220167
j--
221168
}
222169

223-
err := &BuildListError{
224-
Err: node.err,
225-
stack: stack,
170+
isUpgrade := func(from, to module.Version) bool {
171+
return pathUpgrade[from] == to
226172
}
227-
return nil, err
173+
174+
return nil, NewBuildListError(node.err, errPath, isUpgrade)
228175
}
229176

230177
neighbors := node.required

0 commit comments

Comments
 (0)