var emptyMethodSet MethodSet
// Note: NewMethodSet is intended for external use only as it
-// requires interfaces to be complete. If may be used
+// requires interfaces to be complete. It may be used
// internally if LookupFieldOrMethod completed the same
// interfaces beforehand.
// WARNING: The code in this function is extremely subtle - do not modify casually!
// This function and lookupFieldOrMethod should be kept in sync.
- // TODO(gri) This code is out-of-sync with the lookup code at this point.
- // Need to update.
+ // TODO(rfindley) confirm that this code is in sync with lookupFieldOrMethod
+ // with respect to type params.
// method set up to the current depth, allocated lazily
var base methodSet
mset = mset.add(named.methods, e.index, e.indirect, e.multiples)
- // continue with underlying type
+ // continue with underlying type, but only if it's not a type parameter
+ // TODO(rFindley): should this use named.under()? Can there be a difference?
typ = named.underlying
+ if _, ok := typ.(*_TypeParam); ok {
+ continue
+ }
}
switch t := typ.(type) {
case *Interface:
mset = mset.add(t.allMethods, e.index, true, e.multiples)
+
+ case *_TypeParam:
+ mset = mset.add(t.Bound().allMethods, e.index, true, e.multiples)
}
}
--- /dev/null
+// Copyright 2021 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package types_test
+
+import (
+ "testing"
+
+ "go/internal/typeparams"
+ . "go/types"
+)
+
+func TestNewMethodSet(t *testing.T) {
+ type method struct {
+ name string
+ index []int
+ indirect bool
+ }
+
+ // Tests are expressed src -> methods, for simplifying the composite literal.
+ // Should be kept in sync with TestLookupFieldOrMethod.
+ tests := map[string][]method{
+ // Named types
+ "var a T; type T struct{}; func (T) f() {}": {{"f", []int{0}, false}},
+ "var a *T; type T struct{}; func (T) f() {}": {{"f", []int{0}, true}},
+ "var a T; type T struct{}; func (*T) f() {}": {},
+ "var a *T; type T struct{}; func (*T) f() {}": {{"f", []int{0}, true}},
+
+ // Interfaces
+ "var a T; type T interface{ f() }": {{"f", []int{0}, true}},
+ "var a T1; type ( T1 T2; T2 interface{ f() } )": {{"f", []int{0}, true}},
+ "var a T1; type ( T1 interface{ T2 }; T2 interface{ f() } )": {{"f", []int{0}, true}},
+
+ // Embedding
+ "var a struct{ E }; type E interface{ f() }": {{"f", []int{0, 0}, true}},
+ "var a *struct{ E }; type E interface{ f() }": {{"f", []int{0, 0}, true}},
+ "var a struct{ E }; type E struct{}; func (E) f() {}": {{"f", []int{0, 0}, false}},
+ "var a struct{ *E }; type E struct{}; func (E) f() {}": {{"f", []int{0, 0}, true}},
+ "var a struct{ E }; type E struct{}; func (*E) f() {}": {},
+ "var a struct{ *E }; type E struct{}; func (*E) f() {}": {{"f", []int{0, 0}, true}},
+
+ // collisions
+ "var a struct{ E1; *E2 }; type ( E1 interface{ f() }; E2 struct{ f int })": {},
+ "var a struct{ E1; *E2 }; type ( E1 struct{ f int }; E2 struct{} ); func (E2) f() {}": {},
+ }
+
+ genericTests := map[string][]method{
+ // By convention, look up a in the scope of "g"
+ "type C interface{ f() }; func g[T C](a T){}": {{"f", []int{0}, true}},
+ "type C interface{ f() }; func g[T C]() { var a T; _ = a }": {{"f", []int{0}, true}},
+ "type C interface{ f() }; func g[T C]() { var a struct{T}; _ = a }": {{"f", []int{0, 0}, true}},
+
+ // Issue #45639.
+ "type C interface{ f() }; func g[T C]() { type Y T; var a Y; _ = a }": {},
+ }
+
+ check := func(src string, methods []method, generic bool) {
+ pkgName := "p"
+ if generic {
+ // The generic_ prefix causes pkgFor to allow generic code.
+ pkgName = "generic_p"
+ }
+ pkg, err := pkgFor("test", "package "+pkgName+";"+src, nil)
+ if err != nil {
+ t.Errorf("%s: incorrect test case: %s", src, err)
+ return
+ }
+
+ scope := pkg.Scope()
+ if generic {
+ fn := pkg.Scope().Lookup("g").(*Func)
+ scope = fn.Scope()
+ }
+ obj := scope.Lookup("a")
+ if obj == nil {
+ t.Errorf("%s: incorrect test case - no object a", src)
+ return
+ }
+
+ ms := NewMethodSet(obj.Type())
+ if got, want := ms.Len(), len(methods); got != want {
+ t.Errorf("%s: got %d methods, want %d", src, got, want)
+ return
+ }
+ for i, m := range methods {
+ sel := ms.At(i)
+ if got, want := sel.Obj().Name(), m.name; got != want {
+ t.Errorf("%s [method %d]: got name = %q at, want %q", src, i, got, want)
+ }
+ if got, want := sel.Index(), m.index; !sameSlice(got, want) {
+ t.Errorf("%s [method %d]: got index = %v, want %v", src, i, got, want)
+ }
+ if got, want := sel.Indirect(), m.indirect; got != want {
+ t.Errorf("%s [method %d]: got indirect = %v, want %v", src, i, got, want)
+ }
+ }
+ }
+
+ for src, methods := range tests {
+ check(src, methods, false)
+ }
+
+ if typeparams.Enabled {
+ for src, methods := range genericTests {
+ check(src, methods, true)
+ }
+ }
+}