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
1 // Copyright 2013 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 package main
6
7 import (
8         "bytes"
9         "fmt"
10         "go/ast"
11         "go/build"
12         "go/parser"
13         "go/token"
14         "go/types"
15         "io"
16         "log"
17         "os"
18         "sort"
19         "strconv"
20         "strings"
21         "sync"
22
23         "golang.org/x/tools/cmd/guru/serial"
24         "golang.org/x/tools/go/buildutil"
25         "golang.org/x/tools/go/loader"
26         "golang.org/x/tools/imports"
27         "golang.org/x/tools/refactor/importgraph"
28 )
29
30 // The referrers function reports all identifiers that resolve to the same object
31 // as the queried identifier, within any package in the workspace.
32 func referrers(q *Query) error {
33         fset := token.NewFileSet()
34         lconf := loader.Config{Fset: fset, Build: q.Build}
35         allowErrors(&lconf)
36
37         if _, err := importQueryPackage(q.Pos, &lconf); err != nil {
38                 return err
39         }
40
41         // Load tests of the query package
42         // even if the query location is not in the tests.
43         for path := range lconf.ImportPkgs {
44                 lconf.ImportPkgs[path] = true
45         }
46
47         // Load/parse/type-check the query package.
48         lprog, err := lconf.Load()
49         if err != nil {
50                 return err
51         }
52
53         qpos, err := parseQueryPos(lprog, q.Pos, false)
54         if err != nil {
55                 return err
56         }
57
58         id, _ := qpos.path[0].(*ast.Ident)
59         if id == nil {
60                 return fmt.Errorf("no identifier here")
61         }
62
63         obj := qpos.info.ObjectOf(id)
64         if obj == nil {
65                 // Happens for y in "switch y := x.(type)",
66                 // the package declaration,
67                 // and unresolved identifiers.
68                 if _, ok := qpos.path[1].(*ast.File); ok { // package decl?
69                         return packageReferrers(q, qpos.info.Pkg.Path())
70                 }
71                 return fmt.Errorf("no object for identifier: %T", qpos.path[1])
72         }
73
74         // Imported package name?
75         if pkgname, ok := obj.(*types.PkgName); ok {
76                 return packageReferrers(q, pkgname.Imported().Path())
77         }
78
79         if obj.Pkg() == nil {
80                 return fmt.Errorf("references to predeclared %q are everywhere!", obj.Name())
81         }
82
83         q.Output(fset, &referrersInitialResult{
84                 qinfo: qpos.info,
85                 obj:   obj,
86         })
87
88         // For a globally accessible object defined in package P, we
89         // must load packages that depend on P.  Specifically, for a
90         // package-level object, we need load only direct importers
91         // of P, but for a field or method, we must load
92         // any package that transitively imports P.
93
94         if global, pkglevel := classify(obj); global {
95                 if pkglevel {
96                         return globalReferrersPkgLevel(q, obj, fset)
97                 }
98                 // We'll use the object's position to identify it in the larger program.
99                 objposn := fset.Position(obj.Pos())
100                 defpkg := obj.Pkg().Path() // defining package
101                 return globalReferrers(q, qpos.info.Pkg.Path(), defpkg, objposn)
102         }
103
104         outputUses(q, fset, usesOf(obj, qpos.info), obj.Pkg())
105
106         return nil // success
107 }
108
109 // classify classifies objects by how far
110 // we have to look to find references to them.
111 func classify(obj types.Object) (global, pkglevel bool) {
112         if obj.Exported() {
113                 if obj.Parent() == nil {
114                         // selectable object (field or method)
115                         return true, false
116                 }
117                 if obj.Parent() == obj.Pkg().Scope() {
118                         // lexical object (package-level var/const/func/type)
119                         return true, true
120                 }
121         }
122         // object with unexported named or defined in local scope
123         return false, false
124 }
125
126 // packageReferrers reports all references to the specified package
127 // throughout the workspace.
128 func packageReferrers(q *Query, path string) error {
129         // Scan the workspace and build the import graph.
130         // Ignore broken packages.
131         _, rev, _ := importgraph.Build(q.Build)
132
133         // Find the set of packages that directly import the query package.
134         // Only those packages need typechecking of function bodies.
135         users := rev[path]
136
137         // Load the larger program.
138         fset := token.NewFileSet()
139         lconf := loader.Config{
140                 Fset:  fset,
141                 Build: q.Build,
142                 TypeCheckFuncBodies: func(p string) bool {
143                         return users[strings.TrimSuffix(p, "_test")]
144                 },
145         }
146         allowErrors(&lconf)
147
148         // The importgraph doesn't treat external test packages
149         // as separate nodes, so we must use ImportWithTests.
150         for path := range users {
151                 lconf.ImportWithTests(path)
152         }
153
154         // Subtle!  AfterTypeCheck needs no mutex for qpkg because the
155         // topological import order gives us the necessary happens-before edges.
156         // TODO(adonovan): what about import cycles?
157         var qpkg *types.Package
158
159         // For efficiency, we scan each package for references
160         // just after it has been type-checked.  The loader calls
161         // AfterTypeCheck (concurrently), providing us with a stream of
162         // packages.
163         lconf.AfterTypeCheck = func(info *loader.PackageInfo, files []*ast.File) {
164                 // AfterTypeCheck may be called twice for the same package due to augmentation.
165
166                 if info.Pkg.Path() == path && qpkg == nil {
167                         // Found the package of interest.
168                         qpkg = info.Pkg
169                         fakepkgname := types.NewPkgName(token.NoPos, qpkg, qpkg.Name(), qpkg)
170                         q.Output(fset, &referrersInitialResult{
171                                 qinfo: info,
172                                 obj:   fakepkgname, // bogus
173                         })
174                 }
175
176                 // Only inspect packages that directly import the
177                 // declaring package (and thus were type-checked).
178                 if lconf.TypeCheckFuncBodies(info.Pkg.Path()) {
179                         // Find PkgNames that refer to qpkg.
180                         // TODO(adonovan): perhaps more useful would be to show imports
181                         // of the package instead of qualified identifiers.
182                         var refs []*ast.Ident
183                         for id, obj := range info.Uses {
184                                 if obj, ok := obj.(*types.PkgName); ok && obj.Imported() == qpkg {
185                                         refs = append(refs, id)
186                                 }
187                         }
188                         outputUses(q, fset, refs, info.Pkg)
189                 }
190
191                 clearInfoFields(info) // save memory
192         }
193
194         lconf.Load() // ignore error
195
196         if qpkg == nil {
197                 log.Fatalf("query package %q not found during reloading", path)
198         }
199
200         return nil
201 }
202
203 func usesOf(queryObj types.Object, info *loader.PackageInfo) []*ast.Ident {
204         var refs []*ast.Ident
205         for id, obj := range info.Uses {
206                 if sameObj(queryObj, obj) {
207                         refs = append(refs, id)
208                 }
209         }
210         return refs
211 }
212
213 // outputUses outputs a result describing refs, which appear in the package denoted by info.
214 func outputUses(q *Query, fset *token.FileSet, refs []*ast.Ident, pkg *types.Package) {
215         if len(refs) > 0 {
216                 sort.Sort(byNamePos{fset, refs})
217                 q.Output(fset, &referrersPackageResult{
218                         pkg:   pkg,
219                         build: q.Build,
220                         fset:  fset,
221                         refs:  refs,
222                 })
223         }
224 }
225
226 // globalReferrers reports references throughout the entire workspace to the
227 // object (a field or method) at the specified source position.
228 // Its defining package is defpkg, and the query package is qpkg.
229 func globalReferrers(q *Query, qpkg, defpkg string, objposn token.Position) error {
230         // Scan the workspace and build the import graph.
231         // Ignore broken packages.
232         _, rev, _ := importgraph.Build(q.Build)
233
234         // Find the set of packages that depend on defpkg.
235         // Only function bodies in those packages need type-checking.
236         users := rev.Search(defpkg) // transitive importers
237
238         // Prepare to load the larger program.
239         fset := token.NewFileSet()
240         lconf := loader.Config{
241                 Fset:  fset,
242                 Build: q.Build,
243                 TypeCheckFuncBodies: func(p string) bool {
244                         return users[strings.TrimSuffix(p, "_test")]
245                 },
246         }
247         allowErrors(&lconf)
248
249         // The importgraph doesn't treat external test packages
250         // as separate nodes, so we must use ImportWithTests.
251         for path := range users {
252                 lconf.ImportWithTests(path)
253         }
254
255         // The remainder of this function is somewhat tricky because it
256         // operates on the concurrent stream of packages observed by the
257         // loader's AfterTypeCheck hook.  Most of guru's helper
258         // functions assume the entire program has already been loaded,
259         // so we can't use them here.
260         // TODO(adonovan): smooth things out once the other changes have landed.
261
262         // Results are reported concurrently from within the
263         // AfterTypeCheck hook.  The program may provide a useful stream
264         // of information even if the user doesn't let the program run
265         // to completion.
266
267         var (
268                 mu   sync.Mutex
269                 qobj types.Object
270         )
271
272         // For efficiency, we scan each package for references
273         // just after it has been type-checked.  The loader calls
274         // AfterTypeCheck (concurrently), providing us with a stream of
275         // packages.
276         lconf.AfterTypeCheck = func(info *loader.PackageInfo, files []*ast.File) {
277                 // AfterTypeCheck may be called twice for the same package due to augmentation.
278
279                 // Only inspect packages that depend on the declaring package
280                 // (and thus were type-checked).
281                 if lconf.TypeCheckFuncBodies(info.Pkg.Path()) {
282                         // Record the query object and its package when we see it.
283                         mu.Lock()
284                         if qobj == nil && info.Pkg.Path() == defpkg {
285                                 // Find the object by its position (slightly ugly).
286                                 qobj = findObject(fset, &info.Info, objposn)
287                                 if qobj == nil {
288                                         // It really ought to be there;
289                                         // we found it once already.
290                                         log.Fatalf("object at %s not found in package %s",
291                                                 objposn, defpkg)
292                                 }
293                         }
294                         obj := qobj
295                         mu.Unlock()
296
297                         // Look for references to the query object.
298                         if obj != nil {
299                                 outputUses(q, fset, usesOf(obj, info), info.Pkg)
300                         }
301                 }
302
303                 clearInfoFields(info) // save memory
304         }
305
306         lconf.Load() // ignore error
307
308         if qobj == nil {
309                 log.Fatal("query object not found during reloading")
310         }
311
312         return nil // success
313 }
314
315 // globalReferrersPkgLevel reports references throughout the entire workspace to the package-level object obj.
316 // It assumes that the query object itself has already been reported.
317 func globalReferrersPkgLevel(q *Query, obj types.Object, fset *token.FileSet) error {
318         // globalReferrersPkgLevel uses go/ast and friends instead of go/types.
319         // This affords a considerable performance benefit.
320         // It comes at the cost of some code complexity.
321         //
322         // Here's a high level summary.
323         //
324         // The goal is to find references to the query object p.Q.
325         // There are several possible scenarios, each handled differently.
326         //
327         // 1. We are looking in a package other than p, and p is not dot-imported.
328         //    This is the simplest case. Q must be referred to as n.Q,
329         //    where n is the name under which p is imported.
330         //    We look at all imports of p to gather all names under which it is imported.
331         //    (In the typical case, it is imported only once, under its default name.)
332         //    Then we look at all selector expressions and report any matches.
333         //
334         // 2. We are looking in a package other than p, and p is dot-imported.
335         //    In this case, Q will be referred to just as Q.
336         //    Furthermore, go/ast's object resolution will not be able to resolve
337         //    Q to any other object, unlike any local (file- or function- or block-scoped) object.
338         //    So we look at all matching identifiers and report all unresolvable ones.
339         //
340         // 3. We are looking in package p.
341         //    (Care must be taken to separate p and p_test (an xtest package),
342         //    and make sure that they are treated as separate packages.)
343         //    In this case, we give go/ast the entire package for object resolution,
344         //    instead of going file by file.
345         //    We then iterate over all identifiers that resolve to the query object.
346         //    (The query object itself has already been reported, so we don't re-report it.)
347         //
348         // We always skip all files that don't contain the string Q, as they cannot be
349         // relevant to finding references to Q.
350         //
351         // We parse all files leniently. In the presence of parsing errors, results are best-effort.
352
353         // Scan the workspace and build the import graph.
354         // Ignore broken packages.
355         _, rev, _ := importgraph.Build(q.Build)
356
357         // Find the set of packages that directly import defpkg.
358         defpkg := obj.Pkg().Path()
359         defpkg = strings.TrimSuffix(defpkg, "_test") // package x_test actually has package name x
360         defpkg = imports.VendorlessPath(defpkg)      // remove vendor goop
361
362         users := rev[defpkg]
363         if len(users) == 0 {
364                 users = make(map[string]bool)
365         }
366         // We also need to check defpkg itself, and its xtests.
367         // For the reverse graph packages, we process xtests with the main package.
368         // defpkg gets special handling; we must distinguish between in-package vs out-of-package.
369         // To make the control flow below simpler, add defpkg and defpkg xtest placeholders.
370         // Use "!test" instead of "_test" because "!" is not a valid character in an import path.
371         // (More precisely, it is not guaranteed to be a valid character in an import path,
372         // so it is unlikely that it will be in use. See https://golang.org/ref/spec#Import_declarations.)
373         users[defpkg] = true
374         users[defpkg+"!test"] = true
375
376         cwd, err := os.Getwd()
377         if err != nil {
378                 return err
379         }
380
381         defname := obj.Pkg().Name()                    // name of defining package, used for imports using import path only
382         isxtest := strings.HasSuffix(defname, "_test") // indicates whether the query object is defined in an xtest package
383
384         name := obj.Name()
385         namebytes := []byte(name)          // byte slice version of query object name, for early filtering
386         objpos := fset.Position(obj.Pos()) // position of query object, used to prevent re-emitting original decl
387
388         sema := make(chan struct{}, 20) // counting semaphore to limit I/O concurrency
389         var wg sync.WaitGroup
390
391         for u := range users {
392                 u := u
393                 wg.Add(1)
394                 go func() {
395                         defer wg.Done()
396
397                         uIsXTest := strings.HasSuffix(u, "!test") // indicates whether this package is the special defpkg xtest package
398                         u = strings.TrimSuffix(u, "!test")
399
400                         // Resolve package.
401                         sema <- struct{}{} // acquire token
402                         pkg, err := q.Build.Import(u, cwd, build.IgnoreVendor)
403                         <-sema // release token
404                         if err != nil {
405                                 return
406                         }
407
408                         // If we're not in the query package,
409                         // the object is in another package regardless,
410                         // so we want to process all files.
411                         // If we are in the query package,
412                         // we want to only process the files that are
413                         // part of that query package;
414                         // that set depends on whether the query package itself is an xtest.
415                         inQueryPkg := u == defpkg && isxtest == uIsXTest
416                         var files []string
417                         if !inQueryPkg || !isxtest {
418                                 files = append(files, pkg.GoFiles...)
419                                 files = append(files, pkg.TestGoFiles...)
420                                 files = append(files, pkg.CgoFiles...) // use raw cgo files, as we're only parsing
421                         }
422                         if !inQueryPkg || isxtest {
423                                 files = append(files, pkg.XTestGoFiles...)
424                         }
425
426                         if len(files) == 0 {
427                                 return
428                         }
429
430                         var deffiles map[string]*ast.File
431                         if inQueryPkg {
432                                 deffiles = make(map[string]*ast.File)
433                         }
434
435                         buf := new(bytes.Buffer) // reusable buffer for reading files
436
437                         for _, file := range files {
438                                 if !buildutil.IsAbsPath(q.Build, file) {
439                                         file = buildutil.JoinPath(q.Build, pkg.Dir, file)
440                                 }
441                                 buf.Reset()
442                                 sema <- struct{}{} // acquire token
443                                 src, err := readFile(q.Build, file, buf)
444                                 <-sema // release token
445                                 if err != nil {
446                                         continue
447                                 }
448
449                                 // Fast path: If the object's name isn't present anywhere in the source, ignore the file.
450                                 if !bytes.Contains(src, namebytes) {
451                                         continue
452                                 }
453
454                                 if inQueryPkg {
455                                         // If we're in the query package, we defer final processing until we have
456                                         // parsed all of the candidate files in the package.
457                                         // Best effort; allow errors and use what we can from what remains.
458                                         f, _ := parser.ParseFile(fset, file, src, parser.AllErrors)
459                                         if f != nil {
460                                                 deffiles[file] = f
461                                         }
462                                         continue
463                                 }
464
465                                 // We aren't in the query package. Go file by file.
466
467                                 // Parse out only the imports, to check whether the defining package
468                                 // was imported, and if so, under what names.
469                                 // Best effort; allow errors and use what we can from what remains.
470                                 f, _ := parser.ParseFile(fset, file, src, parser.ImportsOnly|parser.AllErrors)
471                                 if f == nil {
472                                         continue
473                                 }
474
475                                 // pkgnames is the set of names by which defpkg is imported in this file.
476                                 // (Multiple imports in the same file are legal but vanishingly rare.)
477                                 pkgnames := make([]string, 0, 1)
478                                 var isdotimport bool
479                                 for _, imp := range f.Imports {
480                                         path, err := strconv.Unquote(imp.Path.Value)
481                                         if err != nil || path != defpkg {
482                                                 continue
483                                         }
484                                         switch {
485                                         case imp.Name == nil:
486                                                 pkgnames = append(pkgnames, defname)
487                                         case imp.Name.Name == ".":
488                                                 isdotimport = true
489                                         default:
490                                                 pkgnames = append(pkgnames, imp.Name.Name)
491                                         }
492                                 }
493                                 if len(pkgnames) == 0 && !isdotimport {
494                                         // Defining package not imported, bail.
495                                         continue
496                                 }
497
498                                 // Re-parse the entire file.
499                                 // Parse errors are ok; we'll do the best we can with a partial AST, if we have one.
500                                 f, _ = parser.ParseFile(fset, file, src, parser.AllErrors)
501                                 if f == nil {
502                                         continue
503                                 }
504
505                                 // Walk the AST looking for references.
506                                 var refs []*ast.Ident
507                                 ast.Inspect(f, func(n ast.Node) bool {
508                                         // Check selector expressions.
509                                         // If the selector matches the target name,
510                                         // and the expression is one of the names
511                                         // that the defining package was imported under,
512                                         // then we have a match.
513                                         if sel, ok := n.(*ast.SelectorExpr); ok && sel.Sel.Name == name {
514                                                 if id, ok := sel.X.(*ast.Ident); ok {
515                                                         for _, n := range pkgnames {
516                                                                 if n == id.Name {
517                                                                         refs = append(refs, sel.Sel)
518                                                                         // Don't recurse further, to avoid duplicate entries
519                                                                         // from the dot import check below.
520                                                                         return false
521                                                                 }
522                                                         }
523                                                 }
524                                         }
525                                         // Dot imports are special.
526                                         // Objects imported from the defining package are placed in the package scope.
527                                         // go/ast does not resolve them to an object.
528                                         // At all other scopes (file, local), go/ast can do the resolution.
529                                         // So we're looking for object-free idents with the right name.
530                                         // The only other way to get something with the right name at the package scope
531                                         // is to *be* the defining package. We handle that case separately (inQueryPkg).
532                                         if isdotimport {
533                                                 if id, ok := n.(*ast.Ident); ok && id.Obj == nil && id.Name == name {
534                                                         refs = append(refs, id)
535                                                         return false
536                                                 }
537                                         }
538                                         return true
539                                 })
540
541                                 // Emit any references we found.
542                                 if len(refs) > 0 {
543                                         q.Output(fset, &referrersPackageResult{
544                                                 pkg:   types.NewPackage(pkg.ImportPath, pkg.Name),
545                                                 build: q.Build,
546                                                 fset:  fset,
547                                                 refs:  refs,
548                                         })
549                                 }
550                         }
551
552                         // If we're in the query package, we've now collected all the files in the package.
553                         // (Or at least the ones that might contain references to the object.)
554                         // Find and emit refs.
555                         if inQueryPkg {
556                                 // Bundle the files together into a package.
557                                 // This does package-level object resolution.
558                                 qpkg, _ := ast.NewPackage(fset, deffiles, nil, nil)
559                                 // Look up the query object; we know that it is defined in the package scope.
560                                 pkgobj := qpkg.Scope.Objects[name]
561                                 if pkgobj == nil {
562                                         panic("missing defpkg object for " + defpkg + "." + name)
563                                 }
564                                 // Find all references to the query object.
565                                 var refs []*ast.Ident
566                                 ast.Inspect(qpkg, func(n ast.Node) bool {
567                                         if id, ok := n.(*ast.Ident); ok {
568                                                 // Check both that this is a reference to the query object
569                                                 // and that it is not the query object itself;
570                                                 // the query object itself was already emitted.
571                                                 if id.Obj == pkgobj && objpos != fset.Position(id.Pos()) {
572                                                         refs = append(refs, id)
573                                                         return false
574                                                 }
575                                         }
576                                         return true
577                                 })
578                                 if len(refs) > 0 {
579                                         q.Output(fset, &referrersPackageResult{
580                                                 pkg:   types.NewPackage(pkg.ImportPath, pkg.Name),
581                                                 build: q.Build,
582                                                 fset:  fset,
583                                                 refs:  refs,
584                                         })
585                                 }
586                                 deffiles = nil // allow GC
587                         }
588                 }()
589         }
590
591         wg.Wait()
592
593         return nil
594 }
595
596 // findObject returns the object defined at the specified position.
597 func findObject(fset *token.FileSet, info *types.Info, objposn token.Position) types.Object {
598         good := func(obj types.Object) bool {
599                 if obj == nil {
600                         return false
601                 }
602                 posn := fset.Position(obj.Pos())
603                 return posn.Filename == objposn.Filename && posn.Offset == objposn.Offset
604         }
605         for _, obj := range info.Defs {
606                 if good(obj) {
607                         return obj
608                 }
609         }
610         for _, obj := range info.Implicits {
611                 if good(obj) {
612                         return obj
613                 }
614         }
615         return nil
616 }
617
618 // same reports whether x and y are identical, or both are PkgNames
619 // that import the same Package.
620 //
621 func sameObj(x, y types.Object) bool {
622         if x == y {
623                 return true
624         }
625         if x, ok := x.(*types.PkgName); ok {
626                 if y, ok := y.(*types.PkgName); ok {
627                         return x.Imported() == y.Imported()
628                 }
629         }
630         return false
631 }
632
633 func clearInfoFields(info *loader.PackageInfo) {
634         // TODO(adonovan): opt: save memory by eliminating unneeded scopes/objects.
635         // (Requires go/types change for Go 1.7.)
636         //   info.Pkg.Scope().ClearChildren()
637
638         // Discard the file ASTs and their accumulated type
639         // information to save memory.
640         info.Files = nil
641         info.Defs = make(map[*ast.Ident]types.Object)
642         info.Uses = make(map[*ast.Ident]types.Object)
643         info.Implicits = make(map[ast.Node]types.Object)
644
645         // Also, disable future collection of wholly unneeded
646         // type information for the package in case there is
647         // more type-checking to do (augmentation).
648         info.Types = nil
649         info.Scopes = nil
650         info.Selections = nil
651 }
652
653 // -------- utils --------
654
655 // An deterministic ordering for token.Pos that doesn't
656 // depend on the order in which packages were loaded.
657 func lessPos(fset *token.FileSet, x, y token.Pos) bool {
658         fx := fset.File(x)
659         fy := fset.File(y)
660         if fx != fy {
661                 return fx.Name() < fy.Name()
662         }
663         return x < y
664 }
665
666 type byNamePos struct {
667         fset *token.FileSet
668         ids  []*ast.Ident
669 }
670
671 func (p byNamePos) Len() int      { return len(p.ids) }
672 func (p byNamePos) Swap(i, j int) { p.ids[i], p.ids[j] = p.ids[j], p.ids[i] }
673 func (p byNamePos) Less(i, j int) bool {
674         return lessPos(p.fset, p.ids[i].NamePos, p.ids[j].NamePos)
675 }
676
677 // referrersInitialResult is the initial result of a "referrers" query.
678 type referrersInitialResult struct {
679         qinfo *loader.PackageInfo
680         obj   types.Object // object it denotes
681 }
682
683 func (r *referrersInitialResult) PrintPlain(printf printfFunc) {
684         printf(r.obj, "references to %s",
685                 types.ObjectString(r.obj, types.RelativeTo(r.qinfo.Pkg)))
686 }
687
688 func (r *referrersInitialResult) JSON(fset *token.FileSet) []byte {
689         var objpos string
690         if pos := r.obj.Pos(); pos.IsValid() {
691                 objpos = fset.Position(pos).String()
692         }
693         return toJSON(&serial.ReferrersInitial{
694                 Desc:   r.obj.String(),
695                 ObjPos: objpos,
696         })
697 }
698
699 // referrersPackageResult is the streaming result for one package of a "referrers" query.
700 type referrersPackageResult struct {
701         pkg   *types.Package
702         build *build.Context
703         fset  *token.FileSet
704         refs  []*ast.Ident // set of all other references to it
705 }
706
707 // forEachRef calls f(id, text) for id in r.refs, in order.
708 // Text is the text of the line on which id appears.
709 func (r *referrersPackageResult) foreachRef(f func(id *ast.Ident, text string)) {
710         // Show referring lines, like grep.
711         type fileinfo struct {
712                 refs     []*ast.Ident
713                 linenums []int            // line number of refs[i]
714                 data     chan interface{} // file contents or error
715         }
716         var fileinfos []*fileinfo
717         fileinfosByName := make(map[string]*fileinfo)
718
719         // First pass: start the file reads concurrently.
720         sema := make(chan struct{}, 20) // counting semaphore to limit I/O concurrency
721         for _, ref := range r.refs {
722                 posn := r.fset.Position(ref.Pos())
723                 fi := fileinfosByName[posn.Filename]
724                 if fi == nil {
725                         fi = &fileinfo{data: make(chan interface{})}
726                         fileinfosByName[posn.Filename] = fi
727                         fileinfos = append(fileinfos, fi)
728
729                         // First request for this file:
730                         // start asynchronous read.
731                         go func() {
732                                 sema <- struct{}{} // acquire token
733                                 content, err := readFile(r.build, posn.Filename, nil)
734                                 <-sema // release token
735                                 if err != nil {
736                                         fi.data <- err
737                                 } else {
738                                         fi.data <- content
739                                 }
740                         }()
741                 }
742                 fi.refs = append(fi.refs, ref)
743                 fi.linenums = append(fi.linenums, posn.Line)
744         }
745
746         // Second pass: print refs in original order.
747         // One line may have several refs at different columns.
748         for _, fi := range fileinfos {
749                 v := <-fi.data // wait for I/O completion
750
751                 // Print one item for all refs in a file that could not
752                 // be loaded (perhaps due to //line directives).
753                 if err, ok := v.(error); ok {
754                         var suffix string
755                         if more := len(fi.refs) - 1; more > 0 {
756                                 suffix = fmt.Sprintf(" (+ %d more refs in this file)", more)
757                         }
758                         f(fi.refs[0], err.Error()+suffix)
759                         continue
760                 }
761
762                 lines := bytes.Split(v.([]byte), []byte("\n"))
763                 for i, ref := range fi.refs {
764                         f(ref, string(lines[fi.linenums[i]-1]))
765                 }
766         }
767 }
768
769 // readFile is like ioutil.ReadFile, but
770 // it goes through the virtualized build.Context.
771 // If non-nil, buf must have been reset.
772 func readFile(ctxt *build.Context, filename string, buf *bytes.Buffer) ([]byte, error) {
773         rc, err := buildutil.OpenFile(ctxt, filename)
774         if err != nil {
775                 return nil, err
776         }
777         defer rc.Close()
778         if buf == nil {
779                 buf = new(bytes.Buffer)
780         }
781         if _, err := io.Copy(buf, rc); err != nil {
782                 return nil, err
783         }
784         return buf.Bytes(), nil
785 }
786
787 func (r *referrersPackageResult) PrintPlain(printf printfFunc) {
788         r.foreachRef(func(id *ast.Ident, text string) {
789                 printf(id, "%s", text)
790         })
791 }
792
793 func (r *referrersPackageResult) JSON(fset *token.FileSet) []byte {
794         refs := serial.ReferrersPackage{Package: r.pkg.Path()}
795         r.foreachRef(func(id *ast.Ident, text string) {
796                 refs.Refs = append(refs.Refs, serial.Ref{
797                         Pos:  fset.Position(id.NamePos).String(),
798                         Text: text,
799                 })
800         })
801         return toJSON(refs)
802 }