go/packages: find filenames in error strings if not in position

In the case of an invalid package name (the package name is a keyword),
`go list` doesn't report any filenames associated with the package.
As we have done previously, we can parse these out of the error message.
However, it seems like, in this case, the filename is in the error
string itself, not the error position.

Updates golang/go#39986
Updates golang/go#39763

Change-Id: Id9c68a93ac4f545e4e2152540ca85b92f1df4640
Reviewed-on: https://go-review.googlesource.com/c/tools/+/244028
Run-TryBot: Rebecca Stambler <rstambler@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Michael Matloob <matloob@golang.org>
diff --git a/go/packages/golist.go b/go/packages/golist.go
index 1a5aba9..220d409 100644
--- a/go/packages/golist.go
+++ b/go/packages/golist.go
@@ -639,16 +639,32 @@
 		// error messages. This happens if there are unrecoverable syntax
 		// errors in the source, so we can't match on a specific error message.
 		if err := p.Error; err != nil && len(err.ImportStack) == 0 && len(pkg.CompiledGoFiles) == 0 {
-			if split := strings.Split(err.Pos, ":"); len(split) > 1 {
-				if filename := split[0]; filename != "" {
-					if !filepath.IsAbs(filename) {
-						filename = filepath.Join(state.cfg.Dir, filename)
-					}
-					if info, _ := os.Stat(filename); info != nil {
-						pkg.CompiledGoFiles = append(pkg.CompiledGoFiles, filename)
-						pkg.GoFiles = append(pkg.GoFiles, filename)
-					}
+			addFilenameFromPos := func(pos string) bool {
+				split := strings.Split(pos, ":")
+				if len(split) < 1 {
+					return false
 				}
+				filename := strings.TrimSpace(split[0])
+				if filename == "" {
+					return false
+				}
+				if !filepath.IsAbs(filename) {
+					filename = filepath.Join(state.cfg.Dir, filename)
+				}
+				info, _ := os.Stat(filename)
+				if info == nil {
+					return false
+				}
+				pkg.CompiledGoFiles = append(pkg.CompiledGoFiles, filename)
+				pkg.GoFiles = append(pkg.GoFiles, filename)
+				return true
+			}
+			found := addFilenameFromPos(err.Pos)
+			// In some cases, go list only reports the error position in the
+			// error text, not the error position. One such case is when the
+			// file's package name is a keyword (see golang.org/issue/39763).
+			if !found {
+				addFilenameFromPos(err.Err)
 			}
 		}
 
diff --git a/go/packages/packages_test.go b/go/packages/packages_test.go
index 2974ecc..a62bac4 100644
--- a/go/packages/packages_test.go
+++ b/go/packages/packages_test.go
@@ -2537,6 +2537,35 @@
 	}
 }
 
+func TestInvalidPackageName(t *testing.T) {
+	packagestest.TestAll(t, testInvalidPackageName)
+}
+
+func testInvalidPackageName(t *testing.T, exporter packagestest.Exporter) {
+	testenv.NeedsGo1Point(t, 15)
+
+	exported := packagestest.Export(t, exporter, []packagestest.Module{{
+		Name: "golang.org/fake",
+		Files: map[string]interface{}{
+			"main.go": `package default
+
+func main() {
+}
+`,
+		},
+	}})
+	defer exported.Cleanup()
+
+	initial, err := packages.Load(exported.Config, "golang.org/fake")
+	if err != nil {
+		t.Fatal(err)
+	}
+	pkg := initial[0]
+	if len(pkg.CompiledGoFiles) != 1 {
+		t.Fatalf("expected 1 Go file in package %s, got %v", pkg.ID, len(pkg.CompiledGoFiles))
+	}
+}
+
 func errorMessages(errors []packages.Error) []string {
 	var msgs []string
 	for _, err := range errors {