internal/lsp/source: fix typeIsValid() inf recursion

typeIsValid() intended to stop on a named type, but
since we called Underlying(), switch case never caught any
named type. To avoid that, do an early check.

Fixes golang/go#36637

Change-Id: I2700afbb8f9678b4542e2e7dccc3be59b1d9ebdf
Reviewed-on: https://go-review.googlesource.com/c/tools/+/215238
Run-TryBot: Rebecca Stambler <rstambler@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
diff --git a/internal/lsp/source/util.go b/internal/lsp/source/util.go
index 4c9854a..51a8742 100644
--- a/internal/lsp/source/util.go
+++ b/internal/lsp/source/util.go
@@ -313,6 +313,12 @@
 
 // typeIsValid reports whether typ doesn't contain any Invalid types.
 func typeIsValid(typ types.Type) bool {
+	// Check named types separately, because we don't want
+	// to call Underlying() on them to avoid problems with recursive types.
+	if _, ok := typ.(*types.Named); ok {
+		return true
+	}
+
 	switch typ := typ.Underlying().(type) {
 	case *types.Basic:
 		return typ.Kind() != types.Invalid
@@ -335,8 +341,8 @@
 			}
 		}
 		return true
-	case *types.Struct, *types.Interface, *types.Named:
-		// Don't bother checking structs, interfaces, or named types for validity.
+	case *types.Struct, *types.Interface:
+		// Don't bother checking structs, interfaces for validity.
 		return true
 	default:
 		return false
diff --git a/internal/lsp/testdata/bad/bad1.go b/internal/lsp/testdata/bad/bad1.go
index a04cb7b..f6ad8c2 100644
--- a/internal/lsp/testdata/bad/bad1.go
+++ b/internal/lsp/testdata/bad/bad1.go
@@ -2,10 +2,13 @@
 
 package bad
 
+// See #36637
+type stateFunc func() stateFunc //@item(stateFunc, "stateFunc", "func() stateFunc", "type")
+
 var a unknown //@item(global_a, "a", "unknown", "var"),diag("unknown", "compiler", "undeclared name: unknown")
 
 func random() int { //@item(random, "random", "func() int", "func")
-	//@complete("", global_a, bob, random, random2, random3, stuff)
+	//@complete("", global_a, bob, random, random2, random3, stateFunc, stuff)
 	return 0
 }
 
@@ -13,18 +16,18 @@
 	x := 6       //@item(x, "x", "int", "var"),diag("x", "compiler", "x declared but not used")
 	var q blah   //@item(q, "q", "blah", "var"),diag("q", "compiler", "q declared but not used"),diag("blah", "compiler", "undeclared name: blah")
 	var t **blob //@item(t, "t", "**blob", "var"),diag("t", "compiler", "t declared but not used"),diag("blob", "compiler", "undeclared name: blob")
-	//@complete("", q, t, x, bad_y_param, global_a, bob, random, random2, random3, stuff)
+	//@complete("", q, t, x, bad_y_param, global_a, bob, random, random2, random3, stateFunc, stuff)
 
 	return y
 }
 
 func random3(y ...int) { //@item(random3, "random3", "func(y ...int)", "func"),item(y_variadic_param, "y", "[]int", "var")
-	//@complete("", y_variadic_param, global_a, bob, random, random2, random3, stuff)
+	//@complete("", y_variadic_param, global_a, bob, random, random2, random3, stateFunc, stuff)
 
 	var ch chan (favType1)   //@item(ch, "ch", "chan (favType1)", "var"),diag("ch", "compiler", "ch declared but not used"),diag("favType1", "compiler", "undeclared name: favType1")
 	var m map[keyType]int    //@item(m, "m", "map[keyType]int", "var"),diag("m", "compiler", "m declared but not used"),diag("keyType", "compiler", "undeclared name: keyType")
 	var arr []favType2       //@item(arr, "arr", "[]favType2", "var"),diag("arr", "compiler", "arr declared but not used"),diag("favType2", "compiler", "undeclared name: favType2")
 	var fn1 func() badResult //@item(fn1, "fn1", "func() badResult", "var"),diag("fn1", "compiler", "fn1 declared but not used"),diag("badResult", "compiler", "undeclared name: badResult")
 	var fn2 func(badParam)   //@item(fn2, "fn2", "func(badParam)", "var"),diag("fn2", "compiler", "fn2 declared but not used"),diag("badParam", "compiler", "undeclared name: badParam")
-	//@complete("", arr, ch, fn1, fn2, m, y_variadic_param, global_a, bob, random, random2, random3, stuff)
+	//@complete("", arr, ch, fn1, fn2, m, y_variadic_param, global_a, bob, random, random2, random3, stateFunc, stuff)
 }