// while constructing a build list. BuildListError prints the chain
// of requirements to the module where the error occurred.
type BuildListError struct {
- Err error
- Stack []module.Version
+ err error
+ stack []buildListErrorElem
+}
+
+type buildListErrorElem struct {
+ m module.Version
+
+ // nextReason is the reason this module depends on the next module in the
+ // stack. Typically either "requires", or "upgraded to".
+ nextReason string
}
func (e *BuildListError) Error() string {
b := &strings.Builder{}
- errMsg := e.Err.Error()
- stack := e.Stack
+ errMsg := e.err.Error()
+ stack := e.stack
// Don't print modules at the beginning of the chain without a
// version. These always seem to be the main module or a
// synthetic module ("target@").
- for len(stack) > 0 && stack[len(stack)-1].Version == "" {
+ for len(stack) > 0 && stack[len(stack)-1].m.Version == "" {
stack = stack[:len(stack)-1]
}
// Don't print the last module if the error message already
// starts with module path and version.
- if len(stack) > 0 && strings.HasPrefix(errMsg, fmt.Sprintf("%s@%s: ", stack[0].Path, stack[0].Version)) {
- // error already mentions module
- stack = stack[1:]
+ errMentionsLast := len(stack) > 0 && strings.HasPrefix(errMsg, fmt.Sprintf("%s@%s: ", stack[0].m.Path, stack[0].m.Version))
+ for i := len(stack) - 1; i >= 1; i-- {
+ fmt.Fprintf(b, "%s@%s %s\n\t", stack[i].m.Path, stack[i].m.Version, stack[i].nextReason)
}
-
- for i := len(stack) - 1; i >= 0; i-- {
- fmt.Fprintf(b, "%s@%s ->\n\t", stack[i].Path, stack[i].Version)
+ if errMentionsLast || len(stack) == 0 {
+ b.WriteString(errMsg)
+ } else {
+ fmt.Fprintf(b, "%s@%s: %s", stack[0].m.Path, stack[0].m.Version, errMsg)
}
- b.WriteString(errMsg)
return b.String()
}
q = q[1:]
if node.err != nil {
- err := &BuildListError{Err: node.err}
- for n := node; n != nil; n = neededBy[n] {
- err.Stack = append(err.Stack, n.m)
+ err := &BuildListError{
+ err: node.err,
+ stack: []buildListErrorElem{{m: node.m}},
+ }
+ for n, prev := neededBy[node], node; n != nil; n, prev = neededBy[n], n {
+ reason := "requires"
+ if n.upgrade == prev.m {
+ reason = "updating to"
+ }
+ err.stack = append(err.stack, buildListErrorElem{m: n.m, nextReason: reason})
}
return nil, err
}
go mod download example.com/badchain/b@v1.1.0
go mod download example.com/badchain/c@v1.1.0
-# Try to upgrade example.com/badchain/a (and its dependencies).
+# Try to update example.com/badchain/a (and its dependencies).
! go get -u example.com/badchain/a
-cmp stderr upgrade-a-expected
+cmp stderr update-a-expected
cmp go.mod go.mod.orig
-# Try to upgrade the main module. This upgrades everything, including
+# Try to update the main module. This updates everything, including
# modules that aren't direct requirements, so the error stack is shorter.
! go get -u
-cmp stderr upgrade-main-expected
+cmp stderr update-main-expected
cmp go.mod go.mod.orig
-# Upgrade manually. Listing modules should produce an error.
+# update manually. Listing modules should produce an error.
go mod edit -require=example.com/badchain/a@v1.1.0
! go list -m
cmp stderr list-expected
module m
require example.com/badchain/a v1.0.0
--- upgrade-main-expected --
-go get: example.com/badchain/c@v1.0.0 ->
+-- update-main-expected --
+go get: example.com/badchain/c@v1.0.0 updating to
example.com/badchain/c@v1.1.0: parsing go.mod: unexpected module path "example.com/badchain/wrong"
--- upgrade-a-expected --
-go get: example.com/badchain/a@v1.1.0 ->
- example.com/badchain/b@v1.1.0 ->
+-- update-a-expected --
+go get: example.com/badchain/a@v1.1.0 requires
+ example.com/badchain/b@v1.1.0 requires
example.com/badchain/c@v1.1.0: parsing go.mod: unexpected module path "example.com/badchain/wrong"
-- list-expected --
-go: example.com/badchain/a@v1.1.0 ->
- example.com/badchain/b@v1.1.0 ->
+go: example.com/badchain/a@v1.1.0 requires
+ example.com/badchain/b@v1.1.0 requires
example.com/badchain/c@v1.1.0: parsing go.mod: unexpected module path "example.com/badchain/wrong"