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:
parent
65aa1a4fbe
commit
df6f0829a2
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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('.')
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue