internal/lsp/semantic: prepare for generics

Semantic tokens will now return typeParameters if the client will accept
them. If the user is not using go1.18, then generic code will not type
check and gopls will not recogize type parameters. If gopls has not
been compiled with go1.18 only some uses of type parameters will be
recognized. In any case the code uses internal/typeparams
to see through IndexListExprs.

There is a global parameter semDebug which should be false in
checked-in code. I use it to see how semantic token decisions are
made; it is for debugging. But there are a lot of small code changes
to restrict logging to a few places.

There is also an unexercised stub to fix a bug where some functions
are misclassified as variables. This will be activated in a future CL,
as it will also change the golden test files.

Change-Id: I5107e67ae25e825b0cdc4c1a744877ce97ba609b
Reviewed-on: https://go-review.googlesource.com/c/tools/+/352570
Trust: Peter Weinberger <pjw@google.com>
Run-TryBot: Peter Weinberger <pjw@google.com>
gopls-CI: kokoro <noreply+kokoro@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Robert Findley <rfindley@google.com>
diff --git a/internal/lsp/semantic.go b/internal/lsp/semantic.go
index 6a98b6c..d59ecad 100644
--- a/internal/lsp/semantic.go
+++ b/internal/lsp/semantic.go
@@ -11,6 +11,7 @@
 	"go/ast"
 	"go/token"
 	"go/types"
+	"log"
 	"path/filepath"
 	"sort"
 	"strings"
@@ -20,6 +21,7 @@
 	"golang.org/x/tools/internal/lsp/protocol"
 	"golang.org/x/tools/internal/lsp/source"
 	"golang.org/x/tools/internal/lsp/template"
+	"golang.org/x/tools/internal/typeparams"
 	errors "golang.org/x/xerrors"
 )
 
@@ -30,6 +32,10 @@
 // reject full semantic token requests for large files
 const maxFullFileSize int = 100000
 
