golang.org/x/tools/gopls@v0.15.3/internal/cache/xrefs/xrefs.go (about) 1 // Copyright 2022 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 xrefs defines the serializable index of cross-package 6 // references that is computed during type checking. 7 // 8 // See ../references.go for the 'references' query. 9 package xrefs 10 11 import ( 12 "go/ast" 13 "go/types" 14 "sort" 15 16 "golang.org/x/tools/go/types/objectpath" 17 "golang.org/x/tools/gopls/internal/cache/metadata" 18 "golang.org/x/tools/gopls/internal/cache/parsego" 19 "golang.org/x/tools/gopls/internal/protocol" 20 "golang.org/x/tools/gopls/internal/util/frob" 21 "golang.org/x/tools/gopls/internal/util/typesutil" 22 "golang.org/x/tools/internal/typeparams" 23 ) 24 25 // Index constructs a serializable index of outbound cross-references 26 // for the specified type-checked package. 27 func Index(files []*parsego.File, pkg *types.Package, info *types.Info) []byte { 28 // pkgObjects maps each referenced package Q to a mapping: 29 // from each referenced symbol in Q to the ordered list 30 // of references to that symbol from this package. 31 // A nil types.Object indicates a reference 32 // to the package as a whole: an import. 33 pkgObjects := make(map[*types.Package]map[types.Object]*gobObject) 34 35 // getObjects returns the object-to-references mapping for a package. 36 getObjects := func(pkg *types.Package) map[types.Object]*gobObject { 37 objects, ok := pkgObjects[pkg] 38 if !ok { 39 objects = make(map[types.Object]*gobObject) 40 pkgObjects[pkg] = objects 41 } 42 return objects 43 } 44 45 objectpathFor := new(objectpath.Encoder).For 46 47 for fileIndex, pgf := range files { 48 49 nodeRange := func(n ast.Node) protocol.Range { 50 rng, err := pgf.PosRange(n.Pos(), n.End()) 51 if err != nil { 52 panic(err) // can't fail 53 } 54 return rng 55 } 56 57 ast.Inspect(pgf.File, func(n ast.Node) bool { 58 switch n := n.(type) { 59 case *ast.Ident: 60 // Report a reference for each identifier that 61 // uses a symbol exported from another package. 62 // (The built-in error.Error method has no package.) 63 if n.IsExported() { 64 if obj, ok := info.Uses[n]; ok && 65 obj.Pkg() != nil && 66 obj.Pkg() != pkg { 67 68 // For instantiations of generic methods, 69 // use the generic object (see issue #60622). 70 if fn, ok := obj.(*types.Func); ok { 71 obj = typeparams.OriginMethod(fn) 72 } 73 74 objects := getObjects(obj.Pkg()) 75 gobObj, ok := objects[obj] 76 if !ok { 77 path, err := objectpathFor(obj) 78 if err != nil { 79 // Capitalized but not exported 80 // (e.g. local const/var/type). 81 return true 82 } 83 gobObj = &gobObject{Path: path} 84 objects[obj] = gobObj 85 } 86 87 gobObj.Refs = append(gobObj.Refs, gobRef{ 88 FileIndex: fileIndex, 89 Range: nodeRange(n), 90 }) 91 } 92 } 93 94 case *ast.ImportSpec: 95 // Report a reference from each import path 96 // string to the imported package. 97 pkgname, ok := typesutil.ImportedPkgName(info, n) 98 if !ok { 99 return true // missing import 100 } 101 objects := getObjects(pkgname.Imported()) 102 gobObj, ok := objects[nil] 103 if !ok { 104 gobObj = &gobObject{Path: ""} 105 objects[nil] = gobObj 106 } 107 gobObj.Refs = append(gobObj.Refs, gobRef{ 108 FileIndex: fileIndex, 109 Range: nodeRange(n.Path), 110 }) 111 } 112 return true 113 }) 114 } 115 116 // Flatten the maps into slices, and sort for determinism. 117 var packages []*gobPackage 118 for p := range pkgObjects { 119 objects := pkgObjects[p] 120 gp := &gobPackage{ 121 PkgPath: metadata.PackagePath(p.Path()), 122 Objects: make([]*gobObject, 0, len(objects)), 123 } 124 for _, gobObj := range objects { 125 gp.Objects = append(gp.Objects, gobObj) 126 } 127 sort.Slice(gp.Objects, func(i, j int) bool { 128 return gp.Objects[i].Path < gp.Objects[j].Path 129 }) 130 packages = append(packages, gp) 131 } 132 sort.Slice(packages, func(i, j int) bool { 133 return packages[i].PkgPath < packages[j].PkgPath 134 }) 135 136 return packageCodec.Encode(packages) 137 } 138 139 // Lookup searches a serialized index produced by an indexPackage 140 // operation on m, and returns the locations of all references from m 141 // to any object in the target set. Each object is denoted by a pair 142 // of (package path, object path). 143 func Lookup(mp *metadata.Package, data []byte, targets map[metadata.PackagePath]map[objectpath.Path]struct{}) (locs []protocol.Location) { 144 var packages []*gobPackage 145 packageCodec.Decode(data, &packages) 146 for _, gp := range packages { 147 if objectSet, ok := targets[gp.PkgPath]; ok { 148 for _, gobObj := range gp.Objects { 149 if _, ok := objectSet[gobObj.Path]; ok { 150 for _, ref := range gobObj.Refs { 151 uri := mp.CompiledGoFiles[ref.FileIndex] 152 locs = append(locs, protocol.Location{ 153 URI: uri, 154 Range: ref.Range, 155 }) 156 } 157 } 158 } 159 } 160 } 161 162 return locs 163 } 164 165 // -- serialized representation -- 166 167 // The cross-reference index records the location of all references 168 // from one package to symbols defined in other packages 169 // (dependencies). It does not record within-package references. 170 // The index for package P consists of a list of gopPackage records, 171 // each enumerating references to symbols defined a single dependency, Q. 172 173 // TODO(adonovan): opt: choose a more compact encoding. 174 // The gobRef.Range field is the obvious place to begin. 175 176 // (The name says gob but in fact we use frob.) 177 var packageCodec = frob.CodecFor[[]*gobPackage]() 178 179 // A gobPackage records the set of outgoing references from the index 180 // package to symbols defined in a dependency package. 181 type gobPackage struct { 182 PkgPath metadata.PackagePath // defining package (Q) 183 Objects []*gobObject // set of Q objects referenced by P 184 } 185 186 // A gobObject records all references to a particular symbol. 187 type gobObject struct { 188 Path objectpath.Path // symbol name within package; "" => import of package itself 189 Refs []gobRef // locations of references within P, in lexical order 190 } 191 192 type gobRef struct { 193 FileIndex int // index of enclosing file within P's CompiledGoFiles 194 Range protocol.Range // source range of reference 195 }