Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.0.0-20201028153306-37f0764111ff / cmd / guru / referrers.go
diff --git a/.config/coc/extensions/coc-go-data/tools/pkg/mod/golang.org/x/tools@v0.0.0-20201028153306-37f0764111ff/cmd/guru/referrers.go b/.config/coc/extensions/coc-go-data/tools/pkg/mod/golang.org/x/tools@v0.0.0-20201028153306-37f0764111ff/cmd/guru/referrers.go
new file mode 100644 (file)
index 0000000..9d15071
--- /dev/null
@@ -0,0 +1,802 @@
+// 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.
+
+package main
+
+import (
+       "bytes"
+       "fmt"
+       "go/ast"
+       "go/build"
+       "go/parser"
+       "go/token"
+       "go/types"
+       "io"
+       "log"
+       "os"
+       "sort"
+       "strconv"
+       "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/imports"
+       "golang.org/x/tools/refactor/importgraph"
+)
+
+// The referrers function 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 tests of the query package
+       // even if the query location is not in the tests.
+       for path := range lconf.ImportPkgs {
+               lconf.ImportPkgs[path] = true
+       }
+
+       // 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())
+       }
+
+       q.Output(fset, &referrersInitialResult{
+               qinfo: qpos.info,
+               obj:   obj,
+       })
+
+       // 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 method, we must load
+       // any package that transitively imports P.
+
+       if global, pkglevel := classify(obj); global {
+               if pkglevel {
+                       return globalReferrersPkgLevel(q, obj, fset)
+               }
+               // We'll use 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)
+       }
+
+       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 (a field or method) at the specified source position.
+// Its defining package is defpkg, and the query package is qpkg.
+func globalReferrers(q *Query, qpkg, defpkg string, objposn token.Position) 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.
+       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
+       )
+
+       // 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)
+                               }
+                       }
+                       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
+}
+
+// globalReferrersPkgLevel reports references throughout the entire workspace to the package-level object obj.
+// It assumes that the query object itself has already been reported.
+func globalReferrersPkgLevel(q *Query, obj types.Object, fset *token.FileSet) error {
+       // globalReferrersPkgLevel uses go/ast and friends instead of go/types.
+       // This affords a considerable performance benefit.
+       // It comes at the cost of some code complexity.
+       //
+       // Here's a high level summary.
+       //
+       // The goal is to find references to the query object p.Q.
+       // There are several possible scenarios, each handled differently.
+       //
+       // 1. We are looking in a package other than p, and p is not dot-imported.
+       //    This is the simplest case. Q must be referred to as n.Q,
+       //    where n is the name under which p is imported.
+       //    We look at all imports of p to gather all names under which it is imported.
+       //    (In the typical case, it is imported only once, under its default name.)
+       //    Then we look at all selector expressions and report any matches.
+       //
+       // 2. We are looking in a package other than p, and p is dot-imported.
+       //    In this case, Q will be referred to just as Q.
+       //    Furthermore, go/ast's object resolution will not be able to resolve
+       //    Q to any other object, unlike any local (file- or function- or block-scoped) object.
+       //    So we look at all matching identifiers and report all unresolvable ones.
+       //
+       // 3. We are looking in package p.
+       //    (Care must be taken to separate p and p_test (an xtest package),
+       //    and make sure that they are treated as separate packages.)
+       //    In this case, we give go/ast the entire package for object resolution,
+       //    instead of going file by file.
+       //    We then iterate over all identifiers that resolve to the query object.
+       //    (The query object itself has already been reported, so we don't re-report it.)
+       //
+       // We always skip all files that don't contain the string Q, as they cannot be
+       // relevant to finding references to Q.
+       //
+       // We parse all files leniently. In the presence of parsing errors, results are best-effort.
+
+       // Scan the workspace and build the import graph.
+       // Ignore broken packages.
+       _, rev, _ := importgraph.Build(q.Build)
+
+       // Find the set of packages that directly import defpkg.
+       defpkg := obj.Pkg().Path()
+       defpkg = strings.TrimSuffix(defpkg, "_test") // package x_test actually has package name x
+       defpkg = imports.VendorlessPath(defpkg)      // remove vendor goop
+
+       users := rev[defpkg]
+       if len(users) == 0 {
+               users = make(map[string]bool)
+       }
+       // We also need to check defpkg itself, and its xtests.
+       // For the reverse graph packages, we process xtests with the main package.
+       // defpkg gets special handling; we must distinguish between in-package vs out-of-package.
+       // To make the control flow below simpler, add defpkg and defpkg xtest placeholders.
+       // Use "!test" instead of "_test" because "!" is not a valid character in an import path.
+       // (More precisely, it is not guaranteed to be a valid character in an import path,
+       // so it is unlikely that it will be in use. See https://golang.org/ref/spec#Import_declarations.)
+       users[defpkg] = true
+       users[defpkg+"!test"] = true
+
+       cwd, err := os.Getwd()
+       if err != nil {
+               return err
+       }
+
+       defname := obj.Pkg().Name()                    // name of defining package, used for imports using import path only
+       isxtest := strings.HasSuffix(defname, "_test") // indicates whether the query object is defined in an xtest package
+
+       name := obj.Name()
+       namebytes := []byte(name)          // byte slice version of query object name, for early filtering
+       objpos := fset.Position(obj.Pos()) // position of query object, used to prevent re-emitting original decl
+
+       sema := make(chan struct{}, 20) // counting semaphore to limit I/O concurrency
+       var wg sync.WaitGroup
+
+       for u := range users {
+               u := u
+               wg.Add(1)
+               go func() {
+                       defer wg.Done()
+
+                       uIsXTest := strings.HasSuffix(u, "!test") // indicates whether this package is the special defpkg xtest package
+                       u = strings.TrimSuffix(u, "!test")
+
+                       // Resolve package.
+                       sema <- struct{}{} // acquire token
+                       pkg, err := q.Build.Import(u, cwd, build.IgnoreVendor)
+                       <-sema // release token
+                       if err != nil {
+                               return
+                       }
+
+                       // If we're not in the query package,
+                       // the object is in another package regardless,
+                       // so we want to process all files.
+                       // If we are in the query package,
+                       // we want to only process the files that are
+                       // part of that query package;
+                       // that set depends on whether the query package itself is an xtest.
+                       inQueryPkg := u == defpkg && isxtest == uIsXTest
+                       var files []string
+                       if !inQueryPkg || !isxtest {
+                               files = append(files, pkg.GoFiles...)
+                               files = append(files, pkg.TestGoFiles...)
+                               files = append(files, pkg.CgoFiles...) // use raw cgo files, as we're only parsing
+                       }
+                       if !inQueryPkg || isxtest {
+                               files = append(files, pkg.XTestGoFiles...)
+                       }
+
+                       if len(files) == 0 {
+                               return
+                       }
+
+                       var deffiles map[string]*ast.File
+                       if inQueryPkg {
+                               deffiles = make(map[string]*ast.File)
+                       }
+
+                       buf := new(bytes.Buffer) // reusable buffer for reading files
+
+                       for _, file := range files {
+                               if !buildutil.IsAbsPath(q.Build, file) {
+                                       file = buildutil.JoinPath(q.Build, pkg.Dir, file)
+                               }
+                               buf.Reset()
+                               sema <- struct{}{} // acquire token
+                               src, err := readFile(q.Build, file, buf)
+                               <-sema // release token
+                               if err != nil {
+                                       continue
+                               }
+
+                               // Fast path: If the object's name isn't present anywhere in the source, ignore the file.
+                               if !bytes.Contains(src, namebytes) {
+                                       continue
+                               }
+
+                               if inQueryPkg {
+                                       // If we're in the query package, we defer final processing until we have
+                                       // parsed all of the candidate files in the package.
+                                       // Best effort; allow errors and use what we can from what remains.
+                                       f, _ := parser.ParseFile(fset, file, src, parser.AllErrors)
+                                       if f != nil {
+                                               deffiles[file] = f
+                                       }
+                                       continue
+                               }
+
+                               // We aren't in the query package. Go file by file.
+
+                               // Parse out only the imports, to check whether the defining package
+                               // was imported, and if so, under what names.
+                               // Best effort; allow errors and use what we can from what remains.
+                               f, _ := parser.ParseFile(fset, file, src, parser.ImportsOnly|parser.AllErrors)
+                               if f == nil {
+                                       continue
+                               }
+
+                               // pkgnames is the set of names by which defpkg is imported in this file.
+                               // (Multiple imports in the same file are legal but vanishingly rare.)
+                               pkgnames := make([]string, 0, 1)
+                               var isdotimport bool
+                               for _, imp := range f.Imports {
+                                       path, err := strconv.Unquote(imp.Path.Value)
+                                       if err != nil || path != defpkg {
+                                               continue
+                                       }
+                                       switch {
+                                       case imp.Name == nil:
+                                               pkgnames = append(pkgnames, defname)
+                                       case imp.Name.Name == ".":
+                                               isdotimport = true
+                                       default:
+                                               pkgnames = append(pkgnames, imp.Name.Name)
+                                       }
+                               }
+                               if len(pkgnames) == 0 && !isdotimport {
+                                       // Defining package not imported, bail.
+                                       continue
+                               }
+
+                               // Re-parse the entire file.
+                               // Parse errors are ok; we'll do the best we can with a partial AST, if we have one.
+                               f, _ = parser.ParseFile(fset, file, src, parser.AllErrors)
+                               if f == nil {
+                                       continue
+                               }
+
+                               // Walk the AST looking for references.
+                               var refs []*ast.Ident
+                               ast.Inspect(f, func(n ast.Node) bool {
+                                       // Check selector expressions.
+                                       // If the selector matches the target name,
+                                       // and the expression is one of the names
+                                       // that the defining package was imported under,
+                                       // then we have a match.
+                                       if sel, ok := n.(*ast.SelectorExpr); ok && sel.Sel.Name == name {
+                                               if id, ok := sel.X.(*ast.Ident); ok {
+                                                       for _, n := range pkgnames {
+                                                               if n == id.Name {
+                                                                       refs = append(refs, sel.Sel)
+                                                                       // Don't recurse further, to avoid duplicate entries
+                                                                       // from the dot import check below.
+                                                                       return false
+                                                               }
+                                                       }
+                                               }
+                                       }
+                                       // Dot imports are special.
+                                       // Objects imported from the defining package are placed in the package scope.
+                                       // go/ast does not resolve them to an object.
+                                       // At all other scopes (file, local), go/ast can do the resolution.
+                                       // So we're looking for object-free idents with the right name.
+                                       // The only other way to get something with the right name at the package scope
+                                       // is to *be* the defining package. We handle that case separately (inQueryPkg).
+                                       if isdotimport {
+                                               if id, ok := n.(*ast.Ident); ok && id.Obj == nil && id.Name == name {
+                                                       refs = append(refs, id)
+                                                       return false
+                                               }
+                                       }
+                                       return true
+                               })
+
+                               // Emit any references we found.
+                               if len(refs) > 0 {
+                                       q.Output(fset, &referrersPackageResult{
+                                               pkg:   types.NewPackage(pkg.ImportPath, pkg.Name),
+                                               build: q.Build,
+                                               fset:  fset,
+                                               refs:  refs,
+                                       })
+                               }
+                       }
+
+                       // If we're in the query package, we've now collected all the files in the package.
+                       // (Or at least the ones that might contain references to the object.)
+                       // Find and emit refs.
+                       if inQueryPkg {
+                               // Bundle the files together into a package.
+                               // This does package-level object resolution.
+                               qpkg, _ := ast.NewPackage(fset, deffiles, nil, nil)
+                               // Look up the query object; we know that it is defined in the package scope.
+                               pkgobj := qpkg.Scope.Objects[name]
+                               if pkgobj == nil {
+                                       panic("missing defpkg object for " + defpkg + "." + name)
+                               }
+                               // Find all references to the query object.
+                               var refs []*ast.Ident
+                               ast.Inspect(qpkg, func(n ast.Node) bool {
+                                       if id, ok := n.(*ast.Ident); ok {
+                                               // Check both that this is a reference to the query object
+                                               // and that it is not the query object itself;
+                                               // the query object itself was already emitted.
+                                               if id.Obj == pkgobj && objpos != fset.Position(id.Pos()) {
+                                                       refs = append(refs, id)
+                                                       return false
+                                               }
+                                       }
+                                       return true
+                               })
+                               if len(refs) > 0 {
+                                       q.Output(fset, &referrersPackageResult{
+                                               pkg:   types.NewPackage(pkg.ImportPath, pkg.Name),
+                                               build: q.Build,
+                                               fset:  fset,
+                                               refs:  refs,
+                                       })
+                               }
+                               deffiles = nil // allow GC
+                       }
+               }()
+       }
+
+       wg.Wait()
+
+       return nil
+}
+
+// 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, nil)
+                               <-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.
+// If non-nil, buf must have been reset.
+func readFile(ctxt *build.Context, filename string, buf *bytes.Buffer) ([]byte, error) {
+       rc, err := buildutil.OpenFile(ctxt, filename)
+       if err != nil {
+               return nil, err
+       }
+       defer rc.Close()
+       if buf == nil {
+               buf = new(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)
+}