internal/lsp: rank types higher when completing in return of a function

Completion now ranks type names higher for func receiver, type params and type results (e.g. func (<>) foo(<>) (<>) {}).

Fixes golang/go#29152

Change-Id: Icdd18b1b344c1cd617a4f45a7b071e53c1345478
GitHub-Last-Rev: e6acb1f2d2a7e571ffcecc500e407fdefd118fed
GitHub-Pull-Request: golang/tools#73
Reviewed-on: https://go-review.googlesource.com/c/159797
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
diff --git a/internal/lsp/lsp_test.go b/internal/lsp/lsp_test.go
index 4a7ecc5..c1078d3 100644
--- a/internal/lsp/lsp_test.go
+++ b/internal/lsp/lsp_test.go
@@ -35,7 +35,7 @@
 
 	// We hardcode the expected number of test cases to ensure that all tests
 	// are being executed. If a test is added, this number must be changed.
-	const expectedCompletionsCount = 60
+	const expectedCompletionsCount = 63
 	const expectedDiagnosticsCount = 13
 	const expectedFormatCount = 3
 	const expectedDefinitionsCount = 16
diff --git a/internal/lsp/source/completion.go b/internal/lsp/source/completion.go
index 21cb4b5..a748a17 100644
--- a/internal/lsp/source/completion.go
+++ b/internal/lsp/source/completion.go
@@ -86,20 +86,24 @@
 	typ := expectedType(path, pos, pkg.TypesInfo)
 	sig := enclosingFunction(path, pos, pkg.TypesInfo)
 	pkgStringer := qualifier(file, pkg.Types, pkg.TypesInfo)
+	preferTypeNames := wantTypeNames(pos, path)
 
 	seen := make(map[types.Object]bool)
-
 	// found adds a candidate completion.
 	// Only the first candidate of a given name is considered.
 	found := func(obj types.Object, weight float64, items []CompletionItem) []CompletionItem {
 		if obj.Pkg() != nil && obj.Pkg() != pkg.Types && !obj.Exported() {
 			return items // inaccessible
 		}
+
 		if !seen[obj] {
 			seen[obj] = true
 			if typ != nil && matchingTypes(typ, obj.Type()) {
 				weight *= 10.0
 			}
+			if _, ok := obj.(*types.TypeName); !ok && preferTypeNames {
+				weight *= 0.01
+			}
 			item := formatCompletion(obj, pkgStringer, weight, func(v *types.Var) bool {
 				return isParameter(sig, v)
 			})
@@ -202,6 +206,34 @@
 	return items, nil
 }
 
+// wantTypeNames checks if given token position is inside func receiver, type params
+// or type results (e.g func (<>) foo(<>) (<>) {} ).
+func wantTypeNames(pos token.Pos, path []ast.Node) bool {
+	for _, p := range path {
+		switch n := p.(type) {
+		case *ast.FuncDecl:
+			recv := n.Recv
+			if recv != nil && recv.Pos() <= pos && pos <= recv.End() {
+				return true
+			}
+
+			if n.Type != nil {
+				params := n.Type.Params
+				if params != nil && params.Pos() <= pos && pos <= params.End() {
+					return true
+				}
+
+				results := n.Type.Results
+				if results != nil && results.Pos() <= pos && pos <= results.End() {
+					return true
+				}
+			}
+			return false
+		}
+	}
+	return false
+}
+
 // lexical finds completions in the lexical environment.
 func lexical(path []ast.Node, pos token.Pos, pkg *types.Package, info *types.Info, found finder) (items []CompletionItem) {
 	var scopes []*types.Scope // scopes[i], where i<len(path), is the possibly nil Scope of path[i].
diff --git a/internal/lsp/testdata/func_rank/func_rank.go.in b/internal/lsp/testdata/func_rank/func_rank.go.in
new file mode 100644
index 0000000..cb5a1b4
--- /dev/null
+++ b/internal/lsp/testdata/func_rank/func_rank.go.in
@@ -0,0 +1,12 @@
+package func_rank
+
+var stringAVar = "var" //@item(stringAVar, "stringAVar", "string", "var")
+func stringBFunc() string { return "str" } //@item(stringBFunc, "stringBFunc()", "string", "func")
+type stringer struct{}   //@item(stringer, "stringer", "struct{...}", "struct")
+
+func _() stringer //@complete("tr", stringer, stringAVar, stringBFunc)
+
+func _(val stringer) {} //@complete("tr", stringer, stringAVar, stringBFunc)
+
+func (stringer) _() {} //@complete("tr", stringer, stringAVar, stringBFunc)
+