diff options
author | Bryan C. Mills <bcmills@google.com> | 2020-08-04 23:27:18 -0400 |
---|---|---|
committer | Bryan C. Mills <bcmills@google.com> | 2020-08-24 20:30:23 +0000 |
commit | 6a718175a6b5532bb49160047731181a4ecec2a1 (patch) | |
tree | 6c53bc1dd3470bbb4952d9739a2c50fa9cdd69a0 | |
parent | 9bcc5d20b6f2574e5b98822e0986a1cfa14032f6 (diff) | |
download | go-6a718175a6b5532bb49160047731181a4ecec2a1.tar.gz go-6a718175a6b5532bb49160047731181a4ecec2a1.zip |
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 <jayconrod@google.com>
-rw-r--r-- | src/cmd/go/internal/mvs/errors.go | 96 | ||||
-rw-r--r-- | src/cmd/go/internal/mvs/mvs.go | 77 |
2 files changed, 108 insertions, 65 deletions
diff --git a/src/cmd/go/internal/mvs/errors.go b/src/cmd/go/internal/mvs/errors.go new file mode 100644 index 0000000000..8577902878 --- /dev/null +++ b/src/cmd/go/internal/mvs/errors.go @@ -0,0 +1,96 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package mvs + +import ( + "fmt" + "strings" + + "golang.org/x/mod/module" +) + +// BuildListError decorates an error that occurred gathering requirements +// while constructing a build list. BuildListError prints the chain +// of requirements to the module where the error occurred. +type BuildListError struct { + 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 "updating to". + nextReason string +} + +// NewBuildListError returns a new BuildListError wrapping an error that +// occurred at a module found along the given path of requirements and/or +// upgrades, which must be non-empty. +// +// The isUpgrade function reports whether a path step is due to an upgrade. +// A nil isUpgrade function indicates that none of the path steps are due to upgrades. +func NewBuildListError(err error, path []module.Version, isUpgrade func(from, to module.Version) bool) *BuildListError { + stack := make([]buildListErrorElem, 0, len(path)) + for len(path) > 1 { + reason := "requires" + if isUpgrade != nil && isUpgrade(path[0], path[1]) { + reason = "updating to" + } + stack = append(stack, buildListErrorElem{ + m: path[0], + nextReason: reason, + }) + path = path[1:] + } + stack = append(stack, buildListErrorElem{m: path[0]}) + + return &BuildListError{ + Err: err, + stack: stack, + } +} + +// Module returns the module where the error occurred. If the module stack +// is empty, this returns a zero value. +func (e *BuildListError) Module() module.Version { + if len(e.stack) == 0 { + return module.Version{} + } + return e.stack[len(e.stack)-1].m +} + +func (e *BuildListError) Error() string { + b := &strings.Builder{} + 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[0].m.Version == "" { + stack = stack[1:] + } + + if len(stack) == 0 { + b.WriteString(e.Err.Error()) + } else { + for _, elem := range stack[:len(stack)-1] { + fmt.Fprintf(b, "%s@%s %s\n\t", elem.m.Path, elem.m.Version, elem.nextReason) + } + // Ensure that the final module path and version are included as part of the + // error message. + m := stack[len(stack)-1].m + if _, ok := e.Err.(*module.ModuleError); ok { + // TODO(bcmills): Also ensure that the module path and version match. + // (Otherwise, we may be reporting an error from a replacement without + // indicating the replacement path.) + fmt.Fprintf(b, "%v", e.Err) + } else { + fmt.Fprintf(b, "%v", module.VersionError(m, e.Err)) + } + } + return b.String() +} diff --git a/src/cmd/go/internal/mvs/mvs.go b/src/cmd/go/internal/mvs/mvs.go index 1056a500ff..ea23a9f45e 100644 --- a/src/cmd/go/internal/mvs/mvs.go +++ b/src/cmd/go/internal/mvs/mvs.go @@ -9,7 +9,6 @@ package mvs import ( "fmt" "sort" - "strings" "sync" "sync/atomic" @@ -61,59 +60,6 @@ type Reqs interface { Previous(m module.Version) (module.Version, error) } -// BuildListError decorates an error that occurred gathering requirements -// while constructing a build list. BuildListError prints the chain -// of requirements to the module where the error occurred. -type BuildListError struct { - 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 -} - -// Module returns the module where the error occurred. If the module stack -// is empty, this returns a zero value. -func (e *BuildListError) Module() module.Version { - if len(e.stack) == 0 { - return module.Version{} - } - return e.stack[len(e.stack)-1].m -} - -func (e *BuildListError) Error() string { - b := &strings.Builder{} - 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[0].m.Version == "" { - stack = stack[1:] - } - - if len(stack) == 0 { - b.WriteString(e.Err.Error()) - } else { - for _, elem := range stack[:len(stack)-1] { - fmt.Fprintf(b, "%s@%s %s\n\t", elem.m.Path, elem.m.Version, elem.nextReason) - } - // Ensure that the final module path and version are included as part of the - // error message. - if _, ok := e.Err.(*module.ModuleError); ok { - fmt.Fprintf(b, "%v", e.Err) - } else { - fmt.Fprintf(b, "%v", module.VersionError(stack[len(stack)-1].m, e.Err)) - } - } - return b.String() -} - // BuildList returns the build list for the target module. // // 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 q = q[1:] if node.err != nil { - // Construct the stack reversed (from the error to the main module), + pathUpgrade := map[module.Version]module.Version{} + + // Construct the error path reversed (from the error to the main module), // then reverse it to obtain the usual order (from the main module to // the error). - stack := []buildListErrorElem{{m: node.m}} + errPath := []module.Version{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" + pathUpgrade[n.m] = prev.m } - stack = append(stack, buildListErrorElem{m: n.m, nextReason: reason}) + errPath = append(errPath, n.m) } - i, j := 0, len(stack)-1 + i, j := 0, len(errPath)-1 for i < j { - stack[i], stack[j] = stack[j], stack[i] + errPath[i], errPath[j] = errPath[j], errPath[i] i++ j-- } - err := &BuildListError{ - Err: node.err, - stack: stack, + isUpgrade := func(from, to module.Version) bool { + return pathUpgrade[from] == to } - return nil, err + + return nil, NewBuildListError(node.err, errPath, isUpgrade) } neighbors := node.required |