+// to control comprehensive logging of decisions (gopls semtok foo.go > /dev/null shows log output)
+// semDebug should NEVER be true in checked-in code
+const semDebug = false
+
 func (s *Server) semanticTokensFull(ctx context.Context, p *protocol.SemanticTokensParams) (*protocol.SemanticTokens, error) {
 	ret, err := s.computeSemanticTokens(ctx, p.TextDocument, nil)
 	return ret, err
@@ -153,6 +159,7 @@
 	tokNamespace tokenType = "namespace"
 	tokType      tokenType = "type"
 	tokInterface tokenType = "interface"
+	tokTypeParam tokenType = "typeParameter"
 	tokParameter tokenType = "parameter"
 	tokVariable  tokenType = "variable"
 	tokMethod    tokenType = "method"
@@ -170,7 +177,6 @@
 
 	if !start.IsValid() {
 		// This is not worth reporting
-		//e.unexpected("token at token.NoPos")
 		return
 	}
 	if start >= e.end || start+token.Pos(leng) <= e.start {
@@ -233,7 +239,8 @@
 // convert the stack to a string, for debugging
 func (e *encoded) strStack() string {
 	msg := []string{"["}
-	for _, s := range e.stack {
+	for i := len(e.stack) - 1; i >= 0; i-- {
+		s := e.stack[i]
 		msg = append(msg, fmt.Sprintf("%T", s)[5:])
 	}
 	if len(e.stack) > 0 {
@@ -368,6 +375,7 @@
 	case *ast.IncDecStmt:
 		e.token(x.TokPos, len(x.Tok.String()), tokOperator, nil)
 	case *ast.IndexExpr:
+	case *typeparams.IndexListExpr: // accomodate generics
 	case *ast.InterfaceType:
 		e.token(x.Interface, len("interface"), tokKeyword, nil)
 	case *ast.KeyValueExpr:
@@ -419,7 +427,7 @@
 	case *ast.Comment, *ast.CommentGroup:
 		pop()
 		return false
-	default: // just to be super safe.
+	default:
 		e.unexpected(fmt.Sprintf("failed to implement %T", x))
 	}
 	return true
@@ -427,29 +435,55 @@
 
 func (e *encoded) ident(x *ast.Ident) {
 	if e.ti == nil {
-		e.unkIdent(x)
+		what, mods := e.unkIdent(x)
+		if what != "" {
+			e.token(x.Pos(), len(x.String()), what, mods)
+		}
+		if semDebug {
+			log.Printf(" nil %s/nil/nil %q %v %s", x.String(), what, mods, e.strStack())
+		}
 		return
 	}
 	def := e.ti.Defs[x]
 	if def != nil {
-		what, mods := e.definitionFor(x)
+		what, mods := e.definitionFor(x, def)
 		if what != "" {
 			e.token(x.Pos(), len(x.String()), what, mods)
 		}
+		if semDebug {
+			log.Printf(" for %s/%T/%T got %s %v (%s)", x.String(), def, def.Type(), what, mods, e.strStack())
+		}
 		return
 	}
 	use := e.ti.Uses[x]
+	tok := func(pos token.Pos, lng int, tok tokenType, mods []string) {
+		e.token(pos, lng, tok, mods)
+		q := "nil"
+		if use != nil {
+			q = fmt.Sprintf("%T", use.Type())
+		}
+		if semDebug {
+			log.Printf(" use %s/%T/%s got %s %v (%s)", x.String(), use, q, tok, mods, e.strStack())
+		}
+	}
+
 	switch y := use.(type) {
 	case nil:
-		e.unkIdent(x)
+		what, mods := e.unkIdent(x)
+		if what != "" {
+			tok(x.Pos(), len(x.String()), what, mods)
+		} else if semDebug {
+			// tok() wasn't called, so didn't log
+			log.Printf(" nil %s/%T/nil %q %v (%s)", x.String(), use, what, mods, e.strStack())
+		}
 		return
 	case *types.Builtin:
-		e.token(x.NamePos, len(x.Name), tokFunction, []string{"defaultLibrary"})
+		tok(x.NamePos, len(x.Name), tokFunction, []string{"defaultLibrary"})
 	case *types.Const:
 		mods := []string{"readonly"}
 		tt := y.Type()
 		if _, ok := tt.(*types.Basic); ok {
-			e.token(x.Pos(), len(x.String()), tokVariable, mods)
+			tok(x.Pos(), len(x.String()), tokVariable, mods)
 			break
 		}
 		if ttx, ok := tt.(*types.Named); ok {
@@ -457,7 +491,7 @@
 				e.unexpected(fmt.Sprintf("iota:%T", ttx))
 			}
 			if _, ok := ttx.Underlying().(*types.Basic); ok {
-				e.token(x.Pos(), len(x.String()), tokVariable, mods)
+				tok(x.Pos(), len(x.String()), tokVariable, mods)
 				break
 			}
 			e.unexpected(fmt.Sprintf("%q/%T", x.String(), tt))
@@ -465,22 +499,31 @@
 		// can this happen? Don't think so
 		e.unexpected(fmt.Sprintf("%s %T %#v", x.String(), tt, tt))
 	case *types.Func:
-		e.token(x.Pos(), len(x.Name), tokFunction, nil)
+		tok(x.Pos(), len(x.Name), tokFunction, nil)
 	case *types.Label:
 		// nothing to map it to
 	case *types.Nil:
 		// nil is a predeclared identifier
-		e.token(x.Pos(), len("nil"), tokVariable, []string{"readonly", "defaultLibrary"})
+		tok(x.Pos(), len("nil"), tokVariable, []string{"readonly", "defaultLibrary"})
 	case *types.PkgName:
-		e.token(x.Pos(), len(x.Name), tokNamespace, nil)
-	case *types.TypeName:
+		tok(x.Pos(), len(x.Name), tokNamespace, nil)
+	case *types.TypeName: // could be a tokTpeParam
 		var mods []string
 		if _, ok := y.Type().(*types.Basic); ok {
 			mods = []string{"defaultLibrary"}
+		} else if _, ok := y.Type().(*typeparams.TypeParam); ok {
+			tok(x.Pos(), len(x.String()), tokTypeParam, mods)
+			break
 		}
-		e.token(x.Pos(), len(x.String()), tokType, mods)
+		tok(x.Pos(), len(x.String()), tokType, mods)
 	case *types.Var:
-		e.token(x.Pos(), len(x.Name), tokVariable, nil)
+		if isSignature(y) {
+			tok(x.Pos(), len(x.Name), tokFunction, nil)
+		} else if _, ok := y.Type().(*typeparams.TypeParam); ok {
+			tok(x.Pos(), len(x.Name), tokTypeParam, nil)
+		} else {
+			tok(x.Pos(), len(x.Name), tokVariable, nil)
+		}
 	default:
 		// can't happen
 		if use == nil {
@@ -495,81 +538,96 @@
 	}
 }
 
-// both e.ti.Defs and e.ti.Uses are nil. use the parse stack
-// a lot of these only happen when the package doesn't compile
-func (e *encoded) unkIdent(x *ast.Ident) {
-	tok := func(tok tokenType, mod []string) {
-		e.token(x.Pos(), len(x.Name), tok, mod)
+func isSignature(use types.Object) bool {
+	if true {
+		return false //PJW: fix after generics seem ok
 	}
+	if _, ok := use.(*types.Var); !ok {
+		return false
+	}
+	v := use.Type()
+	if v == nil {
+		return false
+	}
+	if _, ok := v.(*types.Signature); ok {
+		return true
+	}
+	return false
+}
+
+// both e.ti.Defs and e.ti.Uses are nil. use the parse stack.
+// a lot of these only happen when the package doesn't compile
+// but in that case it is all best-effort from the parse tree
+func (e *encoded) unkIdent(x *ast.Ident) (tokenType, []string) {
 	def := []string{"definition"}
 	n := len(e.stack) - 2 // parent of Ident
 	if n < 0 {
 		e.unexpected("no stack?")
-		return
+		return "", nil
 	}
 	switch nd := e.stack[n].(type) {
 	case *ast.BinaryExpr, *ast.UnaryExpr, *ast.ParenExpr, *ast.StarExpr,
 		*ast.IncDecStmt, *ast.SliceExpr, *ast.ExprStmt, *ast.IndexExpr,
-		*ast.ReturnStmt,
+		*ast.ReturnStmt, *ast.ChanType, *ast.SendStmt,
 		*ast.ForStmt,      // possibly incomplete
 		*ast.IfStmt,       /* condition */
 		*ast.KeyValueExpr: // either key or value
-		tok(tokVariable, nil)
+		return tokVariable, nil
+	case *typeparams.IndexListExpr: // generic?
+		return tokVariable, nil
 	case *ast.Ellipsis:
-		tok(tokType, nil)
+		return tokType, nil
 	case *ast.CaseClause:
 		if n-2 >= 0 {
 			if _, ok := e.stack[n-2].(*ast.TypeSwitchStmt); ok {
-				tok(tokType, nil)
-				return
+				return tokType, nil
 			}
 		}
-		tok(tokVariable, nil)
+		return tokVariable, nil
 	case *ast.ArrayType:
 		if x == nd.Len {
-			tok(tokVariable, nil)
+			// or maybe a Type Param, but we can't just from the parse tree
+			return tokVariable, nil
 		} else {
-			tok(tokType, nil)
+			return tokType, nil
 		}
 	case *ast.MapType:
-		tok(tokType, nil)
+		return tokType, nil
 	case *ast.CallExpr:
 		if x == nd.Fun {
-			tok(tokFunction, nil)
-			return
+			return tokFunction, nil
 		}
-		tok(tokVariable, nil)
+		return tokVariable, nil
+	case *ast.SwitchStmt:
+		return tokVariable, nil
 	case *ast.TypeAssertExpr:
 		if x == nd.X {
-			tok(tokVariable, nil)
+			return tokVariable, nil
 		} else if x == nd.Type {
-			tok(tokType, nil)
+			return tokType, nil
 		}
 	case *ast.ValueSpec:
 		for _, p := range nd.Names {
 			if p == x {
-				tok(tokVariable, def)
-				return
+				return tokVariable, def
 			}
 		}
 		for _, p := range nd.Values {
 			if p == x {
-				tok(tokVariable, nil)
-				return
+				return tokVariable, nil
 			}
 		}
-		tok(tokType, nil)
+		return tokType, nil
 	case *ast.SelectorExpr: // e.ti.Selections[nd] is nil, so no help
 		if n-1 >= 0 {
 			if ce, ok := e.stack[n-1].(*ast.CallExpr); ok {
 				// ... CallExpr SelectorExpr Ident (_.x())
 				if ce.Fun == nd && nd.Sel == x {
-					tok(tokFunction, nil)
-					return
+					return tokFunction, nil
 				}
 			}
 		}
-		tok(tokVariable, nil)
+		return tokVariable, nil
 	case *ast.AssignStmt:
 		for _, p := range nd.Lhs {
 			// x := ..., or x = ...
@@ -577,51 +635,48 @@
 				if nd.Tok != token.DEFINE {
 					def = nil
 				}
-				tok(tokVariable, def)
-				return
+				return tokVariable, def
 			}
 		}
 		// RHS, = x
-		tok(tokVariable, nil)
+		return tokVariable, nil
 	case *ast.TypeSpec: // it's a type if it is either the Name or the Type
 		if x == nd.Type {
 			def = nil
 		}
-		tok(tokType, def)
+		return tokType, def
 	case *ast.Field:
 		// ident could be type in a field, or a method in an interface type, or a variable
 		if x == nd.Type {
-			tok(tokType, nil)
-			return
+			return tokType, nil
 		}
 		if n-2 >= 0 {
 			_, okit := e.stack[n-2].(*ast.InterfaceType)
 			_, okfl := e.stack[n-1].(*ast.FieldList)
 			if okit && okfl {
-				tok(tokMethod, def)
-				return
+				return tokMethod, def
 			}
 		}
-		tok(tokVariable, nil)
+		return tokVariable, nil
 	case *ast.LabeledStmt, *ast.BranchStmt:
 		// nothing to report
 	case *ast.CompositeLit:
 		if nd.Type == x {
-			tok(tokType, nil)
-			return
+			return tokType, nil
 		}
-		tok(tokVariable, nil)
+		return tokVariable, nil
 	case *ast.RangeStmt:
 		if nd.Tok != token.DEFINE {
 			def = nil
 		}
-		tok(tokVariable, def)
+		return tokVariable, def
 	case *ast.FuncDecl:
-		tok(tokFunction, def)
+		return tokFunction, def
 	default:
 		msg := fmt.Sprintf("%T undexpected: %s %s%q", nd, x.Name, e.strStack(), e.srcLine(x))
 		e.unexpected(msg)
 	}
+	return "", nil
 }
 
 func isDeprecated(n *ast.CommentGroup) bool {
@@ -636,7 +691,9 @@
 	return false
 }
 
-func (e *encoded) definitionFor(x *ast.Ident) (tokenType, []string) {
+func (e *encoded) definitionFor(x *ast.Ident, def types.Object) (tokenType, []string) {
+	// PJW: def == types.Label? probably a nothing
+	// PJW: look into replaceing these syntactic tests with types more generally
 	mods := []string{"definition"}
 	for i := len(e.stack) - 1; i >= 0; i-- {
 		s := e.stack[i]
@@ -682,6 +739,10 @@
 			// and in GenDecl/TpeSpec/StructType/FieldList/Field/Ident
 			// (type A struct{b uint64}
 			// but on type B struct{C}), C is a type, but is not being defined.
+			// GenDecl/TypeSpec/FieldList/Field/Ident is a typeParam
+			if _, ok := e.stack[i+1].(*ast.FieldList); ok {
+				return tokTypeParam, mods
+			}
 			fldm := e.stack[len(e.stack)-2]
 			if fld, ok := fldm.(*ast.Field); ok {
 				// if len(fld.names) == 0 this is a tokType, being used
@@ -732,7 +793,7 @@
 	if idx != -1 {
 		return start + token.Pos(idx)
 	}
-	// can't happen
+	//(in unparsable programs: type _ <-<-chan int)
 	e.unexpected(fmt.Sprintf("not found:%s %v", keyword, e.fset.PositionFor(start, false)))
 	return token.NoPos
 }
@@ -829,6 +890,9 @@
 
 // log unexpected state
 func (e *encoded) unexpected(msg string) {
+	if semDebug {
+		panic(msg)
+	}
 	event.Error(e.ctx, e.strStack(), errors.New(msg))
 }