mirror of
https://github.com/golang/go
synced 2024-11-18 20:04:52 -07:00
go.tools/go/types: add API test of Selection mechanism.
Also: fixed a crash in (*Selection).String() for FieldVal of a non-function type. LGTM=gri R=gri CC=golang-codereviews https://golang.org/cl/93830043
This commit is contained in:
parent
e6020f7229
commit
c212b356b8
@ -469,3 +469,170 @@ func TestFiles(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSelection(t *testing.T) {
|
||||||
|
selections := make(map[*ast.SelectorExpr]*Selection)
|
||||||
|
|
||||||
|
fset := token.NewFileSet()
|
||||||
|
conf := Config{
|
||||||
|
Packages: make(map[string]*Package),
|
||||||
|
Import: func(imports map[string]*Package, path string) (*Package, error) {
|
||||||
|
return imports[path], nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
makePkg := func(path, src string) {
|
||||||
|
f, err := parser.ParseFile(fset, path+".go", src, 0)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
pkg, err := conf.Check(path, fset, []*ast.File{f}, &Info{Selections: selections})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
conf.Packages[path] = pkg
|
||||||
|
}
|
||||||
|
|
||||||
|
libSrc := `
|
||||||
|
package lib
|
||||||
|
type T float64
|
||||||
|
const C T = 3
|
||||||
|
var V T
|
||||||
|
func F() {}
|
||||||
|
func (T) M() {}
|
||||||
|
`
|
||||||
|
mainSrc := `
|
||||||
|
package main
|
||||||
|
import "lib"
|
||||||
|
|
||||||
|
type A struct {
|
||||||
|
*B
|
||||||
|
C
|
||||||
|
}
|
||||||
|
|
||||||
|
type B struct {
|
||||||
|
b int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (B) f(int)
|
||||||
|
|
||||||
|
type C struct {
|
||||||
|
c int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (C) g()
|
||||||
|
func (*C) h()
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// qualified identifiers
|
||||||
|
var _ lib.T
|
||||||
|
_ = lib.C
|
||||||
|
_ = lib.F
|
||||||
|
_ = lib.V
|
||||||
|
_ = lib.T.M
|
||||||
|
|
||||||
|
// fields
|
||||||
|
_ = A{}.B
|
||||||
|
_ = new(A).B
|
||||||
|
|
||||||
|
_ = A{}.C
|
||||||
|
_ = new(A).C
|
||||||
|
|
||||||
|
_ = A{}.b
|
||||||
|
_ = new(A).b
|
||||||
|
|
||||||
|
_ = A{}.c
|
||||||
|
_ = new(A).c
|
||||||
|
|
||||||
|
// methods
|
||||||
|
_ = A{}.f
|
||||||
|
_ = new(A).f
|
||||||
|
_ = A{}.g
|
||||||
|
_ = new(A).g
|
||||||
|
_ = new(A).h
|
||||||
|
|
||||||
|
_ = B{}.f
|
||||||
|
_ = new(B).f
|
||||||
|
|
||||||
|
_ = C{}.g
|
||||||
|
_ = new(C).g
|
||||||
|
_ = new(C).h
|
||||||
|
|
||||||
|
// method expressions
|
||||||
|
_ = A.f
|
||||||
|
_ = (*A).f
|
||||||
|
_ = B.f
|
||||||
|
_ = (*B).f
|
||||||
|
}`
|
||||||
|
|
||||||
|
// TODO(adonovan): assert all map entries are used at least once.
|
||||||
|
wantOut := map[string][2]string{
|
||||||
|
"lib.T": {"qualified ident type lib.T float64", ".[]"},
|
||||||
|
"lib.C": {"qualified ident const lib.C lib.T", ".[]"},
|
||||||
|
"lib.F": {"qualified ident func lib.F()", ".[]"},
|
||||||
|
"lib.V": {"qualified ident var lib.V lib.T", ".[]"},
|
||||||
|
"lib.T.M": {"method expr (lib.T) M(lib.T)", ".[0]"},
|
||||||
|
|
||||||
|
"A{}.B": {"field (main.A) B *main.B", ".[0]"},
|
||||||
|
"new(A).B": {"field (*main.A) B *main.B", "->[0]"},
|
||||||
|
"A{}.C": {"field (main.A) C main.C", ".[1]"},
|
||||||
|
"new(A).C": {"field (*main.A) C main.C", "->[1]"},
|
||||||
|
"A{}.b": {"field (main.A) b int", "->[0 0]"},
|
||||||
|
"new(A).b": {"field (*main.A) b int", "->[0 0]"},
|
||||||
|
"A{}.c": {"field (main.A) c int", ".[1 0]"},
|
||||||
|
"new(A).c": {"field (*main.A) c int", "->[1 0]"},
|
||||||
|
|
||||||
|
"A{}.f": {"method (main.A) f(int)", "->[0 0]"},
|
||||||
|
"new(A).f": {"method (*main.A) f(int)", "->[0 0]"},
|
||||||
|
"A{}.g": {"method (main.A) g()", ".[1 0]"},
|
||||||
|
"new(A).g": {"method (*main.A) g()", "->[1 0]"},
|
||||||
|
"new(A).h": {"method (*main.A) h()", "->[1 1]"},
|
||||||
|
"B{}.f": {"method (main.B) f(int)", ".[0]"},
|
||||||
|
"new(B).f": {"method (*main.B) f(int)", "->[0]"},
|
||||||
|
"C{}.g": {"method (main.C) g()", ".[0]"},
|
||||||
|
"new(C).g": {"method (*main.C) g()", "->[0]"},
|
||||||
|
"new(C).h": {"method (*main.C) h()", "->[1]"},
|
||||||
|
|
||||||
|
"A.f": {"method expr (main.A) f(main.A, int)", "->[0 0]"},
|
||||||
|
"(*A).f": {"method expr (*main.A) f(*main.A, int)", "->[0 0]"},
|
||||||
|
"B.f": {"method expr (main.B) f(main.B, int)", ".[0]"},
|
||||||
|
"(*B).f": {"method expr (*main.B) f(*main.B, int)", "->[0]"},
|
||||||
|
}
|
||||||
|
|
||||||
|
makePkg("lib", libSrc)
|
||||||
|
makePkg("main", mainSrc)
|
||||||
|
|
||||||
|
for e, sel := range selections {
|
||||||
|
sel.String() // assertion: must not panic
|
||||||
|
|
||||||
|
start := fset.Position(e.Pos()).Offset
|
||||||
|
end := fset.Position(e.End()).Offset
|
||||||
|
syntax := mainSrc[start:end] // (all SelectorExprs are in main, not lib)
|
||||||
|
|
||||||
|
direct := "."
|
||||||
|
if sel.Indirect() {
|
||||||
|
direct = "->"
|
||||||
|
}
|
||||||
|
got := [2]string{
|
||||||
|
sel.String(),
|
||||||
|
fmt.Sprintf("%s%v", direct, sel.Index()),
|
||||||
|
}
|
||||||
|
want := wantOut[syntax]
|
||||||
|
if want != got {
|
||||||
|
t.Errorf("%s: got %q; want %q", syntax, got, want)
|
||||||
|
}
|
||||||
|
|
||||||
|
// We must explicitly assert properties of the
|
||||||
|
// Signature's receiver since it doesn't participate
|
||||||
|
// in Identical() or String().
|
||||||
|
sig, _ := sel.Type().(*Signature)
|
||||||
|
if sel.Kind() == MethodVal {
|
||||||
|
got := sig.Recv().Type()
|
||||||
|
want := sel.Recv()
|
||||||
|
if !Identical(got, want) {
|
||||||
|
t.Errorf("%s: Recv() = %s, want %s", got, want)
|
||||||
|
}
|
||||||
|
} else if sig != nil && sig.Recv() != nil {
|
||||||
|
t.Error("%s: signature has receiver %s", sig, sig.Recv().Type())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -123,6 +123,12 @@ func (s *Selection) String() string { return SelectionString(nil, s) }
|
|||||||
// Type names are printed package-qualified
|
// Type names are printed package-qualified
|
||||||
// only if they do not belong to this package.
|
// only if they do not belong to this package.
|
||||||
//
|
//
|
||||||
|
// Examples:
|
||||||
|
// "field (T) f int"
|
||||||
|
// "method (T) f(X) Y"
|
||||||
|
// "method expr (T) f(X) Y"
|
||||||
|
// "qualified ident var math.Pi float64"
|
||||||
|
//
|
||||||
func SelectionString(this *Package, s *Selection) string {
|
func SelectionString(this *Package, s *Selection) string {
|
||||||
var k string
|
var k string
|
||||||
switch s.kind {
|
switch s.kind {
|
||||||
@ -142,6 +148,12 @@ func SelectionString(this *Package, s *Selection) string {
|
|||||||
buf.WriteByte('(')
|
buf.WriteByte('(')
|
||||||
WriteType(&buf, this, s.Recv())
|
WriteType(&buf, this, s.Recv())
|
||||||
fmt.Fprintf(&buf, ") %s", s.obj.Name())
|
fmt.Fprintf(&buf, ") %s", s.obj.Name())
|
||||||
WriteSignature(&buf, this, s.Type().(*Signature))
|
if T := s.Type(); s.kind == FieldVal {
|
||||||
|
// TODO(adonovan): use "T.f" not "(T) f".
|
||||||
|
buf.WriteByte(' ')
|
||||||
|
WriteType(&buf, this, T)
|
||||||
|
} else {
|
||||||
|
WriteSignature(&buf, this, T.(*Signature))
|
||||||
|
}
|
||||||
return buf.String()
|
return buf.String()
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user