cmd/go/internal/vgo: allow dotless module paths if replaced

In general a module path's first element should be a domain name,
or else we can't resolve it to find out how to download it.
But if a module is named and has a replace directive giving
a local directory where it can be found, we need not enforce
the leading-dot rule.

This test also serves to test that the go.mod version fixer does
not try to resolve the module path when it's well formed.

Fixes golang/go#24100.

Change-Id: I5cf3b82289db1f29b583f41fcf9f2509c7fc442a
Reviewed-on: https://go-review.googlesource.com/107661
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Bryan C. Mills <bcmills@google.com>
diff --git a/vendor/cmd/go/internal/search/search.go b/vendor/cmd/go/internal/search/search.go
index 8b2f7c6..42a6be0 100644
--- a/vendor/cmd/go/internal/search/search.go
+++ b/vendor/cmd/go/internal/search/search.go
@@ -407,6 +407,12 @@
 // part of the standard distribution. For historical reasons we allow people to add
 // their own code to $GOROOT instead of using $GOPATH, but we assume that
 // code will start with a domain name (dot in the first element).
+//
+// Note that this function is meant to evaluate whether a directory found in GOROOT
+// should be treated as part of the standard library. It should not be used to decide
+// that a directory found in GOPATH should be rejected: directories in GOPATH
+// need not have dots in the first element, and they just take their chances
+// with future collisions in the standard library.
 func IsStandardImportPath(path string) bool {
 	i := strings.Index(path, "/")
 	if i < 0 {
diff --git a/vendor/cmd/go/internal/vgo/build.go b/vendor/cmd/go/internal/vgo/build.go
index d548f22..f2c8d72 100644
--- a/vendor/cmd/go/internal/vgo/build.go
+++ b/vendor/cmd/go/internal/vgo/build.go
@@ -7,11 +7,14 @@
 import (
 	"bytes"
 	"cmd/go/internal/base"
+	"cmd/go/internal/cfg"
 	"cmd/go/internal/modinfo"
 	"cmd/go/internal/module"
 	"cmd/go/internal/search"
 	"encoding/hex"
 	"fmt"
+	"os"
+	"path/filepath"
 )
 
 var (
@@ -19,9 +22,19 @@
 	infoEnd, _   = hex.DecodeString("f932433186182072008242104116d8f2")
 )
 
+func isStandardImportPath(path string) bool {
+	if search.IsStandardImportPath(path) {
+		dir := filepath.Join(cfg.GOROOT, "src", path)
+		if _, err := os.Stat(dir); err == nil {
+			return true
+		}
+	}
+	return false
+}
+
 func PackageModuleInfo(path string) modinfo.ModulePublic {
 	var info modinfo.ModulePublic
-	if search.IsStandardImportPath(path) || !Enabled() {
+	if isStandardImportPath(path) || !Enabled() {
 		return info
 	}
 	target := findModule(path, path)
@@ -32,13 +45,13 @@
 }
 
 func PackageBuildInfo(path string, deps []string) string {
-	if search.IsStandardImportPath(path) || !Enabled() {
+	if isStandardImportPath(path) || !Enabled() {
 		return ""
 	}
 	target := findModule(path, path)
 	mdeps := make(map[module.Version]bool)
 	for _, dep := range deps {
-		if !search.IsStandardImportPath(dep) {
+		if !isStandardImportPath(dep) {
 			mdeps[findModule(path, dep)] = true
 		}
 	}
diff --git a/vendor/cmd/go/internal/vgo/load.go b/vendor/cmd/go/internal/vgo/load.go
index 282a1d3..ea4095b 100644
--- a/vendor/cmd/go/internal/vgo/load.go
+++ b/vendor/cmd/go/internal/vgo/load.go
@@ -103,7 +103,7 @@
 func Lookup(parentPath, path string) (dir, realPath string, err error) {
 	realPath = importmap[path]
 	if realPath == "" {
-		if search.IsStandardImportPath(path) {
+		if isStandardImportPath(path) {
 			dir := filepath.Join(cfg.GOROOT, "src", path)
 			if _, err := os.Stat(dir); err == nil {
 				return dir, path, nil
@@ -246,7 +246,10 @@
 		if strings.HasPrefix(path, "golang_org/") {
 			return filepath.Join(cfg.GOROOT, "src/vendor", path)
 		}
-		return filepath.Join(cfg.GOROOT, "src", path)
+		dir := filepath.Join(cfg.GOROOT, "src", path)
+		if _, err := os.Stat(dir); err == nil {
+			return dir
+		}
 	}
 
 	var mod1 module.Version
diff --git a/vendor/cmd/go/internal/vgo/search.go b/vendor/cmd/go/internal/vgo/search.go
index b32dce2..301c3e5 100644
--- a/vendor/cmd/go/internal/vgo/search.go
+++ b/vendor/cmd/go/internal/vgo/search.go
@@ -165,7 +165,7 @@
 	ld.importList(local, levelTestRecursive)
 	var all []string
 	for _, pkg := range ld.importmap {
-		if !search.IsStandardImportPath(pkg) {
+		if !isStandardImportPath(pkg) {
 			all = append(all, pkg)
 		}
 	}
diff --git a/vendor/cmd/go/vgo_test.go b/vendor/cmd/go/vgo_test.go
index 6cc8297..082c1d7 100644
--- a/vendor/cmd/go/vgo_test.go
+++ b/vendor/cmd/go/vgo_test.go
@@ -51,3 +51,28 @@
 		tg.must(os.Remove(tg.path("x/" + file)))
 	}
 }
+
+func TestLocalModule(t *testing.T) {
+	// Test that local replacements work
+	// and that they can use a dummy name
+	// that isn't resolvable and need not even
+	// include a dot. See golang.org/issue/24100.
+	tg := testgo(t)
+	defer tg.cleanup()
+	tg.makeTempdir()
+
+	tg.must(os.MkdirAll(tg.path("x/y"), 0777))
+	tg.must(os.MkdirAll(tg.path("x/z"), 0777))
+	tg.must(ioutil.WriteFile(tg.path("x/y/go.mod"), []byte(`
+		module x/y
+		require zz v1.0.0
+		replace zz v1.0.0 => ../z
+	`), 0666))
+	tg.must(ioutil.WriteFile(tg.path("x/y/y.go"), []byte(`package y; import _ "zz"`), 0666))
+	tg.must(ioutil.WriteFile(tg.path("x/z/go.mod"), []byte(`
+		module x/z
+	`), 0666))
+	tg.must(ioutil.WriteFile(tg.path("x/z/z.go"), []byte(`package z`), 0666))
+	tg.cd(tg.path("x/y"))
+	tg.run("-vgo", "build")
+}