gopls/internal/regtest: add a test for using staticcheck with generics
For golang/go#52159
Change-Id: I08120331b7f5c9eb06feac0d0eeb76a9a7b629df
Reviewed-on: https://go-review.googlesource.com/c/tools/+/399914
TryBot-Result: Gopher Robot <gobot@golang.org>
Run-TryBot: Robert Findley <rfindley@google.com>
gopls-CI: kokoro <noreply+kokoro@google.com>
Reviewed-by: Dylan Le <dungtuanle@google.com>
diff --git a/gopls/internal/regtest/misc/staticcheck_test.go b/gopls/internal/regtest/misc/staticcheck_test.go
new file mode 100644
index 0000000..94bb399
--- /dev/null
+++ b/gopls/internal/regtest/misc/staticcheck_test.go
@@ -0,0 +1,78 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package misc
+
+import (
+ "testing"
+
+ "golang.org/x/tools/internal/testenv"
+
+ . "golang.org/x/tools/internal/lsp/regtest"
+)
+
+func TestStaticcheckGenerics(t *testing.T) {
+ testenv.NeedsGo1Point(t, 18) // generics were introduced in Go 1.18
+
+ const files = `
+-- go.mod --
+module mod.com
+
+go 1.18
+-- a/a.go --
+package a
+
+import (
+ "errors"
+ "sort"
+ "strings"
+)
+
+func Zero[P any]() P {
+ var p P
+ return p
+}
+
+type Inst[P any] struct {
+ Field P
+}
+
+func testGenerics[P *T, T any](p P) {
+ // Calls to instantiated functions should not break checks.
+ slice := Zero[string]()
+ sort.Slice(slice, func(i, j int) bool {
+ return slice[i] < slice[j]
+ })
+
+ // Usage of instantiated fields should not break checks.
+ g := Inst[string]{"hello"}
+ g.Field = strings.TrimLeft(g.Field, "12234")
+
+ // Use of type parameters should not break checks.
+ var q P
+ p = q // SA4009: p is overwritten before its first use
+ q = &*p // SA4001: &* will be simplified
+}
+
+
+// FooErr should be called ErrFoo (ST1012)
+var FooErr error = errors.New("foo")
+`
+
+ WithOptions(EditorConfig{
+ Settings: map[string]interface{}{
+ "staticcheck": true,
+ },
+ }).Run(t, files, func(t *testing.T, env *Env) {
+ env.OpenFile("a/a.go")
+ env.Await(
+ env.DiagnosticAtRegexpFromSource("a/a.go", "sort.Slice", "sortslice"),
+ env.DiagnosticAtRegexpFromSource("a/a.go", "sort.Slice.(slice)", "SA1028"),
+ env.DiagnosticAtRegexpFromSource("a/a.go", "var (FooErr)", "ST1012"),
+ env.DiagnosticAtRegexpFromSource("a/a.go", `"12234"`, "SA1024"),
+ env.DiagnosticAtRegexpFromSource("a/a.go", "testGenerics.*(p P)", "SA4009"),
+ env.DiagnosticAtRegexpFromSource("a/a.go", "q = (&\\*p)", "SA4001"),
+ )
+ })
+}
diff --git a/internal/lsp/regtest/expectation.go b/internal/lsp/regtest/expectation.go
index 3540235..15de33f 100644
--- a/internal/lsp/regtest/expectation.go
+++ b/internal/lsp/regtest/expectation.go
@@ -465,6 +465,9 @@
// path is the scratch workdir-relative path to the file being asserted on.
path string
+
+ // optionally, the diagnostic source
+ source string
}
// Check implements the Expectation interface.
@@ -489,6 +492,9 @@
continue
}
}
+ if e.source != "" && e.source != d.Source {
+ continue
+ }
found = true
break
}
@@ -515,6 +521,9 @@
if e.message != "" {
desc += fmt.Sprintf(" with message %q", e.message)
}
+ if e.source != "" {
+ desc += fmt.Sprintf(" from source %q", e.source)
+ }
return desc
}
@@ -619,6 +628,14 @@
return DiagnosticExpectation{path: name, pos: &pos, re: re, present: true, message: msg}
}
+// DiagnosticAtRegexpFromSource expects a diagnostic at the first position
+// matching re, from the given source.
+func (e *Env) DiagnosticAtRegexpFromSource(name, re, source string) DiagnosticExpectation {
+ e.T.Helper()
+ pos := e.RegexpSearch(name, re)
+ return DiagnosticExpectation{path: name, pos: &pos, re: re, present: true, source: source}
+}
+
// DiagnosticAt asserts that there is a diagnostic entry at the position
// specified by line and col, for the workdir-relative path name.
func DiagnosticAt(name string, line, col int) DiagnosticExpectation {