Skip to content

Commit 1e7abac

Browse files
committed
internal/lsp: refactor go command error handling
Our handling of go command errors was cobbled together, leading to unexpected gaps and duplication. Refactor it to be more coherent. Our goal is to turn every go command error into a diagnostic in the relevant location. The errors don't contain error positions, so we have to guess where they belong using the module names mentioned in the error. If we can't find any reference to those modules, we are forced to add diagnostics to all go.mod files. I may have destroyed the intent of TestMultiModule_OneBrokenModule but I'm not sure what to do about it. Some cleanup along the way: - Stop parsing modfile.Parse error text: it returns structured errors and we can just use them. - Return CriticalErrors from awaitLoadedAllErrors, and do error extraction lower in the stack. This prevents a ridiculous situation where initialize formed a CriticalError, then awaitLoadedAllErrors returned just its MainError, and then GetCriticalError parsed out a new CriticalError from the MainError we got from a CriticalError. - In initialize, return modDiagnostics even if load succeeds: we are missing packages and should not silently fail, I think? - During testing I tripped over ApplyQuickFixes' willingness to not actually do anything, so I made that an error. Fixes golang/go#44132. I may also have fixed golang/go#44204 but I haven't checked. Change-Id: Ibf819d0f044d4f99795978a28b18915893e50c88 Reviewed-on: https://go-review.googlesource.com/c/tools/+/291192 Trust: Heschi Kreinick <[email protected]> Run-TryBot: Heschi Kreinick <[email protected]> gopls-CI: kokoro <[email protected]> TryBot-Result: Go Bot <[email protected]> Reviewed-by: Rebecca Stambler <[email protected]> Reviewed-by: Robert Findley <[email protected]>
1 parent ffc2075 commit 1e7abac

File tree

11 files changed

+257
-341
lines changed

11 files changed

+257
-341
lines changed

gopls/internal/regtest/diagnostics/diagnostics_test.go

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ import (
1616
"golang.org/x/tools/internal/lsp"
1717
"golang.org/x/tools/internal/lsp/fake"
1818
"golang.org/x/tools/internal/lsp/protocol"
19-
"golang.org/x/tools/internal/lsp/tests"
2019
"golang.org/x/tools/internal/testenv"
2120
)
2221

