go/analysis/passes/tests: update for generics

Warn about Test functions that contain type parameters.

For golang/go#48704

Change-Id: I3f6852613482ec6f33e5650a014564915f11b920
Reviewed-on: https://go-review.googlesource.com/c/tools/+/359494
Run-TryBot: Tim King <taking@google.com>
gopls-CI: kokoro <noreply+kokoro@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Tim King <taking@google.com>
Reviewed-by: Michael Matloob <matloob@golang.org>
Reviewed-by: Robert Findley <rfindley@google.com>
This commit is contained in:
Tim King 2021-10-28 10:20:43 -07:00
parent 4d2571be91
commit a2be0cdcd9
4 changed files with 48 additions and 3 deletions

View File

@ -0,0 +1,10 @@
// Copyright 2021 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 typeparams
func Zero[T any]() T {
var zero T
return zero
}

View File

@ -0,0 +1,21 @@
// Copyright 2021 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 typeparams
import "testing"
func Test(*testing.T) {
_ = Zero[int]() // It is fine to use generics within tests.
}
// Note: We format {Test,Benchmark}typeParam with a 't' in "type" to avoid an error from
// cmd/go/internal/load. That package can also give an error about Test and Benchmark
// functions with TypeParameters. These tests may need to be updated if that logic changes.
func TesttypeParam[T any](*testing.T) {} // want "TesttypeParam has type parameters: it will not be run by go test as a TestXXX function" "TesttypeParam has malformed name"
func BenchmarktypeParam[T any](*testing.B) {} // want "BenchmarktypeParam has type parameters: it will not be run by go test as a BenchmarkXXX function" "BenchmarktypeParam has malformed name"
func ExampleZero[T any]() { // want "ExampleZero should not have type params"
print(Zero[T]())
}

View File

@ -16,6 +16,7 @@ import (
"unicode/utf8"
"golang.org/x/tools/go/analysis"
"golang.org/x/tools/internal/typeparams"
)
const Doc = `check for common mistaken usages of tests and examples
@ -170,6 +171,9 @@ func checkExampleName(pass *analysis.Pass, fn *ast.FuncDecl) {
if results := fn.Type.Results; results != nil && len(results.List) != 0 {
pass.Reportf(fn.Pos(), "%s should return nothing", fnName)
}
if tparams := typeparams.ForFuncType(fn.Type); tparams != nil && len(tparams.List) > 0 {
pass.Reportf(fn.Pos(), "%s should not have type params", fnName)
}
if fnName == "Example" {
// Nothing more to do.
@ -236,6 +240,12 @@ func checkTest(pass *analysis.Pass, fn *ast.FuncDecl, prefix string) {
return
}
if tparams := typeparams.ForFuncType(fn.Type); tparams != nil && len(tparams.List) > 0 {
// Note: cmd/go/internal/load also errors about TestXXX and BenchmarkXXX functions with type parameters.
// We have currently decided to also warn before compilation/package loading. This can help users in IDEs.
pass.Reportf(fn.Pos(), "%s has type parameters: it will not be run by go test as a %sXXX function", fn.Name.Name, prefix)
}
if !isTestSuffix(fn.Name.Name[len(prefix):]) {
pass.Reportf(fn.Pos(), "%s has malformed name: first letter after '%s' must not be lowercase", fn.Name.Name, prefix)
}

View File

@ -9,14 +9,18 @@ import (
"golang.org/x/tools/go/analysis/analysistest"
"golang.org/x/tools/go/analysis/passes/tests"
"golang.org/x/tools/internal/typeparams"
)
func Test(t *testing.T) {
testdata := analysistest.TestData()
analysistest.Run(t, testdata, tests.Analyzer,
pkgs := []string{
"a", // loads "a", "a [a.test]", and "a.test"
"b_x_test", // loads "b" and "b_x_test"
"divergent",
)
}
if typeparams.Enabled {
pkgs = append(pkgs, "typeparams")
}
analysistest.Run(t, testdata, tests.Analyzer, pkgs...)
}