158 lines
3.9 KiB
Go
158 lines
3.9 KiB
Go
|
// Copyright 2020, The Go Authors. All rights reserved.
|
||
|
// Use of this source code is governed by a BSD-style
|
||
|
// license that can be found in the LICENSE file.
|
||
|
|
||
|
package value
|
||
|
|
||
|
import (
|
||
|
"reflect"
|
||
|
"strconv"
|
||
|
)
|
||
|
|
||
|
// TypeString is nearly identical to reflect.Type.String,
|
||
|
// but has an additional option to specify that full type names be used.
|
||
|
func TypeString(t reflect.Type, qualified bool) string {
|
||
|
return string(appendTypeName(nil, t, qualified, false))
|
||
|
}
|
||
|
|
||
|
func appendTypeName(b []byte, t reflect.Type, qualified, elideFunc bool) []byte {
|
||
|
// BUG: Go reflection provides no way to disambiguate two named types
|
||
|
// of the same name and within the same package,
|
||
|
// but declared within the namespace of different functions.
|
||
|
|
||
|
// Named type.
|
||
|
if t.Name() != "" {
|
||
|
if qualified && t.PkgPath() != "" {
|
||
|
b = append(b, '"')
|
||
|
b = append(b, t.PkgPath()...)
|
||
|
b = append(b, '"')
|
||
|
b = append(b, '.')
|
||
|
b = append(b, t.Name()...)
|
||
|
} else {
|
||
|
b = append(b, t.String()...)
|
||
|
}
|
||
|
return b
|
||
|
}
|
||
|
|
||
|
// Unnamed type.
|
||
|
switch k := t.Kind(); k {
|
||
|
case reflect.Bool, reflect.String, reflect.UnsafePointer,
|
||
|
reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
|
||
|
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr,
|
||
|
reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128:
|
||
|
b = append(b, k.String()...)
|
||
|
case reflect.Chan:
|
||
|
if t.ChanDir() == reflect.RecvDir {
|
||
|
b = append(b, "<-"...)
|
||
|
}
|
||
|
b = append(b, "chan"...)
|
||
|
if t.ChanDir() == reflect.SendDir {
|
||
|
b = append(b, "<-"...)
|
||
|
}
|
||
|
b = append(b, ' ')
|
||
|
b = appendTypeName(b, t.Elem(), qualified, false)
|
||
|
case reflect.Func:
|
||
|
if !elideFunc {
|
||
|
b = append(b, "func"...)
|
||
|
}
|
||
|
b = append(b, '(')
|
||
|
for i := 0; i < t.NumIn(); i++ {
|
||
|
if i > 0 {
|
||
|
b = append(b, ", "...)
|
||
|
}
|
||
|
if i == t.NumIn()-1 && t.IsVariadic() {
|
||
|
b = append(b, "..."...)
|
||
|
b = appendTypeName(b, t.In(i).Elem(), qualified, false)
|
||
|
} else {
|
||
|
b = appendTypeName(b, t.In(i), qualified, false)
|
||
|
}
|
||
|
}
|
||
|
b = append(b, ')')
|
||
|
switch t.NumOut() {
|
||
|
case 0:
|
||
|
// Do nothing
|
||
|
case 1:
|
||
|
b = append(b, ' ')
|
||
|
b = appendTypeName(b, t.Out(0), qualified, false)
|
||
|
default:
|
||
|
b = append(b, " ("...)
|
||
|
for i := 0; i < t.NumOut(); i++ {
|
||
|
if i > 0 {
|
||
|
b = append(b, ", "...)
|
||
|
}
|
||
|
b = appendTypeName(b, t.Out(i), qualified, false)
|
||
|
}
|
||
|
b = append(b, ')')
|
||
|
}
|
||
|
case reflect.Struct:
|
||
|
b = append(b, "struct{ "...)
|
||
|
for i := 0; i < t.NumField(); i++ {
|
||
|
if i > 0 {
|
||
|
b = append(b, "; "...)
|
||
|
}
|
||
|
sf := t.Field(i)
|
||
|
if !sf.Anonymous {
|
||
|
if qualified && sf.PkgPath != "" {
|
||
|
b = append(b, '"')
|
||
|
b = append(b, sf.PkgPath...)
|
||
|
b = append(b, '"')
|
||
|
b = append(b, '.')
|
||
|
}
|
||
|
b = append(b, sf.Name...)
|
||
|
b = append(b, ' ')
|
||
|
}
|
||
|
b = appendTypeName(b, sf.Type, qualified, false)
|
||
|
if sf.Tag != "" {
|
||
|
b = append(b, ' ')
|
||
|
b = strconv.AppendQuote(b, string(sf.Tag))
|
||
|
}
|
||
|
}
|
||
|
if b[len(b)-1] == ' ' {
|
||
|
b = b[:len(b)-1]
|
||
|
} else {
|
||
|
b = append(b, ' ')
|
||
|
}
|
||
|
b = append(b, '}')
|
||
|
case reflect.Slice, reflect.Array:
|
||
|
b = append(b, '[')
|
||
|
if k == reflect.Array {
|
||
|
b = strconv.AppendUint(b, uint64(t.Len()), 10)
|
||
|
}
|
||
|
b = append(b, ']')
|
||
|
b = appendTypeName(b, t.Elem(), qualified, false)
|
||
|
case reflect.Map:
|
||
|
b = append(b, "map["...)
|
||
|
b = appendTypeName(b, t.Key(), qualified, false)
|
||
|
b = append(b, ']')
|
||
|
b = appendTypeName(b, t.Elem(), qualified, false)
|
||
|
case reflect.Ptr:
|
||
|
b = append(b, '*')
|
||
|
b = appendTypeName(b, t.Elem(), qualified, false)
|
||
|
case reflect.Interface:
|
||
|
b = append(b, "interface{ "...)
|
||
|
for i := 0; i < t.NumMethod(); i++ {
|
||
|
if i > 0 {
|
||
|
b = append(b, "; "...)
|
||
|
}
|
||
|
m := t.Method(i)
|
||
|
if qualified && m.PkgPath != "" {
|
||
|
b = append(b, '"')
|
||
|
b = append(b, m.PkgPath...)
|
||
|
b = append(b, '"')
|
||
|
b = append(b, '.')
|
||
|
}
|
||
|
b = append(b, m.Name...)
|
||
|
b = appendTypeName(b, m.Type, qualified, true)
|
||
|
}
|
||
|
if b[len(b)-1] == ' ' {
|
||
|
b = b[:len(b)-1]
|
||
|
} else {
|
||
|
b = append(b, ' ')
|
||
|
}
|
||
|
b = append(b, '}')
|
||
|
default:
|
||
|
panic("invalid kind: " + k.String())
|
||
|
}
|
||
|
return b
|
||
|
}
|