@@ -526,20 +525,15 @@ func _() {
526525
`
527526
Run(t, generated, func(t *testing.T, env *Env) {
528527
env.OpenFile("main.go")
529-
original := env.ReadWorkspaceFile("main.go")
530528
var d protocol.PublishDiagnosticsParams
531529
env.Await(
532530
OnceMet(
533531
DiagnosticAt("main.go", 5, 8),
534532
ReadDiagnostics("main.go", &d),
535533
),
536534
)
537-
// Apply fixes and save the buffer.
538-
env.ApplyQuickFixes("main.go", d.Diagnostics)
539-
env.SaveBuffer("main.go")
540-
fixed := env.ReadWorkspaceFile("main.go")
541-
if original != fixed {
542-
t.Fatalf("generated file was changed by quick fixes:\n%s", tests.Diff(t, original, fixed))
535+
if fixes := env.GetQuickFixes("main.go", d.Diagnostics); len(fixes) != 0 {
536+
t.Errorf("got quick fixes %v, wanted none", fixes)
543537
}
544538
})
545539
}

gopls/internal/regtest/expectation.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"golang.org/x/tools/internal/lsp"
1313
"golang.org/x/tools/internal/lsp/fake"
1414
"golang.org/x/tools/internal/lsp/protocol"
15+
"golang.org/x/tools/internal/testenv"
1516
)
1617

1718
// An Expectation asserts that the state of the editor at a point in time
@@ -580,3 +581,16 @@ func NoDiagnosticAt(name string, line, col int) DiagnosticExpectation {
580581
func NoDiagnosticWithMessage(name, msg string) DiagnosticExpectation {
581582
return DiagnosticExpectation{path: name, message: msg, present: false}
582583
}
584+
585+
// GoSum asserts that a "go.sum is out of sync" diagnostic for the given module
586+
// (as formatted in a go.mod file, e.g. "example.com v1.0.0") is present.
587+
func (e *Env) GoSumDiagnostic(name, module string) Expectation {
588+
e.T.Helper()
589+
// In 1.16, go.sum diagnostics should appear on the relevant module. Earlier
590+
// errors have no information and appear on the module declaration.
591+
if testenv.Go1Point() >= 16 {
592+
return e.DiagnosticAtRegexpWithMessage(name, module, "go.sum is out of sync")
593+
} else {
594+
return e.DiagnosticAtRegexpWithMessage(name, `module`, "go.sum is out of sync")
595+
}
596+
}

gopls/internal/regtest/modfile/modfile_test.go

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -548,6 +548,7 @@ func main() {
548548
d := protocol.PublishDiagnosticsParams{}
549549
env.Await(
550550
OnceMet(
551+
// Make sure the diagnostic mentions the new version -- the old diagnostic is in the same place.
551552
env.DiagnosticAtRegexpWithMessage("a/go.mod", "example.com v1.2.3", "[email protected]"),
552553
ReadDiagnostics("a/go.mod", &d),
553554
),
@@ -822,7 +823,7 @@ func main() {
822823
env.OpenFile("go.mod")
823824
env.Await(
824825
OnceMet(
825-
DiagnosticAt("go.mod", 0, 0),
826+
env.GoSumDiagnostic("go.mod", `example.com v1.2.3`),
826827
ReadDiagnostics("go.mod", d),
827828
),
828829
)
@@ -1001,9 +1002,7 @@ require random.com v1.2.3
10011002
}
10021003

10031004
func TestSumUpdateQuickFix(t *testing.T) {
1004-
// Error messages changed in 1.16 that changed the diagnostic positions.
1005-
testenv.NeedsGo1Point(t, 16)
1006-
1005+
testenv.NeedsGo1Point(t, 14)
10071006
const mod = `
10081007
-- go.mod --
10091008
module mod.com
@@ -1030,22 +1029,14 @@ func main() {
10301029
Modes(Singleton),
10311030
).Run(t, mod, func(t *testing.T, env *Env) {
10321031
env.OpenFile("go.mod")
1033-
pos := env.RegexpSearch("go.mod", "example.com")
10341032
params := &protocol.PublishDiagnosticsParams{}
10351033
env.Await(
10361034
OnceMet(
1037-
env.DiagnosticAtRegexp("go.mod", "example.com"),
1035+
env.GoSumDiagnostic("go.mod", "example.com"),
10381036
ReadDiagnostics("go.mod", params),
10391037
),
10401038
)
1041-
var diagnostic protocol.Diagnostic
1042-
for _, d := range params.Diagnostics {
1043-
if d.Range.Start.Line == uint32(pos.Line) {
1044-
diagnostic = d
1045-
break
1046-
}
1047-
}
1048-
env.ApplyQuickFixes("go.mod", []protocol.Diagnostic{diagnostic})
1039+
env.ApplyQuickFixes("go.mod", params.Diagnostics)
10491040
const want = `example.com v1.2.3 h1:Yryq11hF02fEf2JlOS2eph+ICE2/ceevGV3C9dl5V/c=
10501041
example.com v1.2.3/go.mod h1:Y2Rc5rVWjWur0h3pd9aEvK5Pof8YKDANh9gHA2Maujo=
10511042
`

gopls/internal/regtest/workspace/workspace_test.go

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -818,25 +818,25 @@ func main() {
818818
Modes(Experimental),
819819
).Run(t, mod, func(t *testing.T, env *Env) {
820820
params := &protocol.PublishDiagnosticsParams{}
821-
env.OpenFile("a/go.mod")
821+
env.OpenFile("b/go.mod")
822822
env.Await(
823-
ReadDiagnostics("a/go.mod", params),
823+
OnceMet(
824+
env.GoSumDiagnostic("b/go.mod", `example.com v1.2.3`),
825+
ReadDiagnostics("b/go.mod", params),
826+
),
824827
)
825828
for _, d := range params.Diagnostics {
826-
if d.Message != `go.sum is out of sync with go.mod. Please update it by applying the quick fix.` {
829+
if !strings.Contains(d.Message, "go.sum is out of sync") {
827830
continue
828831
}
829-
actions, err := env.Editor.GetQuickFixes(env.Ctx, "a/go.mod", nil, []protocol.Diagnostic{d})
830-
if err != nil {
831-
t.Fatal(err)
832-
}
832+
actions := env.GetQuickFixes("b/go.mod", []protocol.Diagnostic{d})
833833
if len(actions) != 2 {
834834
t.Fatalf("expected 2 code actions, got %v", len(actions))
835835
}
836-
env.ApplyQuickFixes("a/go.mod", []protocol.Diagnostic{d})
836+
env.ApplyQuickFixes("b/go.mod", []protocol.Diagnostic{d})
837837
}
838838
env.Await(
839-
EmptyDiagnostics("a/go.mod"),
839+
EmptyDiagnostics("b/go.mod"),
840840
)
841841
})
842842
}

gopls/internal/regtest/wrappers.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,16 @@ func (e *Env) ApplyQuickFixes(path string, diagnostics []protocol.Diagnostic) {
200200
}
201201
}
202202

203+
// GetQuickFixes returns the available quick fix code actions.
204+
func (e *Env) GetQuickFixes(path string, diagnostics []protocol.Diagnostic) []protocol.CodeAction {
205+
e.T.Helper()
206+
actions, err := e.Editor.GetQuickFixes(e.Ctx, path, nil, diagnostics)
207+
if err != nil {
208+
e.T.Fatal(err)
209+
}
210+
return actions
211+
}
212+
203213
// Hover in the editor, calling t.Fatal on any error.
204214
func (e *Env) Hover(name string, pos fake.Pos) (*protocol.MarkupContent, fake.Pos) {
205215
e.T.Helper()
@@ -265,6 +275,8 @@ func (e *Env) RunGoCommandInDir(dir, verb string, args ...string) {
265275
}
266276
}
267277

278+
// DumpGoSum prints the correct go.sum contents for dir in txtar format,
279+
// for use in creating regtests.
268280
func (e *Env) DumpGoSum(dir string) {
269281
e.T.Helper()
270282

0 commit comments

Comments
 (0)