@@ -73,19 +73,29 @@ func Implementation(ctx context.Context, snapshot *cache.Snapshot, f file.Handle
73
73
}
74
74
75
75
func implementations (ctx context.Context , snapshot * cache.Snapshot , fh file.Handle , pp protocol.Position ) ([]protocol.Location , error ) {
76
+ // First, find the object referenced at the cursor by type checking the
77
+ // current package.
76
78
obj , pkg , err := implementsObj (ctx , snapshot , fh .URI (), pp )
77
79
if err != nil {
78
80
return nil , err
79
81
}
80
82
81
- var localPkgs []* cache.Package
83
+ // If the resulting object has a position, we can expand the search to types
84
+ // in the declaring package(s). In this case, we must re-type check these
85
+ // packages in the same realm.
86
+ var (
87
+ declOffset int
88
+ declURI protocol.DocumentURI
89
+ localPkgs []* cache.Package
90
+ )
82
91
if obj .Pos ().IsValid () { // no local package for error or error.Error
83
92
declPosn := safetoken .StartPosition (pkg .FileSet (), obj .Pos ())
93
+ declOffset = declPosn .Offset
84
94
// Type-check the declaring package (incl. variants) for use
85
95
// by the "local" search, which uses type information to
86
96
// enumerate all types within the package that satisfy the
87
97
// query type, even those defined local to a function.
88
- declURI : = protocol .URIFromPath (declPosn .Filename )
98
+ declURI = protocol .URIFromPath (declPosn .Filename )
89
99
declMPs , err := snapshot .MetadataForFile (ctx , declURI )
90
100
if err != nil {
91
101
return nil , err
@@ -104,20 +114,25 @@ func implementations(ctx context.Context, snapshot *cache.Snapshot, fh file.Hand
104
114
}
105
115
}
106
116
117
+ pkg = nil // no longer used
118
+
107
119
// Is the selected identifier a type name or method?
108
120
// (For methods, report the corresponding method names.)
109
- var queryType types.Type
110
- var queryMethodID string
111
- switch obj := obj .(type ) {
112
- case * types.TypeName :
113
- queryType = obj .Type ()
114
- case * types.Func :
115
- // For methods, use the receiver type, which may be anonymous.
116
- if recv := obj .Type ().(* types.Signature ).Recv (); recv != nil {
117
- queryType = recv .Type ()
118
- queryMethodID = obj .Id ()
121
+ //
122
+ // This logic is reused for local queries.
123
+ typeOrMethod := func (obj types.Object ) (types.Type , string ) {
124
+ switch obj := obj .(type ) {
125
+ case * types.TypeName :
126
+ return obj .Type (), ""
127
+ case * types.Func :
128
+ // For methods, use the receiver type, which may be anonymous.
129
+ if recv := obj .Type ().(* types.Signature ).Recv (); recv != nil {
130
+ return recv .Type (), obj .Id ()
131
+ }
119
132
}
133
+ return nil , ""
120
134
}
135
+ queryType , queryMethodID := typeOrMethod (obj )
121
136
if queryType == nil {
122
137
return nil , bug .Errorf ("%s is not a type or method" , obj .Name ()) // should have been handled by implementsObj
123
138
}
@@ -169,11 +184,42 @@ func implementations(ctx context.Context, snapshot *cache.Snapshot, fh file.Hand
169
184
)
170
185
// local search
171
186
for _ , localPkg := range localPkgs {
172
- localPkg := localPkg
187
+ // The localImplementations algorithm assumes needle and haystack
188
+ // belong to a single package (="realm" of types symbol identities),
189
+ // so we need to recompute obj for each local package.
190
+ // (By contrast the global algorithm is name-based.)
191
+ declPkg := localPkg
173
192
group .Go (func () error {
174
- localLocs , err := localImplementations (ctx , snapshot , localPkg , queryType , queryMethodID )
193
+ pkgID := declPkg .Metadata ().ID
194
+ declFile , err := declPkg .File (declURI )
195
+ if err != nil {
196
+ return err // "can't happen"
197
+ }
198
+
199
+ // Find declaration of corresponding object
200
+ // in this package based on (URI, offset).
201
+ pos , err := safetoken .Pos (declFile .Tok , declOffset )
202
+ if err != nil {
203
+ return err // also "can't happen"
204
+ }
205
+ // TODO(adonovan): simplify: use objectsAt?
206
+ path := pathEnclosingObjNode (declFile .File , pos )
207
+ if path == nil {
208
+ return ErrNoIdentFound // checked earlier
209
+ }
210
+ id , ok := path [0 ].(* ast.Ident )
211
+ if ! ok {
212
+ return ErrNoIdentFound // checked earlier
213
+ }
214
+ // Shadow obj, queryType, and queryMethodID in this package.
215
+ obj := declPkg .TypesInfo ().ObjectOf (id ) // may be nil
216
+ queryType , queryMethodID := typeOrMethod (obj )
217
+ if queryType == nil {
218
+ return fmt .Errorf ("querying method sets in package %q: %v" , pkgID , err )
219
+ }
220
+ localLocs , err := localImplementations (ctx , snapshot , declPkg , queryType , queryMethodID )
175
221
if err != nil {
176
- return err
222
+ return fmt . Errorf ( "querying local implementations %q: %v" , pkgID , err )
177
223
}
178
224
locsMu .Lock ()
179
225
locs = append (locs , localLocs ... )
0 commit comments