Skip to content

Commit 90e9c65

Browse files
committed
gopls/internal/lsp/cache: skip type errors after parse errors
Parser error recovery can delete large swathes of source code; see golang/go#58833 for examples. Type checking syntax trees containing syntax errors may therefore result in a large number of spurious type errors. So, this change suppressed type errors in the presence of syntax errors. Fiddling with these tests is really surprisingly time consuming. Fixes golang/go#59888 Change-Id: Ib489ecf46652c5a346d9caad89fd059434c620f8 Reviewed-on: https://go-review.googlesource.com/c/tools/+/493616 TryBot-Result: Gopher Robot <gobot@golang.org> gopls-CI: kokoro <noreply+kokoro@google.com> Reviewed-by: Robert Findley <rfindley@google.com> Run-TryBot: Alan Donovan <adonovan@google.com>
1 parent 08b24db commit 90e9c65

File tree

12 files changed

+68
-66
lines changed

12 files changed

+68
-66
lines changed

gopls/internal/lsp/cache/analysis.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -605,7 +605,6 @@ func actuallyAnalyze(ctx context.Context, analyzers []*analysis.Analyzer, m *sou
605605

606606
// TODO(adonovan): port the old logic to:
607607
// - gather go/packages diagnostics from m.Errors? (port goPackagesErrorDiagnostics)
608-
// - record unparseable file URIs so we can suppress type errors for these files.
609608
// - gather diagnostics from expandErrors + typeErrorDiagnostics + depsErrors.
610609

611610
// -- analysis --
@@ -762,7 +761,16 @@ func typeCheckForAnalysis(fset *token.FileSet, parsed []*source.ParsedGoFile, m
762761
Sizes: m.TypesSizes,
763762
Error: func(e error) {
764763
pkg.compiles = false // type error
765-
pkg.typeErrors = append(pkg.typeErrors, e.(types.Error))
764+
765+
// Suppress type errors in files with parse errors
766+
// as parser recovery can be quite lossy (#59888).
767+
typeError := e.(types.Error)
768+
for _, p := range parsed {
769+
if p.ParseErr != nil && source.NodeContains(p.File, typeError.Pos) {
770+
return
771+
}
772+
}
773+
pkg.typeErrors = append(pkg.typeErrors, typeError)
766774
},
767775
Importer: importerFunc(func(importPath string) (*types.Package, error) {
768776
if importPath == "unsafe" {

gopls/internal/lsp/cache/load.go

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import (
99
"context"
1010
"errors"
1111
"fmt"
12-
"log"
1312
"path/filepath"
1413
"sort"
1514
"strings"
@@ -487,15 +486,6 @@ func buildMetadata(updates map[PackageID]*source.Metadata, pkg *packages.Package
487486
DepsErrors: packagesinternal.GetDepsErrors(pkg),
488487
}
489488

490-
if strings.Contains(string(m.PkgPath), "var/folders") {
491-
// On macOS, in marker tests, without a go.mod file,
492-
// this statement is reached. ID, Name, and PkgPath
493-
// take on values that match the LoadDir, such as:
494-
// "/var/folders/fy/dn6v01n16zjdwsqy_8qfbbxr000_9w/T/TestMarkersdiagnosticsissue56943.txt2080018120/001/work".
495-
// TODO(adonovan): find out why.
496-
log.Printf("strange package path: m=%+v pkg=%+v", *m, *pkg)
497-
}
498-
499489
updates[id] = m
500490

501491
for _, filename := range pkg.CompiledGoFiles {

gopls/internal/lsp/regtest/marker.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ var update = flag.Bool("update", false, "if set, update test data during marker
133133
// - diag(location, regexp): specifies an expected diagnostic matching the
134134
// given regexp at the given location. The test runner requires
135135
// a 1:1 correspondence between observed diagnostics and diag annotations.
136+
// The diagnostics source and kind fields are ignored, to reduce fuss.
136137
//
137138
// The marker must accurately represent the diagnostic's range.
138139
// Use grouping parens in the location regular expression to indicate

gopls/internal/lsp/source/view.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -530,6 +530,9 @@ type TidiedModule struct {
530530

531531
// Metadata represents package metadata retrieved from go/packages.
532532
// The Deps* maps do not contain self-import edges.
533+
//
534+
// An ad-hoc package (without go.mod or GOPATH) has its ID, PkgPath,
535+
// and LoadDir equal to the absolute path of its directory.
533536
type Metadata struct {
534537
ID PackageID
535538
PkgPath PackagePath

gopls/internal/lsp/testdata/badstmt/badstmt.go.in

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,12 @@ import (
44
"golang.org/lsptests/foo"
55
)
66

7-
// The nonewvars expectation asserts that the go/analysis framework ran.
8-
// See comments in noparse.
7+
// (The syntax error causes suppression of diagnostics for type errors.
8+
// See issue #59888.)
99

1010
func _(x int) {
1111
defer foo.F //@complete(" //", Foo),diag(" //", "syntax", "function must be invoked in defer statement|expression in defer must be function call", "error")
1212
defer foo.F //@complete(" //", Foo)
13-
x := 123 //@diag(":=", "nonewvars", "no new variables", "warning")
1413
}
1514

1615
func _() {

gopls/internal/lsp/testdata/summary.txt.golden

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ DeepCompletionsCount = 5
88
FuzzyCompletionsCount = 8
99
RankedCompletionsCount = 164
1010
CaseSensitiveCompletionsCount = 4
11-
DiagnosticsCount = 24
11+
DiagnosticsCount = 23
1212
FoldingRangesCount = 2
1313
SemanticTokenCount = 3
1414
SuggestedFixCount = 73

gopls/internal/lsp/testdata/summary_go1.18.txt.golden

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ DeepCompletionsCount = 5
88
FuzzyCompletionsCount = 8
99
RankedCompletionsCount = 174
1010
CaseSensitiveCompletionsCount = 4
11-
DiagnosticsCount = 24
11+
DiagnosticsCount = 23
1212
FoldingRangesCount = 2
1313
SemanticTokenCount = 3
1414
SuggestedFixCount = 79

gopls/internal/lsp/testdata/summary_go1.21.txt.golden

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ DeepCompletionsCount = 5
88
FuzzyCompletionsCount = 8
99
RankedCompletionsCount = 174
1010
CaseSensitiveCompletionsCount = 4
11-
DiagnosticsCount = 25
11+
DiagnosticsCount = 24
1212
FoldingRangesCount = 2
1313
SemanticTokenCount = 3
1414
SuggestedFixCount = 79

gopls/internal/regtest/marker/testdata/diagnostics/noparse.txt

Lines changed: 0 additions & 33 deletions
This file was deleted.
Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,27 @@
11

2-
This test verifies that only parse errors are created.
3-
we produce diagnostics related to mismatching
4-
unexported interface methods in non-workspace packages.
2+
This test exercises diagnostics produced for syntax errors.
53

6-
The type error was chosen to exercise the 'nonewvars' type-error analyzer.
7-
(The 'undeclaredname' analyzer depends on the text of the go/types
8-
"undeclared name" error, which changed in go1.20.)
4+
Because parser error recovery can be quite lossy, diagnostics
5+
for type errors are suppressed in files with syntax errors;
6+
see issue #59888. But diagnostics are reported for type errors
7+
in well-formed files of the same package.
98

109
-- go.mod --
1110
module example.com
1211
go 1.12
1312

14-
-- a.go --
15-
package a
13+
-- bad.go --
14+
package p
1615

17-
func bye(x int) {
18-
x := 123 //@diag(re"():=", re"no new variables")
19-
}
20-
21-
func stuff() {
22-
16+
func f() {
17+
append("") // no diagnostic for type error in file containing syntax error
2318
}
2419

2520
func .() {} //@diag(re"func ().", re"expected 'IDENT', found '.'")
2621

22+
-- good.go --
23+
package p
24+
25+
func g() {
26+
append("") //@diag(re`""`, re"a slice")
27+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
2+
This test exercises diagnostics produced for type errors
3+
in the absence of syntax errors.
4+
5+
The type error was chosen to exercise the 'nonewvars' type-error analyzer.
6+
(The 'undeclaredname' analyzer depends on the text of the go/types
7+
"undeclared name" error, which changed in go1.20.)
8+
9+
The append() type error was also carefully chosen to have text and
10+
position that are invariant across all versions of Go run by the builders.
11+
12+
-- go.mod --
13+
module example.com
14+
go 1.12
15+
16+
-- typeerr.go --
17+
package a
18+
19+
func f(x int) {
20+
append("") //@diag(re`""`, re"a slice")
21+
22+
x := 123 //@diag(re"x := 123", re"no new variables"), suggestedfix(re"():", re"no new variables", "quickfix", fix)
23+
}
24+
25+
-- @fix/typeerr.go --
26+
package a
27+
28+
func f(x int) {
29+
append("") //@diag(re`""`, re"a slice")
30+
31+
x = 123 //@diag(re"x := 123", re"no new variables"), suggestedfix(re"():", re"no new variables", "quickfix", fix)
32+
}
33+

gopls/internal/regtest/marker/testdata/format/noparse.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ func what() {
2121
var hi func()
2222
if { hi() //@diag(re"(){", re".*missing.*")
2323
}
24-
hi := nil //@diag(re"():=", re"no new variables")
24+
hi := nil
2525
}
2626
-- @noparse --
2727
7:5: missing condition in if statement

0 commit comments

Comments
 (0)