cmd/guru: describe Go 1.8 aliases

Also:
- always display the value of a constant expr, whether query expr is a
  definition, a reference, or an alias.
- eliminate some go1.5 portability code.
- remove go1.8 fork of referrers; no changes are necessary
  since I decided not to treat aliases specially.
- add tests.

Tested with Go 1.6, Go 1.7, and tip (Go 1.8).

Change-Id: I94624cff82f4d8c0dcbf12d11c8ce16e8168a7fe
Reviewed-on: https://go-review.googlesource.com/32730
Reviewed-by: Robert Griesemer <gri@golang.org>
diff --git a/cmd/guru/callees18.go b/cmd/guru/callees18.go
index 6e26964..0afac90 100644
--- a/cmd/guru/callees18.go
+++ b/cmd/guru/callees18.go
@@ -64,7 +64,7 @@
 	// e.g.  f := func(){}; f().
 	switch funexpr := unparen(e.Fun).(type) {
 	case *ast.Ident:
-		switch obj := qpos.info.Uses[funexpr].(type) {
+		switch obj := original(qpos.info.Uses[funexpr]).(type) {
 		case *types.Builtin:
 			// Reject calls to built-ins.
 			return fmt.Errorf("this is a call to the built-in '%s' operator", obj.Name())
@@ -82,7 +82,7 @@
 			// qualified identifier.
 			// May refer to top level function variable
 			// or to top level function.
-			callee := qpos.info.Uses[funexpr.Sel]
+			callee := original(qpos.info.Uses[funexpr.Sel])
 			if obj, ok := callee.(*types.Func); ok {
 				q.Output(lprog.Fset, &calleesTypesResult{
 					site:   e,
@@ -257,3 +257,11 @@
 func (a byFuncPos) Len() int           { return len(a) }
 func (a byFuncPos) Less(i, j int) bool { return a[i].Pos() < a[j].Pos() }
 func (a byFuncPos) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
+
+// TODO(adonovan): use types.Original when available.
+func original(obj types.Object) types.Object {
+	if alias, ok := obj.(*types.Alias); ok {
+		return alias.Orig()
+	}
+	return obj
+}
diff --git a/cmd/guru/definition18.go b/cmd/guru/definition18.go
index c267f53..b6f7588 100644
--- a/cmd/guru/definition18.go
+++ b/cmd/guru/definition18.go
@@ -177,6 +177,10 @@
 						if spec.Name.Name == member {
 							return token.TYPE, spec.Name.Pos(), nil
 						}
+					case *ast.AliasSpec:
+						if spec.Name.Name == member {
+							return decl.Tok, spec.Name.Pos(), nil
+						}
 					}
 				}
 			case *ast.FuncDecl:
diff --git a/cmd/guru/describe.go b/cmd/guru/describe.go
index 8e06e4c..6493e90 100644
--- a/cmd/guru/describe.go
+++ b/cmd/guru/describe.go
@@ -334,6 +334,9 @@
 		t = types.Typ[types.Invalid]
 	}
 	constVal := qpos.info.Types[expr].Value
+	if c, ok := obj.(*types.Const); ok {
+		constVal = c.Val()
+	}
 
 	return &describeValueResult{
 		qpos:     qpos,
@@ -359,7 +362,7 @@
 func (r *describeValueResult) PrintPlain(printf printfFunc) {
 	var prefix, suffix string
 	if r.constVal != nil {
-		suffix = fmt.Sprintf(" of constant value %s", constValString(r.constVal))
+		suffix = fmt.Sprintf(" of value %s", r.constVal)
 	}
 	switch obj := r.obj.(type) {
 	case *types.Func:
@@ -659,25 +662,13 @@
 	}
 }
 
-// Helper function to adjust go1.5 numeric go/constant formatting.
-// Can be removed once we give up compatibility with go1.5.
-func constValString(v exact.Value) string {
-	if v.Kind() == exact.Float {
-		// In go1.5, go/constant floating-point values are printed
-		// as fractions. Make them appear as floating-point numbers.
-		f, _ := exact.Float64Val(v)
-		return fmt.Sprintf("%g", f)
-	}
-	return v.String()
-}
-
 func formatMember(obj types.Object, maxname int) string {
 	qualifier := types.RelativeTo(obj.Pkg())
 	var buf bytes.Buffer
 	fmt.Fprintf(&buf, "%-5s %-*s", tokenOf(obj), maxname, obj.Name())
 	switch obj := obj.(type) {
 	case *types.Const:
-		fmt.Fprintf(&buf, " %s = %s", types.TypeString(obj.Type(), qualifier), constValString(obj.Val()))
+		fmt.Fprintf(&buf, " %s = %s", types.TypeString(obj.Type(), qualifier), obj.Val())
 
 	case *types.Func:
 		fmt.Fprintf(&buf, " %s", types.TypeString(obj.Type(), qualifier))
@@ -714,7 +705,7 @@
 		var val string
 		switch mem := mem.obj.(type) {
 		case *types.Const:
-			val = constValString(mem.Val())
+			val = mem.Val().String()
 		case *types.TypeName:
 			typ = typ.Underlying()
 		}
diff --git a/cmd/guru/describe18.go b/cmd/guru/describe18.go
index 6ec38fd..3bc91a0 100644
--- a/cmd/guru/describe18.go
+++ b/cmd/guru/describe18.go
@@ -159,6 +159,11 @@
 			}
 			return path, actionUnknown // uninteresting
 
+		case *ast.AliasSpec:
+			// Descend to alias name.
+			path = append([]ast.Node{n.Name}, path...)
+			continue
+
 		case *ast.TypeSpec:
 			// Descend to type name.
 			path = append([]ast.Node{n.Name}, path...)
@@ -217,7 +222,7 @@
 			continue
 
 		case *ast.Ident:
-			switch pkginfo.ObjectOf(n).(type) {
+			switch original(pkginfo.ObjectOf(n)).(type) {
 			case *types.PkgName:
 				return path, actionPackage
 
@@ -334,6 +339,9 @@
 		t = types.Typ[types.Invalid]
 	}
 	constVal := qpos.info.Types[expr].Value
+	if c, ok := original(obj).(*types.Const); ok {
+		constVal = c.Val()
+	}
 
 	return &describeValueResult{
 		qpos:     qpos,
@@ -351,7 +359,7 @@
 	expr     ast.Expr     // query node
 	typ      types.Type   // type of expression
 	constVal exact.Value  // value of expression, if constant
-	obj      types.Object // var/func/const object, if expr was Ident
+	obj      types.Object // var/func/const object, if expr was Ident, or alias to same
 	methods  []*types.Selection
 	fields   []describeField
 }
@@ -359,21 +367,24 @@
 func (r *describeValueResult) PrintPlain(printf printfFunc) {
 	var prefix, suffix string
 	if r.constVal != nil {
-		suffix = fmt.Sprintf(" of constant value %s", constValString(r.constVal))
-	}
-	switch obj := r.obj.(type) {
-	case *types.Func:
-		if recv := obj.Type().(*types.Signature).Recv(); recv != nil {
-			if _, ok := recv.Type().Underlying().(*types.Interface); ok {
-				prefix = "interface method "
-			} else {
-				prefix = "method "
-			}
-		}
+		suffix = fmt.Sprintf(" of value %s", r.constVal)
 	}
 
 	// Describe the expression.
 	if r.obj != nil {
+		switch obj := r.obj.(type) {
+		case *types.Func:
+			if recv := obj.Type().(*types.Signature).Recv(); recv != nil {
+				if _, ok := recv.Type().Underlying().(*types.Interface); ok {
+					prefix = "interface method "
+				} else {
+					prefix = "method "
+				}
+			}
+		case *types.Alias:
+			prefix = tokenOf(obj.Orig()) + " "
+		}
+
 		if r.obj.Pos() == r.expr.Pos() {
 			// defining ident
 			printf(r.expr, "definition of %s%s%s", prefix, r.qpos.objectString(r.obj), suffix)
@@ -436,6 +447,8 @@
 			isDef := t.Obj().Pos() == n.Pos() // see caveats at isDef above
 			if isDef {
 				description = "definition of "
+			} else if _, ok := qpos.info.ObjectOf(n).(*types.Alias); ok {
+				description = "alias of "
 			} else {
 				description = "reference to "
 			}
@@ -659,25 +672,21 @@
 	}
 }
 
-// Helper function to adjust go1.5 numeric go/constant formatting.
-// Can be removed once we give up compatibility with go1.5.
-func constValString(v exact.Value) string {
-	if v.Kind() == exact.Float {
-		// In go1.5, go/constant floating-point values are printed
-		// as fractions. Make them appear as floating-point numbers.
-		f, _ := exact.Float64Val(v)
-		return fmt.Sprintf("%g", f)
-	}
-	return v.String()
-}
-
 func formatMember(obj types.Object, maxname int) string {
 	qualifier := types.RelativeTo(obj.Pkg())
 	var buf bytes.Buffer
 	fmt.Fprintf(&buf, "%-5s %-*s", tokenOf(obj), maxname, obj.Name())
 	switch obj := obj.(type) {
+	case *types.Alias:
+		buf.WriteString(" => ")
+		if orig := obj.Orig(); orig != nil {
+			fmt.Fprintf(&buf, "%s.%s", orig.Pkg().Name(), orig.Name())
+		} else {
+			buf.WriteByte('?')
+		}
+
 	case *types.Const:
-		fmt.Fprintf(&buf, " %s = %s", types.TypeString(obj.Type(), qualifier), constValString(obj.Val()))
+		fmt.Fprintf(&buf, " %s = %s", types.TypeString(obj.Type(), qualifier), obj.Val())
 
 	case *types.Func:
 		fmt.Fprintf(&buf, " %s", types.TypeString(obj.Type(), qualifier))
@@ -714,7 +723,7 @@
 		var val string
 		switch mem := mem.obj.(type) {
 		case *types.Const:
-			val = constValString(mem.Val())
+			val = mem.Val().String()
 		case *types.TypeName:
 			typ = typ.Underlying()
 		}
@@ -739,7 +748,7 @@
 }
 
 func tokenOf(o types.Object) string {
-	switch o.(type) {
+	switch o := o.(type) {
 	case *types.Func:
 		return "func"
 	case *types.Var:
@@ -756,6 +765,11 @@
 		return "nil"
 	case *types.Label:
 		return "label"
+	case *types.Alias:
+		if o.Orig() == nil {
+			return "alias"
+		}
+		return tokenOf(o.Orig())
 	}
 	panic(o)
 }
diff --git a/cmd/guru/guru_test.go b/cmd/guru/guru_test.go
index 62dcd6d..2eb1247 100644
--- a/cmd/guru/guru_test.go
+++ b/cmd/guru/guru_test.go
@@ -230,6 +230,7 @@
 		"testdata/src/reflection/main.go",
 		"testdata/src/what/main.go",
 		"testdata/src/whicherrs/main.go",
+		"testdata/src/alias/main.go", // Go 1.8 only
 		// JSON:
 		// TODO(adonovan): most of these are very similar; combine them.
 		"testdata/src/calls-json/main.go",
@@ -247,6 +248,10 @@
 			// wording for a "no such file or directory" error.
 			continue
 		}
+		if filename == "testdata/src/alias/main.go" &&
+			!strings.Contains(fmt.Sprint(build.Default.ReleaseTags), "go1.8") {
+			continue
+		}
 
 		json := strings.Contains(filename, "-json/")
 		queries := parseQueries(t, filename)
diff --git a/cmd/guru/referrers.go b/cmd/guru/referrers.go
index e80b0a6..a5164a3 100644
--- a/cmd/guru/referrers.go
+++ b/cmd/guru/referrers.go
@@ -2,8 +2,6 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// +build !go1.8
-
 package main
 
 import (
@@ -27,6 +25,12 @@
 
 // Referrers reports all identifiers that resolve to the same object
 // as the queried identifier, within any package in the workspace.
+//
+// Go 1.8 aliases are not treated specially.  A referrers query on an
+// object will report declarations of aliases of that object, but not
+// uses of those aliases; for that, a second query is needed.
+// Similarly, a query on an alias will report all uses of the alias but
+// not of the original object.
 func referrers(q *Query) error {
 	fset := token.NewFileSet()
 	lconf := loader.Config{Fset: fset, Build: q.Build}
diff --git a/cmd/guru/referrers18.go b/cmd/guru/referrers18.go
deleted file mode 100644
index 277f57a..0000000
--- a/cmd/guru/referrers18.go
+++ /dev/null
@@ -1,524 +0,0 @@
-// Copyright 2013 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.
-
-// +build go1.8
-
-package main
-
-import (
-	"bytes"
-	"fmt"
-	"go/ast"
-	"go/build"
-	"go/token"
-	"go/types"
-	"io"
-	"log"
-	"sort"
-	"strings"
-	"sync"
-
-	"golang.org/x/tools/cmd/guru/serial"
-	"golang.org/x/tools/go/buildutil"
-	"golang.org/x/tools/go/loader"
-	"golang.org/x/tools/refactor/importgraph"
-)
-
-// Referrers reports all identifiers that resolve to the same object
-// as the queried identifier, within any package in the workspace.
-func referrers(q *Query) error {
-	fset := token.NewFileSet()
-	lconf := loader.Config{Fset: fset, Build: q.Build}
-	allowErrors(&lconf)
-
-	if _, err := importQueryPackage(q.Pos, &lconf); err != nil {
-		return err
-	}
-
-	// Load/parse/type-check the query package.
-	lprog, err := lconf.Load()
-	if err != nil {
-		return err
-	}
-
-	qpos, err := parseQueryPos(lprog, q.Pos, false)
-	if err != nil {
-		return err
-	}
-
-	id, _ := qpos.path[0].(*ast.Ident)
-	if id == nil {
-		return fmt.Errorf("no identifier here")
-	}
-
-	obj := qpos.info.ObjectOf(id)
-	if obj == nil {
-		// Happens for y in "switch y := x.(type)",
-		// the package declaration,
-		// and unresolved identifiers.
-		if _, ok := qpos.path[1].(*ast.File); ok { // package decl?
-			return packageReferrers(q, qpos.info.Pkg.Path())
-		}
-		return fmt.Errorf("no object for identifier: %T", qpos.path[1])
-	}
-
-	// Imported package name?
-	if pkgname, ok := obj.(*types.PkgName); ok {
-		return packageReferrers(q, pkgname.Imported().Path())
-	}
-
-	if obj.Pkg() == nil {
-		return fmt.Errorf("references to predeclared %q are everywhere!", obj.Name())
-	}
-
-	// For a globally accessible object defined in package P, we
-	// must load packages that depend on P.  Specifically, for a
-	// package-level object, we need load only direct importers
-	// of P, but for a field or interface method, we must load
-	// any package that transitively imports P.
-	if global, pkglevel := classify(obj); global {
-		// We'll use the the object's position to identify it in the larger program.
-		objposn := fset.Position(obj.Pos())
-		defpkg := obj.Pkg().Path() // defining package
-		return globalReferrers(q, qpos.info.Pkg.Path(), defpkg, objposn, pkglevel)
-	}
-
-	q.Output(fset, &referrersInitialResult{
-		qinfo: qpos.info,
-		obj:   obj,
-	})
-
-	outputUses(q, fset, usesOf(obj, qpos.info), obj.Pkg())
-
-	return nil // success
-}
-
-// classify classifies objects by how far
-// we have to look to find references to them.
-func classify(obj types.Object) (global, pkglevel bool) {
-	if obj.Exported() {
-		if obj.Parent() == nil {
-			// selectable object (field or method)
-			return true, false
-		}
-		if obj.Parent() == obj.Pkg().Scope() {
-			// lexical object (package-level var/const/func/type)
-			return true, true
-		}
-	}
-	// object with unexported named or defined in local scope
-	return false, false
-}
-
-// packageReferrers reports all references to the specified package
-// throughout the workspace.
-func packageReferrers(q *Query, path string) error {
-	// Scan the workspace and build the import graph.
-	// Ignore broken packages.
-	_, rev, _ := importgraph.Build(q.Build)
-
-	// Find the set of packages that directly import the query package.
-	// Only those packages need typechecking of function bodies.
-	users := rev[path]
-
-	// Load the larger program.
-	fset := token.NewFileSet()
-	lconf := loader.Config{
-		Fset:  fset,
-		Build: q.Build,
-		TypeCheckFuncBodies: func(p string) bool {
-			return users[strings.TrimSuffix(p, "_test")]
-		},
-	}
-	allowErrors(&lconf)
-
-	// The importgraph doesn't treat external test packages
-	// as separate nodes, so we must use ImportWithTests.
-	for path := range users {
-		lconf.ImportWithTests(path)
-	}
-
-	// Subtle!  AfterTypeCheck needs no mutex for qpkg because the
-	// topological import order gives us the necessary happens-before edges.
-	// TODO(adonovan): what about import cycles?
-	var qpkg *types.Package
-
-	// For efficiency, we scan each package for references
-	// just after it has been type-checked.  The loader calls
-	// AfterTypeCheck (concurrently), providing us with a stream of
-	// packages.
-	lconf.AfterTypeCheck = func(info *loader.PackageInfo, files []*ast.File) {
-		// AfterTypeCheck may be called twice for the same package due to augmentation.
-
-		if info.Pkg.Path() == path && qpkg == nil {
-			// Found the package of interest.
-			qpkg = info.Pkg
-			fakepkgname := types.NewPkgName(token.NoPos, qpkg, qpkg.Name(), qpkg)
-			q.Output(fset, &referrersInitialResult{
-				qinfo: info,
-				obj:   fakepkgname, // bogus
-			})
-		}
-
-		// Only inspect packages that directly import the
-		// declaring package (and thus were type-checked).
-		if lconf.TypeCheckFuncBodies(info.Pkg.Path()) {
-			// Find PkgNames that refer to qpkg.
-			// TODO(adonovan): perhaps more useful would be to show imports
-			// of the package instead of qualified identifiers.
-			var refs []*ast.Ident
-			for id, obj := range info.Uses {
-				if obj, ok := obj.(*types.PkgName); ok && obj.Imported() == qpkg {
-					refs = append(refs, id)
-				}
-			}
-			outputUses(q, fset, refs, info.Pkg)
-		}
-
-		clearInfoFields(info) // save memory
-	}
-
-	lconf.Load() // ignore error
-
-	if qpkg == nil {
-		log.Fatalf("query package %q not found during reloading", path)
-	}
-
-	return nil
-}
-
-func usesOf(queryObj types.Object, info *loader.PackageInfo) []*ast.Ident {
-	var refs []*ast.Ident
-	for id, obj := range info.Uses {
-		if sameObj(queryObj, obj) {
-			refs = append(refs, id)
-		}
-	}
-	return refs
-}
-
-// outputUses outputs a result describing refs, which appear in the package denoted by info.
-func outputUses(q *Query, fset *token.FileSet, refs []*ast.Ident, pkg *types.Package) {
-	if len(refs) > 0 {
-		sort.Sort(byNamePos{fset, refs})
-		q.Output(fset, &referrersPackageResult{
-			pkg:   pkg,
-			build: q.Build,
-			fset:  fset,
-			refs:  refs,
-		})
-	}
-}
-
-// globalReferrers reports references throughout the entire workspace to the
-// object at the specified source position.  Its defining package is defpkg,
-// and the query package is qpkg.  isPkgLevel indicates whether the object
-// is defined at package-level.
-func globalReferrers(q *Query, qpkg, defpkg string, objposn token.Position, isPkgLevel bool) error {
-	// Scan the workspace and build the import graph.
-	// Ignore broken packages.
-	_, rev, _ := importgraph.Build(q.Build)
-
-	// Find the set of packages that depend on defpkg.
-	// Only function bodies in those packages need type-checking.
-	var users map[string]bool
-	if isPkgLevel {
-		users = rev[defpkg] // direct importers
-		if users == nil {
-			users = make(map[string]bool)
-		}
-		users[defpkg] = true // plus the defining package itself
-	} else {
-		users = rev.Search(defpkg) // transitive importers
-	}
-
-	// Prepare to load the larger program.
-	fset := token.NewFileSet()
-	lconf := loader.Config{
-		Fset:  fset,
-		Build: q.Build,
-		TypeCheckFuncBodies: func(p string) bool {
-			return users[strings.TrimSuffix(p, "_test")]
-		},
-	}
-	allowErrors(&lconf)
-
-	// The importgraph doesn't treat external test packages
-	// as separate nodes, so we must use ImportWithTests.
-	for path := range users {
-		lconf.ImportWithTests(path)
-	}
-
-	// The remainder of this function is somewhat tricky because it
-	// operates on the concurrent stream of packages observed by the
-	// loader's AfterTypeCheck hook.  Most of guru's helper
-	// functions assume the entire program has already been loaded,
-	// so we can't use them here.
-	// TODO(adonovan): smooth things out once the other changes have landed.
-
-	// Results are reported concurrently from within the
-	// AfterTypeCheck hook.  The program may provide a useful stream
-	// of information even if the user doesn't let the program run
-	// to completion.
-
-	var (
-		mu    sync.Mutex
-		qobj  types.Object
-		qinfo *loader.PackageInfo // info for qpkg
-	)
-
-	// For efficiency, we scan each package for references
-	// just after it has been type-checked.  The loader calls
-	// AfterTypeCheck (concurrently), providing us with a stream of
-	// packages.
-	lconf.AfterTypeCheck = func(info *loader.PackageInfo, files []*ast.File) {
-		// AfterTypeCheck may be called twice for the same package due to augmentation.
-
-		// Only inspect packages that depend on the declaring package
-		// (and thus were type-checked).
-		if lconf.TypeCheckFuncBodies(info.Pkg.Path()) {
-			// Record the query object and its package when we see it.
-			mu.Lock()
-			if qobj == nil && info.Pkg.Path() == defpkg {
-				// Find the object by its position (slightly ugly).
-				qobj = findObject(fset, &info.Info, objposn)
-				if qobj == nil {
-					// It really ought to be there;
-					// we found it once already.
-					log.Fatalf("object at %s not found in package %s",
-						objposn, defpkg)
-				}
-
-				// Object found.
-				qinfo = info
-				q.Output(fset, &referrersInitialResult{
-					qinfo: qinfo,
-					obj:   qobj,
-				})
-			}
-			obj := qobj
-			mu.Unlock()
-
-			// Look for references to the query object.
-			if obj != nil {
-				outputUses(q, fset, usesOf(obj, info), info.Pkg)
-			}
-		}
-
-		clearInfoFields(info) // save memory
-	}
-
-	lconf.Load() // ignore error
-
-	if qobj == nil {
-		log.Fatal("query object not found during reloading")
-	}
-
-	return nil // success
-}
-
-// findObject returns the object defined at the specified position.
-func findObject(fset *token.FileSet, info *types.Info, objposn token.Position) types.Object {
-	good := func(obj types.Object) bool {
-		if obj == nil {
-			return false
-		}
-		posn := fset.Position(obj.Pos())
-		return posn.Filename == objposn.Filename && posn.Offset == objposn.Offset
-	}
-	for _, obj := range info.Defs {
-		if good(obj) {
-			return obj
-		}
-	}
-	for _, obj := range info.Implicits {
-		if good(obj) {
-			return obj
-		}
-	}
-	return nil
-}
-
-// same reports whether x and y are identical, or both are PkgNames
-// that import the same Package.
-//
-func sameObj(x, y types.Object) bool {
-	if x == y {
-		return true
-	}
-	if x, ok := x.(*types.PkgName); ok {
-		if y, ok := y.(*types.PkgName); ok {
-			return x.Imported() == y.Imported()
-		}
-	}
-	return false
-}
-
-func clearInfoFields(info *loader.PackageInfo) {
-	// TODO(adonovan): opt: save memory by eliminating unneeded scopes/objects.
-	// (Requires go/types change for Go 1.7.)
-	//   info.Pkg.Scope().ClearChildren()
-
-	// Discard the file ASTs and their accumulated type
-	// information to save memory.
-	info.Files = nil
-	info.Defs = make(map[*ast.Ident]types.Object)
-	info.Uses = make(map[*ast.Ident]types.Object)
-	info.Implicits = make(map[ast.Node]types.Object)
-
-	// Also, disable future collection of wholly unneeded
-	// type information for the package in case there is
-	// more type-checking to do (augmentation).
-	info.Types = nil
-	info.Scopes = nil
-	info.Selections = nil
-}
-
-// -------- utils --------
-
-// An deterministic ordering for token.Pos that doesn't
-// depend on the order in which packages were loaded.
-func lessPos(fset *token.FileSet, x, y token.Pos) bool {
-	fx := fset.File(x)
-	fy := fset.File(y)
-	if fx != fy {
-		return fx.Name() < fy.Name()
-	}
-	return x < y
-}
-
-type byNamePos struct {
-	fset *token.FileSet
-	ids  []*ast.Ident
-}
-
-func (p byNamePos) Len() int      { return len(p.ids) }
-func (p byNamePos) Swap(i, j int) { p.ids[i], p.ids[j] = p.ids[j], p.ids[i] }
-func (p byNamePos) Less(i, j int) bool {
-	return lessPos(p.fset, p.ids[i].NamePos, p.ids[j].NamePos)
-}
-
-// referrersInitialResult is the initial result of a "referrers" query.
-type referrersInitialResult struct {
-	qinfo *loader.PackageInfo
-	obj   types.Object // object it denotes
-}
-
-func (r *referrersInitialResult) PrintPlain(printf printfFunc) {
-	printf(r.obj, "references to %s",
-		types.ObjectString(r.obj, types.RelativeTo(r.qinfo.Pkg)))
-}
-
-func (r *referrersInitialResult) JSON(fset *token.FileSet) []byte {
-	var objpos string
-	if pos := r.obj.Pos(); pos.IsValid() {
-		objpos = fset.Position(pos).String()
-	}
-	return toJSON(&serial.ReferrersInitial{
-		Desc:   r.obj.String(),
-		ObjPos: objpos,
-	})
-}
-
-// referrersPackageResult is the streaming result for one package of a "referrers" query.
-type referrersPackageResult struct {
-	pkg   *types.Package
-	build *build.Context
-	fset  *token.FileSet
-	refs  []*ast.Ident // set of all other references to it
-}
-
-// forEachRef calls f(id, text) for id in r.refs, in order.
-// Text is the text of the line on which id appears.
-func (r *referrersPackageResult) foreachRef(f func(id *ast.Ident, text string)) {
-	// Show referring lines, like grep.
-	type fileinfo struct {
-		refs     []*ast.Ident
-		linenums []int            // line number of refs[i]
-		data     chan interface{} // file contents or error
-	}
-	var fileinfos []*fileinfo
-	fileinfosByName := make(map[string]*fileinfo)
-
-	// First pass: start the file reads concurrently.
-	sema := make(chan struct{}, 20) // counting semaphore to limit I/O concurrency
-	for _, ref := range r.refs {
-		posn := r.fset.Position(ref.Pos())
-		fi := fileinfosByName[posn.Filename]
-		if fi == nil {
-			fi = &fileinfo{data: make(chan interface{})}
-			fileinfosByName[posn.Filename] = fi
-			fileinfos = append(fileinfos, fi)
-
-			// First request for this file:
-			// start asynchronous read.
-			go func() {
-				sema <- struct{}{} // acquire token
-				content, err := readFile(r.build, posn.Filename)
-				<-sema // release token
-				if err != nil {
-					fi.data <- err
-				} else {
-					fi.data <- content
-				}
-			}()
-		}
-		fi.refs = append(fi.refs, ref)
-		fi.linenums = append(fi.linenums, posn.Line)
-	}
-
-	// Second pass: print refs in original order.
-	// One line may have several refs at different columns.
-	for _, fi := range fileinfos {
-		v := <-fi.data // wait for I/O completion
-
-		// Print one item for all refs in a file that could not
-		// be loaded (perhaps due to //line directives).
-		if err, ok := v.(error); ok {
-			var suffix string
-			if more := len(fi.refs) - 1; more > 0 {
-				suffix = fmt.Sprintf(" (+ %d more refs in this file)", more)
-			}
-			f(fi.refs[0], err.Error()+suffix)
-			continue
-		}
-
-		lines := bytes.Split(v.([]byte), []byte("\n"))
-		for i, ref := range fi.refs {
-			f(ref, string(lines[fi.linenums[i]-1]))
-		}
-	}
-}
-
-// readFile is like ioutil.ReadFile, but
-// it goes through the virtualized build.Context.
-func readFile(ctxt *build.Context, filename string) ([]byte, error) {
-	rc, err := buildutil.OpenFile(ctxt, filename)
-	if err != nil {
-		return nil, err
-	}
-	defer rc.Close()
-	var buf bytes.Buffer
-	if _, err := io.Copy(&buf, rc); err != nil {
-		return nil, err
-	}
-	return buf.Bytes(), nil
-}
-
-func (r *referrersPackageResult) PrintPlain(printf printfFunc) {
-	r.foreachRef(func(id *ast.Ident, text string) {
-		printf(id, "%s", text)
-	})
-}
-
-func (r *referrersPackageResult) JSON(fset *token.FileSet) []byte {
-	refs := serial.ReferrersPackage{Package: r.pkg.Path()}
-	r.foreachRef(func(id *ast.Ident, text string) {
-		refs.Refs = append(refs.Refs, serial.Ref{
-			Pos:  fset.Position(id.NamePos).String(),
-			Text: text,
-		})
-	})
-	return toJSON(refs)
-}
diff --git a/cmd/guru/testdata/src/alias/main.go b/cmd/guru/testdata/src/alias/main.go
new file mode 100644
index 0000000..164d131
--- /dev/null
+++ b/cmd/guru/testdata/src/alias/main.go
@@ -0,0 +1,25 @@
+package alias // @describe pkg "alias"
+
+// +build go1.8
+
+// Test describe queries on Go 1.8 aliases.
+// See go.tools/guru/guru_test.go for explanation.
+// See alias.golden for expected query results.
+
+import (
+	"aliaslib"
+	"nosuchpkg"
+)
+
+var bad1 => nopkg.NoVar// @describe bad1 "bad1"
+var bad2 => nosuchpkg.NoVar// @describe bad2 "bad2"
+
+var v_ => aliaslib.V // @describe v "v_"
+type t_ => aliaslib.T // @describe t "t_"
+const c_ => aliaslib.C // @describe c "c_"
+func f_ => aliaslib.F // @describe f "f_"
+
+type S1 struct { aliaslib.T } // @describe s1-field "T"
+type S2 struct { t_ } // @describe s2-field "t_"
+
+var x t_ // @describe var-x "t_"
diff --git a/cmd/guru/testdata/src/alias/main.golden b/cmd/guru/testdata/src/alias/main.golden
new file mode 100644
index 0000000..fcd7c39
--- /dev/null
+++ b/cmd/guru/testdata/src/alias/main.golden
@@ -0,0 +1,54 @@
+-------- @describe pkg --------
+definition of package "alias"
+	type  S1   struct{aliaslib.T}
+		method (S1) Method(x *int) *int
+	type  S2   struct{aliaslib.T}
+		method (S2) Method(x *int) *int
+	alias bad1 => ?
+	alias bad2 => ?
+	const c_   => aliaslib.C
+	func  f_   => aliaslib.F
+	type  t_   => aliaslib.T
+	var   v_   => aliaslib.V
+	var   x    aliaslib.T
+
+-------- @describe bad1 --------
+identifier
+
+-------- @describe bad2 --------
+identifier
+
+-------- @describe v --------
+definition of var alias v_ int
+
+-------- @describe t --------
+alias of type aliaslib.T (size 8, align 8)
+defined as int
+Methods:
+	method (T) Method(x *int) *int
+
+-------- @describe c --------
+definition of const alias c_ untyped int of value 3
+
+-------- @describe f --------
+definition of func alias f_ func()
+
+-------- @describe s1-field --------
+reference to field T aliaslib.T
+defined here
+Methods:
+	method (T) Method(x *int) *int
+
+-------- @describe s2-field --------
+type struct{aliaslib.T} (size 8, align 8)
+Methods:
+	method (struct{T}) Method(x *int) *int
+Fields:
+	t_ aliaslib.T
+
+-------- @describe var-x --------
+alias of type aliaslib.T (size 8, align 8)
+defined as int
+Methods:
+	method (T) Method(x *int) *int
+
diff --git a/cmd/guru/testdata/src/aliaslib/aliaslib.go b/cmd/guru/testdata/src/aliaslib/aliaslib.go
new file mode 100644
index 0000000..4f07a42
--- /dev/null
+++ b/cmd/guru/testdata/src/aliaslib/aliaslib.go
@@ -0,0 +1,11 @@
+package aliaslib
+
+type T int
+
+func (T) Method(x *int) *int
+
+func F()
+
+const C = 3
+
+var V = 0
diff --git a/cmd/guru/testdata/src/describe/main.golden b/cmd/guru/testdata/src/describe/main.golden
index 4ddb680..940ef57 100644
--- a/cmd/guru/testdata/src/describe/main.golden
+++ b/cmd/guru/testdata/src/describe/main.golden
@@ -30,16 +30,16 @@
 reference to built-in type float64
 
 -------- @describe const-ref-iota --------
-reference to const iota untyped int of constant value 0
+reference to const iota untyped int of value 0
 
 -------- @describe const-def-pi --------
-definition of const pi untyped float
+definition of const pi untyped float of value 3.141
 
 -------- @describe const-def-pie --------
-definition of const pie cake
+definition of const pie cake of value 3.141
 
 -------- @describe const-ref-pi --------
-reference to const pi untyped float of constant value 3.141
+reference to const pi untyped float of value 3.141
 defined here
 
 -------- @describe func-def-main --------
@@ -143,13 +143,13 @@
 	method (I) f()
 
 -------- @describe const-local-pi --------
-definition of const localpi untyped float
+definition of const localpi untyped float of value 3.141
 
 -------- @describe const-local-pie --------
-definition of const localpie cake
+definition of const localpie cake of value 3.141
 
 -------- @describe const-ref-localpi --------
-reference to const localpi untyped float of constant value 3.141
+reference to const localpi untyped float of value 3.141
 defined here
 
 -------- @describe type-def-T --------
@@ -162,10 +162,10 @@
 No methods.
 
 -------- @describe const-expr --------
-binary * operation of constant value 6
+binary * operation of value 6
 
 -------- @describe const-expr2 --------
-binary - operation of constant value -2
+binary - operation of value -2
 
 -------- @describe map-lookup,ok --------
 index expression of type (*int, bool)
diff --git a/cmd/guru/testdata/src/imports/main.golden b/cmd/guru/testdata/src/imports/main.golden
index 8035515..89cfb2b 100644
--- a/cmd/guru/testdata/src/imports/main.golden
+++ b/cmd/guru/testdata/src/imports/main.golden
@@ -19,7 +19,7 @@
 	var   Var    int
 
 -------- @describe ref-const --------
-reference to const lib.Const untyped int
+reference to const lib.Const untyped int of value 3
 defined here
 
 -------- @describe ref-func --------