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:
parent
6c84e9e3e0
commit
3dafbd1ba4
3
cmd/guru/testdata/src/describe/main.go
vendored
3
cmd/guru/testdata/src/describe/main.go
vendored
@ -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"
|
||||||
|
8
cmd/guru/testdata/src/describe/main.golden
vendored
8
cmd/guru/testdata/src/describe/main.golden
vendored
@ -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
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
63
go/types/typeutil/ui_test.go
Normal file
63
go/types/typeutil/ui_test.go
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user