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
This commit is contained in:
Robert Griesemer 2013-11-18 22:38:48 -08:00
parent 65aa1a4fbe
commit df6f0829a2
10 changed files with 547 additions and 352 deletions

View File

@ -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 {

View File

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

View File

@ -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, "<expr %T>", 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("<nil>")
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 := "<Named w/o object>"
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())
}
}

220
go/types/exprstring.go Normal file
View File

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

100
go/types/exprstring_test.go Normal file
View File

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

View File

@ -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('.')

View File

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

View File

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

193
go/types/typestring.go Normal file
View File

@ -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("<nil>")
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 := "<Named w/o object>"
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)
}

View File

@ -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 = "<src>"
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.(<expr *ast.ArrayType>)"},
}
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)
}
}
}