internal/imports: don't prefix stdlib package with std/

When running in GOROOT/src, `go list -m all` shows the std package (or
cmd package) as the main module. This confuses goimports into adding
std/ or cmd/ at the beginning of import paths. Skip canonicalization for
paths under GOROOT.

Fixes golang/go#31814

Change-Id: Iff5cc7e2a2053e4cc87c1a579a4c47d856cd0a2e
Reviewed-on: https://go-review.googlesource.com/c/tools/+/195063
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Cottrell <iancottrell@google.com>
diff --git a/internal/imports/fix_test.go b/internal/imports/fix_test.go
index 610b9d5..8132954 100644
--- a/internal/imports/fix_test.go
+++ b/internal/imports/fix_test.go
@@ -7,6 +7,7 @@
 import (
 	"flag"
 	"fmt"
+	"go/build"
 	"path/filepath"
 	"runtime"
 	"strings"
@@ -1538,6 +1539,39 @@
 	}
 }
 
+// https://golang.org/issue/31814
+func TestStdlibNotPrefixed(t *testing.T) {
+	const input = `package p
+var _ = bytes.Buffer
+`
+	const want = `package p
+
+import "bytes"
+
+var _ = bytes.Buffer
+`
+	// Force a scan of the stdlib.
+	savedStdlib := stdlib
+	defer func() { stdlib = savedStdlib }()
+	stdlib = map[string]map[string]bool{}
+
+	testConfig{
+		module: packagestest.Module{
+			Name: "ignored.com",
+		},
+	}.test(t, func(t *goimportTest) {
+		// Run in GOROOT/src so that the std module shows up in go list -m all.
+		t.env.WorkingDir = filepath.Join(t.env.GOROOT, "src")
+		got, err := t.processNonModule(filepath.Join(t.env.GOROOT, "src/x.go"), []byte(input), nil)
+		if err != nil {
+			t.Fatalf("Process() = %v", err)
+		}
+		if string(got) != want {
+			t.Errorf("Got:\n%s\nWant:\n%s", got, want)
+		}
+	})
+}
+
 type testConfig struct {
 	gopathOnly             bool
 	goPackagesIncompatible bool
@@ -1609,6 +1643,11 @@
 				},
 				exported: exported,
 			}
+			if it.env.GOROOT == "" {
+				// packagestest clears out GOROOT to work around https://golang.org/issue/32849,
+				// which isn't relevant here. Fill it back in so we can find the standard library.
+				it.env.GOROOT = build.Default.GOROOT
+			}
 			fn(it)
 		})
 	}
diff --git a/internal/imports/mod.go b/internal/imports/mod.go
index 551bae8..387799b 100644
--- a/internal/imports/mod.go
+++ b/internal/imports/mod.go
@@ -313,7 +313,7 @@
 
 		// The rest of this function canonicalizes the packages using the results
 		// of initializing the resolver from 'go list -m'.
-		res, err := r.canonicalize(info.nonCanonicalImportPath, info.dir, info.needsReplace)
+		res, err := r.canonicalize(root.Type, info.nonCanonicalImportPath, info.dir, info.needsReplace)
 		if err != nil {
 			return
 		}
@@ -335,7 +335,7 @@
 			continue
 		}
 
-		res, err := r.canonicalize(info.nonCanonicalImportPath, info.dir, info.needsReplace)
+		res, err := r.canonicalize(gopathwalk.RootModuleCache, info.nonCanonicalImportPath, info.dir, info.needsReplace)
 		if err != nil {
 			continue
 		}
@@ -347,7 +347,15 @@
 
 // canonicalize gets the result of canonicalizing the packages using the results
 // of initializing the resolver from 'go list -m'.
-func (r *ModuleResolver) canonicalize(importPath, dir string, needsReplace bool) (res *pkg, err error) {
+func (r *ModuleResolver) canonicalize(rootType gopathwalk.RootType, importPath, dir string, needsReplace bool) (res *pkg, err error) {
+	// Packages in GOROOT are already canonical, regardless of the std/cmd modules.
+	if rootType == gopathwalk.RootGOROOT {
+		return &pkg{
+			importPathShort: importPath,
+			dir:             dir,
+		}, nil
+	}
+
 	// Check if the directory is underneath a module that's in scope.
 	if mod := r.findModuleByDir(dir); mod != nil {
 		// It is. If dir is the target of a replace directive,