]> Cypherpunks repositories - gostls13.git/commitdiff
exp/types: implement Type.String methods for testing/debugging
authorRobert Griesemer <gri@golang.org>
Wed, 1 Aug 2012 00:09:12 +0000 (17:09 -0700)
committerRobert Griesemer <gri@golang.org>
Wed, 1 Aug 2012 00:09:12 +0000 (17:09 -0700)
Also:
- replaced existing test with a more comprehensive test
- fixed bug in map type creation

R=r
CC=golang-dev
https://golang.org/cl/6450072

src/pkg/exp/types/check.go
src/pkg/exp/types/types.go
src/pkg/exp/types/types_test.go

index aebabd6421bd969eef56f908c678b6c3c201e162..9d7474a446909bd35811c690ac4b5b3b19ff4c9a 100644 (file)
@@ -168,7 +168,7 @@ func (c *checker) makeType(x ast.Expr, cycleOk bool) (typ Type) {
                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)}
index 85d244cf04a5f2a8eca56db25d011330da6ce00f..0e844107cba7ac06728b11917caf5872e7bfc5bb 100644 (file)
@@ -8,6 +8,8 @@
 package types
 
 import (
+       "bytes"
+       "fmt"
        "go/ast"
        "sort"
 )
@@ -15,43 +17,61 @@ import (
 // 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:
@@ -60,49 +80,148 @@ type Struct struct {
        // - 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 {
index feb394855317d6176ed49e69ad58ee84a8983444..b2c22d747a960239ab8a822873d63629a7b7d1cf 100644 (file)
@@ -13,55 +13,116 @@ import (
        "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.
 }