@@ -30,6 +30,7 @@ import (
30
30
"golang.org/x/tools/go/types/objectpath"
31
31
"golang.org/x/tools/gopls/internal/lsp/protocol"
32
32
"golang.org/x/tools/gopls/internal/lsp/safetoken"
33
+ "golang.org/x/tools/gopls/internal/lsp/source/methodsets"
33
34
"golang.org/x/tools/gopls/internal/span"
34
35
"golang.org/x/tools/internal/event"
35
36
)
@@ -248,18 +249,7 @@ func ordinaryReferences(ctx context.Context, snapshot Snapshot, uri span.URI, pp
248
249
break
249
250
}
250
251
if obj == nil {
251
- return nil , ErrNoIdentFound
252
- }
253
-
254
- // If the object is a method, we need to search for all
255
- // matching implementations and/or interfaces, without
256
- // type-checking everything.
257
- //
258
- // That will require an approach such as the one sketched in
259
- // go.dev/cl/452060. Until then, we simply fall back to the
260
- // old implementation for now. TODO(adonovan): fix.
261
- if fn , ok := obj .(* types.Func ); ok && fn .Type ().(* types.Signature ).Recv () != nil {
262
- return nil , ErrFallback
252
+ return nil , ErrNoIdentFound // can't happen
263
253
}
264
254
265
255
// nil, error, iota, or other built-in?
@@ -303,6 +293,18 @@ func ordinaryReferences(ctx context.Context, snapshot Snapshot, uri span.URI, pp
303
293
var exportedObjectPath objectpath.Path
304
294
if path , err := objectpath .For (obj ); err == nil && obj .Exported () {
305
295
exportedObjectPath = path
296
+
297
+ // If the object is an exported method, we need to search for
298
+ // all matching implementations (using the incremental
299
+ // implementation of 'implementations') and then search for
300
+ // the set of corresponding methods (requiring the incremental
301
+ // implementation of 'references' to be generalized to a set
302
+ // of search objects).
303
+ // Until then, we simply fall back to the old implementation for now.
304
+ // TODO(adonovan): fix.
305
+ if fn , ok := obj .(* types.Func ); ok && fn .Type ().(* types.Signature ).Recv () != nil {
306
+ return nil , ErrFallback
307
+ }
306
308
}
307
309
308
310
// If it is exported, how far need we search?
@@ -368,7 +370,7 @@ func ordinaryReferences(ctx context.Context, snapshot Snapshot, uri span.URI, pp
368
370
return refs , nil
369
371
}
370
372
371
- // localReferences reports (concurrently) each reference to the object
373
+ // localReferences reports each reference to the object
372
374
// declared at the specified URI/offset within its enclosing package m.
373
375
func localReferences (ctx context.Context , snapshot Snapshot , declURI span.URI , declOffset int , m * Metadata , report func (loc protocol.Location , isDecl bool )) error {
374
376
pkgs , err := snapshot .TypeCheck (ctx , TypecheckFull , m .ID )
@@ -393,15 +395,57 @@ func localReferences(ctx context.Context, snapshot Snapshot, declURI span.URI, d
393
395
}
394
396
395
397
// Report the locations of the declaration(s).
398
+ // TODO(adonovan): what about for corresponding methods? Add tests.
396
399
for _ , node := range targets {
397
400
report (mustLocation (pgf , node ), true )
398
401
}
399
402
403
+ // receiver returns the effective receiver type for method-set
404
+ // comparisons for obj, if it is a method, or nil otherwise.
405
+ receiver := func (obj types.Object ) types.Type {
406
+ if fn , ok := obj .(* types.Func ); ok {
407
+ if recv := fn .Type ().(* types.Signature ).Recv (); recv != nil {
408
+ return methodsets .EnsurePointer (recv .Type ())
409
+ }
410
+ }
411
+ return nil
412
+ }
413
+
414
+ // If we're searching for references to a method, broaden the
415
+ // search to include references to corresponding methods of
416
+ // mutually assignable receiver types.
417
+ // (We use a slice, but objectsAt never returns >1 methods.)
418
+ var methodRecvs []types.Type
419
+ var methodName string // name of an arbitrary target, iff a method
420
+ for obj := range targets {
421
+ if t := receiver (obj ); t != nil {
422
+ methodRecvs = append (methodRecvs , t )
423
+ methodName = obj .Name ()
424
+ }
425
+ }
426
+
427
+ // matches reports whether obj either is or corresponds to a target.
428
+ // (Correspondence is defined as usual for interface methods.)
429
+ matches := func (obj types.Object ) bool {
430
+ if targets [obj ] != nil {
431
+ return true
432
+ } else if methodRecvs != nil && obj .Name () == methodName {
433
+ if orecv := receiver (obj ); orecv != nil {
434
+ for _ , mrecv := range methodRecvs {
435
+ if concreteImplementsIntf (orecv , mrecv ) {
436
+ return true
437
+ }
438
+ }
439
+ }
440
+ }
441
+ return false
442
+ }
443
+
400
444
// Scan through syntax looking for uses of one of the target objects.
401
445
for _ , pgf := range pkg .CompiledGoFiles () {
402
446
ast .Inspect (pgf .File , func (n ast.Node ) bool {
403
447
if id , ok := n .(* ast.Ident ); ok {
404
- if used , ok := pkg .GetTypesInfo ().Uses [id ]; ok && targets [ used ] != nil {
448
+ if obj , ok := pkg .GetTypesInfo ().Uses [id ]; ok && matches ( obj ) {
405
449
report (mustLocation (pgf , id ), false )
406
450
}
407
451
}
@@ -453,10 +497,14 @@ func objectsAt(info *types.Info, file *ast.File, pos token.Pos) (map[types.Objec
453
497
}
454
498
targets [obj ] = leaf
455
499
}
500
+
501
+ if len (targets ) == 0 {
502
+ return nil , fmt .Errorf ("objectAt: internal error: no targets" ) // can't happen
503
+ }
456
504
return targets , nil
457
505
}
458
506
459
- // globalReferences reports (concurrently) each cross-package
507
+ // globalReferences reports each cross-package
460
508
// reference to the object identified by (pkgPath, objectPath).
461
509
func globalReferences (ctx context.Context , snapshot Snapshot , m * Metadata , pkgPath PackagePath , objectPath objectpath.Path , report func (loc protocol.Location , isDecl bool )) error {
462
510
// TODO(adonovan): opt: don't actually type-check here,
0 commit comments