internal/lsp/source: fix panic in test code lens

Change-Id: I727b2772c63752cb5d3bb4b9165f984b64adc842
Reviewed-on: https://go-review.googlesource.com/c/tools/+/239752
Run-TryBot: Rebecca Stambler <rstambler@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Robert Findley <rfindley@google.com>
diff --git a/internal/lsp/source/code_lens.go b/internal/lsp/source/code_lens.go
index 3de0647..c2a536a 100644
--- a/internal/lsp/source/code_lens.go
+++ b/internal/lsp/source/code_lens.go
@@ -47,9 +47,6 @@
 	return result, nil
 }
 
-var testMatcher = regexp.MustCompile("^Test[^a-z]")
-var benchMatcher = regexp.MustCompile("^Benchmark[^a-z]")
-
 func runTestCodeLens(ctx context.Context, snapshot Snapshot, fh FileHandle, f *ast.File, m *protocol.ColumnMapper) ([]protocol.CodeLens, error) {
 	codeLens := make([]protocol.CodeLens, 0)
 
@@ -90,40 +87,48 @@
 	return codeLens, nil
 }
 
+var testRe = regexp.MustCompile("^Test[^a-z]")
+var benchmarkRe = regexp.MustCompile("^Benchmark[^a-z]")
+
 func isTestFunc(fn *ast.FuncDecl, pkg Package) bool {
-	typesInfo := pkg.GetTypesInfo()
-	if typesInfo == nil {
+	// Make sure that the function name matches either a test or benchmark function.
+	if !(testRe.MatchString(fn.Name.Name) || benchmarkRe.MatchString(fn.Name.Name)) {
 		return false
 	}
-
-	sig, ok := typesInfo.ObjectOf(fn.Name).Type().(*types.Signature)
+	info := pkg.GetTypesInfo()
+	if info == nil {
+		return false
+	}
+	obj := info.ObjectOf(fn.Name)
+	if obj == nil {
+		return false
+	}
+	sig, ok := obj.Type().(*types.Signature)
 	if !ok {
 		return false
 	}
 
-	// test funcs should have a single parameter, so we can exit early if that's not the case.
+	// Test functions should have only one parameter.
 	if sig.Params().Len() != 1 {
 		return false
 	}
 
-	firstParam, ok := sig.Params().At(0).Type().(*types.Pointer)
+	// Check the type of the only parameter to confirm that it is *testing.T
+	// or *testing.B.
+	paramTyp, ok := sig.Params().At(0).Type().(*types.Pointer)
 	if !ok {
 		return false
 	}
-
-	firstParamElem, ok := firstParam.Elem().(*types.Named)
+	named, ok := paramTyp.Elem().(*types.Named)
 	if !ok {
 		return false
 	}
-
-	firstParamObj := firstParamElem.Obj()
-	if firstParamObj.Pkg().Path() != "testing" {
+	namedObj := named.Obj()
+	if namedObj.Pkg().Path() != "testing" {
 		return false
 	}
-
-	firstParamName := firstParamObj.Id()
-	return (firstParamName == "T" && testMatcher.MatchString(fn.Name.Name)) ||
-		(firstParamName == "B" && benchMatcher.MatchString(fn.Name.Name))
+	paramName := namedObj.Id()
+	return paramName == "T" || paramName == "B"
 }
 
 func goGenerateCodeLens(ctx context.Context, snapshot Snapshot, fh FileHandle, f *ast.File, m *protocol.ColumnMapper) ([]protocol.CodeLens, error) {
diff --git a/internal/lsp/testdata/lsp/primarymod/codelens/codelens_test.go b/internal/lsp/testdata/lsp/primarymod/codelens/codelens_test.go
index 604d237..def6a6f 100644
--- a/internal/lsp/testdata/lsp/primarymod/codelens/codelens_test.go
+++ b/internal/lsp/testdata/lsp/primarymod/codelens/codelens_test.go
@@ -2,15 +2,15 @@
 
 import "testing"
 
-// no code lens for TestMain
-func TestMain(m *testing.M) {
-}
+func TestMain(m *testing.M) {} // no code lens for TestMain
 
-func TestFuncWithCodeLens(t *testing.T) { //@ codelens("func", "run test", "test")
+func TestFuncWithCodeLens(t *testing.T) { //@codelens("func", "run test", "test")
 }
 
 func thisShouldNotHaveACodeLens(t *testing.T) {
 }
 
-func BenchmarkFuncWithCodeLens(b *testing.B) { //@ codelens("func", "run test", "test")
+func BenchmarkFuncWithCodeLens(b *testing.B) { //@codelens("func", "run test", "test")
 }
+
+func helper() {} // expect no code lens