1
0
mirror of https://github.com/golang/go synced 2024-11-17 21:34:49 -07:00

go/types: use a typeWriter to write types (cleanup)

This is a port of CL 345890 to go/types.

Change-Id: I98162deaf044b2194b05dc51e6948e227216fc4d
Reviewed-on: https://go-review.googlesource.com/c/go/+/346554
Trust: Robert Findley <rfindley@google.com>
Run-TryBot: Robert Findley <rfindley@google.com>
Reviewed-by: Robert Griesemer <gri@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
This commit is contained in:
Robert Findley 2021-08-31 17:49:53 -04:00
parent b2f09cd717
commit 2d98a4b4bc
5 changed files with 121 additions and 116 deletions

View File

@ -430,7 +430,7 @@ func writeObject(buf *bytes.Buffer, obj Object, qf Qualifier) {
return return
} }
if named, _ := typ.(*Named); named != nil && named.TParams().Len() > 0 { if named, _ := typ.(*Named); named != nil && named.TParams().Len() > 0 {
writeTParamList(buf, named.TParams().list(), qf, nil) newTypeWriter(buf, qf).tParamList(named.TParams().list())
} }
if tname.IsAlias() { if tname.IsAlias() {
buf.WriteString(" =") buf.WriteString(" =")

View File

@ -262,10 +262,9 @@ func instantiatedHash(typ *Named, targs []Type) string {
assert(instanceHashing == 0) assert(instanceHashing == 0)
instanceHashing++ instanceHashing++
var buf bytes.Buffer var buf bytes.Buffer
writeTypeName(&buf, typ.obj, nil) w := newTypeWriter(&buf, nil)
buf.WriteByte('[') w.typeName(typ.obj)
writeTypeList(&buf, targs, nil, nil) w.typeList(targs)
buf.WriteByte(']')
instanceHashing-- instanceHashing--
// With respect to the represented type, whether a // With respect to the represented type, whether a

View File

@ -59,9 +59,7 @@ func (l *TypeList) String() string {
return "[]" return "[]"
} }
var buf bytes.Buffer var buf bytes.Buffer
buf.WriteByte('[') newTypeWriter(&buf, nil).typeList(l.types)
writeTypeList(&buf, l.types, nil, nil)
buf.WriteByte(']')
return buf.String() return buf.String()
} }

View File

