name.go

  1// Copyright 2020, The Go Authors. All rights reserved.
  2// Use of this source code is governed by a BSD-style
  3// license that can be found in the LICENSE file.
  4
  5package value
  6
  7import (
  8	"reflect"
  9	"strconv"
 10)
 11
 12var anyType = reflect.TypeOf((*interface{})(nil)).Elem()
 13
 14// TypeString is nearly identical to reflect.Type.String,
 15// but has an additional option to specify that full type names be used.
 16func TypeString(t reflect.Type, qualified bool) string {
 17	return string(appendTypeName(nil, t, qualified, false))
 18}
 19
 20func appendTypeName(b []byte, t reflect.Type, qualified, elideFunc bool) []byte {
 21	// BUG: Go reflection provides no way to disambiguate two named types
 22	// of the same name and within the same package,
 23	// but declared within the namespace of different functions.
 24
 25	// Use the "any" alias instead of "interface{}" for better readability.
 26	if t == anyType {
 27		return append(b, "any"...)
 28	}
 29
 30	// Named type.
 31	if t.Name() != "" {
 32		if qualified && t.PkgPath() != "" {
 33			b = append(b, '"')
 34			b = append(b, t.PkgPath()...)
 35			b = append(b, '"')
 36			b = append(b, '.')
 37			b = append(b, t.Name()...)
 38		} else {
 39			b = append(b, t.String()...)
 40		}
 41		return b
 42	}
 43
 44	// Unnamed type.
 45	switch k := t.Kind(); k {
 46	case reflect.Bool, reflect.String, reflect.UnsafePointer,
 47		reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
 48		reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr,
 49		reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128:
 50		b = append(b, k.String()...)
 51	case reflect.Chan:
 52		if t.ChanDir() == reflect.RecvDir {
 53			b = append(b, "<-"...)
 54		}
 55		b = append(b, "chan"...)
 56		if t.ChanDir() == reflect.SendDir {
 57			b = append(b, "<-"...)
 58		}
 59		b = append(b, ' ')
 60		b = appendTypeName(b, t.Elem(), qualified, false)
 61	case reflect.Func:
 62		if !elideFunc {
 63			b = append(b, "func"...)
 64		}
 65		b = append(b, '(')
 66		for i := 0; i < t.NumIn(); i++ {
 67			if i > 0 {
 68				b = append(b, ", "...)
 69			}
 70			if i == t.NumIn()-1 && t.IsVariadic() {
 71				b = append(b, "..."...)
 72				b = appendTypeName(b, t.In(i).Elem(), qualified, false)
 73			} else {
 74				b = appendTypeName(b, t.In(i), qualified, false)
 75			}
 76		}
 77		b = append(b, ')')
 78		switch t.NumOut() {
 79		case 0:
 80			// Do nothing
 81		case 1:
 82			b = append(b, ' ')
 83			b = appendTypeName(b, t.Out(0), qualified, false)
 84		default:
 85			b = append(b, " ("...)
 86			for i := 0; i < t.NumOut(); i++ {
 87				if i > 0 {
 88					b = append(b, ", "...)
 89				}
 90				b = appendTypeName(b, t.Out(i), qualified, false)
 91			}
 92			b = append(b, ')')
 93		}
 94	case reflect.Struct:
 95		b = append(b, "struct{ "...)
 96		for i := 0; i < t.NumField(); i++ {
 97			if i > 0 {
 98				b = append(b, "; "...)
 99			}
100			sf := t.Field(i)
101			if !sf.Anonymous {
102				if qualified && sf.PkgPath != "" {
103					b = append(b, '"')
104					b = append(b, sf.PkgPath...)
105					b = append(b, '"')
106					b = append(b, '.')
107				}
108				b = append(b, sf.Name...)
109				b = append(b, ' ')
110			}
111			b = appendTypeName(b, sf.Type, qualified, false)
112			if sf.Tag != "" {
113				b = append(b, ' ')
114				b = strconv.AppendQuote(b, string(sf.Tag))
115			}
116		}
117		if b[len(b)-1] == ' ' {
118			b = b[:len(b)-1]
119		} else {
120			b = append(b, ' ')
121		}
122		b = append(b, '}')
123	case reflect.Slice, reflect.Array:
124		b = append(b, '[')
125		if k == reflect.Array {
126			b = strconv.AppendUint(b, uint64(t.Len()), 10)
127		}
128		b = append(b, ']')
129		b = appendTypeName(b, t.Elem(), qualified, false)
130	case reflect.Map:
131		b = append(b, "map["...)
132		b = appendTypeName(b, t.Key(), qualified, false)
133		b = append(b, ']')
134		b = appendTypeName(b, t.Elem(), qualified, false)
135	case reflect.Ptr:
136		b = append(b, '*')
137		b = appendTypeName(b, t.Elem(), qualified, false)
138	case reflect.Interface:
139		b = append(b, "interface{ "...)
140		for i := 0; i < t.NumMethod(); i++ {
141			if i > 0 {
142				b = append(b, "; "...)
143			}
144			m := t.Method(i)
145			if qualified && m.PkgPath != "" {
146				b = append(b, '"')
147				b = append(b, m.PkgPath...)
148				b = append(b, '"')
149				b = append(b, '.')
150			}
151			b = append(b, m.Name...)
152			b = appendTypeName(b, m.Type, qualified, true)
153		}
154		if b[len(b)-1] == ' ' {
155			b = b[:len(b)-1]
156		} else {
157			b = append(b, ' ')
158		}
159		b = append(b, '}')
160	default:
161		panic("invalid kind: " + k.String())
162	}
163	return b
164}