@@ -17,6 +17,7 @@ import (
17
17
"sync"
18
18
19
19
"golang.org/x/sync/errgroup"
20
+ "golang.org/x/tools/go/types/typeutil"
20
21
"golang.org/x/tools/gopls/internal/cache"
21
22
"golang.org/x/tools/gopls/internal/cache/metadata"
22
23
"golang.org/x/tools/gopls/internal/cache/methodsets"
@@ -345,6 +346,8 @@ func implementsObj(ctx context.Context, snapshot *cache.Snapshot, uri protocol.D
345
346
func localImplementations (ctx context.Context , snapshot * cache.Snapshot , pkg * cache.Package , queryType types.Type , method * types.Func ) ([]protocol.Location , error ) {
346
347
queryType = methodsets .EnsurePointer (queryType )
347
348
349
+ var msets typeutil.MethodSetCache
350
+
348
351
// Scan through all type declarations in the syntax.
349
352
var locs []protocol.Location
350
353
var methodLocs []methodsets.Location
@@ -366,16 +369,16 @@ func localImplementations(ctx context.Context, snapshot *cache.Snapshot, pkg *ca
366
369
// The historical behavior enshrined by this
367
370
// function rejects cases where both are
368
371
// (nontrivial) interface types?
369
- // That seems like useful information.
372
+ // That seems like useful information; see #68641 .
370
373
// TODO(adonovan): UX: report I/I pairs too?
371
374
// The same question appears in the global algorithm (methodsets).
372
- if ! concreteImplementsIntf (candidateType , queryType ) {
375
+ if ! concreteImplementsIntf (& msets , candidateType , queryType ) {
373
376
return true // not assignable
374
377
}
375
378
376
379
// Ignore types with empty method sets.
377
380
// (No point reporting that every type satisfies 'any'.)
378
- mset := types . NewMethodSet (candidateType )
381
+ mset := msets . MethodSet (candidateType )
379
382
if mset .Len () == 0 {
380
383
return true
381
384
}
@@ -449,28 +452,173 @@ func errorLocation(ctx context.Context, snapshot *cache.Snapshot) (protocol.Loca
449
452
return protocol.Location {}, fmt .Errorf ("built-in error type not found" )
450
453
}
451
454
452
- // concreteImplementsIntf returns true if a is an interface type implemented by
453
- // concrete type b, or vice versa.
454
- func concreteImplementsIntf (a , b types.Type ) bool {
455
- aIsIntf , bIsIntf := types .IsInterface (a ), types .IsInterface (b )
455
+ // concreteImplementsIntf reports whether x is an interface type
456
+ // implemented by concrete type y, or vice versa.
457
+ //
458
+ // If one or both types are generic, the result indicates whether the
459
+ // interface may be implemented under some instantiation.
460
+ func concreteImplementsIntf (msets * typeutil.MethodSetCache , x , y types.Type ) bool {
461
+ xiface := types .IsInterface (x )
462
+ yiface := types .IsInterface (y )
456
463
457
464
// Make sure exactly one is an interface type.
458
- if aIsIntf == bIsIntf {
465
+ // TODO(adonovan): rescind this policy choice and report
466
+ // I/I relationships. See CL 619719 + issue #68641.
467
+ if xiface == yiface {
459
468
return false
460
469
}
461
470
462
- // Rearrange if needed so "a" is the concrete type.
463
- if aIsIntf {
464
- a , b = b , a
471
+ // Rearrange if needed so x is the concrete type.
472
+ if xiface {
473
+ x , y = y , x
465
474
}
475
+ // Inv: y is an interface type.
466
476
467
- // TODO(adonovan): this should really use GenericAssignableTo
468
- // to report (e.g.) "ArrayList[T] implements List[T]", but
469
- // GenericAssignableTo doesn't work correctly on pointers to
470
- // generic named types. Thus the legacy implementation and the
471
- // "local" part of implementations fail to report generics.
472
- // The global algorithm based on subsets does the right thing.
473
- return types .AssignableTo (a , b )
477
+ // For each interface method of y, check that x has it too.
478
+ // It is not necessary to compute x's complete method set.
479
+ //
480
+ // If y is a constraint interface (!y.IsMethodSet()), we
481
+ // ignore non-interface terms, leading to occasional spurious
482
+ // matches. We could in future filter based on them, but it
483
+ // would lead to divergence with the global (fingerprint-based)
484
+ // algorithm, which operates only on methodsets.
485
+ ymset := msets .MethodSet (y )
486
+ for i := range ymset .Len () {
487
+ ym := ymset .At (i ).Obj ().(* types.Func )
488
+
489
+ xobj , _ , _ := types .LookupFieldOrMethod (x , false , ym .Pkg (), ym .Name ())
490
+ xm , ok := xobj .(* types.Func )
491
+ if ! ok {
492
+ return false // x lacks a method of y
493
+ }
494
+ if ! unify (xm .Signature (), ym .Signature ()) {
495
+ return false // signatures do not match
496
+ }
497
+ }
498
+ return true // all methods found
499
+ }
500
+
501
+ // unify reports whether the types of x and y match, allowing free
502
+ // type parameters to stand for anything at all, without regard to
503
+ // consistency of substitutions.
504
+ //
505
+ // TODO(adonovan): implement proper unification (#63982), finding the
506
+ // most general unifier across all the interface methods.
507
+ //
508
+ // See also: unify in cache/methodsets/fingerprint, which uses a
509
+ // similar ersatz unification approach on type fingerprints, for
510
+ // the global index.
511
+ func unify (x , y types.Type ) bool {
512
+ x = types .Unalias (x )
513
+ y = types .Unalias (y )
514
+
515
+ // For now, allow a type parameter to match anything,
516
+ // without regard to consistency of substitutions.
517
+ if is [* types.TypeParam ](x ) || is [* types.TypeParam ](y ) {
518
+ return true
519
+ }
520
+
521
+ if reflect .TypeOf (x ) != reflect .TypeOf (y ) {
522
+ return false // mismatched types
523
+ }
524
+
525
+ switch x := x .(type ) {
526
+ case * types.Array :
527
+ y := y .(* types.Array )
528
+ return x .Len () == y .Len () &&
529
+ unify (x .Elem (), y .Elem ())
530
+
531
+ case * types.Basic :
532
+ y := y .(* types.Basic )
533
+ return x .Kind () == y .Kind ()
534
+
535
+ case * types.Chan :
536
+ y := y .(* types.Chan )
537
+ return x .Dir () == y .Dir () &&
538
+ unify (x .Elem (), y .Elem ())
539
+
540
+ case * types.Interface :
541
+ y := y .(* types.Interface )
542
+ // TODO(adonovan): fix: for correctness, we must check
543
+ // that both interfaces have the same set of methods
544
+ // modulo type parameters, while avoiding the risk of
545
+ // unbounded interface recursion.
546
+ //
547
+ // Since non-empty interface literals are vanishingly
548
+ // rare in methods signatures, we ignore this for now.
549
+ // If more precision is needed we could compare method
550
+ // names and arities, still without full recursion.
551
+ return x .NumMethods () == y .NumMethods ()
552
+
553
+ case * types.Map :
554
+ y := y .(* types.Map )
555
+ return unify (x .Key (), y .Key ()) &&
556
+ unify (x .Elem (), y .Elem ())
557
+
558
+ case * types.Named :
559
+ y := y .(* types.Named )
560
+ if x .Origin () != y .Origin () {
561
+ return false // different named types
562
+ }
563
+ xtargs := x .TypeArgs ()
564
+ ytargs := y .TypeArgs ()
565
+ if xtargs .Len () != ytargs .Len () {
566
+ return false // arity error (ill-typed)
567
+ }
568
+ for i := range xtargs .Len () {
569
+ if ! unify (xtargs .At (i ), ytargs .At (i )) {
570
+ return false // mismatched type args
571
+ }
572
+ }
573
+ return true
574
+
575
+ case * types.Pointer :
576
+ y := y .(* types.Pointer )
577
+ return unify (x .Elem (), y .Elem ())
578
+
579
+ case * types.Signature :
580
+ y := y .(* types.Signature )
581
+ return x .Variadic () == y .Variadic () &&
582
+ unify (x .Params (), y .Params ()) &&
583
+ unify (x .Results (), y .Results ())
584
+
585
+ case * types.Slice :
586
+ y := y .(* types.Slice )
587
+ return unify (x .Elem (), y .Elem ())
588
+
589
+ case * types.Struct :
590
+ y := y .(* types.Struct )
591
+ if x .NumFields () != y .NumFields () {
592
+ return false
593
+ }
594
+ for i := range x .NumFields () {
595
+ xf := x .Field (i )
596
+ yf := y .Field (i )
597
+ if xf .Embedded () != yf .Embedded () ||
598
+ xf .Name () != yf .Name () ||
599
+ x .Tag (i ) != y .Tag (i ) ||
600
+ ! xf .Exported () && xf .Pkg () != yf .Pkg () ||
601
+ ! unify (xf .Type (), yf .Type ()) {
602
+ return false
603
+ }
604
+ }
605
+ return true
606
+
607
+ case * types.Tuple :
608
+ y := y .(* types.Tuple )
609
+ if x .Len () != y .Len () {
610
+ return false
611
+ }
612
+ for i := range x .Len () {
613
+ if ! unify (x .At (i ).Type (), y .At (i ).Type ()) {
614
+ return false
615
+ }
616
+ }
617
+ return true
618
+
619
+ default : // incl. *Union, *TypeParam
620
+ panic (fmt .Sprintf ("unexpected Type %#v" , x ))
621
+ }
474
622
}
475
623
476
624
var (
0 commit comments