cmd/go/internal/list: add module support

This CL changes the clumsy hard-coded "list -m" and "list -m -u"
with a real template-based implementation analogous to what we
do for listing packages. (In fact it reuses most of the package
implementation.) See the documentation for more details.

The main visible changes compared to the original vgo are:

- "vgo list -m" is now "vgo list -m all".
- "vgo list -m -u" is now "vgo list -m -u all".
- The default output is no longer a fixed-width-aligned table.
- The output no longer has COLUMN HEADERS.
- "vgo list -m -json [all]" now works.

Fixes golang/go#24347.
Fixes golang/go#25360.

Change-Id: Ie6a677875abca876ae334c8554298d3c56076b94
Reviewed-on: https://go-review.googlesource.com/120198
Reviewed-by: Bryan C. Mills <bcmills@google.com>
diff --git a/vendor/cmd/go/internal/fix/fix.go b/vendor/cmd/go/internal/fix/fix.go
index 56f8832..b0b1aa7 100644
--- a/vendor/cmd/go/internal/fix/fix.go
+++ b/vendor/cmd/go/internal/fix/fix.go
@@ -34,7 +34,7 @@
 func runFix(cmd *base.Command, args []string) {
 	printed := false
 	for _, pkg := range load.Packages(args) {
-		if vgo.Enabled() && !pkg.Module.Top {
+		if vgo.Enabled() && !pkg.Module.Main {
 			if !printed {
 				fmt.Fprintf(os.Stderr, "vgo: not fixing packages in dependency modules\n")
 				printed = true
diff --git a/vendor/cmd/go/internal/fmtcmd/fmt.go b/vendor/cmd/go/internal/fmtcmd/fmt.go
index c3a90d0..adb3cfa 100644
--- a/vendor/cmd/go/internal/fmtcmd/fmt.go
+++ b/vendor/cmd/go/internal/fmtcmd/fmt.go
@@ -60,7 +60,7 @@
 		}()
 	}
 	for _, pkg := range load.PackagesAndErrors(args) {
-		if vgo.Enabled() && !pkg.Module.Top {
+		if vgo.Enabled() && !pkg.Module.Main {
 			if !printed {
 				fmt.Fprintf(os.Stderr, "vgo: not formatting packages in dependency modules\n")
 				printed = true
diff --git a/vendor/cmd/go/internal/generate/generate.go b/vendor/cmd/go/internal/generate/generate.go
index d5d339b..ffe8530 100644
--- a/vendor/cmd/go/internal/generate/generate.go
+++ b/vendor/cmd/go/internal/generate/generate.go
@@ -155,7 +155,7 @@
 	// Even if the arguments are .go files, this loop suffices.
 	printed := false
 	for _, pkg := range load.Packages(args) {
-		if vgo.Enabled() && !pkg.Module.Top {
+		if vgo.Enabled() && !pkg.Module.Main {
 			if !printed {
 				fmt.Fprintf(os.Stderr, "vgo: not generating in packages in dependency modules\n")
 				printed = true
diff --git a/vendor/cmd/go/internal/list/list.go b/vendor/cmd/go/internal/list/list.go
index 19388d4..ad078ee 100644
--- a/vendor/cmd/go/internal/list/list.go
+++ b/vendor/cmd/go/internal/list/list.go
@@ -24,10 +24,15 @@
 )
 
 var CmdList = &base.Command{
-	UsageLine: "list [-cgo] [-deps] [-e] [-export] [-f format] [-json] [-test] [build flags] [packages]",
-	Short:     "list packages",
+	// Note: -f -json -m are listed explicitly because they are the most common list flags.
+	// Do not send CLs removing them because they're covered by [list flags].
+	UsageLine: "list [-f format] [-json] [-m] [list flags] [build flags] [packages]",
+	Short:     "list packages or modules",
 	Long: `
-List lists the packages named by the import paths, one per line.
+List lists the named packages, one per line.
+The most commonly-used flags are -f and -json, which control the form
+of the output printed for each package. Other list flags, documented below,
+control more specific details.
 
 The default output shows the package import path:
 
@@ -37,27 +42,28 @@
     golang.org/x/net/html
 
 The -f flag specifies an alternate format for the list, using the
-syntax of package template. The default output is equivalent to -f
-'{{.ImportPath}}'. The struct being passed to the template is:
+syntax of package template. The default output is equivalent
+to -f '{{.ImportPath}}'. The struct being passed to the template is:
 
     type Package struct {
-        Dir           string // directory containing package sources
-        ImportPath    string // import path of package in dir
-        ImportComment string // path in import comment on package statement
-        Name          string // package name
-        Doc           string // package documentation string
-        Target        string // install path
-        Shlib         string // the shared library that contains this package (only set when -linkshared)
-        Goroot        bool   // is this package in the Go root?
-        Standard      bool   // is this package part of the standard Go library?
-        Stale         bool   // would 'go install' do anything for this package?
-        StaleReason   string // explanation for Stale==true
-        Root          string // Go root or Go path dir containing this package
-        ConflictDir   string // this directory shadows Dir in $GOPATH
-        BinaryOnly    bool   // binary-only package: cannot be recompiled from sources
-        ForTest       string // package is only for use in named test
-        DepOnly       bool   // package is only a dependency, not explicitly listed
-        Export        string // file containing export data (when using -export)
+        Dir           string  // directory containing package sources
+        ImportPath    string  // import path of package in dir
+        ImportComment string  // path in import comment on package statement
+        Name          string  // package name
+        Doc           string  // package documentation string
+        Target        string  // install path
+        Shlib         string  // the shared library that contains this package (only set when -linkshared)
+        Goroot        bool    // is this package in the Go root?
+        Standard      bool    // is this package part of the standard Go library?
+        Stale         bool    // would 'go install' do anything for this package?
+        StaleReason   string  // explanation for Stale==true
+        Root          string  // Go root or Go path dir containing this package
+        ConflictDir   string  // this directory shadows Dir in $GOPATH
+        BinaryOnly    bool    // binary-only package: cannot be recompiled from sources
+        ForTest       string  // package is only for use in named test
+        DepOnly       bool    // package is only a dependency, not explicitly listed
+        Export        string  // file containing export data (when using -export)
+        Module        *Module // info about package's containing module, if any (can be nil)
 
         // Source files
         GoFiles        []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles)
@@ -109,22 +115,25 @@
         Err           string   // the error itself
     }
 
+The module information is a Module struct, defined in the discussion
+of list -m below.
+
 The template function "join" calls strings.Join.
 
 The template function "context" returns the build context, defined as:
 
-	type Context struct {
-		GOARCH        string   // target architecture
-		GOOS          string   // target operating system
-		GOROOT        string   // Go root
-		GOPATH        string   // Go path
-		CgoEnabled    bool     // whether cgo can be used
-		UseAllFiles   bool     // use files regardless of +build lines, file names
-		Compiler      string   // compiler to assume when computing target paths
-		BuildTags     []string // build constraints to match in +build lines
-		ReleaseTags   []string // releases the current release is compatible with
-		InstallSuffix string   // suffix to use in the name of the install dir
-	}
+    type Context struct {
+        GOARCH        string   // target architecture
+        GOOS          string   // target operating system
+        GOROOT        string   // Go root
+        GOPATH        string   // Go path
+        CgoEnabled    bool     // whether cgo can be used
+        UseAllFiles   bool     // use files regardless of +build lines, file names
+        Compiler      string   // compiler to assume when computing target paths
+        BuildTags     []string // build constraints to match in +build lines
+        ReleaseTags   []string // releases the current release is compatible with
+        InstallSuffix string   // suffix to use in the name of the install dir
+    }
 
 For more information about the meaning of these fields see the documentation
 for the go/build package's Context type.
@@ -178,9 +187,82 @@
 referring to cached copies of generated Go source files.
 Although they are Go source files, the paths may not end in ".go".
 
+The -m flag causes list to list modules instead of packages.
+
+When listing modules, the -f flag still specifies a format template
+applied to a Go struct, but now a Module struct:
+
+    type Module struct {
+        Path     string       // module path
+        Version  string       // module version
+        Replace  *Module      // replaced by this module
+        Time     *time.Time   // time version was created
+        Update   *Module      // available update, if any (with -u)
+        Main     bool         // is this the main module?
+        Dir      string       // directory holding files for this module, if any
+        Error    *ModuleError // error loading module
+    }
+
+    type ModuleError struct {
+        Err string // the error itself
+    }
+
+The default output is to print the module path and then
+information about the version and replacement if any.
+For example, 'go list -m all' might print:
+
+    my/main/module
+    golang.org/x/text v0.3.0 => /tmp/text
+    rsc.io/pdf v0.1.1
+
+The Module struct has a String method that formats this
+line of output, so that the default format is equivalent
+to -f '{{.String}}'.
+
+Note that when a module has been replaced, its Replace field
+describes the replacement module, and its Dir field is set to
+the replacement's source code, if present. (That is, if Replace
+is non-nil, then Dir is set to Replace.Dir, with no access to
+the replaced source code.)
+
+The -u flag adds information about available upgrades.
+When the latest version of a given module is newer than
+the current one, list -u sets the Module's Update field
+to information about the newer module.
+The Module's String method indicates an available upgrade by
+formatting the newer version in brackets after the current version.
+For example, 'go list -m -u all' might print:
+
+    my/main/module
+    golang.org/x/text v0.3.0 [v0.4.0] => /tmp/text
+    rsc.io/pdf v0.1.1 [v0.1.2]
+
+(For tools, 'go list -m -u -json all' may be more convenient to parse.)
+
+The arguments to list -m are interpreted as a list of modules, not packages.
+The main module is the module containing the current directory.
+The active modules are the main module and its dependencies.
+With no arguments, list -m shows the main module.
+With arguments, list -m shows the modules specified by the arguments.
+Any of the active modules can be specified by its module path.
+The special pattern "all" specifies all the active modules, first the main
+module and then dependencies sorted by module path.
+A pattern containing "..." specifies the active modules whose
+module paths match the pattern.
+A query of the form path@version specifies the result of that query,
+which is not limited to active modules.
+See 'go help modules' for more about patterns and queries.
+
+The template function "module" takes a single string argument
+that must be a module path or query and returns the specified
+module as a Module struct. If an error occurs, the result will
+be a Module struct with a non-nil Error field.
+
 For more about build flags, see 'go help build'.
 
 For more about specifying packages, see 'go help packages'.
+
+For more about modules, see 'go help modules'.
 	`,
 }
 
@@ -189,41 +271,37 @@
 	work.AddBuildFlags(CmdList)
 }
 
-var listCgo = CmdList.Flag.Bool("cgo", false, "")
-var listDeps = CmdList.Flag.Bool("deps", false, "")
-var listE = CmdList.Flag.Bool("e", false, "")
-var listExport = CmdList.Flag.Bool("export", false, "")
-var listFmt = CmdList.Flag.String("f", "{{.ImportPath}}", "")
-var listJson = CmdList.Flag.Bool("json", false, "")
-var listTest = CmdList.Flag.Bool("test", false, "")
-var nl = []byte{'\n'}
+var (
+	listCgo    = CmdList.Flag.Bool("cgo", false, "")
+	listDeps   = CmdList.Flag.Bool("deps", false, "")
+	listE      = CmdList.Flag.Bool("e", false, "")
+	listExport = CmdList.Flag.Bool("export", false, "")
+	listFmt    = CmdList.Flag.String("f", "", "")
+	listJson   = CmdList.Flag.Bool("json", false, "")
+	listM      = CmdList.Flag.Bool("m", false, "")
+	listU      = CmdList.Flag.Bool("u", false, "")
+	listTest   = CmdList.Flag.Bool("test", false, "")
+)
 
-var listM = CmdList.Flag.Bool("m", false, "")
-var listU = CmdList.Flag.Bool("u", false, "")
-var listT = CmdList.Flag.Bool("t", false, "")
+var nl = []byte{'\n'}
 
 func runList(cmd *base.Command, args []string) {
 	work.BuildInit()
 	out := newTrackingWriter(os.Stdout)
 	defer out.w.Flush()
 
-	if *listM {
-		if *listU {
-			vgo.ListMU()
-			return
+	if *listFmt == "" {
+		if *listM {
+			*listFmt = "{{.String}}"
+		} else {
+			*listFmt = "{{.ImportPath}}"
 		}
-		vgo.ListM()
-		return
-	}
-	if *listT {
-		vgo.ListT(args)
-		return
 	}
 
-	var do func(*load.PackagePublic)
+	var do func(interface{})
 	if *listJson {
-		do = func(p *load.PackagePublic) {
-			b, err := json.MarshalIndent(p, "", "\t")
+		do = func(x interface{}) {
+			b, err := json.MarshalIndent(x, "", "\t")
 			if err != nil {
 				out.Flush()
 				base.Fatalf("%s", err)
@@ -242,13 +320,14 @@
 		fm := template.FuncMap{
 			"join":    strings.Join,
 			"context": context,
+			"module":  vgo.ModuleInfo,
 		}
 		tmpl, err := template.New("main").Funcs(fm).Parse(*listFmt)
 		if err != nil {
 			base.Fatalf("%s", err)
 		}
-		do = func(p *load.PackagePublic) {
-			if err := tmpl.Execute(out, p); err != nil {
+		do = func(x interface{}) {
+			if err := tmpl.Execute(out, x); err != nil {
 				out.Flush()
 				base.Fatalf("%s", err)
 			}
@@ -258,6 +337,48 @@
 		}
 	}
 
+	if *listM {
+		// Module mode.
+		if *listCgo {
+			base.Fatalf("go list -cgo cannot be used with -m")
+		}
+		if *listDeps {
+			// TODO(rsc): Could make this mean something with -m.
+			base.Fatalf("go list -deps cannot be used with -m")
+		}
+		if *listE {
+			// TODO(rsc): Could make this mean something with -m.
+			base.Fatalf("go list -e cannot be used with -m")
+		}
+		if *listExport {
+			base.Fatalf("go list -export cannot be used with -m")
+		}
+		if *listTest {
+			base.Fatalf("go list -test cannot be used with -m")
+		}
+
+		if vgo.Init(); !vgo.Enabled() {
+			base.Fatalf("go list -m: not using modules")
+		}
+		vgo.LoadBuildList()
+
+		mods := vgo.ListModules(args)
+		if *listU {
+			for _, m := range mods {
+				vgo.AddUpdate(m)
+			}
+		}
+		for _, m := range mods {
+			do(m)
+		}
+		return
+	}
+
+	// Package mode (not -m).
+	if *listU {
+		base.Fatalf("go list -u can only be used with -m")
+	}
+
 	var pkgs []*load.Package
 	if *listE {
 		pkgs = load.PackagesAndErrors(args)
diff --git a/vendor/cmd/go/internal/load/pkg.go b/vendor/cmd/go/internal/load/pkg.go
index 0977825..951c7f4 100644
--- a/vendor/cmd/go/internal/load/pkg.go
+++ b/vendor/cmd/go/internal/load/pkg.go
@@ -50,21 +50,22 @@
 	// Note: These fields are part of the go command's public API.
 	// See list.go. It is okay to add fields, but not to change or
 	// remove existing ones. Keep in sync with list.go
-	Dir           string `json:",omitempty"` // directory containing package sources
-	ImportPath    string `json:",omitempty"` // import path of package in dir
-	ImportComment string `json:",omitempty"` // path in import comment on package statement
-	Name          string `json:",omitempty"` // package name
-	Doc           string `json:",omitempty"` // package documentation string
-	Target        string `json:",omitempty"` // installed target for this package (may be executable)
-	Shlib         string `json:",omitempty"` // the shared library that contains this package (only set when -linkshared)
-	Goroot        bool   `json:",omitempty"` // is this package found in the Go root?
-	Standard      bool   `json:",omitempty"` // is this package part of the standard Go library?
-	Root          string `json:",omitempty"` // Go root or Go path dir containing this package
-	ConflictDir   string `json:",omitempty"` // Dir is hidden by this other directory
-	BinaryOnly    bool   `json:",omitempty"` // package cannot be recompiled
-	ForTest       string `json:",omitempty"` // package is only for use in named test
-	DepOnly       bool   `json:",omitempty"` // package is only as a dependency, not explicitly listed
-	Export        string `json:",omitempty"` // file containing export data (set by go list -export)
+	Dir           string                `json:",omitempty"` // directory containing package sources
+	ImportPath    string                `json:",omitempty"` // import path of package in dir
+	ImportComment string                `json:",omitempty"` // path in import comment on package statement
+	Name          string                `json:",omitempty"` // package name
+	Doc           string                `json:",omitempty"` // package documentation string
+	Target        string                `json:",omitempty"` // installed target for this package (may be executable)
+	Shlib         string                `json:",omitempty"` // the shared library that contains this package (only set when -linkshared)
+	Goroot        bool                  `json:",omitempty"` // is this package found in the Go root?
+	Standard      bool                  `json:",omitempty"` // is this package part of the standard Go library?
+	Root          string                `json:",omitempty"` // Go root or Go path dir containing this package
+	ConflictDir   string                `json:",omitempty"` // Dir is hidden by this other directory
+	BinaryOnly    bool                  `json:",omitempty"` // package cannot be recompiled
+	ForTest       string                `json:",omitempty"` // package is only for use in named test
+	DepOnly       bool                  `json:",omitempty"` // package is only as a dependency, not explicitly listed
+	Export        string                `json:",omitempty"` // file containing export data (set by go list -export)
+	Module        *modinfo.ModulePublic `json:",omitempty"` // info about package's module, if any
 
 	// Stale and StaleReason remain here *only* for the list command.
 	// They are only initialized in preparation for list execution.
@@ -112,8 +113,6 @@
 	TestImports  []string `json:",omitempty"` // imports from TestGoFiles
 	XTestGoFiles []string `json:",omitempty"` // _test.go files outside package
 	XTestImports []string `json:",omitempty"` // imports from XTestGoFiles
-
-	Module *modinfo.ModulePublic `json:",omitempty"` // info about package module
 }
 
 // AllFiles returns the names of all the files considered for the package.
diff --git a/vendor/cmd/go/internal/load/search.go b/vendor/cmd/go/internal/load/search.go
index e3f54a6..cf09c7b 100644
--- a/vendor/cmd/go/internal/load/search.go
+++ b/vendor/cmd/go/internal/load/search.go
@@ -14,7 +14,7 @@
 // MatchPackage(pattern, cwd)(p) reports whether package p matches pattern in the working directory cwd.
 func MatchPackage(pattern, cwd string) func(*Package) bool {
 	switch {
-	case strings.HasPrefix(pattern, "./") || strings.HasPrefix(pattern, "../") || pattern == "." || pattern == "..":
+	case search.IsRelativePath(pattern):
 		// Split pattern into leading pattern-free directory path
 		// (including all . and .. elements) and the final pattern.
 		var dir string
diff --git a/vendor/cmd/go/internal/modinfo/info.go b/vendor/cmd/go/internal/modinfo/info.go
index 6dff2d2..25deaa5 100644
--- a/vendor/cmd/go/internal/modinfo/info.go
+++ b/vendor/cmd/go/internal/modinfo/info.go
@@ -4,8 +4,42 @@
 
 package modinfo
 
+import "time"
+
+// Note that these structs are publicly visible (part of go list's API)
+// and the fields are documented in the help text in ../list/list.go
+
 type ModulePublic struct {
-	Top     bool
-	Path    string
-	Version string
+	Path    string        `json:",omitempty"` // module path
+	Version string        `json:",omitempty"` // module version
+	Replace *ModulePublic `json:",omitempty"` // replaced by this module
+	Time    *time.Time    `json:",omitempty"` // time version was created
+	Update  *ModulePublic `json:",omitempty"` // available update (with -u)
+	Main    bool          `json:",omitempty"` // is this the main module?
+	Dir     string        `json:",omitempty"` // directory holding local copy of files, if any
+	Error   *ModuleError  `json:",omitempty"` // error loading module
+}
+
+type ModuleError struct {
+	Err string // error text
+}
+
+func (m *ModulePublic) String() string {
+	s := m.Path
+	if m.Version != "" {
+		s += " " + m.Version
+		if m.Update != nil {
+			s += " [" + m.Update.Version + "]"
+		}
+	}
+	if m.Replace != nil {
+		s += " => " + m.Replace.Path
+		if m.Replace.Version != "" {
+			s += " " + m.Replace.Version
+			if m.Replace.Update != nil {
+				s += " [" + m.Replace.Update.Version + "]"
+			}
+		}
+	}
+	return s
 }
diff --git a/vendor/cmd/go/internal/search/search.go b/vendor/cmd/go/internal/search/search.go
index 42a6be0..14886bf 100644
--- a/vendor/cmd/go/internal/search/search.go
+++ b/vendor/cmd/go/internal/search/search.go
@@ -421,3 +421,10 @@
 	elem := path[:i]
 	return !strings.Contains(elem, ".")
 }
+
+// IsRelativePath reports whether pattern should be interpreted as a directory
+// path relative to the current directory, as opposed to a pattern matching
+// import paths.
+func IsRelativePath(pattern string) bool {
+	return strings.HasPrefix(pattern, "./") || strings.HasPrefix(pattern, "../") || pattern == "." || pattern == ".."
+}
diff --git a/vendor/cmd/go/internal/vgo/build.go b/vendor/cmd/go/internal/vgo/build.go
index c48acf5..19f09bd 100644
--- a/vendor/cmd/go/internal/vgo/build.go
+++ b/vendor/cmd/go/internal/vgo/build.go
@@ -8,13 +8,16 @@
 	"bytes"
 	"cmd/go/internal/base"
 	"cmd/go/internal/cfg"
+	"cmd/go/internal/modfetch"
 	"cmd/go/internal/modinfo"
 	"cmd/go/internal/module"
 	"cmd/go/internal/search"
+	"cmd/go/internal/semver"
 	"encoding/hex"
 	"fmt"
 	"os"
 	"path/filepath"
+	"strings"
 )
 
 var (
@@ -34,16 +37,112 @@
 	return false
 }
 
-func PackageModuleInfo(path string) *modinfo.ModulePublic {
-	var info modinfo.ModulePublic
-	if isStandardImportPath(path) || !Enabled() {
+func PackageModuleInfo(pkgpath string) *modinfo.ModulePublic {
+	if isStandardImportPath(pkgpath) || !Enabled() {
 		return nil
 	}
-	target := findModule(path, path)
-	info.Top = target.Path == buildList[0].Path
-	info.Path = target.Path
-	info.Version = target.Version
-	return &info
+	return moduleInfo(findModule(pkgpath, pkgpath))
+}
+
+func ModuleInfo(path string) *modinfo.ModulePublic {
+	if !Enabled() {
+		return nil
+	}
+
+	if i := strings.Index(path, "@"); i >= 0 {
+		return moduleInfo(module.Version{Path: path[:i], Version: path[i+1:]})
+	}
+
+	for _, m := range buildList {
+		if m.Path == path {
+			return moduleInfo(m)
+		}
+	}
+
+	return &modinfo.ModulePublic{
+		Path: path,
+		Error: &modinfo.ModuleError{
+			Err: "module not in current build",
+		},
+	}
+}
+
+// AddUpdate fills in m.Update if an updated version is available.
+func AddUpdate(m *modinfo.ModulePublic) {
+	addUpdate(m)
+	if m.Replace != nil {
+		addUpdate(m.Replace)
+	}
+}
+
+func addUpdate(m *modinfo.ModulePublic) {
+	if m.Version != "" {
+		if info, err := modfetch.Query(m.Path, "latest", allowed); err == nil && info.Version != m.Version {
+			m.Update = &modinfo.ModulePublic{
+				Path:    m.Path,
+				Version: info.Version,
+				Time:    &info.Time,
+			}
+		}
+	}
+}
+
+func moduleInfo(m module.Version) *modinfo.ModulePublic {
+	if m == Target {
+		return &modinfo.ModulePublic{
+			Path:    m.Path,
+			Version: m.Version,
+			Main:    true,
+		}
+	}
+
+	info := &modinfo.ModulePublic{
+		Path:    m.Path,
+		Version: m.Version,
+	}
+
+	// complete fills in the extra fields in m.
+	complete := func(m *modinfo.ModulePublic) {
+		if m.Version != "" {
+			if q, err := modfetch.Query(m.Path, m.Version, nil); err != nil {
+				m.Error = &modinfo.ModuleError{Err: err.Error()}
+			} else {
+				m.Version = q.Version
+				m.Time = &q.Time
+			}
+
+			if semver.IsValid(m.Version) {
+				dir := filepath.Join(SrcMod, m.Path+"@"+m.Version)
+				if stat, err := os.Stat(dir); err == nil && stat.IsDir() {
+					m.Dir = dir
+				}
+			}
+		}
+		if cfg.BuildGetmode == "vendor" {
+			m.Dir = filepath.Join(ModRoot, "vendor", m.Path)
+		}
+	}
+
+	complete(info)
+
+	if r := Replacement(m); r.Path != "" {
+		info.Replace = &modinfo.ModulePublic{
+			Path:    r.Path,
+			Version: r.Version,
+		}
+		if r.Version == "" {
+			if filepath.IsAbs(r.Path) {
+				info.Replace.Dir = r.Path
+			} else {
+				info.Replace.Dir = filepath.Join(ModRoot, r.Path)
+			}
+		}
+		complete(info.Replace)
+		info.Dir = info.Replace.Dir
+		info.Error = nil // ignore error loading original module version (it has been replaced)
+	}
+
+	return info
 }
 
 func PackageBuildInfo(path string, deps []string) string {
diff --git a/vendor/cmd/go/internal/vgo/list.go b/vendor/cmd/go/internal/vgo/list.go
index e56aae5..5b98d7f 100644
--- a/vendor/cmd/go/internal/vgo/list.go
+++ b/vendor/cmd/go/internal/vgo/list.go
@@ -5,149 +5,56 @@
 package vgo
 
 import (
-	"bufio"
 	"fmt"
-	"io"
 	"os"
-	"regexp"
-	"unicode/utf8"
+	"strings"
 
 	"cmd/go/internal/base"
-	"cmd/go/internal/modfetch"
-	"cmd/go/internal/module"
+	"cmd/go/internal/modinfo"
+	"cmd/go/internal/search"
 )
 
-func ListT(pkgs []string) {
-	if Init(); !Enabled() {
-		base.Fatalf("go list: cannot use -t outside module")
-	}
-	InitMod()
-
-	if len(pkgs) == 0 {
-		base.Fatalf("vgo list -t: need list of modules")
+func ListModules(args []string) []*modinfo.ModulePublic {
+	LoadBuildList()
+	if len(args) == 0 {
+		return []*modinfo.ModulePublic{moduleInfo(buildList[0])}
 	}
 
-	for _, pkg := range pkgs {
-		repo, err := modfetch.Lookup(pkg)
-		if err != nil {
-			base.Errorf("vgo list -t: %v", err)
-			continue
+	var mods []*modinfo.ModulePublic
+	matchedBuildList := make([]bool, len(buildList))
+	for _, arg := range args {
+		if strings.Contains(arg, `\`) {
+			base.Fatalf("vgo: module paths never use backslash")
 		}
-		path := repo.ModulePath()
-		fmt.Printf("%s\n", path)
-		tags, err := repo.Versions("")
-		if err != nil {
-			base.Errorf("vgo list -t: %v", err)
-			continue
+		if search.IsRelativePath(arg) {
+			base.Fatalf("vgo: cannot use relative path %s to specify module", arg)
 		}
-		for _, t := range tags {
-			if excluded[module.Version{Path: path, Version: t}] {
-				t += " # excluded"
-			}
-			fmt.Printf("\t%s\n", t)
+		if strings.Contains(arg, "@") {
+			// TODO(rsc): Add support for 'go list -m golang.org/x/text@v0.3.0'
+			base.Fatalf("vgo: list path@version not implemented")
 		}
-	}
-}
 
-func ListM() {
-	if Init(); !Enabled() {
-		base.Fatalf("go list: cannot use -m outside module")
-	}
-	InitMod()
-	iterate(func(*loader) {})
-	printListM(os.Stdout)
-}
-
-func printListM(w io.Writer) {
-	var rows [][]string
-	rows = append(rows, []string{"MODULE", "VERSION"})
-	for _, mod := range buildList {
-		v := mod.Version
-		if v == "" {
-			v = "-"
-		}
-		rows = append(rows, []string{mod.Path, v})
-		if r := Replacement(mod); r.Path != "" {
-			rows = append(rows, []string{" => " + r.Path, r.Version})
-		}
-	}
-	printTable(w, rows)
-}
-
-func ListMU() {
-	if Init(); !Enabled() {
-		base.Fatalf("go list: cannot use -m outside module")
-	}
-	InitMod()
-
-	modfetch.QuietLookup = true // do not chatter in v.Lookup
-	iterate(func(*loader) {})
-
-	var rows [][]string
-	rows = append(rows, []string{"MODULE", "VERSION", "LATEST"})
-	for _, mod := range buildList {
-		var latest string
-		v := mod.Version
-		if v == "" {
-			v = "-"
-			latest = "-"
+		// Module path or pattern.
+		var match func(string) bool
+		if arg == "all" {
+			match = func(string) bool { return true }
 		} else {
-			info, err := modfetch.Query(mod.Path, "latest", allowed)
-			if err != nil {
-				latest = "ERR: " + err.Error()
-			} else {
-				latest = info.Version
-				if !isPseudoVersion(latest) && !info.Time.IsZero() {
-					latest += info.Time.Local().Format(" (2006-01-02 15:04)")
-				}
-			}
-			if !isPseudoVersion(mod.Version) {
-				if info, err := modfetch.Query(mod.Path, mod.Version, nil); err == nil && !info.Time.IsZero() {
-					v += info.Time.Local().Format(" (2006-01-02 15:04)")
+			match = search.MatchPattern(arg)
+		}
+		matched := false
+		for i, m := range buildList {
+			if match(m.Path) {
+				matched = true
+				if !matchedBuildList[i] {
+					matchedBuildList[i] = true
+					mods = append(mods, moduleInfo(m))
 				}
 			}
 		}
-		if latest == v {
-			latest = "-"
-		}
-		rows = append(rows, []string{mod.Path, v, latest})
-	}
-	printTable(os.Stdout, rows)
-}
-
-var pseudoVersionRE = regexp.MustCompile(`^v[0-9]+\.0\.0-[0-9]{14}-[A-Za-z0-9]+$`)
-
-func isPseudoVersion(v string) bool {
-	return pseudoVersionRE.MatchString(v)
-}
-
-func printTable(w io.Writer, rows [][]string) {
-	var max []int
-	for _, row := range rows {
-		for i, c := range row {
-			n := utf8.RuneCountInString(c)
-			if i >= len(max) {
-				max = append(max, n)
-			} else if max[i] < n {
-				max[i] = n
-			}
+		if !matched {
+			fmt.Fprintf(os.Stderr, "warning: pattern %q matched no module dependencies", arg)
 		}
 	}
 
-	b := bufio.NewWriter(w)
-	for _, row := range rows {
-		for len(row) > 0 && row[len(row)-1] == "" {
-			row = row[:len(row)-1]
-		}
-		for i, c := range row {
-			b.WriteString(c)
-			if i+1 < len(row) {
-				for j := utf8.RuneCountInString(c); j < max[i]+2; j++ {
-					b.WriteRune(' ')
-				}
-			}
-		}
-		b.WriteRune('\n')
-	}
-	b.Flush()
+	return mods
 }
diff --git a/vendor/cmd/go/internal/vgo/load.go b/vendor/cmd/go/internal/vgo/load.go
index 5a2fcf4..8653a27 100644
--- a/vendor/cmd/go/internal/vgo/load.go
+++ b/vendor/cmd/go/internal/vgo/load.go
@@ -346,7 +346,7 @@
 	return ""
 }
 
-// Replacement the replacement for mod, if any, from go.mod.
+// Replacement returns the replacement for mod, if any, from go.mod.
 // If there is no replacement for mod, Replacement returns
 // a module.Version with Path == "".
 func Replacement(mod module.Version) module.Version {
diff --git a/vendor/cmd/go/vgo_test.go b/vendor/cmd/go/vgo_test.go
index dcd90be..df360bf 100644
--- a/vendor/cmd/go/vgo_test.go
+++ b/vendor/cmd/go/vgo_test.go
@@ -325,7 +325,7 @@
 		require github.com/gobuffalo/uuid v1.1.0
 	`), 0666))
 	tg.run("-vgo", "get", "github.com/gobuffalo/uuid@v2.0.0")
-	tg.run("-vgo", "list", "-m")
+	tg.run("-vgo", "list", "-m", "all")
 	tg.grepStdout("github.com/gobuffalo/uuid.*v0.0.0-20180207211247-3a9fb6c5c481", "did downgrade to v0.0.0-*")
 
 	tooSlow(t)
@@ -335,15 +335,16 @@
 		require github.com/gobuffalo/uuid v1.2.0
 	`), 0666))
 	tg.run("-vgo", "get", "github.com/gobuffalo/uuid@v1.1.0")
-	tg.run("-vgo", "list", "-m")
-	tg.grepStdout("github.com/gobuffalo/uuid.*v1.1.0", "did downgrade to v1.1.0")
+	tg.run("-vgo", "list", "-m", "-u", "all")
+	tg.grepStdout(`github.com/gobuffalo/uuid v1.1.0`, "did downgrade to v1.1.0")
+	tg.grepStdout(`github.com/gobuffalo/uuid v1.1.0 \[v1`, "did show upgrade to v1.2.0 or later")
 
 	tg.must(ioutil.WriteFile(tg.path("x/go.mod"), []byte(`
 		module x
 		require github.com/gobuffalo/uuid v1.1.0
 	`), 0666))
 	tg.run("-vgo", "get", "github.com/gobuffalo/uuid@v1.2.0")
-	tg.run("-vgo", "list", "-m")
+	tg.run("-vgo", "list", "-m", "all")
 	tg.grepStdout("github.com/gobuffalo/uuid.*v1.2.0", "did upgrade to v1.2.0")
 }
 
@@ -433,7 +434,7 @@
 	tg.grepStderr(`^unused y.1`, "need y.1 unused")
 	tg.grepStderrNot(`^unused [^y]`, "only y.1 should be unused")
 
-	tg.run("-vgo", "list", "-m")
+	tg.run("-vgo", "list", "-m", "all")
 	tg.grepStdoutNot(`^y.1`, "y should be gone")
 	tg.grepStdout(`^w.1\s+v1.2.0`, "need w.1 to stay at v1.2.0")
 	tg.grepStdout(`^z.1\s+v1.2.0`, "need z.1 to stay at v1.2.0 even though y is gone")
@@ -447,7 +448,7 @@
 	tg.cd(filepath.Join(wd, "testdata/vendormod"))
 	defer os.RemoveAll(filepath.Join(wd, "testdata/vendormod/vendor"))
 
-	tg.run("-vgo", "list", "-m")
+	tg.run("-vgo", "list", "-m", "all")
 	tg.grepStdout(`^x`, "expected to see module x")
 	tg.grepStdout(`=> ./x`, "expected to see replacement for module x")
 	tg.grepStdout(`^w`, "expected to see module w")
@@ -473,9 +474,15 @@
 	tg.run("-vgo", "list", "-f={{.Dir}}", "x")
 	tg.grepStdout(`vendormod[/\\]x$`, "expected x in vendormod/x")
 
+	tg.run("-vgo", "list", "-f={{.Dir}}", "-m", "x")
+	tg.grepStdout(`vendormod[/\\]x$`, "expected x in vendormod/x")
+
 	tg.run("-vgo", "list", "-getmode=vendor", "-f={{.Dir}}", "x")
 	tg.grepStdout(`vendormod[/\\]vendor[/\\]x$`, "expected x in vendormod/vendor/x in -get=vendor mode")
 
+	tg.run("-vgo", "list", "-getmode=vendor", "-f={{.Dir}}", "-m", "x")
+	tg.grepStdout(`vendormod[/\\]vendor[/\\]x$`, "expected x in vendormod/vendor/x in -get=vendor mode")
+
 	tg.run("-vgo", "list", "-f={{.Dir}}", "w")
 	tg.grepStdout(`vendormod[/\\]w$`, "expected w in vendormod/w")
 	tg.runFail("-vgo", "list", "-getmode=vendor", "-f={{.Dir}}", "w")
@@ -520,7 +527,7 @@
 	tg.cd(tg.path("x"))
 	tg.run("-vgo", "build", "-v")
 	tg.grepStderr("copying requirements from .*Gopkg.lock", "did not copy Gopkg.lock")
-	tg.run("-vgo", "list", "-m")
+	tg.run("-vgo", "list", "-m", "all")
 	tg.grepStderrNot("copying requirements from .*Gopkg.lock", "should not copy Gopkg.lock again")
 	tg.grepStdout("rsc.io/sampler.*v1.0.0", "did not copy Gopkg.lock")
 
@@ -619,7 +626,7 @@
 		version = "v0.6.0"`), 0666))
 	tg.must(ioutil.WriteFile(tg.path("x/main.go"), []byte("package x // import \"x\"\n import _ \"github.com/pkg/errors\""), 0666))
 	tg.cd(tg.path("x"))
-	tg.run("-vgo", "list", "-m")
+	tg.run("-vgo", "list", "-m", "all")
 
 	// If the conversion just ignored the Gopkg.lock entirely
 	// it would choose a newer version (like v0.8.0 or maybe