diff --git a/go/analysis/passes/testinggoroutine/testdata/src/typeparams/typeparams.go b/go/analysis/passes/testinggoroutine/testdata/src/typeparams/typeparams.go new file mode 100644 index 00000000000..47e389fe9fe --- /dev/null +++ b/go/analysis/passes/testinggoroutine/testdata/src/typeparams/typeparams.go @@ -0,0 +1,17 @@ +// 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 f[P any](t *testing.T) { + t.Fatal("failed") +} + +func TestBadFatalf[P any](t *testing.T) { + go f[int](t) // want "call to .+T.+Fatal from a non-test goroutine" +} diff --git a/go/analysis/passes/testinggoroutine/testinggoroutine.go b/go/analysis/passes/testinggoroutine/testinggoroutine.go index ce05a56cca3..3d4bd490852 100644 --- a/go/analysis/passes/testinggoroutine/testinggoroutine.go +++ b/go/analysis/passes/testinggoroutine/testinggoroutine.go @@ -11,6 +11,7 @@ import ( "golang.org/x/tools/go/analysis/passes/inspect" "golang.org/x/tools/go/analysis/passes/internal/analysisutil" "golang.org/x/tools/go/ast/inspector" + "golang.org/x/tools/internal/typeparams" ) const Doc = `report calls to (*testing.T).Fatal from goroutines started by a test. @@ -124,16 +125,30 @@ func typeIsTestingDotTOrB(expr ast.Expr) (string, bool) { // function literals declared in the same function, and // static calls within the same package are supported. func goStmtFun(goStmt *ast.GoStmt) ast.Node { - switch goStmt.Call.Fun.(type) { - case *ast.Ident: - id := goStmt.Call.Fun.(*ast.Ident) - // TODO(cuonglm): improve this once golang/go#48141 resolved. + switch fun := goStmt.Call.Fun.(type) { + case *ast.IndexExpr, *typeparams.IndexListExpr: + ix := typeparams.GetIndexExprData(fun) + if ix == nil { + break + } + id, _ := ix.X.(*ast.Ident) + if id == nil { + break + } if id.Obj == nil { break } if funDecl, ok := id.Obj.Decl.(ast.Node); ok { return funDecl } + case *ast.Ident: + // TODO(cuonglm): improve this once golang/go#48141 resolved. + if fun.Obj == nil { + break + } + if funDecl, ok := fun.Obj.Decl.(ast.Node); ok { + return funDecl + } case *ast.FuncLit: return goStmt.Call.Fun } diff --git a/go/analysis/passes/testinggoroutine/testinggoroutine_test.go b/go/analysis/passes/testinggoroutine/testinggoroutine_test.go index 1a590263a14..56c4385c546 100644 --- a/go/analysis/passes/testinggoroutine/testinggoroutine_test.go +++ b/go/analysis/passes/testinggoroutine/testinggoroutine_test.go @@ -9,9 +9,14 @@ import ( "golang.org/x/tools/go/analysis/analysistest" "golang.org/x/tools/go/analysis/passes/testinggoroutine" + "golang.org/x/tools/internal/typeparams" ) func Test(t *testing.T) { testdata := analysistest.TestData() - analysistest.Run(t, testdata, testinggoroutine.Analyzer, "a") + pkgs := []string{"a"} + if typeparams.Enabled { + pkgs = append(pkgs, "typeparams") + } + analysistest.Run(t, testdata, testinggoroutine.Analyzer, pkgs...) }