1
0
mirror of https://github.com/golang/go synced 2024-11-18 11:14:39 -07:00

go/types/typeutil: make IntuitiveMethodSet(*C) nonempty for concrete types C

This fixes a bug in guru describe.

Also, add a test of IntuitiveMethodSet.

Change-Id: Ied3780807afd88e664fdb186619499670529fe33
Reviewed-on: https://go-review.googlesource.com/20166
Reviewed-by: Daniel Morsing <daniel.morsing@gmail.com>
This commit is contained in:
Alan Donovan 2016-03-03 11:32:24 -05:00
parent 6c84e9e3e0
commit 3dafbd1ba4
4 changed files with 94 additions and 6 deletions

View File

@ -37,6 +37,9 @@ func main() { // @describe func-def-main "main"
_ = d.f // @describe func-ref-d.f "d.f" _ = d.f // @describe func-ref-d.f "d.f"
_ = i.f // @describe func-ref-i.f "i.f" _ = i.f // @describe func-ref-i.f "i.f"
var dptr *D // @describe ptr-with-nonptr-methods "dptr"
_ = dptr
// var objects // var objects
anon := func() { anon := func() {
_ = d // @describe ref-lexical-d "d" _ = d // @describe ref-lexical-d "d"

View File

@ -84,6 +84,14 @@ defined here
reference to interface method func (I).f() reference to interface method func (I).f()
defined here defined here
-------- @describe ptr-with-nonptr-methods --------
definition of var dptr *D
Method set:
method (*D) f()
Fields:
Field int
AnotherField string
-------- @describe ref-lexical-d -------- -------- @describe ref-lexical-d --------
reference to var d D reference to var d D
defined here defined here

View File

@ -10,23 +10,36 @@ package typeutil
import "go/types" import "go/types"
// IntuitiveMethodSet returns the intuitive method set of a type, T. // IntuitiveMethodSet returns the intuitive method set of a type T,
// which is the set of methods you can call on an addressable value of
// that type.
// //
// The result contains MethodSet(T) and additionally, if T is a // The result always contains MethodSet(T), and is exactly MethodSet(T)
// concrete type, methods belonging to *T if there is no identically // for interface types and for pointer-to-concrete types.
// named method on T itself. This corresponds to user intuition about // For all other concrete types T, the result additionally
// method sets; this function is intended only for user interfaces. // contains each method belonging to *T if there is no identically
// named method on T itself.
//
// This corresponds to user intuition about method sets;
// this function is intended only for user interfaces.
// //
// The order of the result is as for types.MethodSet(T). // The order of the result is as for types.MethodSet(T).
// //
func IntuitiveMethodSet(T types.Type, msets *MethodSetCache) []*types.Selection { func IntuitiveMethodSet(T types.Type, msets *MethodSetCache) []*types.Selection {
isPointerToConcrete := func(T types.Type) bool {
ptr, ok := T.(*types.Pointer)
return ok && !types.IsInterface(ptr.Elem())
}
var result []*types.Selection var result []*types.Selection
mset := msets.MethodSet(T) mset := msets.MethodSet(T)
if _, ok := T.Underlying().(*types.Interface); ok { if types.IsInterface(T) || isPointerToConcrete(T) {
for i, n := 0, mset.Len(); i < n; i++ { for i, n := 0, mset.Len(); i < n; i++ {
result = append(result, mset.At(i)) result = append(result, mset.At(i))
} }
} else { } else {
// T is some other concrete type.
// Report methods of T and *T, preferring those of T.
pmset := msets.MethodSet(types.NewPointer(T)) pmset := msets.MethodSet(types.NewPointer(T))
for i, n := 0, pmset.Len(); i < n; i++ { for i, n := 0, pmset.Len(); i < n; i++ {
meth := pmset.At(i) meth := pmset.At(i)
@ -35,6 +48,7 @@ func IntuitiveMethodSet(T types.Type, msets *MethodSetCache) []*types.Selection
} }
result = append(result, meth) result = append(result, meth)
} }
} }
return result return result
} }

View File

@ -0,0 +1,63 @@
// +build go1.5
package typeutil_test
import (
"fmt"
"go/ast"
"go/parser"
"go/token"
"go/types"
"strings"
"testing"
"golang.org/x/tools/go/types/typeutil"
)
func TestIntuitiveMethodSet(t *testing.T) {
const source = `
package P
type A int
func (A) f()
func (*A) g()
`
fset := token.NewFileSet()
f, err := parser.ParseFile(fset, "hello.go", source, 0)
if err != nil {
t.Fatal(err)
}
var conf types.Config
pkg, err := conf.Check("P", fset, []*ast.File{f}, nil)
if err != nil {
t.Fatal(err)
}
qual := types.RelativeTo(pkg)
for _, test := range []struct {
expr string // type expression
want string // intuitive method set
}{
{"A", "(A).f (*A).g"},
{"*A", "(*A).f (*A).g"},
{"error", "(error).Error"},
{"*error", ""},
{"struct{A}", "(struct{A}).f (*struct{A}).g"},
{"*struct{A}", "(*struct{A}).f (*struct{A}).g"},
} {
tv, err := types.Eval(fset, pkg, 0, test.expr)
if err != nil {
t.Errorf("Eval(%s) failed: %v", test.expr, err)
}
var names []string
for _, m := range typeutil.IntuitiveMethodSet(tv.Type, nil) {
name := fmt.Sprintf("(%s).%s", types.TypeString(m.Recv(), qual), m.Obj().Name())
names = append(names, name)
}
got := strings.Join(names, " ")
if got != test.want {
t.Errorf("IntuitiveMethodSet(%s) = %q, want %q", test.expr, got, test.want)
}
}
}