github.com/april1989/origin-go-tools@v0.0.32/internal/lsp/source/references.go (about) 1 // Copyright 2019 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 source 6 7 import ( 8 "context" 9 "go/ast" 10 "go/token" 11 "go/types" 12 "sort" 13 14 "github.com/april1989/origin-go-tools/internal/event" 15 "github.com/april1989/origin-go-tools/internal/lsp/protocol" 16 "github.com/april1989/origin-go-tools/internal/span" 17 "golang.org/x/xerrors" 18 ) 19 20 // ReferenceInfo holds information about reference to an identifier in Go source. 21 type ReferenceInfo struct { 22 Name string 23 mappedRange 24 ident *ast.Ident 25 obj types.Object 26 pkg Package 27 isDeclaration bool 28 } 29 30 // References returns a list of references for a given identifier within the packages 31 // containing i.File. Declarations appear first in the result. 32 func References(ctx context.Context, s Snapshot, f FileHandle, pp protocol.Position, includeDeclaration bool) ([]*ReferenceInfo, error) { 33 ctx, done := event.Start(ctx, "source.References") 34 defer done() 35 36 qualifiedObjs, err := qualifiedObjsAtProtocolPos(ctx, s, f, pp) 37 // Don't return references for builtin types. 38 if xerrors.Is(err, errBuiltin) { 39 return nil, nil 40 } 41 if err != nil { 42 return nil, err 43 } 44 refs, err := references(ctx, s, qualifiedObjs, includeDeclaration) 45 if err != nil { 46 return nil, err 47 } 48 toSort := refs 49 if includeDeclaration { 50 toSort = refs[1:] 51 } 52 sort.Slice(toSort, func(i, j int) bool { 53 x := span.CompareURI(toSort[i].URI(), toSort[j].URI()) 54 if x == 0 { 55 return toSort[i].ident.Pos() < toSort[j].ident.Pos() 56 } 57 return x < 0 58 }) 59 return refs, nil 60 } 61 62 // references is a helper function to avoid recomputing qualifiedObjsAtProtocolPos. 63 func references(ctx context.Context, snapshot Snapshot, qos []qualifiedObject, includeDeclaration bool) ([]*ReferenceInfo, error) { 64 var ( 65 references []*ReferenceInfo 66 seen = make(map[token.Position]bool) 67 ) 68 69 // Make sure declaration is the first item in the response. 70 if includeDeclaration { 71 filename := snapshot.FileSet().Position(qos[0].obj.Pos()).Filename 72 pgf, err := qos[0].pkg.File(span.URIFromPath(filename)) 73 if err != nil { 74 return nil, err 75 } 76 ident, err := findIdentifier(ctx, snapshot, qos[0].pkg, pgf.File, qos[0].obj.Pos()) 77 if err != nil { 78 return nil, err 79 } 80 references = append(references, &ReferenceInfo{ 81 mappedRange: ident.mappedRange, 82 Name: qos[0].obj.Name(), 83 ident: ident.ident, 84 obj: qos[0].obj, 85 pkg: ident.pkg, 86 isDeclaration: true, 87 }) 88 } 89 for _, qo := range qos { 90 var searchPkgs []Package 91 92 // Only search dependents if the object is exported. 93 if qo.obj.Exported() { 94 reverseDeps, err := snapshot.GetReverseDependencies(ctx, qo.pkg.ID()) 95 if err != nil { 96 return nil, err 97 } 98 searchPkgs = append(searchPkgs, reverseDeps...) 99 } 100 // Add the package in which the identifier is declared. 101 searchPkgs = append(searchPkgs, qo.pkg) 102 for _, pkg := range searchPkgs { 103 for ident, obj := range pkg.GetTypesInfo().Uses { 104 if obj != qo.obj { 105 continue 106 } 107 pos := snapshot.FileSet().Position(ident.Pos()) 108 if seen[pos] { 109 continue 110 } 111 seen[pos] = true 112 rng, err := posToMappedRange(snapshot, pkg, ident.Pos(), ident.End()) 113 if err != nil { 114 return nil, err 115 } 116 references = append(references, &ReferenceInfo{ 117 Name: ident.Name, 118 ident: ident, 119 pkg: pkg, 120 obj: obj, 121 mappedRange: rng, 122 }) 123 } 124 } 125 } 126 return references, nil 127 }