1
0
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:
Alan Donovan 2014-05-07 18:35:43 -04:00
parent e6020f7229
commit c212b356b8
2 changed files with 180 additions and 1 deletions

View File

@ -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())
}
}
}

View File

@ -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()
} }