return &Interface{Methods: methods}
case *ast.MapType:
- return &Map{Key: c.makeType(t.Key, true), Elt: c.makeType(t.Key, true)}
+ return &Map{Key: c.makeType(t.Key, true), Elt: c.makeType(t.Value, true)}
case *ast.ChanType:
return &Chan{Dir: t.Dir, Elt: c.makeType(t.Value, true)}
package types
import (
+ "bytes"
+ "fmt"
"go/ast"
"sort"
)
// All types implement the Type interface.
type Type interface {
isType()
+ String() string
}
-// All concrete types embed ImplementsType which
+// All concrete types embed implementsType which
// ensures that all types implement the Type interface.
-type ImplementsType struct{}
+type implementsType struct{}
-func (t *ImplementsType) isType() {}
+func (t *implementsType) isType() {}
// A Bad type is a non-nil placeholder type when we don't know a type.
type Bad struct {
- ImplementsType
+ implementsType
Msg string // for better error reporting/debugging
}
+func (t *Bad) String() string {
+ return fmt.Sprintf("badType(%s)", t.Msg)
+}
+
// A Basic represents a (unnamed) basic type.
type Basic struct {
- ImplementsType
+ implementsType
// TODO(gri) need a field specifying the exact basic type
}
+func (t *Basic) String() string {
+ // TODO(gri) print actual type information
+ return "basicType"
+}
+
// An Array represents an array type [Len]Elt.
type Array struct {
- ImplementsType
+ implementsType
Len uint64
Elt Type
}
+func (t *Array) String() string {
+ return fmt.Sprintf("[%d]%s", t.Len, t.Elt)
+}
+
// A Slice represents a slice type []Elt.
type Slice struct {
- ImplementsType
+ implementsType
Elt Type
}
+func (t *Slice) String() string {
+ return "[]" + t.Elt.String()
+}
+
// A Struct represents a struct type struct{...}.
// Anonymous fields are represented by objects with empty names.
type Struct struct {
- ImplementsType
+ implementsType
Fields ObjList // struct fields; or nil
Tags []string // corresponding tags; or nil
// TODO(gri) This type needs some rethinking:
// - there is no scope for fast lookup (but the parser creates one)
}
+func (t *Struct) String() string {
+ buf := bytes.NewBufferString("struct{")
+ for i, fld := range t.Fields {
+ if i > 0 {
+ buf.WriteString("; ")
+ }
+ if fld.Name != "" {
+ buf.WriteString(fld.Name)
+ buf.WriteByte(' ')
+ }
+ buf.WriteString(fld.Type.(Type).String())
+ if i < len(t.Tags) && t.Tags[i] != "" {
+ fmt.Fprintf(buf, " %q", t.Tags[i])
+ }
+ }
+ buf.WriteByte('}')
+ return buf.String()
+}
+
// A Pointer represents a pointer type *Base.
type Pointer struct {
- ImplementsType
+ implementsType
Base Type
}
+func (t *Pointer) String() string {
+ return "*" + t.Base.String()
+}
+
// A Func represents a function type func(...) (...).
// Unnamed parameters are represented by objects with empty names.
type Func struct {
- ImplementsType
+ implementsType
Recv *ast.Object // nil if not a method
Params ObjList // (incoming) parameters from left to right; or nil
Results ObjList // (outgoing) results from left to right; or nil
IsVariadic bool // true if the last parameter's type is of the form ...T
}
+func writeParams(buf *bytes.Buffer, params ObjList, isVariadic bool) {
+ buf.WriteByte('(')
+ for i, par := range params {
+ if i > 0 {
+ buf.WriteString(", ")
+ }
+ if par.Name != "" {
+ buf.WriteString(par.Name)
+ buf.WriteByte(' ')
+ }
+ if isVariadic && i == len(params)-1 {
+ buf.WriteString("...")
+ }
+ buf.WriteString(par.Type.(Type).String())
+ }
+ buf.WriteByte(')')
+}
+
+func writeSignature(buf *bytes.Buffer, t *Func) {
+ writeParams(buf, t.Params, t.IsVariadic)
+ if len(t.Results) == 0 {
+ // no result
+ return
+ }
+
+ buf.WriteByte(' ')
+ if len(t.Results) == 1 && t.Results[0].Name == "" {
+ // single unnamed result
+ buf.WriteString(t.Results[0].Type.(Type).String())
+ return
+ }
+
+ // multiple or named result(s)
+ writeParams(buf, t.Results, false)
+}
+
+func (t *Func) String() string {
+ buf := bytes.NewBufferString("func")
+ writeSignature(buf, t)
+ return buf.String()
+}
+
// An Interface represents an interface type interface{...}.
type Interface struct {
- ImplementsType
+ implementsType
Methods ObjList // interface methods sorted by name; or nil
}
+func (t *Interface) String() string {
+ buf := bytes.NewBufferString("interface{")
+ for i, m := range t.Methods {
+ if i > 0 {
+ buf.WriteString("; ")
+ }
+ buf.WriteString(m.Name)
+ writeSignature(buf, m.Type.(*Func))
+ }
+ buf.WriteByte('}')
+ return buf.String()
+}
+
// A Map represents a map type map[Key]Elt.
type Map struct {
- ImplementsType
+ implementsType
Key, Elt Type
}
+func (t *Map) String() string {
+ return fmt.Sprintf("map[%s]%s", t.Key, t.Elt)
+}
+
// A Chan represents a channel type chan Elt, <-chan Elt, or chan<-Elt.
type Chan struct {
- ImplementsType
+ implementsType
Dir ast.ChanDir
Elt Type
}
+func (t *Chan) String() string {
+ var s string
+ switch t.Dir {
+ case ast.SEND:
+ s = "chan<- "
+ case ast.RECV:
+ s = "<-chan "
+ default:
+ s = "chan "
+ }
+ return s + t.Elt.String()
+}
+
// A Name represents a named type as declared in a type declaration.
type Name struct {
- ImplementsType
+ implementsType
Underlying Type // nil if not fully declared
Obj *ast.Object // corresponding declared object
// TODO(gri) need to remember fields and methods.
}
+func (t *Name) String() string {
+ return t.Obj.Name
+}
+
// If typ is a pointer type, Deref returns the pointer's base type;
// otherwise it returns typ.
func Deref(typ Type) Type {
"testing"
)
-func checkSource(t *testing.T, src string) *ast.Package {
+func makePkg(t *testing.T, src string) (*ast.Package, error) {
const filename = "<src>"
file, err := parser.ParseFile(fset, filename, src, parser.DeclarationErrors)
if err != nil {
- t.Fatal(err)
+ return nil, err
}
files := map[string]*ast.File{filename: file}
pkg, err := ast.NewPackage(fset, files, GcImport, Universe)
if err != nil {
- t.Fatal(err)
+ return nil, err
}
- _, err = Check(fset, pkg)
- if err != nil {
- t.Fatal(err)
+ if _, err := Check(fset, pkg); err != nil {
+ return nil, err
}
- return pkg
+ return pkg, nil
}
-func TestVariadicFunctions(t *testing.T) {
- pkg := checkSource(t, `
-package p
-func f1(arg ...int)
-func f2(arg1 string, arg2 ...int)
-func f3()
-func f4(arg int)
- `)
- f1 := pkg.Scope.Lookup("f1")
- f2 := pkg.Scope.Lookup("f2")
- for _, f := range [...](*ast.Object){f1, f2} {
- ftype := f.Type.(*Func)
- if !ftype.IsVariadic {
- t.Errorf("expected %s to be variadic", f.Name)
- }
- param := ftype.Params[len(ftype.Params)-1]
- if param.Type != Int {
- t.Errorf("expected last parameter of %s to have type int, found %T", f.Name, param.Type)
- }
- }
+type testEntry struct {
+ src, str string
+}
+
+// dup returns a testEntry where both src and str are the same.
+func dup(s string) testEntry {
+ return testEntry{s, s}
+}
+
+var testTypes = []testEntry{
+ // basic types
+ dup("int"),
+ dup("float32"),
+ dup("string"),
+
+ // arrays
+ {"[10]int", "[0]int"}, // TODO(gri) fix array length, add more array tests
+
+ // slices
+ dup("[]int"),
+ dup("[][]int"),
+
+ // structs
+ dup("struct{}"),
+ dup("struct{x int}"),
+ {`struct {
+ x, y int
+ z float32 "foo"
+ }`, `struct{x int; y int; z float32 "foo"}`},
+ {`struct {
+ string
+ elems []T
+ }`, `struct{string; elems []T}`},
- f3 := pkg.Scope.Lookup("f3")
- f4 := pkg.Scope.Lookup("f4")
- for _, f := range [...](*ast.Object){f3, f4} {
- ftype := f.Type.(*Func)
- if ftype.IsVariadic {
- t.Fatalf("expected %s to not be variadic", f.Name)
+ // pointers
+ dup("*int"),
+ dup("***struct{}"),
+ dup("*struct{a int; b float32}"),
+
+ // functions
+ dup("func()"),
+ dup("func(x int)"),
+ {"func(x, y int)", "func(x int, y int)"},
+ {"func(x, y int, z string)", "func(x int, y int, z string)"},
+ dup("func(int)"),
+ dup("func(int, string, byte)"),
+
+ dup("func() int"),
+ {"func() (string)", "func() string"},
+ dup("func() (u int)"),
+ {"func() (u, v int, w string)", "func() (u int, v int, w string)"},
+
+ dup("func(int) string"),
+ dup("func(x int) string"),
+ dup("func(x int) (u string)"),
+ {"func(x, y int) (u string)", "func(x int, y int) (u string)"},
+
+ dup("func(...int) string"),
+ dup("func(x ...int) string"),
+ dup("func(x ...int) (u string)"),
+ {"func(x, y ...int) (u string)", "func(x int, y ...int) (u string)"},
+
+ // interfaces
+ dup("interface{}"),
+ dup("interface{m()}"),
+ {`interface{
+ m(int) float32
+ String() string
+ }`, `interface{String() string; m(int) float32}`}, // methods are sorted
+ // TODO(gri) add test for interface w/ anonymous field
+
+ // maps
+ dup("map[string]int"),
+ {"map[struct{x, y int}][]byte", "map[struct{x int; y int}][]byte"},
+
+ // channels
+ dup("chan int"),
+ dup("chan<- func()"),
+ dup("<-chan []func() int"),
+}
+
+func TestTypes(t *testing.T) {
+ for _, test := range testTypes {
+ src := "package p; type T " + test.src
+ pkg, err := makePkg(t, src)
+ if err != nil {
+ t.Errorf("%s: %s", src, err)
+ continue
+ }
+ typ := Underlying(pkg.Scope.Lookup("T").Type.(Type))
+ str := typ.String()
+ if str != test.str {
+ t.Errorf("%s: got %s, want %s", test.src, str, test.str)
}
}
- // TODO(axw) replace this function's innards with table driven tests.
- // We should have a helper function that prints a type signature. Then
- // we can have a table of function declarations and expected type
- // signatures which can be easily expanded.
}