@ -53,79 +53,87 @@ func TypeString(typ Type, qf Qualifier) string {
// The Qualifier controls the printing of // The Qualifier controls the printing of
// package-level objects, and may be nil. // package-level objects, and may be nil.
func WriteType(buf *bytes.Buffer, typ Type, qf Qualifier) { func WriteType(buf *bytes.Buffer, typ Type, qf Qualifier) {
writeType(buf, typ, qf, make([]Type, 0, 8)) newTypeWriter(buf, qf).typ(typ)
} }
// instanceMarker is the prefix for an instantiated type // instanceMarker is the prefix for an instantiated type in unexpanded form.
// in "non-evaluated" instance form.
const instanceMarker = '#' const instanceMarker = '#'
func writeType(buf *bytes.Buffer, typ Type, qf Qualifier, visited []Type) { type typeWriter struct {
// Theoretically, this is a quadratic lookup algorithm, but in buf *bytes.Buffer
// practice deeply nested composite types with unnamed component seen map[Type]bool
// types are uncommon. This code is likely more efficient than qf Qualifier
// using a map. }
for _, t := range visited {
if t == typ { func newTypeWriter(buf *bytes.Buffer, qf Qualifier) *typeWriter {
fmt.Fprintf(buf, "○%T", goTypeName(typ)) // cycle to typ return &typeWriter{buf, make(map[Type]bool), qf}
return }
}
func (w *typeWriter) byte(b byte) { w.buf.WriteByte(b) }
func (w *typeWriter) string(s string) { w.buf.WriteString(s) }
func (w *typeWriter) writef(format string, args ...interface{}) { fmt.Fprintf(w.buf, format, args...) }
func (w *typeWriter) typ(typ Type) {
if w.seen[typ] {
w.writef("○%T", goTypeName(typ)) // cycle to typ
return
} }
visited = append(visited, typ) w.seen[typ] = true
defer delete(w.seen, typ)
switch t := typ.(type) { switch t := typ.(type) {
case nil: case nil:
buf.WriteString("<nil>") w.string("<nil>")
case *Basic: case *Basic:
// exported basic types go into package unsafe // exported basic types go into package unsafe
// (currently this is just unsafe.Pointer) // (currently this is just unsafe.Pointer)
if token.IsExported(t.name) { if token.IsExported(t.name) {
if obj, _ := Unsafe.scope.Lookup(t.name).(*TypeName); obj != nil { if obj, _ := Unsafe.scope.Lookup(t.name).(*TypeName); obj != nil {
writeTypeName(buf, obj, qf) w.typeName(obj)
break break
} }
} }
buf.WriteString(t.name) w.string(t.name)
case *Array: case *Array:
fmt.Fprintf(buf, "[%d]", t.len) w.writef("[%d]", t.len)
writeType(buf, t.elem, qf, visited) w.typ(t.elem)
case *Slice: case *Slice:
buf.WriteString("[]") w.string("[]")
writeType(buf, t.elem, qf, visited) w.typ(t.elem)
case *Struct: case *Struct:
buf.WriteString("struct{") w.string("struct{")
for i, f := range t.fields { for i, f := range t.fields {
if i > 0 { if i > 0 {
buf.WriteString("; ") w.string("; ")
} }
// This doesn't do the right thing for embedded type // This doesn't do the right thing for embedded type
// aliases where we should print the alias name, not // aliases where we should print the alias name, not
// the aliased type (see issue #44410). // the aliased type (see issue #44410).
if !f.embedded { if !f.embedded {
buf.WriteString(f.name) w.string(f.name)
buf.WriteByte(' ') w.byte(' ')
} }
writeType(buf, f.typ, qf, visited) w.typ(f.typ)
if tag := t.Tag(i); tag != "" { if tag := t.Tag(i); tag != "" {
fmt.Fprintf(buf, " %q", tag) w.writef(" %q", tag)
} }
} }
buf.WriteByte('}') w.byte('}')
case *Pointer: case *Pointer:
buf.WriteByte('*') w.byte('*')
writeType(buf, t.base, qf, visited) w.typ(t.base)
case *Tuple: case *Tuple:
writeTuple(buf, t, false, qf, visited) w.tuple(t, false)
case *Signature: case *Signature:
buf.WriteString("func") w.string("func")
writeSignature(buf, t, qf, visited) w.signature(t)
case *Union: case *Union:
// Unions only appear as (syntactic) embedded elements // Unions only appear as (syntactic) embedded elements
@ -135,40 +143,39 @@ func writeType(buf *bytes.Buffer, typ Type, qf Qualifier, visited []Type) {
} }
for i, t := range t.terms { for i, t := range t.terms {
if i > 0 { if i > 0 {
buf.WriteByte('|') w.byte('|')
} }
if t.tilde { if t.tilde {
buf.WriteByte('~') w.byte('~')
} }
writeType(buf, t.typ, qf, visited) w.typ(t.typ)
} }
case *Interface: case *Interface:
buf.WriteString("interface{") w.string("interface{")
first := true first := true
// print explicit interface methods and embedded types
for _, m := range t.methods { for _, m := range t.methods {
if !first { if !first {
buf.WriteString("; ") w.string("; ")
} }
first = false first = false
buf.WriteString(m.name) w.string(m.name)
writeSignature(buf, m.typ.(*Signature), qf, visited) w.signature(m.typ.(*Signature))
} }
for _, typ := range t.embeddeds { for _, typ := range t.embeddeds {
if !first { if !first {
buf.WriteString("; ") w.string("; ")
} }
first = false first = false
writeType(buf, typ, qf, visited) w.typ(typ)
} }
buf.WriteByte('}') w.byte('}')
case *Map: case *Map:
buf.WriteString("map[") w.string("map[")
writeType(buf, t.key, qf, visited) w.typ(t.key)
buf.WriteByte(']') w.byte(']')
writeType(buf, t.elem, qf, visited) w.typ(t.elem)
case *Chan: case *Chan:
var s string var s string
@ -187,66 +194,65 @@ func writeType(buf *bytes.Buffer, typ Type, qf Qualifier, visited []Type) {
default: default:
unreachable() unreachable()
} }
buf.WriteString(s) w.string(s)
if parens { if parens {
buf.WriteByte('(') w.byte('(')
} }
writeType(buf, t.elem, qf, visited) w.typ(t.elem)
if parens { if parens {
buf.WriteByte(')') w.byte(')')
} }
case *Named: case *Named:
if t.instPos != nil { if t.instPos != nil {
buf.WriteByte(instanceMarker) w.byte(instanceMarker)
} }
writeTypeName(buf, t.obj, qf) w.typeName(t.obj)
if t.targs != nil { if t.targs != nil {
// instantiated type // instantiated type
buf.WriteByte('[') w.typeList(t.targs.list())
writeTypeList(buf, t.targs.list(), qf, visited)
buf.WriteByte(']')
} else if t.TParams().Len() != 0 { } else if t.TParams().Len() != 0 {
// parameterized type // parameterized type
writeTParamList(buf, t.TParams().list(), qf, visited) w.tParamList(t.TParams().list())
} }
case *TypeParam: case *TypeParam:
s := "?" s := "?"
if t.obj != nil { if t.obj != nil {
// Optionally write out package for typeparams (like Named). // Optionally write out package for typeparams (like Named).
// TODO(rfindley): this is required for import/export, so // TODO(danscales): this is required for import/export, so
// we maybe need a separate function that won't be changed // we maybe need a separate function that won't be changed
// for debugging purposes. // for debugging purposes.
if t.obj.pkg != nil { if t.obj.pkg != nil {
writePackage(buf, t.obj.pkg, qf) writePackage(w.buf, t.obj.pkg, w.qf)
} }
s = t.obj.name s = t.obj.name
} }
buf.WriteString(s + subscript(t.id)) w.string(s + subscript(t.id))
case *top: case *top:
buf.WriteString("") w.string("")
default: default:
// For externally defined implementations of Type. // For externally defined implementations of Type.
// Note: In this case cycles won't be caught. // Note: In this case cycles won't be caught.
buf.WriteString(t.String()) w.string(t.String())
} }
} }
func writeTypeList(buf *bytes.Buffer, list []Type, qf Qualifier, visited []Type) { func (w *typeWriter) typeList(list []Type) {
w.byte('[')
for i, typ := range list { for i, typ := range list {
if i > 0 { if i > 0 {
buf.WriteString(", ") w.string(", ")
} }
writeType(buf, typ, qf, visited) w.typ(typ)
} }
w.byte(']')
} }
func writeTParamList(buf *bytes.Buffer, list []*TypeParam, qf Qualifier, visited []Type) { func (w *typeWriter) tParamList(list []*TypeParam) {
// TODO(rFindley) compare this with the corresponding implementation in types2 w.byte('[')
buf.WriteString("[")
var prev Type var prev Type
for i, tpar := range list { for i, tpar := range list {
// Determine the type parameter and its constraint. // Determine the type parameter and its constraint.
@ -260,35 +266,36 @@ func writeTParamList(buf *bytes.Buffer, list []*TypeParam, qf Qualifier, visited
if i > 0 { if i > 0 {
if bound != prev { if bound != prev {
// bound changed - write previous one before advancing // bound changed - write previous one before advancing
buf.WriteByte(' ') w.byte(' ')
writeType(buf, prev, qf, visited) w.typ(prev)
} }
buf.WriteString(", ") w.string(", ")
} }
prev = bound prev = bound
if tpar != nil { if tpar != nil {
writeType(buf, tpar, qf, visited) w.typ(tpar)
} else { } else {
buf.WriteString(tpar.obj.name) w.string(tpar.obj.name)
} }
} }
if prev != nil { if prev != nil {
buf.WriteByte(' ') w.byte(' ')
writeType(buf, prev, qf, visited) w.typ(prev)
} }
buf.WriteByte(']') w.byte(']')
} }
func writeTypeName(buf *bytes.Buffer, obj *TypeName, qf Qualifier) { func (w *typeWriter) typeName(obj *TypeName) {
if obj == nil { if obj == nil {
buf.WriteString("<Named w/o object>") assert(instanceHashing == 0) // we need an object for instance hashing
w.string("<Named w/o object>")
return return
} }
if obj.pkg != nil { if obj.pkg != nil {
writePackage(buf, obj.pkg, qf) writePackage(w.buf, obj.pkg, w.qf)
} }
buf.WriteString(obj.name) w.string(obj.name)
if instanceHashing != 0 { if instanceHashing != 0 {
// For local defined types, use the (original!) TypeName's scope // For local defined types, use the (original!) TypeName's scope
@ -300,7 +307,7 @@ func writeTypeName(buf *bytes.Buffer, obj *TypeName, qf Qualifier) {
for typ.orig != typ { for typ.orig != typ {
typ = typ.orig typ = typ.orig
} }
writeScopeNumbers(buf, typ.obj.parent) w.writeScopeNumbers(typ.obj.parent)
} }
} }
@ -308,28 +315,28 @@ func writeTypeName(buf *bytes.Buffer, obj *TypeName, qf Qualifier) {
// in the form ".i.j.k" where i, j, k, etc. stand for scope numbers. // in the form ".i.j.k" where i, j, k, etc. stand for scope numbers.
// If a scope is nil or has no parent (such as a package scope), nothing // If a scope is nil or has no parent (such as a package scope), nothing
// is written. // is written.
func writeScopeNumbers(buf *bytes.Buffer, s *Scope) { func (w *typeWriter) writeScopeNumbers(s *Scope) {
if s != nil && s.number > 0 { if s != nil && s.number > 0 {
writeScopeNumbers(buf, s.parent) w.writeScopeNumbers(s.parent)
fmt.Fprintf(buf, ".%d", s.number) w.writef(".%d", s.number)
} }
} }
func writeTuple(buf *bytes.Buffer, tup *Tuple, variadic bool, qf Qualifier, visited []Type) { func (w *typeWriter) tuple(tup *Tuple, variadic bool) {
buf.WriteByte('(') w.byte('(')
if tup != nil { if tup != nil {
for i, v := range tup.vars { for i, v := range tup.vars {
if i > 0 { if i > 0 {
buf.WriteString(", ") w.string(", ")
} }
if v.name != "" { if v.name != "" {
buf.WriteString(v.name) w.string(v.name)
buf.WriteByte(' ') w.byte(' ')
} }
typ := v.typ typ := v.typ
if variadic && i == len(tup.vars)-1 { if variadic && i == len(tup.vars)-1 {
if s, ok := typ.(*Slice); ok { if s, ok := typ.(*Slice); ok {
buf.WriteString("...") w.string("...")
typ = s.elem typ = s.elem
} else { } else {
// special case: // special case:
@ -337,15 +344,15 @@ func writeTuple(buf *bytes.Buffer, tup *Tuple, variadic bool, qf Qualifier, visi
if t := asBasic(typ); t == nil || t.kind != String { if t := asBasic(typ); t == nil || t.kind != String {
panic("expected string type") panic("expected string type")
} }
writeType(buf, typ, qf, visited) w.typ(typ)
buf.WriteString("...") w.string("...")
continue continue
} }
} }
writeType(buf, typ, qf, visited) w.typ(typ)
} }
} }
buf.WriteByte(')') w.byte(')')
} }
// WriteSignature writes the representation of the signature sig to buf, // WriteSignature writes the representation of the signature sig to buf,
@ -353,15 +360,15 @@ func writeTuple(buf *bytes.Buffer, tup *Tuple, variadic bool, qf Qualifier, visi
// The Qualifier controls the printing of // The Qualifier controls the printing of
// package-level objects, and may be nil. // package-level objects, and may be nil.
func WriteSignature(buf *bytes.Buffer, sig *Signature, qf Qualifier) { func WriteSignature(buf *bytes.Buffer, sig *Signature, qf Qualifier) {
writeSignature(buf, sig, qf, make([]Type, 0, 8)) newTypeWriter(buf, qf).signature(sig)
} }
func writeSignature(buf *bytes.Buffer, sig *Signature, qf Qualifier, visited []Type) { func (w *typeWriter) signature(sig *Signature) {
if sig.TParams().Len() != 0 { if sig.TParams().Len() != 0 {
writeTParamList(buf, sig.TParams().list(), qf, visited) w.tParamList(sig.TParams().list())
} }
writeTuple(buf, sig.params, sig.variadic, qf, visited) w.tuple(sig.params, sig.variadic)
n := sig.results.Len() n := sig.results.Len()
if n == 0 { if n == 0 {
@ -369,15 +376,15 @@ func writeSignature(buf *bytes.Buffer, sig *Signature, qf Qualifier, visited []T
return return
} }
buf.WriteByte(' ') w.byte(' ')
if n == 1 && sig.results.vars[0].name == "" { if n == 1 && sig.results.vars[0].name == "" {
// single unnamed result // single unnamed result
writeType(buf, sig.results.vars[0].typ, qf, visited) w.typ(sig.results.vars[0].typ)
return return
} }
// multiple or named result(s) // multiple or named result(s)
writeTuple(buf, sig.results, false, qf, visited) w.tuple(sig.results, false)
} }
// subscript returns the decimal (utf8) representation of x using subscript digits. // subscript returns the decimal (utf8) representation of x using subscript digits.

View File

@ -77,16 +77,17 @@ type tparamsList struct {
// String returns a string representation for a tparamsList. For debugging. // String returns a string representation for a tparamsList. For debugging.
func (d *tparamsList) String() string { func (d *tparamsList) String() string {
var buf bytes.Buffer var buf bytes.Buffer
buf.WriteByte('[') w := newTypeWriter(&buf, nil)
w.byte('[')
for i, tpar := range d.tparams { for i, tpar := range d.tparams {
if i > 0 { if i > 0 {
buf.WriteString(", ") w.string(", ")
} }
writeType(&buf, tpar, nil, nil) w.typ(tpar)
buf.WriteString(": ") w.string(": ")
writeType(&buf, d.at(i), nil, nil) w.typ(d.at(i))
} }
buf.WriteByte(']') w.byte(']')
return buf.String() return buf.String()
} }