GoPLS Viewer

Home|gopls/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
5package main
6
7import (
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.
32func referrers(q *Queryerror {
33    fset := token.NewFileSet()
34    lconf := loader.Config{FsetfsetBuildq.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    lprogerr := lconf.Load()
49    if err != nil {
50        return err
51    }
52
53    qposerr := parseQueryPos(lprogq.Posfalse)
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(qqpos.info.Pkg.Path())
70        }
71        return fmt.Errorf("no object for identifier: %T"qpos.path[1])
72    }
73
74    // Imported package name?
75    if pkgnameok := obj.(*types.PkgName); ok {
76        return packageReferrers(qpkgname.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        qinfoqpos.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 globalpkglevel := classify(obj); global {
95        if pkglevel {
96            return globalReferrersPkgLevel(qobjfset)
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(qqpos.info.Pkg.Path(), defpkgobjposn)
102    }
103
104    outputUses(qfsetusesOf(objqpos.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.
111func classify(obj types.Object) (globalpkglevel bool) {
112    if obj.Exported() {
113        if obj.Parent() == nil {
114            // selectable object (field or method)
115            return truefalse
116        }
117        if obj.Parent() == obj.Pkg().Scope() {
118            // lexical object (package-level var/const/func/type)
119            return truetrue
120        }
121    }
122    // object with unexported named or defined in local scope
123    return falsefalse
124}
125
126// packageReferrers reports all references to the specified package
127// throughout the workspace.
128func packageReferrers(q *Querypath stringerror {
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        Buildq.Build,
142        TypeCheckFuncBodies: func(p stringbool {
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.PackageInfofiles []*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.NoPosqpkgqpkg.Name(), qpkg)
170            q.Output(fset, &referrersInitialResult{
171                qinfoinfo,
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 idobj := range info.Uses {
184                if objok := obj.(*types.PkgName); ok && obj.Imported() == qpkg {
185                    refs = append(refsid)
186                }
187            }
188            outputUses(qfsetrefsinfo.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
203func usesOf(queryObj types.Objectinfo *loader.PackageInfo) []*ast.Ident {
204    var refs []*ast.Ident
205    for idobj := range info.Uses {
206        if sameObj(queryObjobj) {
207            refs = append(refsid)
208        }
209    }
210    return refs
211}
212
213// outputUses outputs a result describing refs, which appear in the package denoted by info.
214func outputUses(q *Queryfset *token.FileSetrefs []*ast.Identpkg *types.Package) {
215    if len(refs) > 0 {
216        sort.Sort(byNamePos{fsetrefs})
217        q.Output(fset, &referrersPackageResult{
218            pkg:   pkg,
219            buildq.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.
229func globalReferrers(q *Queryqpkgdefpkg stringobjposn token.Positionerror {
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        Buildq.Build,
243        TypeCheckFuncBodies: func(p stringbool {
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.PackageInfofiles []*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.Infoobjposn)
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                        objposndefpkg)
292                }
293            }
294            obj := qobj
295            mu.Unlock()
296
297            // Look for references to the query object.
298            if obj != nil {
299                outputUses(qfsetusesOf(objinfo), 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.
317func globalReferrersPkgLevel(q *Queryobj types.Objectfset *token.FileSeterror {
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    cwderr := 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            pkgerr := q.Build.Import(ucwdbuild.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(filespkg.GoFiles...)
419                files = append(filespkg.TestGoFiles...)
420                files = append(filespkg.CgoFiles...) // use raw cgo files, as we're only parsing
421            }
422            if !inQueryPkg || isxtest {
423                files = append(filespkg.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.Buildfile) {
439                    file = buildutil.JoinPath(q.Buildpkg.Dirfile)
440                }
441                buf.Reset()
442                sema <- struct{}{} // acquire token
443                srcerr := readFile(q.Buildfilebuf)
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(srcnamebytes) {
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(fsetfilesrcparser.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(fsetfilesrcparser.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([]string01)
478                var isdotimport bool
479                for _imp := range f.Imports {
480                    patherr := strconv.Unquote(imp.Path.Value)
481                    if err != nil || path != defpkg {
482                        continue
483                    }
484                    switch {
485                    case imp.Name == nil:
486                        pkgnames = append(pkgnamesdefname)
487                    case imp.Name.Name == ".":
488                        isdotimport = true
489                    default:
490                        pkgnames = append(pkgnamesimp.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(fsetfilesrcparser.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.Nodebool {
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 selok := n.(*ast.SelectorExpr); ok && sel.Sel.Name == name {
514                        if idok := sel.X.(*ast.Ident); ok {
515                            for _n := range pkgnames {
516                                if n == id.Name {
517                                    refs = append(refssel.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 idok := n.(*ast.Ident); ok && id.Obj == nil && id.Name == name {
534                            refs = append(refsid)
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.ImportPathpkg.Name),
545                        buildq.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(fsetdeffilesnilnil)
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.Nodebool {
567                    if idok := 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(refsid)
573                            return false
574                        }
575                    }
576                    return true
577                })
578                if len(refs) > 0 {
579                    q.Output(fset, &referrersPackageResult{
580                        pkg:   types.NewPackage(pkg.ImportPathpkg.Name),
581                        buildq.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.
597func findObject(fset *token.FileSetinfo *types.Infoobjposn token.Positiontypes.Object {
598    good := func(obj types.Objectbool {
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.
620func sameObj(xy types.Objectbool {
621    if x == y {
622        return true
623    }
624    if xok := x.(*types.PkgName); ok {
625        if yok := y.(*types.PkgName); ok {
626            return x.Imported() == y.Imported()
627        }
628    }
629    return false
630}
631
632func clearInfoFields(info *loader.PackageInfo) {
633    // TODO(adonovan): opt: save memory by eliminating unneeded scopes/objects.
634    // (Requires go/types change for Go 1.7.)
635    //   info.Pkg.Scope().ClearChildren()
636
637    // Discard the file ASTs and their accumulated type
638    // information to save memory.
639    info.Files = nil
640    info.Defs = make(map[*ast.Ident]types.Object)
641    info.Uses = make(map[*ast.Ident]types.Object)
642    info.Implicits = make(map[ast.Node]types.Object)
643
644    // Also, disable future collection of wholly unneeded
645    // type information for the package in case there is
646    // more type-checking to do (augmentation).
647    info.Types = nil
648    info.Scopes = nil
649    info.Selections = nil
650}
651
652// -------- utils --------
653
654// An deterministic ordering for token.Pos that doesn't
655// depend on the order in which packages were loaded.
656func lessPos(fset *token.FileSetxy token.Posbool {
657    fx := fset.File(x)
658    fy := fset.File(y)
659    if fx != fy {
660        return fx.Name() < fy.Name()
661    }
662    return x < y
663}
664
665type byNamePos struct {
666    fset *token.FileSet
667    ids  []*ast.Ident
668}
669
670func (p byNamePosLen() int      { return len(p.ids) }
671func (p byNamePosSwap(ij int) { p.ids[i], p.ids[j] = p.ids[j], p.ids[i] }
672func (p byNamePosLess(ij intbool {
673    return lessPos(p.fsetp.ids[i].NamePosp.ids[j].NamePos)
674}
675
676// referrersInitialResult is the initial result of a "referrers" query.
677type referrersInitialResult struct {
678    qinfo *loader.PackageInfo
679    obj   types.Object // object it denotes
680}
681
682func (r *referrersInitialResultPrintPlain(printf printfFunc) {
683    printf(r.obj"references to %s",
684        types.ObjectString(r.objtypes.RelativeTo(r.qinfo.Pkg)))
685}
686
687func (r *referrersInitialResultJSON(fset *token.FileSet) []byte {
688    var objpos string
689    if pos := r.obj.Pos(); pos.IsValid() {
690        objpos = fset.Position(pos).String()
691    }
692    return toJSON(&serial.ReferrersInitial{
693        Desc:   r.obj.String(),
694        ObjPosobjpos,
695    })
696}
697
698// referrersPackageResult is the streaming result for one package of a "referrers" query.
699type referrersPackageResult struct {
700    pkg   *types.Package
701    build *build.Context
702    fset  *token.FileSet
703    refs  []*ast.Ident // set of all other references to it
704}
705
706// forEachRef calls f(id, text) for id in r.refs, in order.
707// Text is the text of the line on which id appears.
708func (r *referrersPackageResultforeachRef(f func(id *ast.Identtext string)) {
709    // Show referring lines, like grep.
710    type fileinfo struct {
711        refs     []*ast.Ident
712        linenums []int            // line number of refs[i]
713        data     chan interface{} // file contents or error
714    }
715    var fileinfos []*fileinfo
716    fileinfosByName := make(map[string]*fileinfo)
717
718    // First pass: start the file reads concurrently.
719    sema := make(chan struct{}, 20// counting semaphore to limit I/O concurrency
720    for _ref := range r.refs {
721        posn := r.fset.Position(ref.Pos())
722        fi := fileinfosByName[posn.Filename]
723        if fi == nil {
724            fi = &fileinfo{datamake(chan interface{})}
725            fileinfosByName[posn.Filename] = fi
726            fileinfos = append(fileinfosfi)
727
728            // First request for this file:
729            // start asynchronous read.
730            go func() {
731                sema <- struct{}{} // acquire token
732                contenterr := readFile(r.buildposn.Filenamenil)
733                <-sema // release token
734                if err != nil {
735                    fi.data <- err
736                } else {
737                    fi.data <- content
738                }
739            }()
740        }
741        fi.refs = append(fi.refsref)
742        fi.linenums = append(fi.linenumsposn.Line)
743    }
744
745    // Second pass: print refs in original order.
746    // One line may have several refs at different columns.
747    for _fi := range fileinfos {
748        v := <-fi.data // wait for I/O completion
749
750        // Print one item for all refs in a file that could not
751        // be loaded (perhaps due to //line directives).
752        if errok := v.(error); ok {
753            var suffix string
754            if more := len(fi.refs) - 1more > 0 {
755                suffix = fmt.Sprintf(" (+ %d more refs in this file)"more)
756            }
757            f(fi.refs[0], err.Error()+suffix)
758            continue
759        }
760
761        lines := bytes.Split(v.([]byte), []byte("\n"))
762        for iref := range fi.refs {
763            f(refstring(lines[fi.linenums[i]-1]))
764        }
765    }
766}
767
768// readFile is like ioutil.ReadFile, but
769// it goes through the virtualized build.Context.
770// If non-nil, buf must have been reset.
771func readFile(ctxt *build.Contextfilename stringbuf *bytes.Buffer) ([]byteerror) {
772    rcerr := buildutil.OpenFile(ctxtfilename)
773    if err != nil {
774        return nilerr
775    }
776    defer rc.Close()
777    if buf == nil {
778        buf = new(bytes.Buffer)
779    }
780    if _err := io.Copy(bufrc); err != nil {
781        return nilerr
782    }
783    return buf.Bytes(), nil
784}
785
786func (r *referrersPackageResultPrintPlain(printf printfFunc) {
787    r.foreachRef(func(id *ast.Identtext string) {
788        printf(id"%s"text)
789    })
790}
791
792func (r *referrersPackageResultJSON(fset *token.FileSet) []byte {
793    refs := serial.ReferrersPackage{Packager.pkg.Path()}
794    r.foreachRef(func(id *ast.Identtext string) {
795        refs.Refs = append(refs.Refsserial.Ref{
796            Pos:  fset.Position(id.NamePos).String(),
797            Texttext,
798        })
799    })
800    return toJSON(refs)
801}
802
MembersX
usesOf.refs
globalReferrersPkgLevel.RangeStmt_12798.BlockStmt.BlockStmt.uIsXTest
clearInfoFields.info
readFile.rc
packageReferrers.path
globalReferrers.q
globalReferrers.qpkg
referrersInitialResult.PrintPlain.printf
referrersPackageResult.foreachRef.RangeStmt_22747.ref
globalReferrers.BlockStmt.BlockStmt.obj
globalReferrersPkgLevel.fset
globalReferrersPkgLevel.rev
globalReferrersPkgLevel.isxtest
globalReferrersPkgLevel.RangeStmt_12798.BlockStmt.u
globalReferrersPkgLevel.RangeStmt_12798.BlockStmt.BlockStmt.BlockStmt.refs
byNamePos.ids
referrersPackageResult.foreachRef.RangeStmt_22747.BlockStmt.posn
referrersPackageResult.PrintPlain
referrersPackageResult.PrintPlain.printf
packageReferrers.lconf
globalReferrersPkgLevel.RangeStmt_12798.BlockStmt.BlockStmt.RangeStmt_14121.BlockStmt.RangeStmt_15542.BlockStmt.path
findObject.BlockStmt.posn
byNamePos.Swap
referrersPackageResult.foreachRef
usesOf
globalReferrers._
lessPos.fy
byNamePos.Swap.p
referrersPackageResult
findObject.RangeStmt_19541.obj
referrersPackageResult.fset
globalReferrersPkgLevel.cwd
globalReferrersPkgLevel.RangeStmt_12798.BlockStmt.BlockStmt.RangeStmt_14121.BlockStmt.BlockStmt._
lessPos.fset
referrersPackageResult.pkg
referrersPackageResult.foreachRef.RangeStmt_22747.BlockStmt.BlockStmt.BlockStmt.err
referrersPackageResult.foreachRef.RangeStmt_23484.BlockStmt.RangeStmt_23940.i
referrersPackageResult.JSON.refs
packageReferrers._
globalReferrersPkgLevel.RangeStmt_12798.BlockStmt.BlockStmt.err
referrersPackageResult.refs
readFile.err
packageReferrers.BlockStmt.BlockStmt.refs
globalReferrers.qobj
sameObj.x
packageReferrers.qpkg
globalReferrersPkgLevel.RangeStmt_12798.BlockStmt.BlockStmt.RangeStmt_14121.BlockStmt.src
findObject.fset
sameObj.y
byNamePos.Len.p
referrersPackageResult.foreachRef.fileinfo.refs
referrersPackageResult.PrintPlain.r
packageReferrers
packageReferrers.q
byNamePos.Less
referrersPackageResult.foreachRef.r
outputUses
outputUses.fset
outputUses.pkg
globalReferrers.rev
globalReferrersPkgLevel.namebytes
globalReferrersPkgLevel.RangeStmt_12798.BlockStmt.BlockStmt.RangeStmt_14121.BlockStmt.RangeStmt_15542.imp
globalReferrersPkgLevel.RangeStmt_12798.BlockStmt.BlockStmt.RangeStmt_14121.BlockStmt.RangeStmt_15542.BlockStmt.err
referrersPackageResult.foreachRef.RangeStmt_22747.BlockStmt.BlockStmt.BlockStmt.content
referrersPackageResult.foreachRef.RangeStmt_23484.BlockStmt.lines
referrers.obj
classify.global
globalReferrers.defpkg
globalReferrersPkgLevel.err
globalReferrersPkgLevel.RangeStmt_12798.u
lessPos.y
byNamePos
byNamePos.fset
referrersPackageResult.foreachRef.fileinfos
readFile.ctxt
packageReferrers.rev
packageReferrers.RangeStmt_3935.path
globalReferrers.objposn
globalReferrersPkgLevel.q
globalReferrersPkgLevel.name
globalReferrersPkgLevel.RangeStmt_12798.BlockStmt.BlockStmt.pkg
referrersPackageResult.foreachRef.f
referrers._
referrers.pkglevel
usesOf.RangeStmt_5647.id
usesOf.RangeStmt_5647.obj
outputUses.q
findObject.RangeStmt_19612.obj
lessPos.x
referrersPackageResult.foreachRef.RangeStmt_23484.fi
referrersPackageResult.foreachRef.RangeStmt_23484.BlockStmt.RangeStmt_23940.ref
referrers.fset
globalReferrersPkgLevel.RangeStmt_12798.BlockStmt.BlockStmt.deffiles
globalReferrersPkgLevel.RangeStmt_12798.BlockStmt.BlockStmt.RangeStmt_14121.BlockStmt._
byNamePos.Swap.j
referrersPackageResult.build
readFile
referrers.global
referrers.BlockStmt.defpkg
globalReferrersPkgLevel.RangeStmt_12798.BlockStmt.BlockStmt.RangeStmt_14121.BlockStmt.err
globalReferrersPkgLevel.RangeStmt_12798.BlockStmt.BlockStmt.BlockStmt.qpkg
referrersInitialResult.PrintPlain.r
referrersInitialResult.JSON
readFile.filename
outputUses.refs
globalReferrers.fset
globalReferrersPkgLevel.defname
globalReferrersPkgLevel.sema
globalReferrersPkgLevel.RangeStmt_12798.BlockStmt.BlockStmt.BlockStmt._
readFile.buf
readFile._
globalReferrers
globalReferrersPkgLevel.RangeStmt_12798.BlockStmt.BlockStmt.RangeStmt_14121.BlockStmt.isdotimport
referrersPackageResult.foreachRef.sema
referrers
usesOf.info
globalReferrers.RangeStmt_7058.path
globalReferrersPkgLevel._
globalReferrersPkgLevel.wg
globalReferrersPkgLevel.RangeStmt_12798.BlockStmt.BlockStmt.files
referrers.err
referrers.RangeStmt_950.path
classify
referrersPackageResult.foreachRef.fileinfosByName
referrers.BlockStmt.objposn
globalReferrersPkgLevel
globalReferrersPkgLevel.RangeStmt_12798.BlockStmt.BlockStmt.buf
referrersPackageResult.JSON
referrers.lprog
packageReferrers.BlockStmt.BlockStmt.fakepkgname
referrersInitialResult.qinfo
referrersInitialResult.JSON.r
referrersPackageResult.foreachRef.fileinfo
classify.obj
globalReferrers.lconf
sameObj
byNamePos.Less.i
referrersInitialResult.obj
referrersInitialResult.JSON.pos
referrers.lconf
globalReferrersPkgLevel.RangeStmt_12798.BlockStmt.BlockStmt.RangeStmt_14121.BlockStmt.BlockStmt.f
byNamePos.Len
referrersInitialResult
referrersInitialResult.PrintPlain
referrersInitialResult.JSON.objpos
referrersPackageResult.JSON.fset
referrers.q
globalReferrersPkgLevel.RangeStmt_12798.BlockStmt.BlockStmt.RangeStmt_14121.file
lessPos
referrersInitialResult.JSON.fset
packageReferrers.BlockStmt.BlockStmt.RangeStmt_5182.obj
globalReferrersPkgLevel.RangeStmt_12798.BlockStmt.BlockStmt.RangeStmt_14121.BlockStmt.refs
globalReferrersPkgLevel.RangeStmt_12798.BlockStmt.BlockStmt.RangeStmt_14121.BlockStmt.BlockStmt.BlockStmt.BlockStmt.RangeStmt_16687.n
findObject
findObject.objposn
globalReferrersPkgLevel.defpkg
referrersPackageResult.foreachRef.fileinfo.linenums
referrersPackageResult.JSON.r
imports
referrers.qpos
classify.pkglevel
globalReferrers.mu
byNamePos.Swap.i
byNamePos.Less.p
byNamePos.Less.j
globalReferrersPkgLevel.objpos
globalReferrersPkgLevel.RangeStmt_12798.BlockStmt.BlockStmt.RangeStmt_14121.BlockStmt.pkgnames
findObject.info
lessPos.fx
referrersPackageResult.foreachRef.RangeStmt_23484.BlockStmt.BlockStmt.suffix
packageReferrers.fset
packageReferrers.BlockStmt.BlockStmt.RangeStmt_5182.id
usesOf.queryObj
globalReferrers.users
globalReferrersPkgLevel.obj
globalReferrersPkgLevel.RangeStmt_12798.BlockStmt.BlockStmt.RangeStmt_14121.BlockStmt.f
clearInfoFields
referrersPackageResult.foreachRef.fileinfo.data
Members
X