internal/lsp/cmd: add -config option to gopls vulncheck

If -config=true, gopls vulncheck reads the package load
configuration JSON (build flags, env, tests, and later
maybe overlay info) from stdin.

And, add some logging to gopls/internal/vulncheck/command.go
that helps measuring the package loading overhead.

Update golang/go#50577

Change-Id: I5c1ce145b07f2bed03911613f42c09a3d6be6c28
Reviewed-on: https://go-review.googlesource.com/c/tools/+/404575
Reviewed-by: Robert Findley <rfindley@google.com>
diff --git a/gopls/internal/vulncheck/command.go b/gopls/internal/vulncheck/command.go
index 1cd56b4..7040ae7 100644
--- a/gopls/internal/vulncheck/command.go
+++ b/gopls/internal/vulncheck/command.go
@@ -10,6 +10,7 @@
 import (
 	"context"
 	"fmt"
+	"log"
 	"os"
 	"strings"
 
@@ -83,10 +84,15 @@
 		packages.NeedCompiledGoFiles | packages.NeedImports | packages.NeedTypes |
 		packages.NeedTypesSizes | packages.NeedSyntax | packages.NeedTypesInfo | packages.NeedDeps
 
+	log.Println("loading packages...")
+
 	loadedPkgs, err := packages.Load(packagesCfg, patterns...)
 	if err != nil {
+		log.Printf("package load failed: %v", err)
 		return nil, err
 	}
+	log.Printf("loaded %d packages\n", len(loadedPkgs))
+
 	pkgs := vulncheck.Convert(loadedPkgs)
 	res, err := vulncheck.Source(ctx, pkgs, &vulncheck.Config{
 		Client:      c.Client,
diff --git a/internal/lsp/cmd/usage/vulncheck.hlp b/internal/lsp/cmd/usage/vulncheck.hlp
index 99fe13c..19a674b 100644
--- a/internal/lsp/cmd/usage/vulncheck.hlp
+++ b/internal/lsp/cmd/usage/vulncheck.hlp
@@ -8,6 +8,8 @@
 	By default, the command outputs a JSON-encoded
 	golang.org/x/tools/internal/lsp/command.VulncheckResult
 	message.
-
 	Example:
 	$ gopls vulncheck <packages>
+
+  -config
+    	If true, the command reads a JSON-encoded package load configuration from stdin
diff --git a/internal/lsp/cmd/vulncheck.go b/internal/lsp/cmd/vulncheck.go
index 8173778..4d245ce 100644
--- a/internal/lsp/cmd/vulncheck.go
+++ b/internal/lsp/cmd/vulncheck.go
@@ -20,9 +20,25 @@
 
 // vulncheck implements the vulncheck command.
 type vulncheck struct {
-	app *Application
+	Config bool `flag:"config" help:"If true, the command reads a JSON-encoded package load configuration from stdin"`
+	app    *Application
 }
 
+type pkgLoadConfig struct {
+	// BuildFlags is a list of command-line flags to be passed through to
+	// the build system's query tool.
+	BuildFlags []string
+
+	// Env is the environment to use when invoking the build system's query tool.
+	// If Env is nil, the current environment is used.
+	Env []string
+
+	// If Tests is set, the loader includes related test packages.
+	Tests bool
+}
+
+// TODO(hyangah): document pkgLoadConfig
+
 func (v *vulncheck) Name() string   { return "vulncheck" }
 func (v *vulncheck) Parent() string { return v.app.Name() }
 func (v *vulncheck) Usage() string  { return "" }
@@ -36,9 +52,9 @@
 	By default, the command outputs a JSON-encoded
 	golang.org/x/tools/internal/lsp/command.VulncheckResult
 	message.
-
 	Example:
 	$ gopls vulncheck <packages>
+
 `)
 	printFlagDefaults(f)
 }
@@ -56,6 +72,12 @@
 	if err != nil {
 		return tool.CommandLineErrorf("failed to get current directory: %v", err)
 	}
+	var cfg pkgLoadConfig
+	if v.Config {
+		if err := json.NewDecoder(os.Stdin).Decode(&cfg); err != nil {
+			return tool.CommandLineErrorf("failed to parse cfg: %v", err)
+		}
+	}
 
 	opts := source.DefaultOptions().Clone()
 	v.app.options(opts) // register hook
@@ -64,7 +86,10 @@
 	}
 
 	loadCfg := &packages.Config{
-		Context: ctx,
+		Context:    ctx,
+		Tests:      cfg.Tests,
+		BuildFlags: cfg.BuildFlags,
+		Env:        cfg.Env,
 	}
 
 	res, err := opts.Hooks.Govulncheck(ctx, loadCfg, command.VulncheckArgs{
@@ -72,11 +97,11 @@
 		Pattern: pattern,
 	})
 	if err != nil {
-		return err
+		return tool.CommandLineErrorf("govulncheck failed: %v", err)
 	}
 	data, err := json.MarshalIndent(res, " ", " ")
 	if err != nil {
-		return fmt.Errorf("failed to decode results: %v", err)
+		return tool.CommandLineErrorf("failed to decode results: %v", err)
 	}
 	fmt.Printf("%s", data)
 	return nil