From 728485ffcbd3e81e24539b97097e4d87479102d9 Mon Sep 17 00:00:00 2001 From: Robert Findley Date: Tue, 12 Apr 2022 10:53:04 -0400 Subject: [PATCH] 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 Run-TryBot: Robert Findley gopls-CI: kokoro Reviewed-by: Dylan Le --- .../internal/regtest/misc/staticcheck_test.go | 78 +++++++++++++++++++ internal/lsp/regtest/expectation.go | 17 ++++ 2 files changed, 95 insertions(+) create mode 100644 gopls/internal/regtest/misc/staticcheck_test.go diff --git a/gopls/internal/regtest/misc/staticcheck_test.go b/gopls/internal/regtest/misc/staticcheck_test.go new file mode 100644 index 0000000000..94bb39903a --- /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 35402352c3..15de33f349 100644 --- a/internal/lsp/regtest/expectation.go +++ b/internal/lsp/regtest/expectation.go @@ -465,6 +465,9 @@ type DiagnosticExpectation struct { // 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 @@ func (e DiagnosticExpectation) Check(s State) Verdict { continue } } + if e.source != "" && e.source != d.Source { + continue + } found = true break } @@ -515,6 +521,9 @@ func (e DiagnosticExpectation) Description() string { 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 @@ func (e *Env) DiagnosticAtRegexpWithMessage(name, re, msg string) DiagnosticExpe 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 {