cmd/go/internal/vgo: ignore appengine imports

As an unfortunate special case, just ignore import of appengine or appengine/*
when scanning a source tree to find modules that need to be imported.
A more aggressive option would be to ignore all paths that don't start
with an element with a dot (that is, don't start with a plausible domain name),
but I don't want to require that all modules have a dot at the start of the name.

The real problem here is that appengine was introduced so long ago that it
predates "go get" and did not use URL-like paths, and some code exists that
is still using those old imports. I am unaware of any other special cases like
that. If they arise, we can add them too, or we can revisit whether there is a
more general fix. For now this should suffice.

Fixes golang/go#25881.

Change-Id: I38cdbc1cd83a2f55d87f598bd0cbf1f6bcade88a
Reviewed-on: https://go-review.googlesource.com/118757
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ross Light <light@google.com>
diff --git a/vendor/cmd/go/internal/vgo/get.go b/vendor/cmd/go/internal/vgo/get.go
index 9fd8497..b61f7bc 100644
--- a/vendor/cmd/go/internal/vgo/get.go
+++ b/vendor/cmd/go/internal/vgo/get.go
@@ -131,7 +131,7 @@
 				if err != nil {
 					return err // TODO
 				}
-				imports, testImports, err := imports.ScanDir(dir, v.Tags)
+				imports, testImports, err := scanDir(dir, v.Tags)
 				for _, path := range imports {
 					xxx
 				}
diff --git a/vendor/cmd/go/internal/vgo/load.go b/vendor/cmd/go/internal/vgo/load.go
index 5be03b4..fee3ff0 100644
--- a/vendor/cmd/go/internal/vgo/load.go
+++ b/vendor/cmd/go/internal/vgo/load.go
@@ -220,7 +220,7 @@
 
 	ld.pkgdir[realPath] = dir
 
-	imports, testImports, err := imports.ScanDir(dir, ld.tags)
+	imports, testImports, err := scanDir(dir, ld.tags)
 	if err != nil {
 		base.Errorf("vgo: %s [%s]: %v", ld.stackText(), dir, err)
 		return
@@ -580,3 +580,31 @@
 	}
 	return module.Version{Path: m.Path, Version: "none"}, nil
 }
+
+// scanDir is like imports.ScanDir but elides known magic imports from the list,
+// so that vgo does not go looking for packages that don't really exist.
+//
+// The only known magic imports are appengine and appengine/*.
+// These are so old that they predate "go get" and did not use URL-like paths.
+// Most code today now uses google.golang.org/appengine instead,
+// but not all code has been so updated. When we mostly ignore build tags
+// during "vgo vendor", we look into "// +build appengine" files and
+// may see these legacy imports. We drop them so that the module
+// search does not look for modules to try to satisfy them.
+func scanDir(path string, tags map[string]bool) (imports_, testImports []string, err error) {
+	imports_, testImports, err = imports.ScanDir(path, tags)
+
+	filter := func(x []string) []string {
+		w := 0
+		for _, pkg := range x {
+			if pkg != "appengine" && !strings.HasPrefix(pkg, "appengine/") &&
+				pkg != "appengine_internal" && !strings.HasPrefix(pkg, "appengine_internal/") {
+				x[w] = pkg
+				w++
+			}
+		}
+		return x
+	}
+
+	return filter(imports_), filter(testImports), err
+}
diff --git a/vendor/cmd/go/internal/vgo/search.go b/vendor/cmd/go/internal/vgo/search.go
index c3f7ab1..fb2bac4 100644
--- a/vendor/cmd/go/internal/vgo/search.go
+++ b/vendor/cmd/go/internal/vgo/search.go
@@ -145,7 +145,7 @@
 			if !have[name] {
 				have[name] = true
 				if match(name) {
-					if _, _, err := imports.ScanDir(path, imports.Tags()); err != imports.ErrNoGo {
+					if _, _, err := scanDir(path, imports.Tags()); err != imports.ErrNoGo {
 						pkgs = append(pkgs, name)
 					}
 				}
diff --git a/vendor/cmd/go/testdata/vendormod/appengine.go b/vendor/cmd/go/testdata/vendormod/appengine.go
new file mode 100644
index 0000000..da4d04c
--- /dev/null
+++ b/vendor/cmd/go/testdata/vendormod/appengine.go
@@ -0,0 +1,6 @@
+// +build appengine
+
+package m
+
+import _ "appengine"
+import _ "appengine/datastore"
diff --git a/vendor/cmd/go/vgo_test.go b/vendor/cmd/go/vgo_test.go
index 34a8c71..54ad36f 100644
--- a/vendor/cmd/go/vgo_test.go
+++ b/vendor/cmd/go/vgo_test.go
@@ -217,7 +217,7 @@
 	tg.grepStderr("unknown module x/y.z: not a domain name", "expected domain error")
 
 	tg.runFail("-vgo", "build")
-	tg.grepStderr("unknown module appengine: not a domain name", "expected domain error")
+	tg.grepStderrNot("unknown module appengine: not a domain name", "expected nothing about appengine")
 	tg.grepStderr("tcp.*nonexistent.rsc.io", "expected error for nonexistent.rsc.io")
 }