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 5package source 6 7import ( 8 "context" 9 "go/ast" 10 "go/types" 11 12 "golang.org/x/tools/internal/telemetry/trace" 13 errors "golang.org/x/xerrors" 14) 15 16// ReferenceInfo holds information about reference to an identifier in Go source. 17type ReferenceInfo struct { 18 Name string 19 mappedRange 20 ident *ast.Ident 21 obj types.Object 22 pkg Package 23 isDeclaration bool 24} 25 26// References returns a list of references for a given identifier within the packages 27// containing i.File. Declarations appear first in the result. 28func (i *IdentifierInfo) References(ctx context.Context) ([]*ReferenceInfo, error) { 29 ctx, done := trace.StartSpan(ctx, "source.References") 30 defer done() 31 32 var references []*ReferenceInfo 33 34 // If the object declaration is nil, assume it is an import spec and do not look for references. 35 if i.Declaration.obj == nil { 36 return nil, errors.Errorf("no references for an import spec") 37 } 38 info := i.pkg.GetTypesInfo() 39 if info == nil { 40 return nil, errors.Errorf("package %s has no types info", i.pkg.PkgPath()) 41 } 42 if i.Declaration.wasImplicit { 43 // The definition is implicit, so we must add it separately. 44 // This occurs when the variable is declared in a type switch statement 45 // or is an implicit package name. Both implicits are local to a file. 46 references = append(references, &ReferenceInfo{ 47 Name: i.Declaration.obj.Name(), 48 mappedRange: i.Declaration.mappedRange, 49 obj: i.Declaration.obj, 50 pkg: i.pkg, 51 isDeclaration: true, 52 }) 53 } 54 for ident, obj := range info.Defs { 55 if obj == nil || !sameObj(obj, i.Declaration.obj) { 56 continue 57 } 58 rng, err := posToMappedRange(ctx, i.View, i.pkg, ident.Pos(), ident.End()) 59 if err != nil { 60 return nil, err 61 } 62 // Add the declarations at the beginning of the references list. 63 references = append([]*ReferenceInfo{{ 64 Name: ident.Name, 65 ident: ident, 66 obj: obj, 67 pkg: i.pkg, 68 isDeclaration: true, 69 mappedRange: rng, 70 }}, references...) 71 } 72 for ident, obj := range info.Uses { 73 if obj == nil || !sameObj(obj, i.Declaration.obj) { 74 continue 75 } 76 rng, err := posToMappedRange(ctx, i.View, i.pkg, ident.Pos(), ident.End()) 77 if err != nil { 78 return nil, err 79 } 80 references = append(references, &ReferenceInfo{ 81 Name: ident.Name, 82 ident: ident, 83 pkg: i.pkg, 84 obj: obj, 85 mappedRange: rng, 86 }) 87 } 88 return references, nil 89} 90 91// sameObj returns true if obj is the same as declObj. 92// Objects are the same if they have the some Pos and Name. 93func sameObj(obj, declObj types.Object) bool { 94 // TODO(suzmue): support the case where an identifier may have two different 95 // declaration positions. 96 return obj.Pos() == declObj.Pos() && obj.Name() == declObj.Name() 97} 98