From df6f0829a2262d19411ebbd5db9baee8cb5ffa36 Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Mon, 18 Nov 2013 22:38:48 -0800 Subject: [PATCH] go.tools/go/types: complete expr and type string functions - consolidate them in (expr|type)string[_test].go - support for printing all ast.Expr - fix printing of type: chan (<-chan int) (parentheses) - more consistent names, comments, and exports R=adonovan CC=golang-dev https://golang.org/cl/28680043 --- go/gcimporter/gcimporter.go | 2 +- go/types/api_test.go | 27 +- go/types/errors.go | 271 ------------------ go/types/exprstring.go | 220 ++++++++++++++ go/types/exprstring_test.go | 100 +++++++ go/types/objects.go | 4 +- go/types/operand.go | 2 +- go/types/selection.go | 2 +- go/types/typestring.go | 193 +++++++++++++ .../{types_test.go => typestring_test.go} | 78 ++--- 10 files changed, 547 insertions(+), 352 deletions(-) create mode 100644 go/types/exprstring.go create mode 100644 go/types/exprstring_test.go create mode 100644 go/types/typestring.go rename go/types/{types_test.go => typestring_test.go} (67%) diff --git a/go/gcimporter/gcimporter.go b/go/gcimporter/gcimporter.go index 439269c2..99e4217a 100644 --- a/go/gcimporter/gcimporter.go +++ b/go/gcimporter/gcimporter.go @@ -328,7 +328,7 @@ func (p *parser) parseQualifiedName() (id, name string) { p.expect('@') id = p.parsePackageId() p.expect('.') - // Per rev f280b8a485fd (10/2/2013), qualified names may be used for anoymous fields. + // Per rev f280b8a485fd (10/2/2013), qualified names may be used for anonymous fields. if p.tok == '?' { p.next() } else { diff --git a/go/types/api_test.go b/go/types/api_test.go index eca697e9..39f0554a 100644 --- a/go/types/api_test.go +++ b/go/types/api_test.go @@ -236,10 +236,10 @@ func TestInitOrder(t *testing.T) { "z = 0", "a, b = f()", }}, {`package p7; var (a = func() int { return b }(); b = 1)`, []string{ - "b = 1", "a = (func literal)()", + "b = 1", "a = (func() int literal)()", }}, {`package p8; var (a, b = func() (_, _ int) { return c, c }(); c = 1)`, []string{ - "c = 1", "a, b = (func literal)()", + "c = 1", "a, b = (func() (_, _ int) literal)()", }}, {`package p9; type T struct{}; func (T) m() int { _ = y; return 0 }; var x, y = T.m, 1`, []string{ "y = 1", "x = T.m", @@ -266,26 +266,3 @@ func TestInitOrder(t *testing.T) { } } } - -func TestTypeString(t *testing.T) { - p, _ := pkgFor("p.go", "package p; type T int", nil) - q, _ := pkgFor("q.go", "package q", nil) - - pT := p.Scope().Lookup("T").Type() - for _, test := range []struct { - typ Type - this *Package - want string - }{ - {pT, nil, "p.T"}, - {pT, p, "T"}, - {pT, q, "p.T"}, - {NewPointer(pT), p, "*T"}, - {NewPointer(pT), q, "*p.T"}, - } { - if got := TypeString(test.this, test.typ); got != test.want { - t.Errorf("TypeString(%s, %s) = %s, want %s", - test.this, test.typ, got, test.want) - } - } -} diff --git a/go/types/errors.go b/go/types/errors.go index 125457f6..470e512b 100644 --- a/go/types/errors.go +++ b/go/types/errors.go @@ -7,7 +7,6 @@ package types import ( - "bytes" "fmt" "go/ast" "go/token" @@ -80,273 +79,3 @@ func (check *checker) invalidArg(pos token.Pos, format string, args ...interface func (check *checker) invalidOp(pos token.Pos, format string, args ...interface{}) { check.errorf(pos, "invalid operation: "+format, args...) } - -// exprString returns a (simplified) string representation for an expression. -func ExprString(expr ast.Expr) string { - var buf bytes.Buffer - WriteExpr(&buf, expr) - return buf.String() -} - -// TODO(gri) Need to merge with TypeString since some expressions are types (try: ([]int)(a)) -func WriteExpr(buf *bytes.Buffer, expr ast.Expr) { - switch x := expr.(type) { - case *ast.Ident: - buf.WriteString(x.Name) - - case *ast.BasicLit: - buf.WriteString(x.Value) - - case *ast.FuncLit: - buf.WriteString("(func literal)") - - case *ast.CompositeLit: - buf.WriteString("(composite literal)") - - case *ast.ParenExpr: - buf.WriteByte('(') - WriteExpr(buf, x.X) - buf.WriteByte(')') - - case *ast.SelectorExpr: - WriteExpr(buf, x.X) - buf.WriteByte('.') - buf.WriteString(x.Sel.Name) - - case *ast.IndexExpr: - WriteExpr(buf, x.X) - buf.WriteByte('[') - WriteExpr(buf, x.Index) - buf.WriteByte(']') - - case *ast.SliceExpr: - WriteExpr(buf, x.X) - buf.WriteByte('[') - if x.Low != nil { - WriteExpr(buf, x.Low) - } - buf.WriteByte(':') - if x.High != nil { - WriteExpr(buf, x.High) - } - buf.WriteByte(']') - - case *ast.TypeAssertExpr: - WriteExpr(buf, x.X) - buf.WriteString(".(") - // TODO(gri) expand WriteExpr so that types are not handled by default case - WriteExpr(buf, x.Type) - buf.WriteByte(')') - - case *ast.CallExpr: - WriteExpr(buf, x.Fun) - buf.WriteByte('(') - for i, arg := range x.Args { - if i > 0 { - buf.WriteString(", ") - } - WriteExpr(buf, arg) - } - buf.WriteByte(')') - - case *ast.StarExpr: - buf.WriteByte('*') - WriteExpr(buf, x.X) - - case *ast.UnaryExpr: - buf.WriteString(x.Op.String()) - WriteExpr(buf, x.X) - - case *ast.BinaryExpr: - // The AST preserves source-level parentheses so there is - // no need to introduce parentheses here for correctness. - WriteExpr(buf, x.X) - buf.WriteByte(' ') - buf.WriteString(x.Op.String()) - buf.WriteByte(' ') - WriteExpr(buf, x.Y) - - default: - // TODO(gri) Consider just calling x.String(). May cause - // infinite recursion if we missed a local type. - fmt.Fprintf(buf, "", x) - } -} - -// TypeString returns the string form of typ. -// Named types are printed package-qualified only -// if they do not belong to this package. -// -func TypeString(this *Package, typ Type) string { - var buf bytes.Buffer - writeType(&buf, this, typ) - return buf.String() -} - -func writeTuple(buf *bytes.Buffer, this *Package, tup *Tuple, isVariadic bool) { - buf.WriteByte('(') - if tup != nil { - for i, v := range tup.vars { - if i > 0 { - buf.WriteString(", ") - } - if v.name != "" { - buf.WriteString(v.name) - buf.WriteByte(' ') - } - typ := v.typ - if isVariadic && i == len(tup.vars)-1 { - buf.WriteString("...") - typ = typ.(*Slice).elem - } - writeType(buf, this, typ) - } - } - buf.WriteByte(')') -} - -func writeSignature(buf *bytes.Buffer, this *Package, sig *Signature) { - writeTuple(buf, this, sig.params, sig.isVariadic) - - n := sig.results.Len() - if n == 0 { - // no result - return - } - - buf.WriteByte(' ') - if n == 1 && sig.results.vars[0].name == "" { - // single unnamed result - writeType(buf, this, sig.results.vars[0].typ) - return - } - - // multiple or named result(s) - writeTuple(buf, this, sig.results, false) -} - -func writeType(buf *bytes.Buffer, this *Package, typ Type) { - switch t := typ.(type) { - case nil: - buf.WriteString("") - - case *Basic: - if t.kind == UnsafePointer { - buf.WriteString("unsafe.") - } - buf.WriteString(t.name) - - case *Array: - fmt.Fprintf(buf, "[%d]", t.len) - writeType(buf, this, t.elem) - - case *Slice: - buf.WriteString("[]") - writeType(buf, this, t.elem) - - case *Struct: - buf.WriteString("struct{") - for i, f := range t.fields { - if i > 0 { - buf.WriteString("; ") - } - if !f.anonymous { - buf.WriteString(f.name) - buf.WriteByte(' ') - } - writeType(buf, this, f.typ) - if tag := t.Tag(i); tag != "" { - fmt.Fprintf(buf, " %q", tag) - } - } - buf.WriteByte('}') - - case *Pointer: - buf.WriteByte('*') - writeType(buf, this, t.base) - - case *Tuple: - writeTuple(buf, this, t, false) - - case *Signature: - buf.WriteString("func") - writeSignature(buf, this, t) - - case *Interface: - // We write the source-level methods and embedded types rather - // than the actual method set since resolved method signatures - // may have non-printable cycles if parameters have anonymous - // interface types that (directly or indirectly) embed the - // current interface. For instance, consider the result type - // of m: - // - // type T interface{ - // m() interface{ T } - // } - // - buf.WriteString("interface{") - for i, m := range t.methods { - if i > 0 { - buf.WriteString("; ") - } - buf.WriteString(m.name) - writeSignature(buf, this, m.typ.(*Signature)) - } - for i, typ := range t.types { - if i > 0 || len(t.methods) > 0 { - buf.WriteString("; ") - } - writeType(buf, this, typ) - } - buf.WriteByte('}') - - case *Map: - buf.WriteString("map[") - writeType(buf, this, t.key) - buf.WriteByte(']') - writeType(buf, this, t.elem) - - case *Chan: - var s string - switch t.dir { - case ast.SEND: - s = "chan<- " - case ast.RECV: - s = "<-chan " - default: - s = "chan " - } - buf.WriteString(s) - writeType(buf, this, t.elem) - - case *Named: - s := "" - if obj := t.obj; obj != nil { - if obj.pkg != nil { - if obj.pkg != this { - buf.WriteString(obj.pkg.path) - buf.WriteByte('.') - } - // TODO(gri) Ideally we only want the qualification - // if we are referring to a type that was imported; - // but not when we are at the "top". We don't have - // this information easily available here. - - // Some applications may want another variant that accepts a - // file Scope and prints packages using that file's local - // import names. (Printing just pkg.name may be ambiguous - // or incorrect in other scopes.) - - // TODO(gri): function-local named types should be displayed - // differently from named types at package level to avoid - // ambiguity. - } - s = t.obj.name - } - buf.WriteString(s) - - default: - // For externally defined implementations of Type. - buf.WriteString(t.String()) - } -} diff --git a/go/types/exprstring.go b/go/types/exprstring.go new file mode 100644 index 00000000..370bdf35 --- /dev/null +++ b/go/types/exprstring.go @@ -0,0 +1,220 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file implements printing of expressions. + +package types + +import ( + "bytes" + "go/ast" +) + +// ExprString returns the (possibly simplified) string representation for x. +func ExprString(x ast.Expr) string { + var buf bytes.Buffer + WriteExpr(&buf, x) + return buf.String() +} + +// WriteExpr writes the (possibly simplified) string representation for x to buf. +func WriteExpr(buf *bytes.Buffer, x ast.Expr) { + // The AST preserves source-level parentheses so there is + // no need to introduce them here to correct for different + // operator precedences. (This assumes that the AST was + // generated by a Go parser.) + + switch x := x.(type) { + default: + buf.WriteString("(bad expr)") // nil, ast.BadExpr, ast.KeyValueExpr + + case *ast.Ident: + buf.WriteString(x.Name) + + case *ast.Ellipsis: + buf.WriteString("...") + if x.Elt != nil { + WriteExpr(buf, x.Elt) + } + + case *ast.BasicLit: + buf.WriteString(x.Value) + + case *ast.FuncLit: + buf.WriteByte('(') + WriteExpr(buf, x.Type) + buf.WriteString(" literal)") // simplified + + case *ast.CompositeLit: + buf.WriteByte('(') + WriteExpr(buf, x.Type) + buf.WriteString(" literal)") // simplified + + case *ast.ParenExpr: + buf.WriteByte('(') + WriteExpr(buf, x.X) + buf.WriteByte(')') + + case *ast.SelectorExpr: + WriteExpr(buf, x.X) + buf.WriteByte('.') + buf.WriteString(x.Sel.Name) + + case *ast.IndexExpr: + WriteExpr(buf, x.X) + buf.WriteByte('[') + WriteExpr(buf, x.Index) + buf.WriteByte(']') + + case *ast.SliceExpr: + WriteExpr(buf, x.X) + buf.WriteByte('[') + if x.Low != nil { + WriteExpr(buf, x.Low) + } + buf.WriteByte(':') + if x.High != nil { + WriteExpr(buf, x.High) + } + if x.Slice3 { + buf.WriteByte(':') + if x.Max != nil { + WriteExpr(buf, x.Max) + } + } + buf.WriteByte(']') + + case *ast.TypeAssertExpr: + WriteExpr(buf, x.X) + buf.WriteString(".(") + WriteExpr(buf, x.Type) + buf.WriteByte(')') + + case *ast.CallExpr: + WriteExpr(buf, x.Fun) + buf.WriteByte('(') + for i, arg := range x.Args { + if i > 0 { + buf.WriteString(", ") + } + WriteExpr(buf, arg) + } + if x.Ellipsis.IsValid() { + buf.WriteString("...") + } + buf.WriteByte(')') + + case *ast.StarExpr: + buf.WriteByte('*') + WriteExpr(buf, x.X) + + case *ast.UnaryExpr: + buf.WriteString(x.Op.String()) + WriteExpr(buf, x.X) + + case *ast.BinaryExpr: + WriteExpr(buf, x.X) + buf.WriteByte(' ') + buf.WriteString(x.Op.String()) + buf.WriteByte(' ') + WriteExpr(buf, x.Y) + + case *ast.ArrayType: + buf.WriteByte('[') + if x.Len != nil { + WriteExpr(buf, x.Len) + } + buf.WriteByte(']') + WriteExpr(buf, x.Elt) + + case *ast.StructType: + buf.WriteString("struct{") + writeFieldList(buf, x.Fields, "; ", false) + buf.WriteByte('}') + + case *ast.FuncType: + buf.WriteString("func") + writeSigExpr(buf, x) + + case *ast.InterfaceType: + buf.WriteString("interface{") + writeFieldList(buf, x.Methods, "; ", true) + buf.WriteByte('}') + + case *ast.MapType: + buf.WriteString("map[") + WriteExpr(buf, x.Key) + buf.WriteByte(']') + WriteExpr(buf, x.Value) + + case *ast.ChanType: + var s string + switch x.Dir { + case ast.SEND: + s = "chan<- " + case ast.RECV: + s = "<-chan " + default: + s = "chan " + } + buf.WriteString(s) + WriteExpr(buf, x.Value) + } +} + +func writeSigExpr(buf *bytes.Buffer, sig *ast.FuncType) { + buf.WriteByte('(') + writeFieldList(buf, sig.Params, ", ", false) + buf.WriteByte(')') + + res := sig.Results + n := res.NumFields() + if n == 0 { + // no result + return + } + + buf.WriteByte(' ') + if n == 1 && len(res.List[0].Names) == 0 { + // single unnamed result + WriteExpr(buf, res.List[0].Type) + return + } + + // multiple or named result(s) + buf.WriteByte('(') + writeFieldList(buf, res, ", ", false) + buf.WriteByte(')') +} + +func writeFieldList(buf *bytes.Buffer, fields *ast.FieldList, sep string, iface bool) { + for i, f := range fields.List { + if i > 0 { + buf.WriteString(sep) + } + + // field list names + for i, name := range f.Names { + if i > 0 { + buf.WriteString(", ") + } + buf.WriteString(name.Name) + } + + // types of interface methods consist of signatures only + if sig, _ := f.Type.(*ast.FuncType); sig != nil && iface { + writeSigExpr(buf, sig) + continue + } + + // named fields are separated with a blank from the field type + if len(f.Names) > 0 { + buf.WriteByte(' ') + } + + WriteExpr(buf, f.Type) + + // ignore tag + } +} diff --git a/go/types/exprstring_test.go b/go/types/exprstring_test.go new file mode 100644 index 00000000..41a86e2c --- /dev/null +++ b/go/types/exprstring_test.go @@ -0,0 +1,100 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package types_test + +import ( + "go/parser" + "testing" + + . "code.google.com/p/go.tools/go/types" +) + +var testExprs = []testEntry{ + // basic type literals + dup("x"), + dup("true"), + dup("42"), + dup("3.1415"), + dup("2.71828i"), + dup(`'a'`), + dup(`"foo"`), + dup("`bar`"), + + // func and composite literals + {"func(){}", "(func() literal)"}, + {"func(x int) complex128 {}", "(func(x int) complex128 literal)"}, + {"[]int{1, 2, 3}", "([]int literal)"}, + + // non-type expressions + dup("(x)"), + dup("x.f"), + dup("a[i]"), + + dup("s[:]"), + dup("s[i:]"), + dup("s[:j]"), + dup("s[i:j]"), + dup("s[::]"), + dup("s[i::]"), + dup("s[:j:]"), + dup("s[::k]"), + dup("s[i:j:]"), + dup("s[i::k]"), + dup("s[:j:k]"), + dup("s[i:j:k]"), + + dup("x.(T)"), + + dup("x.([10]int)"), + dup("x.([...]int)"), + + dup("x.(struct{})"), + dup("x.(struct{x int; y, z float32; E})"), + + dup("x.(func())"), + dup("x.(func(x int))"), + dup("x.(func() int)"), + dup("x.(func(x, y int, z float32) (r int))"), + dup("x.(func(a, b, c int))"), + dup("x.(func(x ...T))"), + + dup("x.(interface{})"), + dup("x.(interface{m(); n(x int); E})"), + dup("x.(interface{m(); n(x int) T; E; F})"), + + dup("x.(map[K]V)"), + + dup("x.(chan E)"), + dup("x.(<-chan E)"), + dup("x.(chan<- chan int)"), + dup("x.(chan<- <-chan int)"), + dup("x.(<-chan chan int)"), + dup("x.(chan (<-chan int))"), + + dup("f()"), + dup("f(x)"), + dup("int(x)"), + dup("f(x, x + y)"), + dup("f(s...)"), + dup("f(a, s...)"), + + dup("*x"), + dup("&x"), + dup("x + y"), + dup("x + y << (2 * s)"), +} + +func TestExprString(t *testing.T) { + for _, test := range testExprs { + x, err := parser.ParseExpr(test.src) + if err != nil { + t.Errorf("%s: %s", test.src, err) + continue + } + if got := ExprString(x); got != test.str { + t.Errorf("%s: got %s, want %s", test.src, got, test.str) + } + } +} diff --git a/go/types/objects.go b/go/types/objects.go index 3101bda8..71809249 100644 --- a/go/types/objects.go +++ b/go/types/objects.go @@ -270,7 +270,7 @@ func writeObject(buf *bytes.Buffer, this *Package, obj Object) { buf.WriteString(obj.Name()) if typ != nil { buf.WriteByte(' ') - writeType(buf, this, typ) + WriteType(buf, this, typ) } } @@ -305,7 +305,7 @@ func writeFuncName(buf *bytes.Buffer, this *Package, f *Func) { // Don't print it in full. buf.WriteString("interface") } else { - writeType(buf, this, recv.Type()) + WriteType(buf, this, recv.Type()) } buf.WriteByte(')') buf.WriteByte('.') diff --git a/go/types/operand.go b/go/types/operand.go index 4ad29e47..fc474834 100644 --- a/go/types/operand.go +++ b/go/types/operand.go @@ -147,7 +147,7 @@ func (x *operand) String() string { if hasType { if x.typ != Typ[Invalid] { buf.WriteString(" of type ") - writeType(&buf, nil, x.typ) + WriteType(&buf, nil, x.typ) } else { buf.WriteString(" with invalid type") } diff --git a/go/types/selection.go b/go/types/selection.go index 992ecf9c..26f39c5b 100644 --- a/go/types/selection.go +++ b/go/types/selection.go @@ -139,7 +139,7 @@ func SelectionString(this *Package, s *Selection) string { var buf bytes.Buffer buf.WriteString(k) buf.WriteByte('(') - writeType(&buf, this, s.Recv()) + WriteType(&buf, this, s.Recv()) fmt.Fprintf(&buf, ") %s", s.obj.Name()) writeSignature(&buf, this, s.Type().(*Signature)) return buf.String() diff --git a/go/types/typestring.go b/go/types/typestring.go new file mode 100644 index 00000000..77323415 --- /dev/null +++ b/go/types/typestring.go @@ -0,0 +1,193 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file implements printing of types. + +package types + +import ( + "bytes" + "fmt" + "go/ast" +) + +// TypeString returns the string representation of typ. +// Named types are printed package-qualified if they +// do not belong to this package. +func TypeString(this *Package, typ Type) string { + var buf bytes.Buffer + WriteType(&buf, this, typ) + return buf.String() +} + +// WriteType writes the string representation of typ to buf. +// Named types are printed package-qualified if they +// do not belong to this package. +func WriteType(buf *bytes.Buffer, this *Package, typ Type) { + switch t := typ.(type) { + case nil: + buf.WriteString("") + + case *Basic: + if t.kind == UnsafePointer { + buf.WriteString("unsafe.") + } + buf.WriteString(t.name) + + case *Array: + fmt.Fprintf(buf, "[%d]", t.len) + WriteType(buf, this, t.elem) + + case *Slice: + buf.WriteString("[]") + WriteType(buf, this, t.elem) + + case *Struct: + buf.WriteString("struct{") + for i, f := range t.fields { + if i > 0 { + buf.WriteString("; ") + } + if !f.anonymous { + buf.WriteString(f.name) + buf.WriteByte(' ') + } + WriteType(buf, this, f.typ) + if tag := t.Tag(i); tag != "" { + fmt.Fprintf(buf, " %q", tag) + } + } + buf.WriteByte('}') + + case *Pointer: + buf.WriteByte('*') + WriteType(buf, this, t.base) + + case *Tuple: + writeTuple(buf, this, t, false) + + case *Signature: + buf.WriteString("func") + writeSignature(buf, this, t) + + case *Interface: + // We write the source-level methods and embedded types rather + // than the actual method set since resolved method signatures + // may have non-printable cycles if parameters have anonymous + // interface types that (directly or indirectly) embed the + // current interface. For instance, consider the result type + // of m: + // + // type T interface{ + // m() interface{ T } + // } + // + buf.WriteString("interface{") + for i, m := range t.methods { + if i > 0 { + buf.WriteString("; ") + } + buf.WriteString(m.name) + writeSignature(buf, this, m.typ.(*Signature)) + } + for i, typ := range t.types { + if i > 0 || len(t.methods) > 0 { + buf.WriteString("; ") + } + WriteType(buf, this, typ) + } + buf.WriteByte('}') + + case *Map: + buf.WriteString("map[") + WriteType(buf, this, t.key) + buf.WriteByte(']') + WriteType(buf, this, t.elem) + + case *Chan: + var s string + var parens bool + switch t.dir { + case ast.SEND: + s = "chan<- " + case ast.RECV: + s = "<-chan " + default: + s = "chan " + if c, _ := t.elem.(*Chan); c != nil { + parens = c.dir == ast.RECV + } + } + buf.WriteString(s) + if parens { + buf.WriteByte('(') + } + WriteType(buf, this, t.elem) + if parens { + buf.WriteByte(')') + } + + case *Named: + s := "" + if obj := t.obj; obj != nil { + if obj.pkg != nil { + if obj.pkg != this { + buf.WriteString(obj.pkg.path) + buf.WriteByte('.') + } + // TODO(gri): function-local named types should be displayed + // differently from named types at package level to avoid + // ambiguity. + } + s = t.obj.name + } + buf.WriteString(s) + + default: + // For externally defined implementations of Type. + buf.WriteString(t.String()) + } +} + +func writeTuple(buf *bytes.Buffer, this *Package, tup *Tuple, isVariadic bool) { + buf.WriteByte('(') + if tup != nil { + for i, v := range tup.vars { + if i > 0 { + buf.WriteString(", ") + } + if v.name != "" { + buf.WriteString(v.name) + buf.WriteByte(' ') + } + typ := v.typ + if isVariadic && i == len(tup.vars)-1 { + buf.WriteString("...") + typ = typ.(*Slice).elem + } + WriteType(buf, this, typ) + } + } + buf.WriteByte(')') +} + +func writeSignature(buf *bytes.Buffer, this *Package, sig *Signature) { + writeTuple(buf, this, sig.params, sig.isVariadic) + + n := sig.results.Len() + if n == 0 { + // no result + return + } + + buf.WriteByte(' ') + if n == 1 && sig.results.vars[0].name == "" { + // single unnamed result + WriteType(buf, this, sig.results.vars[0].typ) + return + } + + // multiple or named result(s) + writeTuple(buf, this, sig.results, false) +} diff --git a/go/types/types_test.go b/go/types/typestring_test.go similarity index 67% rename from go/types/types_test.go rename to go/types/typestring_test.go index 31d1a744..b003cbb0 100644 --- a/go/types/types_test.go +++ b/go/types/typestring_test.go @@ -2,14 +2,12 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// This file contains tests verifying the types associated with an AST after -// type checking. - package types_test import ( "go/ast" "go/parser" + "go/token" "testing" _ "code.google.com/p/go.tools/go/gcimporter" @@ -19,6 +17,7 @@ import ( const filename = "" func makePkg(t *testing.T, src string) (*Package, error) { + fset := token.NewFileSet() file, err := parser.ParseFile(fset, filename, src, parser.DeclarationErrors) if err != nil { return nil, err @@ -100,7 +99,10 @@ var independentTestTypes = []testEntry{ {"map[struct{x, y int}][]byte", "map[struct{x int; y int}][]byte"}, // channels - dup("chan int"), + dup("chan<- chan int"), + dup("chan<- <-chan int"), + dup("<-chan <-chan int"), + dup("chan (<-chan int)"), dup("chan<- func()"), dup("<-chan []func() int"), } @@ -113,7 +115,7 @@ var dependentTestTypes = []testEntry{ {`interface{m() interface{T}}`, `interface{m() interface{p.T}}`}, } -func TestTypes(t *testing.T) { +func TestTypeString(t *testing.T) { var tests []testEntry tests = append(tests, independentTestTypes...) tests = append(tests, dependentTestTypes...) @@ -126,57 +128,31 @@ func TestTypes(t *testing.T) { continue } typ := pkg.Scope().Lookup("T").Type().Underlying() - str := typ.String() - if str != test.str { - t.Errorf("%s: got %s, want %s", test.src, str, test.str) + if got := typ.String(); got != test.str { + t.Errorf("%s: got %s, want %s", test.src, got, test.str) } } } -var testExprs = []testEntry{ - // basic type literals - dup("x"), - dup("true"), - dup("42"), - dup("3.1415"), - dup("2.71828i"), - dup(`'a'`), - dup(`"foo"`), - dup("`bar`"), +func TestQualifiedTypeString(t *testing.T) { + p, _ := pkgFor("p.go", "package p; type T int", nil) + q, _ := pkgFor("q.go", "package q", nil) - // arbitrary expressions - dup("&x"), - dup("*&x"), - dup("(x)"), - dup("x + y"), - dup("x + y * 10"), - dup("t.foo"), - dup("s[0]"), - dup("s[x:y]"), - dup("s[:y]"), - dup("s[x:]"), - dup("s[:]"), - dup("f(1, 2.3)"), - dup("-f(10, 20)"), - dup("f(x + y, +3.1415)"), - {"func(a, b int) {}", "(func literal)"}, - {"func(a, b int) []int {}(1, 2)[x]", "(func literal)(1, 2)[x]"}, - {"[]int{1, 2, 3}", "(composite literal)"}, - {"[]int{1, 2, 3}[x:]", "(composite literal)[x:]"}, - {"i.([]string)", "i.()"}, -} - -func TestExprs(t *testing.T) { - for _, test := range testExprs { - src := "package p; var _ = " + test.src + "; var (x, y int; s []string; f func(int, float32) int; i interface{}; t interface { foo() })" - file, err := parser.ParseFile(fset, filename, src, parser.DeclarationErrors) - if err != nil { - t.Errorf("%s: %s", src, err) - continue - } - str := ExprString(file.Decls[0].(*ast.GenDecl).Specs[0].(*ast.ValueSpec).Values[0]) - if str != test.str { - t.Errorf("%s: got %s, want %s", test.src, str, test.str) + pT := p.Scope().Lookup("T").Type() + for _, test := range []struct { + typ Type + this *Package + want string + }{ + {pT, nil, "p.T"}, + {pT, p, "T"}, + {pT, q, "p.T"}, + {NewPointer(pT), p, "*T"}, + {NewPointer(pT), q, "*p.T"}, + } { + if got := TypeString(test.this, test.typ); got != test.want { + t.Errorf("TypeString(%s, %s) = %s, want %s", + test.this, test.typ, got, test.want) } } }