internal/lsp/source: refactor completion

This change separates the completion formatting functions from the
completion logic. It also simplifies the completion logic by necessary
values per-request into a struct that is used throughout.

Change-Id: Ieb6b09b7076ecf89c8b76ec12c1f1c9b10618cfe
Reviewed-on: https://go-review.googlesource.com/c/tools/+/173779
Run-TryBot: Rebecca Stambler <rstambler@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Cottrell <iancottrell@google.com>
diff --git a/internal/lsp/lsp_test.go b/internal/lsp/lsp_test.go
index 2f6d8f1..84b4e6e 100644
--- a/internal/lsp/lsp_test.go
+++ b/internal/lsp/lsp_test.go
@@ -165,7 +165,6 @@
 			t.Errorf("%s: %s", src, diff)
 		}
 	}
-
 	// Make sure we don't crash completing the first position in file set.
 	firstFile := r.data.Config.Fset.Position(1).Filename
 
diff --git a/internal/lsp/source/completion.go b/internal/lsp/source/completion.go
index d9879f5..52b5944 100644
--- a/internal/lsp/source/completion.go
+++ b/internal/lsp/source/completion.go
@@ -1,13 +1,15 @@
+// Copyright 2018 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 source
 
 import (
-	"bytes"
 	"context"
 	"fmt"
 	"go/ast"
 	"go/token"
 	"go/types"
-	"strings"
 
 	"golang.org/x/tools/go/ast/astutil"
 )
@@ -34,19 +36,79 @@
 	PackageCompletionItem
 )
 
-// stdScore is the base score value set for all completion items.
-const stdScore float64 = 1.0
+// Scoring constants are used for weighting the relevance of different candidates.
+const (
+	// stdScore is the base score for all completion items.
+	stdScore float64 = 1.0
 
-// finder is a function used to record a completion candidate item in a list of
-// completion items.
-type finder func(types.Object, float64, []CompletionItem) []CompletionItem
+	// highScore indicates a very relevant completion item.
+	highScore float64 = 10.0
+
+	// lowScore indicates an irrelevant or not useful completion item.
+	lowScore float64 = 0.01
+)
+
+// completer contains the necessary information for a single completion request.
+type completer struct {
+	// Package-specific fields.
+	types *types.Package
+	info  *types.Info
+	qf    types.Qualifier
+
+	// pos is the position at which the request was triggered.
+	pos token.Pos
+
+	// path is the path of AST nodes enclosing the position.
+	path []ast.Node
+
+	// seen is the map that ensures we do not return duplicate results.
+	seen map[types.Object]bool
+
+	// items is the list of completion items returned.
+	items []CompletionItem
+
+	// prefix is the already-typed portion of the completion candidates.
+	prefix string
+
+	// expectedType is the type we expect the completion candidate to be.
+	// It may not be set.
+	expectedType types.Type
+
+	// enclosingFunction is the function declaration enclosing the position.
+	enclosingFunction *types.Signature
+
+	// preferTypeNames is true if we are completing at a position that expects a type,
+	// not a value.
+	preferTypeNames bool
+}
+
+// found adds a candidate completion.
+//
+// Only the first candidate of a given name is considered.
+func (c *completer) found(obj types.Object, weight float64) {
+	if obj.Pkg() != nil && obj.Pkg() != c.types && !obj.Exported() {
+		return // inaccessible
+	}
+	if c.seen[obj] {
+		return
+	}
+	c.seen[obj] = true
+	if c.matchingType(obj.Type()) {
+		weight *= highScore
+	}
+	if _, ok := obj.(*types.TypeName); !ok && c.preferTypeNames {
+		weight *= lowScore
+	}
+	c.items = append(c.items, c.item(obj, weight))
+}
 
 // Completion returns a list of possible candidates for completion, given a
