go.tools/go/types: make WriteType cycle-resistent

Also:
- expose WriteSignature
- s/identicalInternal/identical/g

LGTM=adonovan
R=adonovan
CC=golang-codereviews
https://golang.org/cl/57860043
This commit is contained in:
Robert Griesemer 2014-01-28 13:42:46 -08:00
parent 7dbf7c000d
commit e5e20e3af5
4 changed files with 56 additions and 32 deletions

View File

@ -247,7 +247,7 @@ func writeObject(buf *bytes.Buffer, this *Package, obj Object) {
buf.WriteString("func ") buf.WriteString("func ")
writeFuncName(buf, this, obj) writeFuncName(buf, this, obj)
if typ != nil { if typ != nil {
writeSignature(buf, this, typ.(*Signature)) WriteSignature(buf, this, typ.(*Signature))
} }
return return

View File

@ -110,7 +110,7 @@ func hasNil(typ Type) bool {
// Identical reports whether x and y are identical. // Identical reports whether x and y are identical.
func Identical(x, y Type) bool { func Identical(x, y Type) bool {
return identicalInternal(x, y, nil) return identical(x, y, nil)
} }
// An ifacePair is a node in a stack of interface type pairs compared for identity. // An ifacePair is a node in a stack of interface type pairs compared for identity.
@ -123,7 +123,7 @@ func (p *ifacePair) identical(q *ifacePair) bool {
return p.x == q.x && p.y == q.y || p.x == q.y && p.y == q.x return p.x == q.x && p.y == q.y || p.x == q.y && p.y == q.x
} }
func identicalInternal(x, y Type, p *ifacePair) bool { func identical(x, y Type, p *ifacePair) bool {
if x == y { if x == y {
return true return true
} }
@ -141,13 +141,13 @@ func identicalInternal(x, y Type, p *ifacePair) bool {
// Two array types are identical if they have identical element types // Two array types are identical if they have identical element types
// and the same array length. // and the same array length.
if y, ok := y.(*Array); ok { if y, ok := y.(*Array); ok {
return x.len == y.len && identicalInternal(x.elem, y.elem, p) return x.len == y.len && identical(x.elem, y.elem, p)
} }
case *Slice: case *Slice:
// Two slice types are identical if they have identical element types. // Two slice types are identical if they have identical element types.
if y, ok := y.(*Slice); ok { if y, ok := y.(*Slice); ok {
return identicalInternal(x.elem, y.elem, p) return identical(x.elem, y.elem, p)
} }
case *Struct: case *Struct:
@ -162,7 +162,7 @@ func identicalInternal(x, y Type, p *ifacePair) bool {
if f.anonymous != g.anonymous || if f.anonymous != g.anonymous ||
x.Tag(i) != y.Tag(i) || x.Tag(i) != y.Tag(i) ||
!f.sameId(g.pkg, g.name) || !f.sameId(g.pkg, g.name) ||
!identicalInternal(f.typ, g.typ, p) { !identical(f.typ, g.typ, p) {
return false return false
} }
} }
@ -173,7 +173,7 @@ func identicalInternal(x, y Type, p *ifacePair) bool {
case *Pointer: case *Pointer:
// Two pointer types are identical if they have identical base types. // Two pointer types are identical if they have identical base types.
if y, ok := y.(*Pointer); ok { if y, ok := y.(*Pointer); ok {
return identicalInternal(x.base, y.base, p) return identical(x.base, y.base, p)
} }
case *Tuple: case *Tuple:
@ -184,7 +184,7 @@ func identicalInternal(x, y Type, p *ifacePair) bool {
if x != nil { if x != nil {
for i, v := range x.vars { for i, v := range x.vars {
w := y.vars[i] w := y.vars[i]
if !identicalInternal(v.typ, w.typ, p) { if !identical(v.typ, w.typ, p) {
return false return false
} }
} }
@ -200,8 +200,8 @@ func identicalInternal(x, y Type, p *ifacePair) bool {
// names are not required to match. // names are not required to match.
if y, ok := y.(*Signature); ok { if y, ok := y.(*Signature); ok {
return x.variadic == y.variadic && return x.variadic == y.variadic &&
identicalInternal(x.params, y.params, p) && identical(x.params, y.params, p) &&
identicalInternal(x.results, y.results, p) identical(x.results, y.results, p)
} }
case *Interface: case *Interface:
@ -247,7 +247,7 @@ func identicalInternal(x, y Type, p *ifacePair) bool {
} }
for i, f := range a { for i, f := range a {
g := b[i] g := b[i]
if f.Id() != g.Id() || !identicalInternal(f.typ, g.typ, q) { if f.Id() != g.Id() || !identical(f.typ, g.typ, q) {
return false return false
} }
} }
@ -258,14 +258,14 @@ func identicalInternal(x, y Type, p *ifacePair) bool {
case *Map: case *Map:
// Two map types are identical if they have identical key and value types. // Two map types are identical if they have identical key and value types.
if y, ok := y.(*Map); ok { if y, ok := y.(*Map); ok {
return identicalInternal(x.key, y.key, p) && identicalInternal(x.elem, y.elem, p) return identical(x.key, y.key, p) && identical(x.elem, y.elem, p)
} }
case *Chan: case *Chan:
// Two channel types are identical if they have identical value types // Two channel types are identical if they have identical value types
// and the same direction. // and the same direction.
if y, ok := y.(*Chan); ok { if y, ok := y.(*Chan); ok {
return x.dir == y.dir && identicalInternal(x.elem, y.elem, p) return x.dir == y.dir && identical(x.elem, y.elem, p)
} }
case *Named: case *Named:

View File

@ -141,6 +141,6 @@ func SelectionString(this *Package, s *Selection) string {
buf.WriteByte('(') buf.WriteByte('(')
WriteType(&buf, this, s.Recv()) WriteType(&buf, this, s.Recv())
fmt.Fprintf(&buf, ") %s", s.obj.Name()) fmt.Fprintf(&buf, ") %s", s.obj.Name())
writeSignature(&buf, this, s.Type().(*Signature)) WriteSignature(&buf, this, s.Type().(*Signature))
return buf.String() return buf.String()
} }

View File

@ -40,6 +40,22 @@ func TypeString(this *Package, typ Type) string {
// Named types are printed package-qualified if they // Named types are printed package-qualified if they
// do not belong to this package. // do not belong to this package.
func WriteType(buf *bytes.Buffer, this *Package, typ Type) { func WriteType(buf *bytes.Buffer, this *Package, typ Type) {
writeType(buf, this, typ, make([]Type, 8))
}
func writeType(buf *bytes.Buffer, this *Package, typ Type, visited []Type) {
// Theoretically, this is a quadratic lookup algorithm, but in
// practice deeply nested composite types with unnamed component
// types are uncommon. This code is likely more efficient than
// using a map.
for _, t := range visited {
if t == typ {
fmt.Fprintf(buf, "○%T", typ) // cycle to typ
return
}
}
visited = append(visited, typ)
switch t := typ.(type) { switch t := typ.(type) {
case nil: case nil:
buf.WriteString("<nil>") buf.WriteString("<nil>")
@ -61,11 +77,11 @@ func WriteType(buf *bytes.Buffer, this *Package, typ Type) {
case *Array: case *Array:
fmt.Fprintf(buf, "[%d]", t.len) fmt.Fprintf(buf, "[%d]", t.len)
WriteType(buf, this, t.elem) writeType(buf, this, t.elem, visited)
case *Slice: case *Slice:
buf.WriteString("[]") buf.WriteString("[]")
WriteType(buf, this, t.elem) writeType(buf, this, t.elem, visited)
case *Struct: case *Struct:
buf.WriteString("struct{") buf.WriteString("struct{")
@ -77,7 +93,7 @@ func WriteType(buf *bytes.Buffer, this *Package, typ Type) {
buf.WriteString(f.name) buf.WriteString(f.name)
buf.WriteByte(' ') buf.WriteByte(' ')
} }
WriteType(buf, this, f.typ) writeType(buf, this, f.typ, visited)
if tag := t.Tag(i); tag != "" { if tag := t.Tag(i); tag != "" {
fmt.Fprintf(buf, " %q", tag) fmt.Fprintf(buf, " %q", tag)
} }
@ -86,14 +102,14 @@ func WriteType(buf *bytes.Buffer, this *Package, typ Type) {
case *Pointer: case *Pointer:
buf.WriteByte('*') buf.WriteByte('*')
WriteType(buf, this, t.base) writeType(buf, this, t.base, visited)
case *Tuple: case *Tuple:
writeTuple(buf, this, t, false) writeTuple(buf, this, t, false, visited)
case *Signature: case *Signature:
buf.WriteString("func") buf.WriteString("func")
writeSignature(buf, this, t) writeSignature(buf, this, t, visited)
case *Interface: case *Interface:
// We write the source-level methods and embedded types rather // We write the source-level methods and embedded types rather
@ -116,7 +132,7 @@ func WriteType(buf *bytes.Buffer, this *Package, typ Type) {
buf.WriteString("; ") buf.WriteString("; ")
} }
buf.WriteString(m.name) buf.WriteString(m.name)
writeSignature(buf, this, m.typ.(*Signature)) writeSignature(buf, this, m.typ.(*Signature), visited)
} }
} else { } else {
// print explicit interface methods and embedded types // print explicit interface methods and embedded types
@ -125,22 +141,22 @@ func WriteType(buf *bytes.Buffer, this *Package, typ Type) {
buf.WriteString("; ") buf.WriteString("; ")
} }
buf.WriteString(m.name) buf.WriteString(m.name)
writeSignature(buf, this, m.typ.(*Signature)) writeSignature(buf, this, m.typ.(*Signature), visited)
} }
for i, typ := range t.embeddeds { for i, typ := range t.embeddeds {
if i > 0 || len(t.methods) > 0 { if i > 0 || len(t.methods) > 0 {
buf.WriteString("; ") buf.WriteString("; ")
} }
WriteType(buf, this, typ) writeType(buf, this, typ, visited)
} }
} }
buf.WriteByte('}') buf.WriteByte('}')
case *Map: case *Map:
buf.WriteString("map[") buf.WriteString("map[")
WriteType(buf, this, t.key) writeType(buf, this, t.key, visited)
buf.WriteByte(']') buf.WriteByte(']')
WriteType(buf, this, t.elem) writeType(buf, this, t.elem, visited)
case *Chan: case *Chan:
var s string var s string
@ -163,7 +179,7 @@ func WriteType(buf *bytes.Buffer, this *Package, typ Type) {
if parens { if parens {
buf.WriteByte('(') buf.WriteByte('(')
} }
WriteType(buf, this, t.elem) writeType(buf, this, t.elem, visited)
if parens { if parens {
buf.WriteByte(')') buf.WriteByte(')')
} }
@ -190,7 +206,7 @@ func WriteType(buf *bytes.Buffer, this *Package, typ Type) {
} }
} }
func writeTuple(buf *bytes.Buffer, this *Package, tup *Tuple, variadic bool) { func writeTuple(buf *bytes.Buffer, this *Package, tup *Tuple, variadic bool, visited []Type) {
buf.WriteByte('(') buf.WriteByte('(')
if tup != nil { if tup != nil {
for i, v := range tup.vars { for i, v := range tup.vars {
@ -206,14 +222,22 @@ func writeTuple(buf *bytes.Buffer, this *Package, tup *Tuple, variadic bool) {
buf.WriteString("...") buf.WriteString("...")
typ = typ.(*Slice).elem typ = typ.(*Slice).elem
} }
WriteType(buf, this, typ) writeType(buf, this, typ, visited)
} }
} }
buf.WriteByte(')') buf.WriteByte(')')
} }
func writeSignature(buf *bytes.Buffer, this *Package, sig *Signature) { // WriteSignature writes the representation of the signature sig to buf,
writeTuple(buf, this, sig.params, sig.variadic) // without a leading "func" keyword.
// Named types are printed package-qualified if they
// do not belong to this package.
func WriteSignature(buf *bytes.Buffer, this *Package, sig *Signature) {
writeSignature(buf, this, sig, make([]Type, 8))
}
func writeSignature(buf *bytes.Buffer, this *Package, sig *Signature, visited []Type) {
writeTuple(buf, this, sig.params, sig.variadic, visited)
n := sig.results.Len() n := sig.results.Len()
if n == 0 { if n == 0 {
@ -224,10 +248,10 @@ func writeSignature(buf *bytes.Buffer, this *Package, sig *Signature) {
buf.WriteByte(' ') buf.WriteByte(' ')
if n == 1 && sig.results.vars[0].name == "" { if n == 1 && sig.results.vars[0].name == "" {
// single unnamed result // single unnamed result
WriteType(buf, this, sig.results.vars[0].typ) writeType(buf, this, sig.results.vars[0].typ, visited)
return return
} }
// multiple or named result(s) // multiple or named result(s)
writeTuple(buf, this, sig.results, false) writeTuple(buf, this, sig.results, false, visited)
} }