func typeMarshaler(t reflect.Type) marshalFunc { if t.Implements(textMarshalerType) { return textMarshaler } switch t.Kind() { case reflect.Bool: return boolMarshaler case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: return intMarshaler case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: return uintMarshaler case reflect.Float32: return float32Marshaler case reflect.Float64: return float64Marshaler case reflect.String: return stringMarshaler case reflect.Slice: return newSliceMarshaler(t) case reflect.Array: return newArrayMarshaler(t) default: panic(fmt.Sprintf("dynamodb: %s type is not supported", t.Kind())) } }
// makeWriter creates a writer function for the given type. func makeWriter(typ reflect.Type) (writer, error) { kind := typ.Kind() switch { case typ.Implements(encoderInterface): return writeEncoder, nil case kind != reflect.Ptr && reflect.PtrTo(typ).Implements(encoderInterface): return writeEncoderNoPtr, nil case kind == reflect.Interface: return writeInterface, nil case typ.AssignableTo(reflect.PtrTo(bigInt)): return writeBigIntPtr, nil case typ.AssignableTo(bigInt): return writeBigIntNoPtr, nil case isUint(kind): return writeUint, nil case kind == reflect.Bool: return writeBool, nil case kind == reflect.String: return writeString, nil case kind == reflect.Slice && isByte(typ.Elem()): return writeBytes, nil case kind == reflect.Array && isByte(typ.Elem()): return writeByteArray, nil case kind == reflect.Slice || kind == reflect.Array: return makeSliceWriter(typ) case kind == reflect.Struct: return makeStructWriter(typ) case kind == reflect.Ptr: return makePtrWriter(typ) default: return nil, fmt.Errorf("rlp: type %v is not RLP-serializable", typ) } }
func convertVal(t reflect.Type, v reflect.Value) reflect.Value { var ptr Pointer if v.Type() == obj_t { ptr = v.Interface().(*Object).GetPtr() } else if v.Type() == ptr_t { ptr = v.Interface().(Pointer) } if ptr != nil { var ret reflect.Value if t.Implements(ptr_setter_i) { // Desired type implements PointerSetter so we are creating // new value with desired type and set it from ptr if t.Kind() == reflect.Ptr { ret = reflect.New(t.Elem()) } else { ret = reflect.Zero(t) } ret.Interface().(PointerSetter).SetPtr(ptr) } else if t.Kind() == reflect.Ptr { // t doesn't implements PointerSetter but it is pointer // so we bypass type checking and setting it from ptr. ret = valueFromPointer(ptr, t) } return ret } return v }
func getValueParser(t reflect.Type) func(s string) (reflect.Value, error) { if parser, ok := valueParsers[t]; ok { return parser } if t.Implements(unmarshalerType) { return func(s string) (reflect.Value, error) { v := reflect.Zero(t) buff, err := json.Marshal(s) if err != nil { return v, err } err = v.Interface().(json.Unmarshaler).UnmarshalJSON(buff) return v, err } } tt := reflect.PtrTo(t) if tt.Implements(unmarshalerType) { return func(s string) (reflect.Value, error) { v := reflect.New(t) buff, err := json.Marshal(s) if err != nil { return v, err } err = v.Interface().(json.Unmarshaler).UnmarshalJSON(buff) return reflect.Indirect(v), err } } return nil }
func (m *structCache) getDecoder(typ reflect.Type) decoderFunc { if decoder, ok := typDecMap[typ]; ok { return decoder } if typ.Implements(unmarshalerType) { return unmarshalValue } kind := typ.Kind() switch kind { case reflect.Slice: elemKind := typ.Elem().Kind() if dec := sliceDecoders[elemKind]; dec != nil { return dec } case reflect.Ptr: fallthrough case reflect.Struct: if m.ext != nil { if decoder, ok := m.ext.decTypeMap[typ]; ok { return decoder } } } return valueDecoders[kind] }
// ShouldNotImplement receives exactly two parameters and ensures // that the first does NOT implement the interface type of the second. func ShouldNotImplement(actual interface{}, expectedList ...interface{}) string { if fail := need(1, expectedList); fail != success { return fail } expected := expectedList[0] if fail := ShouldBeNil(expected); fail != success { return shouldCompareWithInterfacePointer } if fail := ShouldNotBeNil(actual); fail != success { return shouldNotBeNilActual } var actualType reflect.Type if reflect.TypeOf(actual).Kind() != reflect.Ptr { actualType = reflect.PtrTo(reflect.TypeOf(actual)) } else { actualType = reflect.TypeOf(actual) } expectedType := reflect.TypeOf(expected) if fail := ShouldNotBeNil(expectedType); fail != success { return shouldCompareWithInterfacePointer } expectedInterface := expectedType.Elem() if actualType.Implements(expectedInterface) { return fmt.Sprintf(shouldNotHaveImplemented, actualType, expectedInterface) } return success }
func Decoder(typ reflect.Type) valueDecoder { if typ == timeType { return decodeTimeValue } if reflect.PtrTo(typ).Implements(scannerType) { return decodeScannerAddrValue } if typ.Implements(scannerType) { return decodeScannerValue } kind := typ.Kind() if kind == reflect.Slice && typ.Elem().Kind() == reflect.Uint8 { return decodeBytesValue } if dec := valueDecoders[kind]; dec != nil { return dec } return nil }
// isScalar returns true if the type can be parsed from a single string func isScalar(t reflect.Type) (scalar, boolean bool) { // If it implements encoding.TextUnmarshaler then use that if t.Implements(textUnmarshalerType) { // scalar=YES, boolean=NO return true, false } // If we have a pointer then dereference it if t.Kind() == reflect.Ptr { t = t.Elem() } // Check for other special types switch t { case durationType, mailAddressType, ipType, macType: // scalar=YES, boolean=NO return true, false } // Fall back to checking the kind switch t.Kind() { case reflect.Bool: // scalar=YES, boolean=YES return true, true case reflect.String, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64: // scalar=YES, boolean=NO return true, false } // scalar=NO, boolean=NO return false, false }
func getTypeEncoder(typ reflect.Type) encoderFunc { kind := typ.Kind() if typ.Implements(encoderType) { return encodeCustomValue } // Addressable struct field value. if reflect.PtrTo(typ).Implements(encoderType) { return encodeCustomValuePtr } if typ.Implements(marshalerType) { return marshalValue } if encoder, ok := typEncMap[typ]; ok { return encoder } if kind == reflect.Slice && typ.Elem().Kind() == reflect.Uint8 { return encodeBytesValue } return valueEncoders[kind] }
func getDecoder(typ reflect.Type) decoderFunc { kind := typ.Kind() // Addressable struct field value. if kind != reflect.Ptr && reflect.PtrTo(typ).Implements(decoderType) { return decodeCustomValuePtr } if typ.Implements(decoderType) { return decodeCustomValue } if typ.Implements(unmarshalerType) { return unmarshalValue } if decoder, ok := typDecMap[typ]; ok { return decoder } if kind == reflect.Slice && typ.Elem().Kind() == reflect.Uint8 { return decodeBytesValue } return valueDecoders[kind] }
func makeDecoder(typ reflect.Type, tags tags) (dec decoder, err error) { kind := typ.Kind() switch { case typ == rawValueType: return decodeRawValue, nil case typ.Implements(decoderInterface): return decodeDecoder, nil case kind != reflect.Ptr && reflect.PtrTo(typ).Implements(decoderInterface): return decodeDecoderNoPtr, nil case typ.AssignableTo(reflect.PtrTo(bigInt)): return decodeBigInt, nil case typ.AssignableTo(bigInt): return decodeBigIntNoPtr, nil case isUint(kind): return decodeUint, nil case kind == reflect.Bool: return decodeBool, nil case kind == reflect.String: return decodeString, nil case kind == reflect.Slice || kind == reflect.Array: return makeListDecoder(typ, tags) case kind == reflect.Struct: return makeStructDecoder(typ) case kind == reflect.Ptr: if tags.nilOK { return makeOptionalPtrDecoder(typ) } return makePtrDecoder(typ) case kind == reflect.Interface: return decodeInterface, nil default: return nil, fmt.Errorf("rlp: type %v is not RLP-serializable", typ) } }
func getSetter(outt reflect.Type, out reflect.Value) Setter { setterMutex.RLock() style := setterStyle[outt] setterMutex.RUnlock() if style == setterNone { return nil } if style == setterUnknown { setterMutex.Lock() defer setterMutex.Unlock() if outt.Implements(setterIface) { setterStyle[outt] = setterType } else if reflect.PtrTo(outt).Implements(setterIface) { setterStyle[outt] = setterAddr } else { setterStyle[outt] = setterNone return nil } style = setterStyle[outt] } if style == setterAddr { if !out.CanAddr() { return nil } out = out.Addr() } else if outt.Kind() == reflect.Ptr && out.IsNil() { out.Set(reflect.New(outt.Elem())) } return out.Interface().(Setter) }
func IsSupportUnmarshal(t reflect.Type) bool { if t.Kind() != reflect.Ptr { t = reflect.PtrTo(t) } return t.Implements(unmarshalerType) }
func implementsUnpacker(t reflect.Type) bool { for _, tUnpack := range tUnpackers { if t.Implements(tUnpack) { return true } } return false }
func isOkType(v reflect.Type) bool { if v.Implements(typeOfPropertyLoadSaver) { return true } if v.Kind() == reflect.Ptr && v.Elem().Kind() == reflect.Struct { return true } return false }
// isUnmarshaler reports whether type t implements Unmarshaler. func isUnmarshaler(t reflect.Type) bool { // We're checking for (likely) pointer-receiver methods // so if t is not a pointer, something is very wrong. // The calls above only invoke isUnmarshaler on pointer types. if t.Kind() != reflect.Ptr { panic("proto: misuse of isUnmarshaler") } return t.Implements(unmarshalerType) }
func (t Type) Match(rt reflect.Type) bool { if rt.Implements(object_caster) { return t.IsA(TYPE_OBJECT) } if rt.Implements(type_getter) { if rt.Kind() == reflect.Ptr { rt = rt.Elem() } r := reflect.New(rt).Interface().(TypeGetter).Type() return t.QName() == r.QName() } switch rt.Kind() { case reflect.Invalid: return t == TYPE_INVALID case reflect.String: return t == TYPE_STRING case reflect.Int: return t == TYPE_GO_INT case reflect.Uint: return t == TYPE_GO_UINT case reflect.Int8: return t == TYPE_CHAR case reflect.Uint8: return t == TYPE_UCHAR case reflect.Int32: return t == TYPE_GO_INT32 case reflect.Uint32: return t == TYPE_GO_UINT32 case reflect.Int64: return t == TYPE_INT64 case reflect.Uint64: return t == TYPE_UINT64 case reflect.Bool: return t == TYPE_BOOLEAN case reflect.Float32: return t == TYPE_FLOAT case reflect.Float64: return t == TYPE_DOUBLE case reflect.Ptr: return t == TYPE_POINTER } return false }
func validateModelType(sqlType string, goType reflect.Type) error { // If the go type implements the driver.Valuer and sql.Scanner // interfaces, just ignore the sql type and assume it is doing the // right thing. It would be possible to instantiate the go type and // check to see if it can handle the sql type, but that seems overly // complicated for the benefit. if goType.Implements(valuerType) && reflect.PtrTo(goType).Implements(scannerType) { return nil } sqlType = strings.ToLower(sqlType) baseType := sqlBaseType(sqlType) valid := false switch baseType { case "int": valid = validateIntType(32, sqlType, goType) case "tinyint": // The "(1)" is really a formatting specifier, but MySQL uses it // when "bool" or "boolean" is specified as the type. valid = (sqlType == "tinyint(1)" && goType.Kind() == reflect.Bool) valid = valid || validateIntType(8, sqlType, goType) case "smallint": valid = validateIntType(16, sqlType, goType) case "mediumint": valid = validateIntType(32, sqlType, goType) case "bigint": valid = validateIntType(64, sqlType, goType) case "bit": valid = validateBitType(sqlType, goType) case "float": valid = validateFloatType(32, goType) case "double": valid = validateFloatType(64, goType) case "decimal": valid = validateFloatType(64, goType) case "date", "time", "datetime", "timestamp": valid = validateDateTimeType(goType) case "char", "varchar", "binary", "varbinary": fallthrough case "blob", "tinyblob", "mediumblob", "longblob": fallthrough case "text", "tinytext", "mediumtext", "longtext": valid = validateStringType(goType) case "enum": valid = (goType.Kind() == reflect.String) case "set": valid = (goType == setStringType) } if valid { return nil } return fmt.Errorf("incompatible types: \"%s\" vs \"%s\"", sqlType, goType) }
// Call this once after each struct type declaration func GetModelInfoFromType(modelType reflect.Type) *ModelInfo { if modelType.Kind() == reflect.Ptr { modelType = modelType.Elem() } if modelType.Kind() != reflect.Struct { return nil } if modelType.Implements(reflect.TypeOf((*sql.Scanner)(nil)).Elem()) { return nil } modelName := modelType.Name() // Check cache if allModelInfos[modelName] != nil { return allModelInfos[modelName] } // Construct m := &ModelInfo{} allModelInfos[modelName] = m m.Type = modelType m.TableName = strings.ToLower(modelName) // Fields numFields := m.Type.NumField() for i := 0; i < numFields; i++ { field := m.Type.Field(i) if field.Tag.Get("db") != "" { column, null, autoinc := parseDBTag(field.Tag.Get("db")) m.Fields = append(m.Fields, &ModelField{field, column, null, autoinc}) } } // Simple & Prefixed fieldNames := []string{} fieldInsertNames := []string{} ph := []string{} for _, field := range m.Fields { fieldName, _, _ := parseDBTag(field.Tag.Get("db")) fieldNames = append(fieldNames, fieldName) if !field.Autoinc { fieldInsertNames = append(fieldInsertNames, fieldName) ph = append(ph, fmt.Sprintf("$%v", len(ph)+1)) } } m.FieldsSimple = strings.Join(fieldNames, ", ") m.FieldsPrefixed = m.TableName + "." + strings.Join(fieldNames, ", "+m.TableName+".") m.FieldsInsert = strings.Join(fieldInsertNames, ", ") m.Placeholders = strings.Join(ph, ", ") return m }
func IsImplemented(t, target reflect.Type) bool { if t.Implements(target) { return true } if t.Kind() == reflect.Struct { return reflect.PtrTo(t).Implements(target) } else if t.Kind() == reflect.Ptr { return t.Elem().Implements(target) } return false }
func typeIsUnpacker(t reflect.Type) (reflect.Value, bool) { if t.Implements(tUnpacker) { return reflect.New(t).Elem(), true } if reflect.PtrTo(t).Implements(tUnpacker) { return reflect.New(t), true } return reflect.Value{}, false }
func IfImplementsModel(t reflect.Type) bool { if t.Implements(_modelType) { return true } if t.Kind() != reflect.Ptr { if reflect.PtrTo(t).Implements(_modelType) { return true } } return false }
//获取t对应的类型信息,不支持slice, function, map, pointer, interface, channel //如果parentIdx的长度>0,则表示t是strut中的字段的类型信息, t为字段对应的类型 func getKeyInfoByParent(t reflect.Type, parent *keyInfo, parentIdx []int) (ki *keyInfo, err error) { ki = &keyInfo{} //判断是否实现了hasher接口 if t.Implements(hasherT) { ki.isHasher = true return } ki.kind = t.Kind() if _, ok := engM[ki.kind]; ok { //简单类型,不需要再分解元素类型的信息 ki.index = parentIdx } else { //some types can be used as key, we can use equals to test switch ki.kind { case reflect.Chan, reflect.Slice, reflect.Func, reflect.Map, reflect.Ptr, reflect.Interface: err = NonSupportKey case reflect.Struct: if parent == nil { //parent==nil表示t不是一个嵌套的struct,所以这里需要初始化fields parent = ki ki.fields = make([]*keyInfo, 0, t.NumField()) } for i := 0; i < t.NumField(); i++ { f := t.Field(i) //skip unexported field, if len(f.PkgPath) > 0 { continue } idx := make([]int, len(parentIdx), len(parentIdx)+1) copy(idx, parentIdx) idx = append(idx, i) if fi, e := getKeyInfoByParent(f.Type, parent, idx); e != nil { err = e return } else { //fi.index = i parent.fields = append(ki.fields, fi) } } case reflect.Array: if ki.elementInfo, err = getKeyInfo(t.Elem()); err != nil { return } ki.size = t.Len() ki.index = parentIdx } } return }
func (p *protoUnmarshaller) getObject(reflectType reflect.Type, object []byte) (interface{}, error) { if reflectType.Implements(reflect.TypeOf((*proto.Message)(nil)).Elem()) { protoMessage := reflect.New(reflectType.Elem()).Interface().(proto.Message) if err := proto.Unmarshal(object, protoMessage); err != nil { return nil, err } return protoMessage, nil } objectPtr := reflect.New(reflectType).Interface() if err := gob.NewDecoder(bytes.NewBuffer(object)).Decode(objectPtr); err != nil { return nil, err } return reflect.ValueOf(objectPtr).Elem().Interface(), nil }
func getDecoder(typ reflect.Type) decoderFunc { kind := typ.Kind() // Addressable struct field value. if kind != reflect.Ptr && reflect.PtrTo(typ).Implements(decoderType) { return decodeCustomValuePtr } if typ.Implements(decoderType) { return decodeCustomValue } if typ.Implements(unmarshalerType) { return unmarshalValue } if decoder, ok := typDecMap[typ]; ok { return decoder } switch kind { case reflect.Ptr: return ptrDecoderFunc(typ) case reflect.Slice: elem := typ.Elem() switch elem.Kind() { case reflect.Uint8: return decodeBytesValue } switch elem { case stringType: return decodeStringSliceValue } case reflect.Array: if typ.Elem().Kind() == reflect.Uint8 { return decodeByteArrayValue } case reflect.Map: if typ.Key() == stringType { switch typ.Elem() { case stringType: return decodeMapStringStringValue case interfaceType: return decodeMapStringInterfaceValue } } } return valueDecoders[kind] }
// newTypeEncoder constructs an encoderFunc for a type. // The returned encoder only checks CanAddr when allowAddr is true. func newTypeEncoder(t reflect.Type, allowAddr bool) encoderFunc { if t.Implements(marshalerType) { return marshalerEncoder } if t.Kind() != reflect.Ptr && allowAddr { if reflect.PtrTo(t).Implements(marshalerType) { return newCondAddrEncoder(addrMarshalerEncoder, newTypeEncoder(t, false)) } } // Check for psuedo-types first switch t { case timeType: return timePseudoTypeEncoder } switch t.Kind() { case reflect.Bool: return boolEncoder case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: return intEncoder case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: return uintEncoder case reflect.Float32, reflect.Float64: return floatEncoder case reflect.String: return stringEncoder case reflect.Interface: return interfaceEncoder case reflect.Struct: return newStructEncoder(t) case reflect.Map: return newMapEncoder(t) case reflect.Slice: return newSliceEncoder(t) case reflect.Array: return newArrayEncoder(t) case reflect.Ptr: return newPtrEncoder(t) case reflect.Func: // functions are a special case as they can be used internally for // optional arguments. Just return the raw function, if somebody tries // to pass a function to the database the JSON marshaller will catch this // anyway. return funcEncoder default: return unsupportedTypeEncoder } }
// newTypeEncoder constructs an encoderFunc for a type. // The returned encoder only checks CanAddr when allowAddr is true. func newTypeEncoder(t reflect.Type, allowAddr bool) encoderFunc { // Special case for time.Time because it already implements // TextMarshaler which is not what we want as EDN. if t == timeType { return timeEncoder } if t.Kind() == reflect.Ptr && t.Elem() == timeType { return newPtrEncoder(t) } if t.Implements(textMarshalerType) { return textMarshalerEncoder } if t.Kind() != reflect.Ptr && allowAddr { if reflect.PtrTo(t).Implements(textMarshalerType) { return newCondAddrEncoder(addrTextMarshalerEncoder, newTypeEncoder(t, false)) } } switch t.Kind() { case reflect.Bool: return boolEncoder case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: return intEncoder case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: return uintEncoder case reflect.Float32: return float32Encoder case reflect.Float64: return float64Encoder case reflect.String: return stringEncoder case reflect.Interface: return interfaceEncoder case reflect.Map: return newMapEncoder(t) case reflect.Slice: return newSliceEncoder(t) case reflect.Array: return newArrayEncoder(t) case reflect.Ptr: return newPtrEncoder(t) default: if t == listType { return listEncoder } return unsupportedTypeEncoder } }
// newTypeEncoder constructs an encoderFunc for a type. // The returned encoder only checks CanAddr when allowAddr is true. func newTypeEncoder(t reflect.Type, allowAddr bool) encoderFunc { if t.Implements(marshalerType) { return marshalerEncoder } if t.Kind() != reflect.Ptr && allowAddr { if reflect.PtrTo(t).Implements(marshalerType) { return newCondAddrEncoder(addrMarshalerEncoder, newTypeEncoder(t, false)) } } if t.Implements(textMarshalerType) { return textMarshalerEncoder } if t.Kind() != reflect.Ptr && allowAddr { if reflect.PtrTo(t).Implements(textMarshalerType) { return newCondAddrEncoder(addrTextMarshalerEncoder, newTypeEncoder(t, false)) } } switch t.Kind() { case reflect.Bool: return boolEncoder case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: return intEncoder case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: return uintEncoder case reflect.Float32: return float32Encoder case reflect.Float64: return float64Encoder case reflect.String: return stringEncoder case reflect.Interface: return interfaceEncoder case reflect.Struct: return newStructEncoder(t) case reflect.Map: return newMapEncoder(t) case reflect.Slice: return newSliceEncoder(t) case reflect.Array: return newArrayEncoder(t) case reflect.Ptr: return newPtrEncoder(t) default: return unsupportedTypeEncoder } }
func (m *structCache) getTypeEncoder(typ reflect.Type) encoderFunc { if typ.Implements(marshalerType) { return marshalValue } kind := typ.Kind() switch kind { case reflect.Slice: elemKind := typ.Elem().Kind() if enc := sliceEncoders[elemKind]; enc != nil { return enc } } return valueEncoders[kind] }
func ensureTags(t *testing.T, gvk schema.GroupVersionKind, tp reflect.Type, parents []reflect.Type) { // This type handles its own encoding/decoding and doesn't need json tags if tp.Implements(marshalerType) && (tp.Implements(unmarshalerType) || reflect.PtrTo(tp).Implements(unmarshalerType)) { return } parents = append(parents, tp) switch tp.Kind() { case reflect.Map, reflect.Slice, reflect.Ptr: ensureTags(t, gvk, tp.Elem(), parents) case reflect.String, reflect.Bool, reflect.Float32, reflect.Int, reflect.Int32, reflect.Int64, reflect.Uint8, reflect.Uintptr, reflect.Uint32, reflect.Uint64, reflect.Interface: // no-op case reflect.Struct: for i := 0; i < tp.NumField(); i++ { f := tp.Field(i) jsonTag := f.Tag.Get("json") if len(jsonTag) == 0 { t.Errorf("External types should have json tags. %#v tags on field %v are: %s", gvk, f.Name, f.Tag) for i, tp := range parents { t.Logf("%s%v", strings.Repeat(" ", i), tp) } } jsonTagName := strings.Split(jsonTag, ",")[0] if len(jsonTagName) > 0 && (jsonTagName[0] < 'a' || jsonTagName[0] > 'z') && jsonTagName != "-" && allowedNonstandardJSONNames[tp] != jsonTagName { t.Errorf("External types should have json names starting with lowercase letter. %#v has json tag on field %v with name %s", gvk, f.Name, jsonTagName) t.Log(tp) t.Log(allowedNonstandardJSONNames[tp]) for i, tp := range parents { t.Logf("%s%v", strings.Repeat(" ", i), tp) } } ensureTags(t, gvk, f.Type, parents) } default: t.Errorf("Unexpected type %v in %#v", tp.Kind(), gvk) for i, tp := range parents { t.Logf("%s%v:", strings.Repeat(" ", i), tp) } } }