Skip to content

Commit

Permalink
gopls/internal/lsp/cmd: list bug reports in 'gopls bug'
Browse files Browse the repository at this point in the history
This change causes gopls bug to print cached bug reports to stdout,
and to summarize them (by file:line) in the GitHub Issue form.
We don't post the bug contents, but we invite the user to copy
them.

Also, move the undocumented bug injection (for testing) from
'gopls stats' to 'gopls bug'.

Updates golang/go#60969

Change-Id: I19648e978dca3b74954d17ac449e22537c5f1e02
Reviewed-on: https://go-review.googlesource.com/c/tools/+/505579
Reviewed-by: Hyang-Ah Hana Kim <hyangah@gmail.com>
Run-TryBot: Alan Donovan <adonovan@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
  • Loading branch information
adonovan committed Jun 26, 2023
1 parent 8f9bce4 commit 9d8d408
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 16 deletions.
74 changes: 70 additions & 4 deletions gopls/internal/lsp/cmd/info.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,13 @@ import (
"fmt"
"net/url"
"os"
"sort"
"strings"

goplsbug "golang.org/x/tools/gopls/internal/bug"
"golang.org/x/tools/gopls/internal/lsp/browser"
"golang.org/x/tools/gopls/internal/lsp/debug"
"golang.org/x/tools/gopls/internal/lsp/filecache"
"golang.org/x/tools/gopls/internal/lsp/source"
"golang.org/x/tools/internal/tool"
)
Expand Down Expand Up @@ -128,10 +131,50 @@ A failing unit test is the best.
// Run collects some basic information and then prepares an issue ready to
// be reported.
func (b *bug) Run(ctx context.Context, args ...string) error {
buf := &bytes.Buffer{}
fmt.Fprint(buf, goplsBugHeader)
debug.PrintVersionInfo(ctx, buf, true, debug.Markdown)
body := buf.String()
// This undocumented environment variable allows
// the cmd integration test (and maintainers) to
// trigger a call to bug.Report.
if msg := os.Getenv("TEST_GOPLS_BUG"); msg != "" {
filecache.Start() // register bug handler
goplsbug.Report(msg)
return nil
}

// Enumerate bug reports, grouped and sorted.
_, reports := filecache.BugReports()
sort.Slice(reports, func(i, j int) bool {
x, y := reports[i], reports[i]
if x.Key != y.Key {
return x.Key < y.Key // ascending key order
}
return y.AtTime.Before(x.AtTime) // most recent first
})
keyDenom := make(map[string]int) // key is "file:line"
for _, report := range reports {
keyDenom[report.Key]++
}

// Privacy: the content of 'public' will be posted to GitHub
// to populate an issue textarea. Even though the user must
// submit the form to share the information with the world,
// merely populating the form causes us to share the
// information with GitHub itself.
//
// For that reason, we cannot write private information to
// public, such as bug reports, which may quote source code.
public := &bytes.Buffer{}
fmt.Fprint(public, goplsBugHeader)
if len(reports) > 0 {
fmt.Fprintf(public, "#### Internal errors\n\n")
fmt.Fprintf(public, "Gopls detected %d internal errors, %d distinct:\n",
len(reports), len(keyDenom))
for key, denom := range keyDenom {
fmt.Fprintf(public, "- %s (%d)\n", key, denom)
}
fmt.Fprintf(public, "\nPlease copy the full information printed by `gopls bug` here, if you are comfortable sharing it.\n\n")
}
debug.PrintVersionInfo(ctx, public, true, debug.Markdown)
body := public.String()
title := strings.Join(args, " ")
if !strings.HasPrefix(title, goplsBugPrefix) {
title = goplsBugPrefix + title
Expand All @@ -140,6 +183,29 @@ func (b *bug) Run(ctx context.Context, args ...string) error {
fmt.Print("Please file a new issue at golang.org/issue/new using this template:\n\n")
fmt.Print(body)
}

// Print bug reports to stdout (not GitHub).
keyNum := make(map[string]int)
for _, report := range reports {
fmt.Printf("-- %v -- \n", report.AtTime)

// Append seq number (e.g. " (1/2)") for repeated keys.
var seq string
if denom := keyDenom[report.Key]; denom > 1 {
keyNum[report.Key]++
seq = fmt.Sprintf(" (%d/%d)", keyNum[report.Key], denom)
}

// Privacy:
// - File and Stack may contain the name of the user that built gopls.
// - Description may contain names of the user's packages/files/symbols.
fmt.Printf("%s:%d: %s%s\n\n", report.File, report.Line, report.Description, seq)
fmt.Printf("%s\n\n", report.Stack)
}
if len(reports) > 0 {
fmt.Printf("Please copy the above information into the GitHub issue, if you are comfortable sharing it.\n")
}

return nil
}

Expand Down
8 changes: 0 additions & 8 deletions gopls/internal/lsp/cmd/stats.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,14 +57,6 @@ Example:
}

func (s *stats) Run(ctx context.Context, args ...string) error {
// This undocumented environment variable allows
// the cmd integration test to trigger a call to bug.Report.
if msg := os.Getenv("TEST_GOPLS_BUG"); msg != "" {
filecache.Start() // effect: register bug handler
goplsbug.Report(msg)
return nil
}

if s.app.Remote != "" {
// stats does not work with -remote.
// Other sessions on the daemon may interfere with results.
Expand Down
6 changes: 3 additions & 3 deletions gopls/internal/lsp/cmd/test/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -702,7 +702,7 @@ package foo
oops := fmt.Sprintf("oops-%d", rand.Int())
{
env := []string{"TEST_GOPLS_BUG=" + oops}
res := goplsWithEnv(t, tree, env, "stats")
res := goplsWithEnv(t, tree, env, "bug")
res.checkExit(true)
}

Expand Down Expand Up @@ -745,8 +745,8 @@ package foo
{
got := fmt.Sprint(stats.BugReports)
wants := []string{
"cmd/stats.go", // File containing call to bug.Report
oops, // Description
"cmd/info.go", // File containing call to bug.Report
oops, // Description
}
for _, want := range wants {
if !strings.Contains(got, want) {
Expand Down
2 changes: 1 addition & 1 deletion gopls/internal/lsp/filecache/filecache.go
Original file line number Diff line number Diff line change
Expand Up @@ -552,7 +552,7 @@ func init() {
// used by this process (or "" on initialization error).
func BugReports() (string, []bug.Bug) {
// To test this logic, run:
// $ TEST_GOPLS_BUG=oops gopls stats # trigger a bug
// $ TEST_GOPLS_BUG=oops gopls bug # trigger a bug
// $ gopls stats # list the bugs

dir, err := getCacheDir()
Expand Down

0 comments on commit 9d8d408

Please sign in to comment.