cmd/go/internal/list: add list -m -versions

The new -versions flag gives access to the list of available versions
of a given module.

Change-Id: I618aae3cfc682597c0c01b7f975644a6a9207c04
Reviewed-on: https://go-review.googlesource.com/122075
Reviewed-by: Bryan C. Mills <bcmills@google.com>
diff --git a/vendor/cmd/go/internal/list/list.go b/vendor/cmd/go/internal/list/list.go
index c5f0550..15ac82c 100644
--- a/vendor/cmd/go/internal/list/list.go
+++ b/vendor/cmd/go/internal/list/list.go
@@ -195,6 +195,7 @@
     type Module struct {
         Path     string       // module path
         Version  string       // module version
+        Versions []string     // available module versions (with -versions)
         Replace  *Module      // replaced by this module
         Time     *time.Time   // time version was created
         Update   *Module      // available update, if any (with -u)
@@ -240,6 +241,12 @@
 
 (For tools, 'go list -m -u -json all' may be more convenient to parse.)
 
+The -versions flag causes list to set the Module's Versions field
+to a list of all known versions of that module, ordered according
+to semantic versioning, earliest to latest. The flag also changes
+the default output format to display the module path followed by the
+space-separated version list.
+
 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.
@@ -273,15 +280,16 @@
 }
 
 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, "")
+	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, "")
+	listVersions = CmdList.Flag.Bool("versions", false, "")
 )
 
 var nl = []byte{'\n'}
@@ -294,6 +302,9 @@
 	if *listFmt == "" {
 		if *listM {
 			*listFmt = "{{.String}}"
+			if *listVersions {
+				*listFmt = `{{.Path}}{{range .Versions}} {{.}}{{end}}`
+			}
 		} else {
 			*listFmt = "{{.ImportPath}}"
 		}
@@ -363,12 +374,7 @@
 		}
 		vgo.LoadBuildList()
 
-		mods := vgo.ListModules(args)
-		if *listU {
-			for _, m := range mods {
-				vgo.AddUpdate(m)
-			}
-		}
+		mods := vgo.ListModules(args, *listU, *listVersions)
 		for _, m := range mods {
 			do(m)
 		}
@@ -379,6 +385,9 @@
 	if *listU {
 		base.Fatalf("go list -u can only be used with -m")
 	}
+	if *listVersions {
+		base.Fatalf("go list -versions can only be used with -m")
+	}
 
 	var pkgs []*load.Package
 	if *listE {
diff --git a/vendor/cmd/go/internal/modfetch/proxy.go b/vendor/cmd/go/internal/modfetch/proxy.go
index 419323b..25cad78 100644
--- a/vendor/cmd/go/internal/modfetch/proxy.go
+++ b/vendor/cmd/go/internal/modfetch/proxy.go
@@ -55,6 +55,7 @@
 			list = append(list, f[0])
 		}
 	}
+	SortVersions(list)
 	return list, nil
 }
 
diff --git a/vendor/cmd/go/internal/modinfo/info.go b/vendor/cmd/go/internal/modinfo/info.go
index 5a7d6bb..3920546 100644
--- a/vendor/cmd/go/internal/modinfo/info.go
+++ b/vendor/cmd/go/internal/modinfo/info.go
@@ -12,6 +12,7 @@
 type ModulePublic struct {
 	Path     string        `json:",omitempty"` // module path
 	Version  string        `json:",omitempty"` // module version
+	Versions []string      `json:",omitempty"` // available module versions
 	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)
diff --git a/vendor/cmd/go/internal/vgo/build.go b/vendor/cmd/go/internal/vgo/build.go
index 6afe336..ce06ee2 100644
--- a/vendor/cmd/go/internal/vgo/build.go
+++ b/vendor/cmd/go/internal/vgo/build.go
@@ -67,14 +67,7 @@
 	}
 }
 
-// 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)
-	}
-}
-
+// addUpdate fills in m.Update if an updated version is available.
 func addUpdate(m *modinfo.ModulePublic) {
 	if m.Version != "" {
 		if info, err := modfetch.Query(m.Path, "latest", allowed); err == nil && info.Version != m.Version {
@@ -87,6 +80,11 @@
 	}
 }
 
+// addVersions fills in m.Versions with the list of known versions.
+func addVersions(m *modinfo.ModulePublic) {
+	m.Versions, _ = versions(m.Path)
+}
+
 func moduleInfo(m module.Version, fromBuildList bool) *modinfo.ModulePublic {
 	if m == Target {
 		return &modinfo.ModulePublic{
diff --git a/vendor/cmd/go/internal/vgo/list.go b/vendor/cmd/go/internal/vgo/list.go
index c31e958..7470625 100644
--- a/vendor/cmd/go/internal/vgo/list.go
+++ b/vendor/cmd/go/internal/vgo/list.go
@@ -11,10 +11,34 @@
 
 	"cmd/go/internal/base"
 	"cmd/go/internal/modinfo"
+	"cmd/go/internal/par"
 	"cmd/go/internal/search"
 )
 
-func ListModules(args []string) []*modinfo.ModulePublic {
+func ListModules(args []string, listU, listVersions bool) []*modinfo.ModulePublic {
+	mods := listModules(args)
+	if listU || listVersions {
+		var work par.Work
+		for _, m := range mods {
+			work.Add(m)
+			if m.Replace != nil {
+				work.Add(m.Replace)
+			}
+		}
+		work.Do(10, func(item interface{}) {
+			m := item.(*modinfo.ModulePublic)
+			if listU {
+				addUpdate(m)
+			}
+			if listVersions {
+				addVersions(m)
+			}
+		})
+	}
+	return mods
+}
+
+func listModules(args []string) []*modinfo.ModulePublic {
 	LoadBuildList()
 	if len(args) == 0 {
 		return []*modinfo.ModulePublic{moduleInfo(buildList[0], true)}
@@ -52,7 +76,7 @@
 			}
 		}
 		if !matched {
-			fmt.Fprintf(os.Stderr, "warning: pattern %q matched no module dependencies", arg)
+			fmt.Fprintf(os.Stderr, "warning: pattern %q matched no module dependencies\n", arg)
 		}
 	}
 
diff --git a/vendor/cmd/go/vgo_test.go b/vendor/cmd/go/vgo_test.go
index 08d4e5e..52a6135 100644
--- a/vendor/cmd/go/vgo_test.go
+++ b/vendor/cmd/go/vgo_test.go
@@ -495,6 +495,12 @@
 	tg.run("-vgo", "list")
 	tg.grepStderr(`adding rsc.io/quote v1.5.2`, "should have added quote v1.5.2")
 	tg.grepStderrNot(`v1.5.3-pre1`, "should not mention v1.5.3-pre1")
+
+	tg.run("-vgo", "list", "-m", "-versions", "rsc.io/quote")
+	want := "rsc.io/quote v1.0.0 v1.1.0 v1.2.0 v1.2.1 v1.3.0 v1.4.0 v1.5.0 v1.5.1 v1.5.2 v1.5.3-pre1\n"
+	if tg.getStdout() != want {
+		t.Errorf("vgo list versions:\nhave:\n%s\nwant:\n%s", tg.getStdout(), want)
+	}
 }
 
 func TestVgoBadDomain(t *testing.T) {