]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/go/internal/mvs: export a NewBuildListError function
authorBryan C. Mills <bcmills@google.com>
Wed, 5 Aug 2020 03:27:18 +0000 (23:27 -0400)
committerBryan C. Mills <bcmills@google.com>
Mon, 24 Aug 2020 20:30:23 +0000 (20:30 +0000)
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>
src/cmd/go/internal/mvs/errors.go [new file with mode: 0644]
src/cmd/go/internal/mvs/mvs.go

diff --git a/src/cmd/go/internal/mvs/errors.go b/src/cmd/go/internal/mvs/errors.go
new file mode 100644 (file)
index 0000000..8577902
--- /dev/null
@@ -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()
+}
index 1056a500ff4dd579ac13b432008cae324133d230..ea23a9f45e172aaafdc174994438bd1bd3d61a1b 100644 (file)
@@ -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