cmd/go/internal/vgo: auto-populate go.mod already containing module line

If go.mod doesn't exist, vgo guesses the module and fills in
requirements from Gopkg.lock or the like.
But if go.mod does exist, then vgo skips all that.
This means that if you need to override vgo's guess about
the module path you don't get the conversion from Gopkg.lock.

Change vgo so that if there is a module line and no other
syntax in the file (not even a standalone comment block)
then it still runs the "convert from Gopkg.lock etc" logic.

Fixes golang/go#25545.

Change-Id: I29dc688277f414f8576633b6b180b165ff66639b
Reviewed-on: https://go-review.googlesource.com/114496
Run-TryBot: Russ Cox <rsc@golang.org>
Reviewed-by: Bryan C. Mills <bcmills@google.com>
diff --git a/vendor/cmd/go/internal/modfetch/convert.go b/vendor/cmd/go/internal/modfetch/convert.go
index 8b06b39..7166bfc 100644
--- a/vendor/cmd/go/internal/modfetch/convert.go
+++ b/vendor/cmd/go/internal/modfetch/convert.go
@@ -6,6 +6,7 @@
 
 import (
 	"fmt"
+	"os"
 	"sort"
 	"strings"
 
@@ -50,10 +51,12 @@
 
 		repo, err := Lookup(r.Path)
 		if err != nil {
+			fmt.Fprintf(os.Stderr, "vgo: lookup %s: %v", r.Path, err)
 			continue
 		}
 		info, err := repo.Stat(r.Version)
 		if err != nil {
+			fmt.Fprintf(os.Stderr, "vgo: stat %s@%s: %v", r.Path, r.Version, err)
 			continue
 		}
 		path := repo.ModulePath()
diff --git a/vendor/cmd/go/internal/modfile/rule.go b/vendor/cmd/go/internal/modfile/rule.go
index 640c287..9c50102 100644
--- a/vendor/cmd/go/internal/modfile/rule.go
+++ b/vendor/cmd/go/internal/modfile/rule.go
@@ -61,6 +61,21 @@
 	})
 }
 
+func (f *File) AddComment(text string) {
+	if f.Syntax == nil {
+		f.Syntax = new(FileSyntax)
+	}
+	f.Syntax.Stmt = append(f.Syntax.Stmt, &CommentBlock{
+		Comments: Comments{
+			Before: []Comment{
+				{
+					Token: text,
+				},
+			},
+		},
+	})
+}
+
 type VersionFixer func(path, version string) (string, error)
 
 func Parse(file string, data []byte, fix VersionFixer) (*File, error) {
diff --git a/vendor/cmd/go/internal/vgo/init.go b/vendor/cmd/go/internal/vgo/init.go
index 13b5a68..0c595d1 100644
--- a/vendor/cmd/go/internal/vgo/init.go
+++ b/vendor/cmd/go/internal/vgo/init.go
@@ -142,6 +142,12 @@
 		f.AddModuleStmt(path)
 	}
 
+	if len(f.Syntax.Stmt) == 1 && f.Module != nil {
+		// Entire file is just a module statement.
+		// Populate require if possible.
+		legacyModInit()
+	}
+
 	excluded = make(map[module.Version]bool)
 	for _, x := range f.Exclude {
 		excluded[x.Mod] = true
@@ -155,15 +161,17 @@
 }
 
 func legacyModInit() {
-	path, err := FindModulePath(ModRoot)
-	if err != nil {
-		base.Fatalf("vgo: %v", err)
+	if modFile == nil {
+		path, err := FindModulePath(ModRoot)
+		if err != nil {
+			base.Fatalf("vgo: %v", err)
+		}
+		fmt.Fprintf(os.Stderr, "vgo: creating new go.mod: module %s\n", path)
+		modFile = new(modfile.File)
+		modFile.AddModuleStmt(path)
 	}
-	fmt.Fprintf(os.Stderr, "vgo: module %s\n", path)
-	modFile = new(modfile.File)
-	modFile.AddModuleStmt(path)
-	Target = module.Version{Path: path}
 
+	Target = modFile.Module.Mod
 	for _, name := range altConfigs {
 		cfg := filepath.Join(ModRoot, name)
 		data, err := ioutil.ReadFile(cfg)
@@ -172,14 +180,17 @@
 			if convert == nil {
 				return
 			}
+			fmt.Fprintf(os.Stderr, "vgo: copying requirements from %s\n", cfg)
 			if err := modfetch.ConvertLegacyConfig(modFile, cfg, data); err != nil {
 				base.Fatalf("vgo: %v", err)
 			}
+			if len(modFile.Syntax.Stmt) == 1 {
+				// Add comment to prevent vgo from re-converting every time it runs.
+				modFile.AddComment("// vgo: no requirements found in " + name)
+			}
 			return
 		}
 	}
-
-	base.Fatalf("vgo: internal error: cannot find legacy config file (it was here a minute ago!)")
 }
 
 var altConfigs = []string{
diff --git a/vendor/cmd/go/vgo_test.go b/vendor/cmd/go/vgo_test.go
index b1c32dd..70d1086 100644
--- a/vendor/cmd/go/vgo_test.go
+++ b/vendor/cmd/go/vgo_test.go
@@ -7,6 +7,7 @@
 import (
 	"cmd/go/internal/modconv"
 	"cmd/go/internal/vgo"
+	"internal/testenv"
 	"io/ioutil"
 	"os"
 	"runtime"
@@ -135,3 +136,44 @@
 	tg.run("-vgo", "list", "-f={{.GoFiles}}", "-tags", "tag1 tag2")
 	tg.grepStdout(`\[x.go y.go\]`, "Go source files for tag1 and tag2")
 }
+
+func TestFillGoMod(t *testing.T) {
+	testenv.MustHaveExternalNetwork(t)
+	tg := testgo(t)
+	defer tg.cleanup()
+	tg.makeTempdir()
+
+	tg.setenv("HOME", tg.path("."))
+	tg.must(os.MkdirAll(tg.path("x"), 0777))
+	tg.must(ioutil.WriteFile(tg.path("x/x.go"), []byte(`
+		package x
+	`), 0666))
+
+	tg.must(ioutil.WriteFile(tg.path("x/go.mod"), []byte(`
+		module x
+	`), 0666))
+	tg.must(ioutil.WriteFile(tg.path("x/Gopkg.lock"), []byte(`
+[[projects]]
+  name = "rsc.io/sampler"
+  version = "v1.0.0"
+	`), 0666))
+
+	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.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")
+
+	tg.must(ioutil.WriteFile(tg.path("x/go.mod"), []byte(`
+		module x
+	`), 0666))
+	tg.must(ioutil.WriteFile(tg.path("x/Gopkg.lock"), []byte(`
+	`), 0666))
+
+	tg.run("-vgo", "list")
+	tg.grepStderr("copying requirements from .*Gopkg.lock", "did not copy Gopkg.lock")
+	tg.run("-vgo", "list")
+	tg.grepStderrNot("copying requirements from .*Gopkg.lock", "should not copy Gopkg.lock again")
+
+}