diff --git a/go/types/objects.go b/go/types/objects.go index ba2ebe15..670b8d6b 100644 --- a/go/types/objects.go +++ b/go/types/objects.go @@ -247,7 +247,7 @@ func writeObject(buf *bytes.Buffer, this *Package, obj Object) { buf.WriteString("func ") writeFuncName(buf, this, obj) if typ != nil { - writeSignature(buf, this, typ.(*Signature)) + WriteSignature(buf, this, typ.(*Signature)) } return diff --git a/go/types/predicates.go b/go/types/predicates.go index 1e291048..352e2306 100644 --- a/go/types/predicates.go +++ b/go/types/predicates.go @@ -110,7 +110,7 @@ func hasNil(typ Type) bool { // Identical reports whether x and y are identical. 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. @@ -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 } -func identicalInternal(x, y Type, p *ifacePair) bool { +func identical(x, y Type, p *ifacePair) bool { if x == y { 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 // and the same array length. 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: // Two slice types are identical if they have identical element types. if y, ok := y.(*Slice); ok { - return identicalInternal(x.elem, y.elem, p) + return identical(x.elem, y.elem, p) } case *Struct: @@ -162,7 +162,7 @@ func identicalInternal(x, y Type, p *ifacePair) bool { if f.anonymous != g.anonymous || x.Tag(i) != y.Tag(i) || !f.sameId(g.pkg, g.name) || - !identicalInternal(f.typ, g.typ, p) { + !identical(f.typ, g.typ, p) { return false } } @@ -173,7 +173,7 @@ func identicalInternal(x, y Type, p *ifacePair) bool { case *Pointer: // Two pointer types are identical if they have identical base types. if y, ok := y.(*Pointer); ok { - return identicalInternal(x.base, y.base, p) + return identical(x.base, y.base, p) } case *Tuple: @@ -184,7 +184,7 @@ func identicalInternal(x, y Type, p *ifacePair) bool { if x != nil { for i, v := range x.vars { w := y.vars[i] - if !identicalInternal(v.typ, w.typ, p) { + if !identical(v.typ, w.typ, p) { return false } } @@ -200,8 +200,8 @@ func identicalInternal(x, y Type, p *ifacePair) bool { // names are not required to match. if y, ok := y.(*Signature); ok { return x.variadic == y.variadic && - identicalInternal(x.params, y.params, p) && - identicalInternal(x.results, y.results, p) + identical(x.params, y.params, p) && + identical(x.results, y.results, p) } case *Interface: @@ -247,7 +247,7 @@ func identicalInternal(x, y Type, p *ifacePair) bool { } for i, f := range a { 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 } } @@ -258,14 +258,14 @@ func identicalInternal(x, y Type, p *ifacePair) bool { case *Map: // Two map types are identical if they have identical key and value types. 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: // Two channel types are identical if they have identical value types // and the same direction. 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: diff --git a/go/types/selection.go b/go/types/selection.go index 19303df1..fe089331 100644 --- a/go/types/selection.go +++ b/go/types/selection.go @@ -141,6 +141,6 @@ func SelectionString(this *Package, s *Selection) string { buf.WriteByte('(') WriteType(&buf, this, s.Recv()) fmt.Fprintf(&buf, ") %s", s.obj.Name()) - writeSignature(&buf, this, s.Type().(*Signature)) + WriteSignature(&buf, this, s.Type().(*Signature)) return buf.String() } diff --git a/go/types/typestring.go b/go/types/typestring.go index 5d726dc5..7364a0fe 100644 --- a/go/types/typestring.go +++ b/go/types/typestring.go @@ -40,6 +40,22 @@ func TypeString(this *Package, typ Type) string { // Named types are printed package-qualified if they // do not belong to this package. 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) { case nil: buf.WriteString("") @@ -61,11 +77,11 @@ func WriteType(buf *bytes.Buffer, this *Package, typ Type) { case *Array: fmt.Fprintf(buf, "[%d]", t.len) - WriteType(buf, this, t.elem) + writeType(buf, this, t.elem, visited) case *Slice: buf.WriteString("[]") - WriteType(buf, this, t.elem) + writeType(buf, this, t.elem, visited) case *Struct: buf.WriteString("struct{") @@ -77,7 +93,7 @@ func WriteType(buf *bytes.Buffer, this *Package, typ Type) { buf.WriteString(f.name) buf.WriteByte(' ') } - WriteType(buf, this, f.typ) + writeType(buf, this, f.typ, visited) if tag := t.Tag(i); tag != "" { fmt.Fprintf(buf, " %q", tag) } @@ -86,14 +102,14 @@ func WriteType(buf *bytes.Buffer, this *Package, typ Type) { case *Pointer: buf.WriteByte('*') - WriteType(buf, this, t.base) + writeType(buf, this, t.base, visited) case *Tuple: - writeTuple(buf, this, t, false) + writeTuple(buf, this, t, false, visited) case *Signature: buf.WriteString("func") - writeSignature(buf, this, t) + writeSignature(buf, this, t, visited) case *Interface: // 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(m.name) - writeSignature(buf, this, m.typ.(*Signature)) + writeSignature(buf, this, m.typ.(*Signature), visited) } } else { // print explicit interface methods and embedded types @@ -125,22 +141,22 @@ func WriteType(buf *bytes.Buffer, this *Package, typ Type) { buf.WriteString("; ") } buf.WriteString(m.name) - writeSignature(buf, this, m.typ.(*Signature)) + writeSignature(buf, this, m.typ.(*Signature), visited) } for i, typ := range t.embeddeds { if i > 0 || len(t.methods) > 0 { buf.WriteString("; ") } - WriteType(buf, this, typ) + writeType(buf, this, typ, visited) } } buf.WriteByte('}') case *Map: buf.WriteString("map[") - WriteType(buf, this, t.key) + writeType(buf, this, t.key, visited) buf.WriteByte(']') - WriteType(buf, this, t.elem) + writeType(buf, this, t.elem, visited) case *Chan: var s string @@ -163,7 +179,7 @@ func WriteType(buf *bytes.Buffer, this *Package, typ Type) { if parens { buf.WriteByte('(') } - WriteType(buf, this, t.elem) + writeType(buf, this, t.elem, visited) if parens { 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('(') if tup != nil { for i, v := range tup.vars { @@ -206,14 +222,22 @@ func writeTuple(buf *bytes.Buffer, this *Package, tup *Tuple, variadic bool) { buf.WriteString("...") typ = typ.(*Slice).elem } - WriteType(buf, this, typ) + writeType(buf, this, typ, visited) } } buf.WriteByte(')') } -func writeSignature(buf *bytes.Buffer, this *Package, sig *Signature) { - writeTuple(buf, this, sig.params, sig.variadic) +// WriteSignature writes the representation of the signature sig to buf, +// 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() if n == 0 { @@ -224,10 +248,10 @@ func writeSignature(buf *bytes.Buffer, this *Package, sig *Signature) { buf.WriteByte(' ') if n == 1 && sig.results.vars[0].name == "" { // single unnamed result - WriteType(buf, this, sig.results.vars[0].typ) + writeType(buf, this, sig.results.vars[0].typ, visited) return } // multiple or named result(s) - writeTuple(buf, this, sig.results, false) + writeTuple(buf, this, sig.results, false, visited) }