Skip to content

Commit e3f1180

Browse files
adonovangopherbot
authored andcommitted
gopls/internal/cache: fix crash in analysis
The "missing Diagnostic.Code" assertion checks an invariant that isn't guaranteed. This change downgrades it to a bug.Reportf. Also, we add a similar assertion to analysistest.RunWithSuggestedFixes which has much better coverage of all the analyzer's different ways of constructing a SuggestedFix, unlike gopl's own superficial tests of each analyzer it carries. (This assertion may be enabled only for analyzers in x/tools, since the invariant is stricter than is required by the public API.) Also, fix a couple of places in unusedvariable where it could conceivably return a fix with no TextEdits; they are not intended to be lazy fixes. Fixes golang/go#65578 Change-Id: I5ed6301b028d184ea896988ca8f210fb8f3dd64f Reviewed-on: https://go-review.googlesource.com/c/tools/+/562397 LUCI-TryBot-Result: Go LUCI <[email protected]> Auto-Submit: Alan Donovan <[email protected]> Commit-Queue: Alan Donovan <[email protected]> Reviewed-by: Robert Findley <[email protected]>
1 parent 76ef6b6 commit e3f1180

File tree

3 files changed

+38
-8
lines changed

3 files changed

+38
-8
lines changed

go/analysis/analysistest/analysistest.go

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
"os"
1616
"path/filepath"
1717
"regexp"
18+
"runtime"
1819
"sort"
1920
"strconv"
2021
"strings"
@@ -128,6 +129,19 @@ type Testing interface {
128129
func RunWithSuggestedFixes(t Testing, dir string, a *analysis.Analyzer, patterns ...string) []*Result {
129130
r := Run(t, dir, a, patterns...)
130131

132+
// If the immediate caller of RunWithSuggestedFixes is in
133+
// x/tools, we apply stricter checks as required by gopls.
134+
inTools := false
135+
{
136+
var pcs [1]uintptr
137+
n := runtime.Callers(1, pcs[:])
138+
frames := runtime.CallersFrames(pcs[:n])
139+
fr, _ := frames.Next()
140+
if fr.Func != nil && strings.HasPrefix(fr.Func.Name(), "golang.org/x/tools/") {
141+
inTools = true
142+
}
143+
}
144+
131145
// Process each result (package) separately, matching up the suggested
132146
// fixes into a diff, which we will compare to the .golden file. We have
133147
// to do this per-result in case a file appears in two packages, such as in
@@ -145,8 +159,14 @@ func RunWithSuggestedFixes(t Testing, dir string, a *analysis.Analyzer, patterns
145159

146160
// Validate edits, prepare the fileEdits map and read the file contents.
147161
for _, diag := range act.Diagnostics {
148-
for _, sf := range diag.SuggestedFixes {
149-
for _, edit := range sf.TextEdits {
162+
for _, fix := range diag.SuggestedFixes {
163+
164+
// Assert that lazy fixes have a Category (#65578, #65087).
165+
if inTools && len(fix.TextEdits) == 0 && diag.Category == "" {
166+
t.Errorf("missing Diagnostic.Category for SuggestedFix without TextEdits (gopls requires the category for the name of the fix command")
167+
}
168+
169+
for _, edit := range fix.TextEdits {
150170
start, end := edit.Pos, edit.End
151171
if !end.IsValid() {
152172
end = start
@@ -175,7 +195,7 @@ func RunWithSuggestedFixes(t Testing, dir string, a *analysis.Analyzer, patterns
175195
if _, ok := fileEdits[file]; !ok {
176196
fileEdits[file] = make(map[string][]diff.Edit)
177197
}
178-
fileEdits[file][sf.Message] = append(fileEdits[file][sf.Message], diff.Edit{
198+
fileEdits[file][fix.Message] = append(fileEdits[file][fix.Message], diff.Edit{
179199
Start: file.Offset(start),
180200
End: file.Offset(end),
181201
New: string(edit.NewText),

gopls/internal/analysis/unusedvariable/unusedvariable.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -155,10 +155,14 @@ func removeVariableFromSpec(pass *analysis.Pass, path []ast.Node, stmt *ast.Valu
155155
// Find parent DeclStmt and delete it
156156
for _, node := range path {
157157
if declStmt, ok := node.(*ast.DeclStmt); ok {
158+
edits := deleteStmtFromBlock(path, declStmt)
159+
if len(edits) == 0 {
160+
return nil // can this happen?
161+
}
158162
return []analysis.SuggestedFix{
159163
{
160164
Message: suggestedFixMessage(ident.Name),
161-
TextEdits: deleteStmtFromBlock(path, declStmt),
165+
TextEdits: edits,
162166
},
163167
}
164168
}
@@ -208,10 +212,14 @@ func removeVariableFromAssignment(path []ast.Node, stmt *ast.AssignStmt, ident *
208212
}
209213

210214
// RHS does not have any side effects, delete the whole statement
215+
edits := deleteStmtFromBlock(path, stmt)
216+
if len(edits) == 0 {
217+
return nil // can this happen?
218+
}
211219
return []analysis.SuggestedFix{
212220
{
213221
Message: suggestedFixMessage(ident.Name),
214-
TextEdits: deleteStmtFromBlock(path, stmt),
222+
TextEdits: edits,
215223
},
216224
}
217225
}

gopls/internal/cache/errors.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,10 @@ import (
2121
"strings"
2222

2323
"golang.org/x/tools/go/packages"
24-
"golang.org/x/tools/gopls/internal/file"
2524
"golang.org/x/tools/gopls/internal/cache/metadata"
26-
"golang.org/x/tools/gopls/internal/protocol/command"
25+
"golang.org/x/tools/gopls/internal/file"
2726
"golang.org/x/tools/gopls/internal/protocol"
27+
"golang.org/x/tools/gopls/internal/protocol/command"
2828
"golang.org/x/tools/gopls/internal/settings"
2929
"golang.org/x/tools/gopls/internal/util/bug"
3030
"golang.org/x/tools/internal/typesinternal"
@@ -345,8 +345,10 @@ func toSourceDiagnostic(srcAnalyzer *settings.Analyzer, gobDiag *gobDiagnostic)
345345
}
346346

347347
// Ensure that the analyzer specifies a category for all its no-edit fixes.
348+
// This is asserted by analysistest.RunWithSuggestedFixes, but there
349+
// may be gaps in test coverage.
348350
if diag.Code == "" || diag.Code == "default" {
349-
panic(fmt.Sprintf("missing Diagnostic.Code: %#v", *diag))
351+
bug.Reportf("missing Diagnostic.Code: %#v", *diag)
350352
}
351353
}
352354
}

0 commit comments

Comments
 (0)