-// a file and a position. The prefix is computed based on the preceding
-// identifier and can be used by the client to score the quality of the
-// completion. For instance, some clients may tolerate imperfect matches as
-// valid completion results, since users may make typos.
-func Completion(ctx context.Context, f File, pos token.Pos) (items []CompletionItem, prefix string, err error) {
+// a file and a position.
+//
+// The prefix is computed based on the preceding identifier and can be used by
+// the client to score the quality of the completion. For instance, some clients
+// may tolerate imperfect matches as valid completion results, since users may make typos.
+func Completion(ctx context.Context, f File, pos token.Pos) ([]CompletionItem, string, error) {
 	file := f.GetAST(ctx)
 	pkg := f.GetPackage(ctx)
 	if pkg.IsIllTyped() {
@@ -54,68 +116,58 @@
 	}
 
 	// Completion is based on what precedes the cursor.
-	// To understand what we are completing, find the path to the
-	// position before pos.
+	// Find the path to the position before pos.
 	path, _ := astutil.PathEnclosingInterval(file, pos-1, pos-1)
 	if path == nil {
 		return nil, "", fmt.Errorf("cannot find node enclosing position")
 	}
-
 	// Skip completion inside comments.
-	if inComment(pos, file.Comments) {
-		return items, prefix, nil
+	for _, g := range file.Comments {
+		if g.Pos() <= pos && pos <= g.End() {
+			return nil, "", nil
+		}
 	}
-
 	// Skip completion inside any kind of literal.
 	if _, ok := path[0].(*ast.BasicLit); ok {
-		return items, prefix, nil
+		return nil, "", nil
 	}
 
-	// Save certain facts about the query position, including the expected type
-	// of the completion result, the signature of the function enclosing the
-	// position.
-	typ := expectedType(path, pos, pkg.GetTypesInfo())
-	sig := enclosingFunction(path, pos, pkg.GetTypesInfo())
-	pkgStringer := qualifier(file, pkg.GetTypes(), pkg.GetTypesInfo())
-	preferTypeNames := wantTypeNames(pos, path)
+	c := &completer{
+		types:             pkg.GetTypes(),
+		info:              pkg.GetTypesInfo(),
+		qf:                qualifier(file, pkg.GetTypes(), pkg.GetTypesInfo()),
+		path:              path,
+		pos:               pos,
+		seen:              make(map[types.Object]bool),
+		expectedType:      expectedType(path, pos, pkg.GetTypesInfo()),
+		enclosingFunction: enclosingFunction(path, pos, pkg.GetTypesInfo()),
+		preferTypeNames:   preferTypeNames(path, pos),
+	}
 
-	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.GetTypes() && !obj.Exported() {
-			return items // inaccessible
-		}
+	// Composite literals are handled entirely separately.
+	if lit, kv, ok := c.enclosingCompositeLiteral(); lit != nil {
+		c.expectedType = c.expectedCompositeLiteralType(lit, kv)
 
-		if !seen[obj] {
-			seen[obj] = true
-			if typ != nil && matchingTypes(typ, obj.Type()) {
-				weight *= 10.0
+		// ok means that we should return composite literal completions for this position.
+		if ok {
+			if err := c.compositeLiteral(lit, kv); err != nil {
+				return nil, "", err
 			}
-			if _, ok := obj.(*types.TypeName); !ok && preferTypeNames {
-				weight *= 0.01
-			}
-			item := formatCompletion(obj, pkgStringer, weight, func(v *types.Var) bool {
-				return isParameter(sig, v)
-			})
-			items = append(items, item)
+			return c.items, c.prefix, nil
 		}
-		return items
 	}
 
-	// The position is within a composite literal.
-	if items, prefix, ok := complit(path, pos, pkg.GetTypes(), pkg.GetTypesInfo(), found); ok {
-		return items, prefix, nil
-	}
 	switch n := path[0].(type) {
 	case *ast.Ident:
 		// Set the filter prefix.
-		prefix = n.Name[:pos-n.Pos()]
+		c.prefix = n.Name[:pos-n.Pos()]
 
 		// Is this the Sel part of a selector?
 		if sel, ok := path[1].(*ast.SelectorExpr); ok && sel.Sel == n {
-			items, err = selector(sel, pos, pkg.GetTypesInfo(), found)
-			return items, prefix, err
+			if err := c.selector(sel); err != nil {
+				return nil, "", err
+			}
+			return c.items, c.prefix, nil
 		}
 		// reject defining identifiers
 		if obj, ok := pkg.GetTypesInfo().Defs[n]; ok {
@@ -130,114 +182,86 @@
 				return nil, "", fmt.Errorf("this is a definition%s", of)
 			}
 		}
-
-		items = append(items, lexical(path, pos, pkg.GetTypes(), pkg.GetTypesInfo(), found)...)
+		if err := c.lexical(); err != nil {
+			return nil, "", err
+		}
 
 	// The function name hasn't been typed yet, but the parens are there:
 	//   recv.‸(arg)
 	case *ast.TypeAssertExpr:
 		// Create a fake selector expression.
-		items, err = selector(&ast.SelectorExpr{X: n.X}, pos, pkg.GetTypesInfo(), found)
-		return items, prefix, err
+		if err := c.selector(&ast.SelectorExpr{X: n.X}); err != nil {
+			return nil, "", err
+		}
 
 	case *ast.SelectorExpr:
-		items, err = selector(n, pos, pkg.GetTypesInfo(), found)
-		return items, prefix, err
+		if err := c.selector(n); err != nil {
+			return nil, "", err
+		}
 
 	default:
 		// fallback to lexical completions
-		return lexical(path, pos, pkg.GetTypes(), pkg.GetTypesInfo(), found), "", nil
+		if err := c.lexical(); err != nil {
+			return nil, "", err
+		}
 	}
-	return items, prefix, nil
+	return c.items, c.prefix, nil
 }
 
-// selector finds completions for
-// the specified selector expression.
-// TODO(rstambler): Set the prefix filter correctly for selectors.
-func selector(sel *ast.SelectorExpr, pos token.Pos, info *types.Info, found finder) (items []CompletionItem, err error) {
+// selector finds completions for the specified selector expression.
+func (c *completer) selector(sel *ast.SelectorExpr) error {
 	// Is sel a qualified identifier?
 	if id, ok := sel.X.(*ast.Ident); ok {
-		if pkgname, ok := info.Uses[id].(*types.PkgName); ok {
+		if pkgname, ok := c.info.Uses[id].(*types.PkgName); ok {
 			// Enumerate package members.
-			// TODO(adonovan): can Imported() be nil?
 			scope := pkgname.Imported().Scope()
-			// TODO testcase: bad import
 			for _, name := range scope.Names() {
-				items = found(scope.Lookup(name), stdScore, items)
+				c.found(scope.Lookup(name), stdScore)
 			}
-			return items, nil
+			return nil
 		}
 	}
 
-	// Inv: sel is a true selector.
-	tv, ok := info.Types[sel.X]
+	// Invariant: sel is a true selector.
+	tv, ok := c.info.Types[sel.X]
 	if !ok {
-		return nil, fmt.Errorf("cannot resolve %s", sel.X)
+		return fmt.Errorf("cannot resolve %s", sel.X)
 	}
 
-	// methods of T
+	// Add methods of T.
 	mset := types.NewMethodSet(tv.Type)
 	for i := 0; i < mset.Len(); i++ {
-		items = found(mset.At(i).Obj(), stdScore, items)
+		c.found(mset.At(i).Obj(), stdScore)
 	}
 
-	// methods of *T
+	// Add methods of *T.
 	if tv.Addressable() && !types.IsInterface(tv.Type) && !isPointer(tv.Type) {
 		mset := types.NewMethodSet(types.NewPointer(tv.Type))
 		for i := 0; i < mset.Len(); i++ {
-			items = found(mset.At(i).Obj(), stdScore, items)
+			c.found(mset.At(i).Obj(), stdScore)
 		}
 	}
 
-	// fields of T
+	// Add fields of T.
 	for _, f := range fieldSelections(tv.Type) {
-		items = found(f, stdScore, items)
+		c.found(f, stdScore)
 	}
-
-	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
+	return nil
 }
 
 // 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) {
+func (c *completer) lexical() error {
 	var scopes []*types.Scope // scopes[i], where i<len(path), is the possibly nil Scope of path[i].
-	for _, n := range path {
+	for _, n := range c.path {
 		switch node := n.(type) {
 		case *ast.FuncDecl:
 			n = node.Type
 		case *ast.FuncLit:
 			n = node.Type
 		}
-		scopes = append(scopes, info.Scopes[n])
+		scopes = append(scopes, c.info.Scopes[n])
 	}
-	scopes = append(scopes, pkg.Scope(), types.Universe)
+	scopes = append(scopes, c.types.Scope(), types.Universe)
 
 	// Track seen variables to avoid showing completions for shadowed variables.
 	// This works since we look at scopes from innermost to outermost.
@@ -249,7 +273,7 @@
 			continue
 		}
 		for _, name := range scope.Names() {
-			declScope, obj := scope.LookupParent(name, pos)
+			declScope, obj := scope.LookupParent(name, c.pos)
 			if declScope != scope {
 				continue // Name was declared in some enclosing scope, or not at all.
 			}
@@ -259,13 +283,13 @@
 				// Match the scope to its ast.Node. If the scope is the package scope,
 				// use the *ast.File as the starting node.
 				var node ast.Node
-				if i < len(path) {
-					node = path[i]
-				} else if i == len(path) { // use the *ast.File for package scope
-					node = path[i-1]
+				if i < len(c.path) {
+					node = c.path[i]
+				} else if i == len(c.path) { // use the *ast.File for package scope
+					node = c.path[i-1]
 				}
 				if node != nil {
-					if resolved := resolveInvalid(obj, node, info); resolved != nil {
+					if resolved := resolveInvalid(obj, node, c.info); resolved != nil {
 						obj = resolved
 					}
 				}
@@ -279,102 +303,32 @@
 			// If we haven't already added a candidate for an object with this name.
 			if _, ok := seen[obj.Name()]; !ok {
 				seen[obj.Name()] = struct{}{}
-				items = found(obj, score, items)
+				c.found(obj, score)
 			}
 		}
 	}
-	return items
+	return nil
 }
 
-// inComment checks if given token position is inside ast.Comment node.
-func inComment(pos token.Pos, commentGroups []*ast.CommentGroup) bool {
-	for _, g := range commentGroups {
-		if g.Pos() <= pos && pos <= g.End() {
-			return true
-		}
-	}
-	return false
-}
-
-// complit finds completions for field names inside a composite literal.
-// It reports whether the node was handled as part of a composite literal.
-func complit(path []ast.Node, pos token.Pos, pkg *types.Package, info *types.Info, found finder) (items []CompletionItem, prefix string, ok bool) {
-	var lit *ast.CompositeLit
-
-	// First, determine if the pos is within a composite literal.
-	switch n := path[0].(type) {
-	case *ast.CompositeLit:
-		// The enclosing node will be a composite literal if the user has just
-		// opened the curly brace (e.g. &x{<>) or the completion request is triggered
-		// from an already completed composite literal expression (e.g. &x{foo: 1, <>})
-		//
-		// If the cursor position is within a key-value expression inside the composite
-		// literal, we try to determine if it is before or after the colon. If it is before
-		// the colon, we return field completions. If the cursor does not belong to any
-		// expression within the composite literal, we show composite literal completions.
-		var expr ast.Expr
-		for _, e := range n.Elts {
-			if e.Pos() <= pos && pos < e.End() {
-				expr = e
-				break
-			}
-		}
-		lit = n
-		// If the position belongs to a key-value expression and is after the colon,
-		// don't show composite literal completions.
-		if kv, ok := expr.(*ast.KeyValueExpr); ok && pos > kv.Colon {
-			lit = nil
-		}
-	case *ast.KeyValueExpr:
-		// If the enclosing node is a key-value expression (e.g. &x{foo: <>}),
-		// we show composite literal completions if the cursor position is before the colon.
-		if len(path) > 1 && pos < n.Colon {
-			if l, ok := path[1].(*ast.CompositeLit); ok {
-				lit = l
-			}
-		}
+// compositeLiteral finds completions for field names inside a composite literal.
+func (c *completer) compositeLiteral(lit *ast.CompositeLit, kv *ast.KeyValueExpr) error {
+	switch n := c.path[0].(type) {
 	case *ast.Ident:
-		prefix = n.Name[:pos-n.Pos()]
-
-		// If the enclosing node is an identifier, it can either be an identifier that is
-		// part of a composite literal (e.g. &x{fo<>}), or it can be an identifier that is
-		// part of a key-value expression, which is part of a composite literal (e.g. &x{foo: ba<>).
-		// We handle both of these cases, showing composite literal completions only if
-		// the cursor position for the key-value expression is before the colon.
-		if len(path) > 1 {
-			if l, ok := path[1].(*ast.CompositeLit); ok {
-				lit = l
-			} else if len(path) > 2 {
-				if l, ok := path[2].(*ast.CompositeLit); ok {
-					// Confirm that cursor position is inside curly braces.
-					if l.Lbrace <= pos && pos <= l.Rbrace {
-						lit = l
-						if kv, ok := path[1].(*ast.KeyValueExpr); ok {
-							if pos > kv.Colon {
-								lit = nil
-							}
-						}
-					}
-				}
-			}
-		}
-	}
-	// We are not in a composite literal.
-	if lit == nil {
-		return nil, prefix, false
+		c.prefix = n.Name[:c.pos-n.Pos()]
 	}
 	// Mark fields of the composite literal that have already been set,
 	// except for the current field.
-	hasKeys := false // true if the composite literal already has key-value pairs
+	hasKeys := kv != nil // true if the composite literal already has key-value pairs
 	addedFields := make(map[*types.Var]bool)
 	for _, el := range lit.Elts {
-		if kv, ok := el.(*ast.KeyValueExpr); ok {
-			hasKeys = true
-			if kv.Pos() <= pos && pos <= kv.End() {
+		if kvExpr, ok := el.(*ast.KeyValueExpr); ok {
+			if kv == kvExpr {
 				continue
 			}
-			if key, ok := kv.Key.(*ast.Ident); ok {
-				if used, ok := info.Uses[key]; ok {
+
+			hasKeys = true
+			if key, ok := kvExpr.Key.(*ast.Ident); ok {
+				if used, ok := c.info.Uses[key]; ok {
 					if usedVar, ok := used.(*types.Var); ok {
 						addedFields[usedVar] = true
 					}
@@ -384,52 +338,67 @@
 	}
 	// If the underlying type of the composite literal is a struct,
 	// collect completions for the fields of this struct.
-	if tv, ok := info.Types[lit]; ok {
-		var structPkg *types.Package // package containing the struct type declaration
-		if s, ok := tv.Type.Underlying().(*types.Struct); ok {
-			for i := 0; i < s.NumFields(); i++ {
-				field := s.Field(i)
+	if tv, ok := c.info.Types[lit]; ok {
+		switch t := tv.Type.Underlying().(type) {
+		case *types.Struct:
+			var structPkg *types.Package // package that struct is declared in
+			for i := 0; i < t.NumFields(); i++ {
+				field := t.Field(i)
 				if i == 0 {
 					structPkg = field.Pkg()
 				}
 				if !addedFields[field] {
-					items = found(field, 10.0, items)
+					c.found(field, highScore)
 				}
 			}
 			// Add lexical completions if the user hasn't typed a key value expression
 			// and if the struct fields are defined in the same package as the user is in.
-			if !hasKeys && structPkg == pkg {
-				items = append(items, lexical(path, pos, pkg, info, found)...)
+			if !hasKeys && structPkg == c.types {
+				return c.lexical()
 			}
-			return items, prefix, true
+		default:
+			return c.lexical()
 		}
 	}
-	return items, prefix, false
+	return nil
 }
 
-// enclosingCompLit returns the composite literal and key value expression, if
-// any, enclosing the given position.
-func enclosingCompLit(pos token.Pos, path []ast.Node) (*ast.CompositeLit, *ast.KeyValueExpr) {
-	var keyVal *ast.KeyValueExpr
-
-	for _, n := range path {
+func (c *completer) enclosingCompositeLiteral() (lit *ast.CompositeLit, kv *ast.KeyValueExpr, ok bool) {
+	for _, n := range c.path {
 		switch n := n.(type) {
 		case *ast.CompositeLit:
-			// pos isn't part of the composite literal unless it falls within the curly
-			// braces (e.g. "foo.Foo<>Struct{}").
-			if n.Lbrace <= pos && pos <= n.Rbrace {
-				if keyVal == nil {
-					if i := exprAtPos(pos, n.Elts); i < len(n.Elts) {
-						keyVal, _ = n.Elts[i].(*ast.KeyValueExpr)
-					}
+			// The enclosing node will be a composite literal if the user has just
+			// opened the curly brace (e.g. &x{<>) or the completion request is triggered
+			// from an already completed composite literal expression (e.g. &x{foo: 1, <>})
+			//
+			// The position is not part of the composite literal unless it falls within the
+			// curly braces (e.g. "foo.Foo<>Struct{}").
+			if n.Lbrace <= c.pos && c.pos <= n.Rbrace {
+				lit = n
+
+				// If the cursor position is within a key-value expression inside the composite
+				// literal, we try to determine if it is before or after the colon. If it is before
+				// the colon, we return field completions. If the cursor does not belong to any
+				// expression within the composite literal, we show composite literal completions.
+				if expr, isKeyValue := exprAtPos(c.pos, n.Elts).(*ast.KeyValueExpr); kv == nil && isKeyValue {
+					kv = expr
+
+					// If the position belongs to a key-value expression and is after the colon,
+					// don't show composite literal completions.
+					ok = c.pos <= kv.Colon
+				} else if kv == nil {
+					ok = true
 				}
-
-				return n, keyVal
 			}
-
-			return nil, nil
+			return lit, kv, ok
 		case *ast.KeyValueExpr:
-			keyVal = n
+			if kv == nil {
+				kv = n
+
+				// If the position belongs to a key-value expression and is after the colon,
+				// don't show composite literal completions.
+				ok = c.pos <= kv.Colon
+			}
 		case *ast.FuncType, *ast.CallExpr, *ast.TypeAssertExpr:
 			// These node types break the type link between the leaf node and
 			// the composite literal. The type of the leaf node becomes unrelated
@@ -437,164 +406,13 @@
 			// inappropriate completions. For example, "Foo{Bar: x.Baz(<>)}"
 			// should complete as a function argument to Baz, not part of the Foo
 			// composite literal.
-			return nil, nil
+			return nil, nil, false
 		}
 	}
-
-	return nil, nil
+	return lit, kv, ok
 }
 
-// formatCompletion creates a completion item for a given types.Object.
-func formatCompletion(obj types.Object, qualifier types.Qualifier, score float64, isParam func(*types.Var) bool) CompletionItem {
-	label := obj.Name()
-	detail := types.TypeString(obj.Type(), qualifier)
-	var kind CompletionItemKind
-
-	switch o := obj.(type) {
-	case *types.TypeName:
-		detail, kind = formatType(o.Type(), qualifier)
-		if obj.Parent() == types.Universe {
-			detail = ""
-		}
-	case *types.Const:
-		if obj.Parent() == types.Universe {
-			detail = ""
-		} else {
-			val := o.Val().ExactString()
-			if !strings.Contains(val, "\\n") { // skip any multiline constants
-				label += " = " + o.Val().ExactString()
-			}
-		}
-		kind = ConstantCompletionItem
-	case *types.Var:
-		if _, ok := o.Type().(*types.Struct); ok {
-			detail = "struct{...}" // for anonymous structs
-		}
-		if o.IsField() {
-			kind = FieldCompletionItem
-		} else if isParam(o) {
-			kind = ParameterCompletionItem
-		} else {
-			kind = VariableCompletionItem
-		}
-	case *types.Func:
-		if sig, ok := o.Type().(*types.Signature); ok {
-			label += formatParams(sig.Params(), sig.Variadic(), qualifier)
-			detail = strings.Trim(types.TypeString(sig.Results(), qualifier), "()")
-			kind = FunctionCompletionItem
-			if sig.Recv() != nil {
-				kind = MethodCompletionItem
-			}
-		}
-	case *types.Builtin:
-		item, ok := builtinDetails[obj.Name()]
-		if !ok {
-			break
-		}
-		label, detail = item.label, item.detail
-		kind = FunctionCompletionItem
-	case *types.PkgName:
-		kind = PackageCompletionItem
-		detail = fmt.Sprintf("\"%s\"", o.Imported().Path())
-	case *types.Nil:
-		kind = VariableCompletionItem
-		detail = ""
-	}
-	detail = strings.TrimPrefix(detail, "untyped ")
-
-	return CompletionItem{
-		Label:  label,
-		Detail: detail,
-		Kind:   kind,
-		Score:  score,
-	}
-}
-
-// formatType returns the detail and kind for an object of type *types.TypeName.
-func formatType(typ types.Type, qualifier types.Qualifier) (detail string, kind CompletionItemKind) {
-	if types.IsInterface(typ) {
-		detail = "interface{...}"
-		kind = InterfaceCompletionItem
-	} else if _, ok := typ.(*types.Struct); ok {
-		detail = "struct{...}"
-		kind = StructCompletionItem
-	} else if typ != typ.Underlying() {
-		detail, kind = formatType(typ.Underlying(), qualifier)
-	} else {
-		detail = types.TypeString(typ, qualifier)
-		kind = TypeCompletionItem
-	}
-	return detail, kind
-}
-
-// formatParams correctly format the parameters of a function.
-func formatParams(t *types.Tuple, variadic bool, qualifier types.Qualifier) string {
-	var b bytes.Buffer
-	b.WriteByte('(')
-	for i := 0; i < t.Len(); i++ {
-		if i > 0 {
-			b.WriteString(", ")
-		}
-		el := t.At(i)
-		typ := types.TypeString(el.Type(), qualifier)
-		// Handle a variadic parameter (can only be the final parameter).
-		if variadic && i == t.Len()-1 {
-			typ = strings.Replace(typ, "[]", "...", 1)
-		}
-		if el.Name() == "" {
-			fmt.Fprintf(&b, "%v", typ)
-		} else {
-			fmt.Fprintf(&b, "%v %v", el.Name(), typ)
-		}
-	}
-	b.WriteByte(')')
-	return b.String()
-}
-
-// isParameter returns true if the given *types.Var is a parameter to the given
-// *types.Signature.
-func isParameter(sig *types.Signature, v *types.Var) bool {
-	if sig == nil {
-		return false
-	}
-	for i := 0; i < sig.Params().Len(); i++ {
-		if sig.Params().At(i) == v {
-			return true
-		}
-	}
-	return false
-}
-
-// qualifier returns a function that appropriately formats a types.PkgName
-// appearing in a *ast.File.
-func qualifier(f *ast.File, pkg *types.Package, info *types.Info) types.Qualifier {
-	// Construct mapping of import paths to their defined or implicit names.
-	imports := make(map[*types.Package]string)
-	for _, imp := range f.Imports {
-		var obj types.Object
-		if imp.Name != nil {
-			obj = info.Defs[imp.Name]
-		} else {
-			obj = info.Implicits[imp]
-		}
-		if pkgname, ok := obj.(*types.PkgName); ok {
-			imports[pkgname.Imported()] = pkgname.Name()
-		}
-	}
-	// Define qualifier to replace full package paths with names of the imports.
-	return func(p *types.Package) string {
-		if p == pkg {
-			return ""
-		}
-		if name, ok := imports[p]; ok {
-			return name
-		}
-		return p.Name()
-	}
-}
-
-// enclosingFunction returns the signature of the function enclosing the given
-// position.
+// enclosingFunction returns the signature of the function enclosing the given position.
 func enclosingFunction(path []ast.Node, pos token.Pos, info *types.Info) *types.Signature {
 	for _, node := range path {
 		switch t := node.(type) {
@@ -611,77 +429,57 @@
 	return nil
 }
 
-func expectedCompLitType(cl *ast.CompositeLit, kv *ast.KeyValueExpr, pos token.Pos, info *types.Info) types.Type {
-	// Get the type of the *ast.CompositeLit we belong to.
-	clType, ok := info.Types[cl]
+func (c *completer) expectedCompositeLiteralType(cl *ast.CompositeLit, kv *ast.KeyValueExpr) types.Type {
+	clType, ok := c.info.Types[cl]
 	if !ok {
 		return nil
 	}
-
 	switch t := clType.Type.Underlying().(type) {
 	case *types.Slice:
 		return t.Elem()
 	case *types.Array:
 		return t.Elem()
 	case *types.Map:
-		// If pos isn't in a key/value expression or it is on the left side
-		// of a key/value colon, a key must be entered next.
-		if kv == nil || pos <= kv.Colon {
+		if kv == nil || c.pos <= kv.Colon {
 			return t.Key()
 		}
-
 		return t.Elem()
 	case *types.Struct:
-		// pos is in a key/value expression
+		//  If we are in a key-value expression.
 		if kv != nil {
-			// If pos is to left of the colon, it is a struct field name,
-			// so there is no expected type.
-			if pos <= kv.Colon {
+			// There is no expected type for a struct field name.
+			if c.pos <= kv.Colon {
 				return nil
 			}
-
-			if keyIdent, ok := kv.Key.(*ast.Ident); ok {
-				// Find the type of the struct field whose name matches the key.
+			// Find the type of the struct field whose name matches the key.
+			if key, ok := kv.Key.(*ast.Ident); ok {
 				for i := 0; i < t.NumFields(); i++ {
-					if field := t.Field(i); field.Name() == keyIdent.Name {
+					if field := t.Field(i); field.Name() == key.Name {
 						return field.Type()
 					}
 				}
 			}
-
 			return nil
 		}
-
-		hasKeys := false // true if the composite literal has any key/value pairs
+		// We are in a struct literal, but not a specific key-value pair.
+		// If the struct literal doesn't have explicit field names,
+		// we may still be able to suggest an expected type.
 		for _, el := range cl.Elts {
 			if _, ok := el.(*ast.KeyValueExpr); ok {
-				hasKeys = true
-				break
+				return nil
 			}
 		}
-
-		// The struct literal is using field names, but pos is not in a key/value
-		// pair. A field name must be entered next, so there is no expected type.
-		if hasKeys {
-			return nil
-		}
-
 		// The order of the literal fields must match the order in the struct definition.
-		// Find the element pos falls in and use the corresponding field's type.
-		if i := exprAtPos(pos, cl.Elts); i < t.NumFields() {
+		// Find the element that the position belongs to and suggest that field's type.
+		if i := indexExprAtPos(c.pos, cl.Elts); i < t.NumFields() {
 			return t.Field(i).Type()
 		}
 	}
-
 	return nil
 }
 
 // expectedType returns the expected type for an expression at the query position.
 func expectedType(path []ast.Node, pos token.Pos, info *types.Info) types.Type {
-	if compLit, keyVal := enclosingCompLit(pos, path); compLit != nil {
-		return expectedCompLitType(compLit, keyVal, pos, info)
-	}
-
 	for i, node := range path {
 		if i == 2 {
 			break
@@ -701,7 +499,7 @@
 			if pos <= expr.TokPos {
 				break
 			}
-			i := exprAtPos(pos, expr.Rhs)
+			i := indexExprAtPos(pos, expr.Rhs)
 			if i >= len(expr.Lhs) {
 				i = len(expr.Lhs) - 1
 			}
@@ -714,7 +512,7 @@
 					if sig.Params().Len() == 0 {
 						return nil
 					}
-					i := exprAtPos(pos, expr.Args)
+					i := indexExprAtPos(pos, expr.Args)
 					// Make sure not to run past the end of expected parameters.
 					if i >= sig.Params().Len() {
 						i = sig.Params().Len() - 1
@@ -727,170 +525,43 @@
 	return nil
 }
 
+// preferTypeNames checks if given token position is inside func receiver,
+// type params, or type results. For example:
+//
+// func (<>) foo(<>) (<>) {}
+//
+func preferTypeNames(path []ast.Node, pos token.Pos) bool {
+	for _, p := range path {
+		switch n := p.(type) {
+		case *ast.FuncDecl:
+			if r := n.Recv; r != nil && r.Pos() <= pos && pos <= r.End() {
+				return true
+			}
+			if t := n.Type; t != nil {
+				if p := t.Params; p != nil && p.Pos() <= pos && pos <= p.End() {
+					return true
+				}
+				if r := t.Results; r != nil && r.Pos() <= pos && pos <= r.End() {
+					return true
+				}
+			}
+			return false
+		}
+	}
+	return false
+}
+
 // matchingTypes reports whether actual is a good candidate type
 // for a completion in a context of the expected type.
-func matchingTypes(expected, actual types.Type) bool {
+func (c *completer) matchingType(actual types.Type) bool {
+	if c.expectedType == nil {
+		return false
+	}
 	// Use a function's return type as its type.
 	if sig, ok := actual.(*types.Signature); ok {
 		if sig.Results().Len() == 1 {
 			actual = sig.Results().At(0).Type()
 		}
 	}
-	return types.Identical(types.Default(expected), types.Default(actual))
-}
-
-// exprAtPos returns the index of the expression containing pos.
-func exprAtPos(pos token.Pos, args []ast.Expr) int {
-	for i, expr := range args {
-		if expr.Pos() <= pos && pos <= expr.End() {
-			return i
-		}
-	}
-	return len(args)
-}
-
-// fieldSelections returns the set of fields that can
-// be selected from a value of type T.
-func fieldSelections(T types.Type) (fields []*types.Var) {
-	// TODO(adonovan): this algorithm doesn't exclude ambiguous
-	// selections that match more than one field/method.
-	// types.NewSelectionSet should do that for us.
-
-	seen := make(map[types.Type]bool) // for termination on recursive types
-	var visit func(T types.Type)
-	visit = func(T types.Type) {
-		if !seen[T] {
-			seen[T] = true
-			if T, ok := deref(T).Underlying().(*types.Struct); ok {
-				for i := 0; i < T.NumFields(); i++ {
-					f := T.Field(i)
-					fields = append(fields, f)
-					if f.Anonymous() {
-						visit(f.Type())
-					}
-				}
-			}
-		}
-	}
-	visit(T)
-
-	return fields
-}
-
-func isPointer(T types.Type) bool {
-	_, ok := T.(*types.Pointer)
-	return ok
-}
-
-// deref returns a pointer's element type; otherwise it returns typ.
-func deref(typ types.Type) types.Type {
-	if p, ok := typ.Underlying().(*types.Pointer); ok {
-		return p.Elem()
-	}
-	return typ
-}
-
-// resolveInvalid traverses the node of the AST that defines the scope
-// containing the declaration of obj, and attempts to find a user-friendly
-// name for its invalid type. The resulting Object and its Type are fake.
-func resolveInvalid(obj types.Object, node ast.Node, info *types.Info) types.Object {
-	// Construct a fake type for the object and return a fake object with this type.
-	formatResult := func(expr ast.Expr) types.Object {
-		var typename string
-		switch t := expr.(type) {
-		case *ast.SelectorExpr:
-			typename = fmt.Sprintf("%s.%s", t.X, t.Sel)
-		case *ast.Ident:
-			typename = t.String()
-		default:
-			return nil
-		}
-		typ := types.NewNamed(types.NewTypeName(token.NoPos, obj.Pkg(), typename, nil), nil, nil)
-		return types.NewVar(obj.Pos(), obj.Pkg(), obj.Name(), typ)
-	}
-	var resultExpr ast.Expr
-	ast.Inspect(node, func(node ast.Node) bool {
-		switch n := node.(type) {
-		case *ast.ValueSpec:
-			for _, name := range n.Names {
-				if info.Defs[name] == obj {
-					resultExpr = n.Type
-				}
-			}
-			return false
-		case *ast.Field: // This case handles parameters and results of a FuncDecl or FuncLit.
-			for _, name := range n.Names {
-				if info.Defs[name] == obj {
-					resultExpr = n.Type
-				}
-			}
-			return false
-		// TODO(rstambler): Handle range statements.
-		default:
-			return true
-		}
-	})
-	return formatResult(resultExpr)
-}
-
-type itemDetails struct {
-	label, detail string
-}
-
-var builtinDetails = map[string]itemDetails{
-	"append": { // append(slice []T, elems ...T)
-		label:  "append(slice []T, elems ...T)",
-		detail: "[]T",
-	},
-	"cap": { // cap(v []T) int
-		label:  "cap(v []T)",
-		detail: "int",
-	},
-	"close": { // close(c chan<- T)
-		label: "close(c chan<- T)",
-	},
-	"complex": { // complex(r, i float64) complex128
-		label:  "complex(real float64, imag float64)",
-		detail: "complex128",
-	},
-	"copy": { // copy(dst, src []T) int
-		label:  "copy(dst []T, src []T)",
-		detail: "int",
-	},
-	"delete": { // delete(m map[T]T1, key T)
-		label: "delete(m map[K]V, key K)",
-	},
-	"imag": { // imag(c complex128) float64
-		label:  "imag(complex128)",
-		detail: "float64",
-	},
-	"len": { // len(v T) int
-		label:  "len(T)",
-		detail: "int",
-	},
-	"make": { // make(t T, size ...int) T
-		label:  "make(t T, size ...int)",
-		detail: "T",
-	},
-	"new": { // new(T) *T
-		label:  "new(T)",
-		detail: "*T",
-	},
-	"panic": { // panic(v interface{})
-		label: "panic(interface{})",
-	},
-	"print": { // print(args ...T)
-		label: "print(args ...T)",
-	},
-	"println": { // println(args ...T)
-		label: "println(args ...T)",
-	},
-	"real": { // real(c complex128) float64
-		label:  "real(complex128)",
-		detail: "float64",
-	},
-	"recover": { // recover() interface{}
-		label:  "recover()",
-		detail: "interface{}",
-	},
+	return types.Identical(types.Default(c.expectedType), types.Default(actual))
 }
diff --git a/internal/lsp/source/completion_format.go b/internal/lsp/source/completion_format.go
new file mode 100644
index 0000000..dd5fab9
--- /dev/null
+++ b/internal/lsp/source/completion_format.go
@@ -0,0 +1,224 @@
+// Copyright 2019 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 source
+
+import (
+	"bytes"
+	"fmt"
+	"go/ast"
+	"go/types"
+	"strings"
+)
+
+// formatCompletion creates a completion item for a given types.Object.
+func (c *completer) item(obj types.Object, score float64) CompletionItem {
+	label := obj.Name()
+	detail := types.TypeString(obj.Type(), c.qf)
+	var kind CompletionItemKind
+
+	switch o := obj.(type) {
+	case *types.TypeName:
+		detail, kind = formatType(o.Type(), c.qf)
+		if obj.Parent() == types.Universe {
+			detail = ""
+		}
+	case *types.Const:
+		if obj.Parent() == types.Universe {
+			detail = ""
+		} else {
+			val := o.Val().ExactString()
+			if !strings.Contains(val, "\\n") { // skip any multiline constants
+				label += " = " + o.Val().ExactString()
+			}
+		}
+		kind = ConstantCompletionItem
+	case *types.Var:
+		if _, ok := o.Type().(*types.Struct); ok {
+			detail = "struct{...}" // for anonymous structs
+		}
+		if o.IsField() {
+			kind = FieldCompletionItem
+		} else if c.isParameter(o) {
+			kind = ParameterCompletionItem
+		} else {
+			kind = VariableCompletionItem
+		}
+	case *types.Func:
+		if sig, ok := o.Type().(*types.Signature); ok {
+			label += formatParams(sig, c.qf)
+			detail = strings.Trim(types.TypeString(sig.Results(), c.qf), "()")
+			kind = FunctionCompletionItem
+			if sig.Recv() != nil {
+				kind = MethodCompletionItem
+			}
+		}
+	case *types.Builtin:
+		item, ok := builtinDetails[obj.Name()]
+		if !ok {
+			break
+		}
+		label, detail = item.label, item.detail
+		kind = FunctionCompletionItem
+	case *types.PkgName:
+		kind = PackageCompletionItem
+		detail = fmt.Sprintf("\"%s\"", o.Imported().Path())
+	case *types.Nil:
+		kind = VariableCompletionItem
+		detail = ""
+	}
+	detail = strings.TrimPrefix(detail, "untyped ")
+
+	return CompletionItem{
+		Label:  label,
+		Detail: detail,
+		Kind:   kind,
+		Score:  score,
+	}
+}
+
+// isParameter returns true if the given *types.Var is a parameter
+// of the enclosingFunction.
+func (c *completer) isParameter(v *types.Var) bool {
+	if c.enclosingFunction == nil {
+		return false
+	}
+	for i := 0; i < c.enclosingFunction.Params().Len(); i++ {
+		if c.enclosingFunction.Params().At(i) == v {
+			return true
+		}
+	}
+	return false
+}
+
+// formatType returns the detail and kind for an object of type *types.TypeName.
+func formatType(typ types.Type, qf types.Qualifier) (detail string, kind CompletionItemKind) {
+	if types.IsInterface(typ) {
+		detail = "interface{...}"
+		kind = InterfaceCompletionItem
+	} else if _, ok := typ.(*types.Struct); ok {
+		detail = "struct{...}"
+		kind = StructCompletionItem
+	} else if typ != typ.Underlying() {
+		detail, kind = formatType(typ.Underlying(), qf)
+	} else {
+		detail = types.TypeString(typ, qf)
+		kind = TypeCompletionItem
+	}
+	return detail, kind
+}
+
+// formatParams correctly format the parameters of a function.
+func formatParams(sig *types.Signature, qf types.Qualifier) string {
+	var b bytes.Buffer
+	b.WriteByte('(')
+	for i := 0; i < sig.Params().Len(); i++ {
+		if i > 0 {
+			b.WriteString(", ")
+		}
+		el := sig.Params().At(i)
+		typ := types.TypeString(el.Type(), qf)
+		// Handle a variadic parameter (can only be the final parameter).
+		if sig.Variadic() && i == sig.Params().Len()-1 {
+			typ = strings.Replace(typ, "[]", "...", 1)
+		}
+		if el.Name() == "" {
+			fmt.Fprintf(&b, "%v", typ)
+		} else {
+			fmt.Fprintf(&b, "%v %v", el.Name(), typ)
+		}
+	}
+	b.WriteByte(')')
+	return b.String()
+}
+
+// qualifier returns a function that appropriately formats a types.PkgName
+// appearing in a *ast.File.
+func qualifier(f *ast.File, pkg *types.Package, info *types.Info) types.Qualifier {
+	// Construct mapping of import paths to their defined or implicit names.
+	imports := make(map[*types.Package]string)
+	for _, imp := range f.Imports {
+		var obj types.Object
+		if imp.Name != nil {
+			obj = info.Defs[imp.Name]
+		} else {
+			obj = info.Implicits[imp]
+		}
+		if pkgname, ok := obj.(*types.PkgName); ok {
+			imports[pkgname.Imported()] = pkgname.Name()
+		}
+	}
+	// Define qualifier to replace full package paths with names of the imports.
+	return func(p *types.Package) string {
+		if p == pkg {
+			return ""
+		}
+		if name, ok := imports[p]; ok {
+			return name
+		}
+		return p.Name()
+	}
+}
+
+type itemDetails struct {
+	label, detail string
+}
+
+var builtinDetails = map[string]itemDetails{
+	"append": { // append(slice []T, elems ...T)
+		label:  "append(slice []T, elems ...T)",
+		detail: "[]T",
+	},
+	"cap": { // cap(v []T) int
+		label:  "cap(v []T)",
+		detail: "int",
+	},
+	"close": { // close(c chan<- T)
+		label: "close(c chan<- T)",
+	},
+	"complex": { // complex(r, i float64) complex128
+		label:  "complex(real float64, imag float64)",
+		detail: "complex128",
+	},
+	"copy": { // copy(dst, src []T) int
+		label:  "copy(dst []T, src []T)",
+		detail: "int",
+	},
+	"delete": { // delete(m map[T]T1, key T)
+		label: "delete(m map[K]V, key K)",
+	},
+	"imag": { // imag(c complex128) float64
+		label:  "imag(complex128)",
+		detail: "float64",
+	},
+	"len": { // len(v T) int
+		label:  "len(T)",
+		detail: "int",
+	},
+	"make": { // make(t T, size ...int) T
+		label:  "make(t T, size ...int)",
+		detail: "T",
+	},
+	"new": { // new(T) *T
+		label:  "new(T)",
+		detail: "*T",
+	},
+	"panic": { // panic(v interface{})
+		label: "panic(interface{})",
+	},
+	"print": { // print(args ...T)
+		label: "print(args ...T)",
+	},
+	"println": { // println(args ...T)
+		label: "println(args ...T)",
+	},
+	"real": { // real(c complex128) float64
+		label:  "real(complex128)",
+		detail: "float64",
+	},
+	"recover": { // recover() interface{}
+		label:  "recover()",
+		detail: "interface{}",
+	},
+}
diff --git a/internal/lsp/source/signature_help.go b/internal/lsp/source/signature_help.go
index bcb4091..54433f6 100644
--- a/internal/lsp/source/signature_help.go
+++ b/internal/lsp/source/signature_help.go
@@ -59,11 +59,11 @@
 		return nil, fmt.Errorf("cannot find signature for Fun %[1]T (%[1]v)", callExpr.Fun)
 	}
 
-	pkgStringer := qualifier(fAST, pkg.GetTypes(), pkg.GetTypesInfo())
+	qf := qualifier(fAST, pkg.GetTypes(), pkg.GetTypesInfo())
 	var paramInfo []ParameterInformation
 	for i := 0; i < sig.Params().Len(); i++ {
 		param := sig.Params().At(i)
-		label := types.TypeString(param.Type(), pkgStringer)
+		label := types.TypeString(param.Type(), qf)
 		if sig.Variadic() && i == sig.Params().Len()-1 {
 			label = strings.Replace(label, "[]", "...", 1)
 		}
@@ -112,9 +112,9 @@
 		label = "func"
 	}
 
-	label += formatParams(sig.Params(), sig.Variadic(), pkgStringer)
+	label += formatParams(sig, qf)
 	if sig.Results().Len() > 0 {
-		results := types.TypeString(sig.Results(), pkgStringer)
+		results := types.TypeString(sig.Results(), qf)
 		if sig.Results().Len() == 1 && sig.Results().At(0).Name() == "" {
 			// Trim off leading/trailing parens to avoid results like "foo(a int) (int)".
 			results = strings.Trim(results, "()")
diff --git a/internal/lsp/source/util.go b/internal/lsp/source/util.go
new file mode 100644
index 0000000..a4d3745
--- /dev/null
+++ b/internal/lsp/source/util.go
@@ -0,0 +1,111 @@
+package source
+
+import (
+	"fmt"
+	"go/ast"
+	"go/token"
+	"go/types"
+)
+
+// indexExprAtPos returns the index of the expression containing pos.
+func indexExprAtPos(pos token.Pos, args []ast.Expr) int {
+	for i, expr := range args {
+		if expr.Pos() <= pos && pos <= expr.End() {
+			return i
+		}
+	}
+	return len(args)
+}
+
+func exprAtPos(pos token.Pos, args []ast.Expr) ast.Expr {
+	for _, expr := range args {
+		if expr.Pos() <= pos && pos <= expr.End() {
+			return expr
+		}
+	}
+	return nil
+}
+
+// fieldSelections returns the set of fields that can
+// be selected from a value of type T.
+func fieldSelections(T types.Type) (fields []*types.Var) {
+	// TODO(adonovan): this algorithm doesn't exclude ambiguous
+	// selections that match more than one field/method.
+	// types.NewSelectionSet should do that for us.
+
+	seen := make(map[types.Type]bool) // for termination on recursive types
+	var visit func(T types.Type)
+	visit = func(T types.Type) {
+		if !seen[T] {
+			seen[T] = true
+			if T, ok := deref(T).Underlying().(*types.Struct); ok {
+				for i := 0; i < T.NumFields(); i++ {
+					f := T.Field(i)
+					fields = append(fields, f)
+					if f.Anonymous() {
+						visit(f.Type())
+					}
+				}
+			}
+		}
+	}
+	visit(T)
+
+	return fields
+}
+
+// resolveInvalid traverses the node of the AST that defines the scope
+// containing the declaration of obj, and attempts to find a user-friendly
+// name for its invalid type. The resulting Object and its Type are fake.
+func resolveInvalid(obj types.Object, node ast.Node, info *types.Info) types.Object {
+	// Construct a fake type for the object and return a fake object with this type.
+	formatResult := func(expr ast.Expr) types.Object {
+		var typename string
+		switch t := expr.(type) {
+		case *ast.SelectorExpr:
+			typename = fmt.Sprintf("%s.%s", t.X, t.Sel)
+		case *ast.Ident:
+			typename = t.String()
+		default:
+			return nil
+		}
+		typ := types.NewNamed(types.NewTypeName(token.NoPos, obj.Pkg(), typename, nil), nil, nil)
+		return types.NewVar(obj.Pos(), obj.Pkg(), obj.Name(), typ)
+	}
+	var resultExpr ast.Expr
+	ast.Inspect(node, func(node ast.Node) bool {
+		switch n := node.(type) {
+		case *ast.ValueSpec:
+			for _, name := range n.Names {
+				if info.Defs[name] == obj {
+					resultExpr = n.Type
+				}
+			}
+			return false
+		case *ast.Field: // This case handles parameters and results of a FuncDecl or FuncLit.
+			for _, name := range n.Names {
+				if info.Defs[name] == obj {
+					resultExpr = n.Type
+				}
+			}
+			return false
+		// TODO(rstambler): Handle range statements.
+		default:
+			return true
+		}
+	})
+	return formatResult(resultExpr)
+}
+
+func isPointer(T types.Type) bool {
+	_, ok := T.(*types.Pointer)
+	return ok
+}
+
+// deref returns a pointer's element type; otherwise it returns typ.
+func deref(typ types.Type) types.Type {
+	if p, ok := typ.Underlying().(*types.Pointer); ok {
+		return p.Elem()
+	}
+	return typ
+}