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:
parent
7dbf7c000d
commit
e5e20e3af5
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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("<nil>")
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue