[gopls-release-branch.0.5] internal/lsp, go/packages: fix Go 1.15-related overlay bug

Something about the ordering of `go list` results must have changed in
Go 1.15, so overlays with test variants were failing to get the correct
imports. Technically, the correct solution would have been to support
overlays for a package *and* its test variant, but for the purposes of
gopls, it's actually more important that the overlays apply to the
package itself (rather than its test variant).

Fixes golang/go#40685

Change-Id: I3282557502f7f30bf484e1e6c17b90db824bc7d0
Reviewed-on: https://go-review.googlesource.com/c/tools/+/253800
Run-TryBot: Rebecca Stambler <rstambler@golang.org>
Reviewed-by: Michael Matloob <matloob@golang.org>
Reviewed-by: Heschi Kreinick <heschi@google.com>
diff --git a/go/packages/golist_overlay.go b/go/packages/golist_overlay.go
index 4eabfd9..154facc 100644
--- a/go/packages/golist_overlay.go
+++ b/go/packages/golist_overlay.go
@@ -89,9 +89,19 @@
 					// because the file is generated in another directory.
 					testVariantOf = p
 					continue nextPackage
+				} else if !isTestFile && hasTestFiles(p) {
+					// We're examining a test variant, but the overlaid file is
+					// a non-test file. Because the overlay implementation
+					// (currently) only adds a file to one package, skip this
+					// package, so that we can add the file to the production
+					// variant of the package. (https://golang.org/issue/36857
+					// tracks handling overlays on both the production and test
+					// variant of a package).
+					continue nextPackage
 				}
-				// We must have already seen the package of which this is a test variant.
 				if pkg != nil && p != pkg && pkg.PkgPath == p.PkgPath {
+					// We have already seen the production version of the
+					// for which p is a test variant.
 					if hasTestFiles(p) {
 						testVariantOf = pkg
 					}
diff --git a/go/packages/overlay_test.go b/go/packages/overlay_test.go
index 6d9be43..b70a875 100644
--- a/go/packages/overlay_test.go
+++ b/go/packages/overlay_test.go
@@ -863,3 +863,72 @@
 		t.Fatalf("expected at least 2 CompiledGoFiles for %s, got %v", pkg.PkgPath, len(pkg.CompiledGoFiles))
 	}
 }
+
+// Reproduces golang/go#40685.
+func TestAddImportInOverlay(t *testing.T) {
+	packagestest.TestAll(t, testAddImportInOverlay)
+}
+func testAddImportInOverlay(t *testing.T, exporter packagestest.Exporter) {
+	exported := packagestest.Export(t, exporter, []packagestest.Module{
+		{
+			Name: "golang.org/fake",
+			Files: map[string]interface{}{
+				"a/a.go": `package a
+
+import (
+	"fmt"
+)
+
+func _() {
+	fmt.Println("")
+	os.Stat("")
+}`,
+				"a/a_test.go": `package a
+
+import (
+	"os"
+	"testing"
+)
+
+func TestA(t *testing.T) {
+	os.Stat("")
+}`,
+			},
+		},
+	})
+	defer exported.Cleanup()
+
+	exported.Config.Mode = everythingMode
+	exported.Config.Tests = true
+
+	dir := filepath.Dir(exported.File("golang.org/fake", "a/a.go"))
+	exported.Config.Overlay = map[string][]byte{
+		filepath.Join(dir, "a.go"): []byte(`package a
+
+import (
+	"fmt"
+	"os"
+)
+
+func _() {
+	fmt.Println("")
+	os.Stat("")
+}
+`),
+	}
+	initial, err := packages.Load(exported.Config, "golang.org/fake/a")
+	if err != nil {
+		t.Fatal(err)
+	}
+	pkg := initial[0]
+	var foundOs bool
+	for _, imp := range pkg.Imports {
+		if imp.PkgPath == "os" {
+			foundOs = true
+			break
+		}
+	}
+	if !foundOs {
+		t.Fatalf(`expected import "os", found none: %v`, pkg.Imports)
+	}
+}
diff --git a/gopls/internal/regtest/imports_test.go b/gopls/internal/regtest/imports_test.go
index 5f16ae7..63835cd 100644
--- a/gopls/internal/regtest/imports_test.go
+++ b/gopls/internal/regtest/imports_test.go
@@ -7,10 +7,13 @@
 	"strings"
 	"testing"
 
+	"golang.org/x/tools/internal/lsp"
 	"golang.org/x/tools/internal/lsp/fake"
+	"golang.org/x/tools/internal/lsp/protocol"
 	"golang.org/x/tools/internal/testenv"
 )
 
+// Tests golang/go#38815.
 func TestIssue38815(t *testing.T) {
 	const needs = `
 -- go.mod --
@@ -156,3 +159,52 @@
 		}
 	})
 }
+
+// Tests golang/go#40685.
+func TestAcceptImportsQuickFixTestVariant(t *testing.T) {
+	const pkg = `
+-- go.mod --
+module mod.com
+
+go 1.12
+-- a/a.go --
+package a
+
+import (
+	"fmt"
+)
+
+func _() {
+	fmt.Println("")
+	os.Stat("")
+}
+-- a/a_test.go --
+package a
+
+import (
+	"os"
+	"testing"
+)
+
+func TestA(t *testing.T) {
+	os.Stat("")
+}
+`
+	run(t, pkg, func(t *testing.T, env *Env) {
+		env.Await(
+			CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromInitialWorkspaceLoad), 1),
+		)
+		env.OpenFile("a/a.go")
+		metBy := env.Await(
+			env.DiagnosticAtRegexp("a/a.go", "os.Stat"),
+		)
+		d, ok := metBy[0].(*protocol.PublishDiagnosticsParams)
+		if !ok {
+			t.Fatalf("expected *protocol.PublishDiagnosticsParams, got %v (%T)", d, d)
+		}
+		env.ApplyQuickFixes("a/a.go", d.Diagnostics)
+		env.Await(
+			EmptyDiagnostics("a/a.go"),
+		)
+	})
+}