gopls/internal/vulncheck: handle package errors

x/tools/go/packages.Load can succeed even when the packages have
errors. vulncheck analysis requires clean build for the analyzed
code. So, check returned packages and stop if any of them has
errors.

Change-Id: I25a272631aa80f0c22abb00cf52776db2640cb80
Reviewed-on: https://go-review.googlesource.com/c/tools/+/454437
gopls-CI: kokoro <noreply+kokoro@google.com>
Reviewed-by: Alan Donovan <adonovan@google.com>
Run-TryBot: Hyang-Ah Hana Kim <hyangah@gmail.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
diff --git a/gopls/internal/regtest/misc/vuln_test.go b/gopls/internal/regtest/misc/vuln_test.go
index 377e9a2..b043e0c 100644
--- a/gopls/internal/regtest/misc/vuln_test.go
+++ b/gopls/internal/regtest/misc/vuln_test.go
@@ -56,6 +56,40 @@
 	})
 }
 
+func TestRunGovulncheckError2(t *testing.T) {
+	const files = `
+-- go.mod --
+module mod.com
+
+go 1.12
+-- foo.go --
+package foo
+
+func F() { // build error incomplete
+`
+	WithOptions(
+		EnvVars{
+			"_GOPLS_TEST_BINARY_RUN_AS_GOPLS": "true", // needed to run `gopls vulncheck`.
+		},
+		Settings{
+			"codelenses": map[string]bool{
+				"run_govulncheck": true,
+			},
+		},
+	).Run(t, files, func(t *testing.T, env *Env) {
+		env.OpenFile("go.mod")
+		var result command.RunVulncheckResult
+		env.ExecuteCodeLensCommand("go.mod", command.RunGovulncheck, &result)
+		env.Await(
+			OnceMet(
+				CompletedProgress(result.Token),
+				// TODO(hyangah): find a way to inspect $/progress 'report' message.
+				LogMatching(protocol.Info, "failed to load packages due to errors", 1, false),
+			),
+		)
+	})
+}
+
 const vulnsData = `
 -- GO-2022-01.yaml --
 modules:
diff --git a/gopls/internal/vulncheck/command.go b/gopls/internal/vulncheck/command.go
index b1bdfba..54c9fe5 100644
--- a/gopls/internal/vulncheck/command.go
+++ b/gopls/internal/vulncheck/command.go
@@ -10,6 +10,7 @@
 import (
 	"context"
 	"encoding/json"
+	"errors"
 	"fmt"
 	"log"
 	"os"
@@ -91,7 +92,6 @@
 		logger.Printf("%v", err)
 		return nil, fmt.Errorf("package load failed")
 	}
-
 	logger.Printf("analyzing %d packages...\n", len(loadedPkgs))
 
 	r, err := vulncheck.Source(ctx, loadedPkgs, &vulncheck.Config{Client: c.Client, SourceGoVersion: goVersion()})
@@ -236,6 +236,11 @@
 			logf("Failed to load packages: %v", err)
 			return err
 		}
+		if n := packages.PrintErrors(pkgs); n > 0 {
+			err := errors.New("failed to load packages due to errors")
+			logf("%v", err)
+			return err
+		}
 		logf("Loaded %d packages and their dependencies", len(pkgs))
 		cli, err := client.NewClient(findGOVULNDB(cfg.Env), client.Options{
 			HTTPCache: govulncheck.DefaultCache(),