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