@@ -15,44 +15,48 @@ import (
15
15
16
16
"golang.org/x/tools/go/ast/astutil"
17
17
"golang.org/x/tools/gopls/internal/lsp/protocol"
18
+ "golang.org/x/tools/gopls/internal/lsp/safetoken"
19
+ "golang.org/x/tools/gopls/internal/span"
20
+ "golang.org/x/tools/internal/bug"
18
21
"golang.org/x/tools/internal/event"
19
22
"golang.org/x/tools/internal/event/tag"
20
23
)
21
24
22
25
// PrepareCallHierarchy returns an array of CallHierarchyItem for a file and the position within the file.
23
- func PrepareCallHierarchy (ctx context.Context , snapshot Snapshot , fh FileHandle , pos protocol.Position ) ([]protocol.CallHierarchyItem , error ) {
26
+ func PrepareCallHierarchy (ctx context.Context , snapshot Snapshot , fh FileHandle , pp protocol.Position ) ([]protocol.CallHierarchyItem , error ) {
24
27
ctx , done := event .Start (ctx , "source.PrepareCallHierarchy" )
25
28
defer done ()
26
29
27
- identifier , err := Identifier (ctx , snapshot , fh , pos )
30
+ pkg , pgf , err := PackageForFile (ctx , snapshot , fh .URI (), TypecheckFull , NarrowestPackage )
31
+ if err != nil {
32
+ return nil , err
33
+ }
34
+ pos , err := pgf .PositionPos (pp )
28
35
if err != nil {
29
- if errors .Is (err , ErrNoIdentFound ) || errors .Is (err , errNoObjectFound ) {
30
- return nil , nil
31
- }
32
36
return nil , err
33
37
}
34
38
35
- // The identifier can be nil if it is an import spec.
36
- if identifier == nil || identifier . Declaration . obj == nil {
39
+ obj := referencedObject ( pkg , pgf , pos )
40
+ if obj == nil {
37
41
return nil , nil
38
42
}
39
43
40
- if _ , ok := identifier . Declaration . obj .Type ().Underlying ().(* types.Signature ); ! ok {
44
+ if _ , ok := obj .Type ().Underlying ().(* types.Signature ); ! ok {
41
45
return nil , nil
42
46
}
43
47
44
- if len (identifier .Declaration .MappedRange ) == 0 {
45
- return nil , nil
48
+ declLoc , err := mapPosition (ctx , pkg .FileSet (), snapshot , obj .Pos (), adjustedObjEnd (obj ))
49
+ if err != nil {
50
+ return nil , err
46
51
}
47
- declMappedRange := identifier .Declaration .MappedRange [0 ]
48
- rng := declMappedRange .Range ()
52
+ rng := declLoc .Range
49
53
50
54
callHierarchyItem := protocol.CallHierarchyItem {
51
- Name : identifier .Name ,
55
+ Name : obj .Name () ,
52
56
Kind : protocol .Function ,
53
57
Tags : []protocol.SymbolTag {},
54
- Detail : fmt .Sprintf ("%s • %s" , identifier . Declaration . obj .Pkg ().Path (), filepath .Base (declMappedRange .URI ().Filename ())),
55
- URI : protocol . DocumentURI ( declMappedRange . URI ()) ,
58
+ Detail : fmt .Sprintf ("%s • %s" , obj .Pkg ().Path (), filepath .Base (declLoc .URI . SpanURI ().Filename ())),
59
+ URI : declLoc . URI ,
56
60
Range : rng ,
57
61
SelectionRange : rng ,
58
62
}
@@ -174,41 +178,71 @@ outer:
174
178
}
175
179
176
180
// OutgoingCalls returns an array of CallHierarchyOutgoingCall for a file and the position within the file.
177
- func OutgoingCalls (ctx context.Context , snapshot Snapshot , fh FileHandle , pos protocol.Position ) ([]protocol.CallHierarchyOutgoingCall , error ) {
181
+ func OutgoingCalls (ctx context.Context , snapshot Snapshot , fh FileHandle , pp protocol.Position ) ([]protocol.CallHierarchyOutgoingCall , error ) {
178
182
ctx , done := event .Start (ctx , "source.OutgoingCalls" )
179
183
defer done ()
180
184
181
- identifier , err := Identifier (ctx , snapshot , fh , pos )
185
+ pkg , pgf , err := PackageForFile (ctx , snapshot , fh .URI (), TypecheckFull , NarrowestPackage )
186
+ if err != nil {
187
+ return nil , err
188
+ }
189
+ pos , err := pgf .PositionPos (pp )
182
190
if err != nil {
183
- if errors .Is (err , ErrNoIdentFound ) || errors .Is (err , errNoObjectFound ) {
184
- return nil , nil
185
- }
186
191
return nil , err
187
192
}
188
193
189
- if _ , ok := identifier .Declaration .obj .Type ().Underlying ().(* types.Signature ); ! ok {
194
+ obj := referencedObject (pkg , pgf , pos )
195
+ if obj == nil {
190
196
return nil , nil
191
197
}
192
- node := identifier . Declaration . node
193
- if node == nil {
198
+
199
+ if _ , ok := obj . Type (). Underlying ().( * types. Signature ); ! ok {
194
200
return nil , nil
195
201
}
196
- callExprs , err := collectCallExpressions (identifier .Declaration .nodeFile , node )
202
+
203
+ // Skip builtins.
204
+ if obj .Pkg () == nil {
205
+ return nil , nil
206
+ }
207
+
208
+ if ! obj .Pos ().IsValid () {
209
+ return nil , bug .Errorf ("internal error: object %s.%s missing position" , obj .Pkg ().Path (), obj .Name ())
210
+ }
211
+
212
+ declFile := pkg .FileSet ().File (obj .Pos ())
213
+ if declFile == nil {
214
+ return nil , bug .Errorf ("file not found for %d" , obj .Pos ())
215
+ }
216
+
217
+ uri := span .URIFromPath (declFile .Name ())
218
+ offset , err := safetoken .Offset (declFile , obj .Pos ())
197
219
if err != nil {
198
220
return nil , err
199
221
}
200
222
201
- return toProtocolOutgoingCalls (ctx , snapshot , fh , callExprs )
202
- }
223
+ // Use TypecheckFull as we want to inspect the body of the function declaration.
224
+ declPkg , declPGF , err := PackageForFile (ctx , snapshot , uri , TypecheckFull , NarrowestPackage )
225
+ if err != nil {
226
+ return nil , err
227
+ }
203
228
204
- // collectCallExpressions collects call expression ranges inside a function.
205
- func collectCallExpressions (pgf * ParsedGoFile , node ast.Node ) ([]protocol.Range , error ) {
206
- type callPos struct {
207
- start , end token.Pos
229
+ declPos , err := safetoken .Pos (declPGF .Tok , offset )
230
+ if err != nil {
231
+ return nil , err
208
232
}
209
- callPositions := []callPos {}
210
233
211
- ast .Inspect (node , func (n ast.Node ) bool {
234
+ declNode , _ := FindDeclAndField ([]* ast.File {declPGF .File }, declPos )
235
+ if declNode == nil {
236
+ // TODO(rfindley): why don't we return an error here, or even bug.Errorf?
237
+ return nil , nil
238
+ // return nil, bug.Errorf("failed to find declaration for object %s.%s", obj.Pkg().Path(), obj.Name())
239
+ }
240
+
241
+ type callRange struct {
242
+ start , end token.Pos
243
+ }
244
+ callRanges := []callRange {}
245
+ ast .Inspect (declNode , func (n ast.Node ) bool {
212
246
if call , ok := n .(* ast.CallExpr ); ok {
213
247
var start , end token.Pos
214
248
switch n := call .Fun .(type ) {
@@ -225,70 +259,48 @@ func collectCallExpressions(pgf *ParsedGoFile, node ast.Node) ([]protocol.Range,
225
259
// for ex: direct function literal calls since that's not an 'outgoing' call
226
260
return false
227
261
}
228
- callPositions = append (callPositions , callPos {start : start , end : end })
262
+ callRanges = append (callRanges , callRange {start : start , end : end })
229
263
}
230
264
return true
231
265
})
232
266
233
- callRanges := []protocol.Range {}
234
- for _ , call := range callPositions {
235
- callRange , err := pgf .PosRange (call .start , call .end )
236
- if err != nil {
237
- return nil , err
238
- }
239
- callRanges = append (callRanges , callRange )
240
- }
241
- return callRanges , nil
242
- }
243
-
244
- // toProtocolOutgoingCalls returns an array of protocol.CallHierarchyOutgoingCall for ast call expressions.
245
- // Calls to the same function are assigned to the same declaration.
246
- func toProtocolOutgoingCalls (ctx context.Context , snapshot Snapshot , fh FileHandle , callRanges []protocol.Range ) ([]protocol.CallHierarchyOutgoingCall , error ) {
247
- // Multiple calls could be made to the same function, defined by "same declaration
248
- // AST node & same identifier name" to provide a unique identifier key even when
249
- // the func is declared in a struct or interface.
250
- type key struct {
251
- decl ast.Node
252
- name string
253
- }
254
- outgoingCalls := map [key ]* protocol.CallHierarchyOutgoingCall {}
267
+ outgoingCalls := map [token.Pos ]* protocol.CallHierarchyOutgoingCall {}
255
268
for _ , callRange := range callRanges {
256
- identifier , err := Identifier (ctx , snapshot , fh , callRange .Start )
257
- if err != nil {
258
- if errors .Is (err , ErrNoIdentFound ) || errors .Is (err , errNoObjectFound ) {
259
- continue
260
- }
261
- return nil , err
269
+ obj := referencedObject (declPkg , declPGF , callRange .start )
270
+ if obj == nil {
271
+ continue
262
272
}
263
273
264
274
// ignore calls to builtin functions
265
- if identifier . Declaration . obj .Pkg () == nil {
275
+ if obj .Pkg () == nil {
266
276
continue
267
277
}
268
278
269
- if outgoingCall , ok := outgoingCalls [key {identifier .Declaration .node , identifier .Name }]; ok {
270
- outgoingCall .FromRanges = append (outgoingCall .FromRanges , callRange )
271
- continue
279
+ outgoingCall , ok := outgoingCalls [obj .Pos ()]
280
+ if ! ok {
281
+ loc , err := mapPosition (ctx , declPkg .FileSet (), snapshot , obj .Pos (), obj .Pos ()+ token .Pos (len (obj .Name ())))
282
+ if err != nil {
283
+ return nil , err
284
+ }
285
+ outgoingCall = & protocol.CallHierarchyOutgoingCall {
286
+ To : protocol.CallHierarchyItem {
287
+ Name : obj .Name (),
288
+ Kind : protocol .Function ,
289
+ Tags : []protocol.SymbolTag {},
290
+ Detail : fmt .Sprintf ("%s • %s" , obj .Pkg ().Path (), filepath .Base (loc .URI .SpanURI ().Filename ())),
291
+ URI : loc .URI ,
292
+ Range : loc .Range ,
293
+ SelectionRange : loc .Range ,
294
+ },
295
+ }
296
+ outgoingCalls [obj .Pos ()] = outgoingCall
272
297
}
273
298
274
- if len (identifier .Declaration .MappedRange ) == 0 {
275
- continue
276
- }
277
- declMappedRange := identifier .Declaration .MappedRange [0 ]
278
- rng := declMappedRange .Range ()
279
-
280
- outgoingCalls [key {identifier .Declaration .node , identifier .Name }] = & protocol.CallHierarchyOutgoingCall {
281
- To : protocol.CallHierarchyItem {
282
- Name : identifier .Name ,
283
- Kind : protocol .Function ,
284
- Tags : []protocol.SymbolTag {},
285
- Detail : fmt .Sprintf ("%s • %s" , identifier .Declaration .obj .Pkg ().Path (), filepath .Base (declMappedRange .URI ().Filename ())),
286
- URI : protocol .DocumentURI (declMappedRange .URI ()),
287
- Range : rng ,
288
- SelectionRange : rng ,
289
- },
290
- FromRanges : []protocol.Range {callRange },
299
+ rng , err := declPGF .PosRange (callRange .start , callRange .end )
300
+ if err != nil {
301
+ return nil , err
291
302
}
303
+ outgoingCall .FromRanges = append (outgoingCall .FromRanges , rng )
292
304
}
293
305
294
306
outgoingCallItems := make ([]protocol.CallHierarchyOutgoingCall , 0 , len (outgoingCalls ))
0 commit comments