cmp/report_reflect.go
func (opts formatOptions) FormatValue(v reflect.Value, m visitedPointers) (out textNode) {
	if !v.IsValid() {
		return nil
	}
	t := v.Type()

	// Check whether there is an Error or String method to call.
	if !opts.AvoidStringer && v.CanInterface() {
		// Avoid calling Error or String methods on nil receivers since many
		// implementations crash when doing so.
		if (t.Kind() != reflect.Ptr && t.Kind() != reflect.Interface) || !v.IsNil() {
			switch v := v.Interface().(type) {
			case error:
				return textLine("e" + formatString(v.Error()))
			case fmt.Stringer:
				return textLine("s" + formatString(v.String()))
			}
		}
	}

	// Check whether to explicitly wrap the result with the type.
	var skipType bool
	defer func() {
		if !skipType {
			out = opts.FormatType(t, out)
		}
	}()

	var ptr string
	switch t.Kind() {
	case reflect.Bool:
		return textLine(fmt.Sprint(v.Bool()))
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return textLine(fmt.Sprint(v.Int()))
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
		// Unnamed uints are usually bytes or words, so use hexadecimal.
		if t.PkgPath() == "" || t.Kind() == reflect.Uintptr {
			return textLine(formatHex(v.Uint()))
		}
		return textLine(fmt.Sprint(v.Uint()))
	case reflect.Float32, reflect.Float64:
		return textLine(fmt.Sprint(v.Float()))
	case reflect.Complex64, reflect.Complex128:
		return textLine(fmt.Sprint(v.Complex()))
	case reflect.String:
		return textLine(formatString(v.String()))
	case reflect.UnsafePointer, reflect.Chan, reflect.Func:
		return textLine(formatPointer(v))
	case reflect.Struct:
		var list textList
		for i := 0; i < v.NumField(); i++ {
			vv := v.Field(i)
			if value.IsZero(vv) {
				continue // Elide fields with zero values
			}
			s := opts.WithTypeMode(autoType).FormatValue(vv, m)
			list = append(list, textRecord{Key: t.Field(i).Name, Value: s})
		}
		return textWrap{"{", list, "}"}
	case reflect.Slice:
		if v.IsNil() {
			return textNil
		}
		if opts.PrintAddresses {
			ptr = formatPointer(v)
		}
		fallthrough
	case reflect.Array:
		var list textList
		for i := 0; i < v.Len(); i++ {
			vi := v.Index(i)
			if vi.CanAddr() { // Check for cyclic elements
				p := vi.Addr()
				if m.Visit(p) {
					var out textNode
					out = textLine(formatPointer(p))
					out = opts.WithTypeMode(emitType).FormatType(p.Type(), out)
					out = textWrap{"*", out, ""}
					list = append(list, textRecord{Value: out})
					continue
				}
			}
			s := opts.WithTypeMode(elideType).FormatValue(vi, m)
			list = append(list, textRecord{Value: s})
		}
		return textWrap{ptr + "{", list, "}"}
	case reflect.Map:
		if v.IsNil() {
			return textNil
		}
		if m.Visit(v) {
			return textLine(formatPointer(v))
		}

		var list textList
		for _, k := range value.SortKeys(v.MapKeys()) {
			sk := formatMapKey(k)
			sv := opts.WithTypeMode(elideType).FormatValue(v.MapIndex(k), m)
			list = append(list, textRecord{Key: sk, Value: sv})
		}
		if opts.PrintAddresses {
			ptr = formatPointer(v)
		}
		return textWrap{ptr + "{", list, "}"}
	case reflect.Ptr:
		if v.IsNil() {
			return textNil
		}
		if m.Visit(v) || opts.ShallowPointers {
			return textLine(formatPointer(v))
		}
		if opts.PrintAddresses {
			ptr = formatPointer(v)
		}
		skipType = true // Let the underlying value print the type instead
		return textWrap{"&" + ptr, opts.FormatValue(v.Elem(), m), ""}
	case reflect.Interface:
		if v.IsNil() {
			return textNil
		}
		// Interfaces accept different concrete types,
		// so configure the underlying value to explicitly print the type.
		skipType = true // Print the concrete type instead
		return opts.WithTypeMode(emitType).FormatValue(v.Elem(), m)
	default:
		panic(fmt.Sprintf("%v kind not handled", v.Kind()))
	}
}
func (opts formatOptions) FormatValue(v reflect.Value, m visitedPointers) (out textNode) {
	if !v.IsValid() {
		return nil
	}
	t := v.Type()

	// Check whether there is an Error or String method to call.
	if !opts.AvoidStringer && v.CanInterface() {
		// Avoid calling Error or String methods on nil receivers since many
		// implementations crash when doing so.
		if (t.Kind() != reflect.Ptr && t.Kind() != reflect.Interface) || !v.IsNil() {
			switch v := v.Interface().(type) {
			case error:
				return textLine("e" + formatString(v.Error()))
			case fmt.Stringer:
				return textLine("s" + formatString(v.String()))
			}
		}
	}

	// Check whether to explicitly wrap the result with the type.
	var skipType bool
	defer func() {
		if !skipType {
			out = opts.FormatType(t, out)
		}
	}()

	var ptr string
	switch t.Kind() {
	case reflect.Bool:
		return textLine(fmt.Sprint(v.Bool()))
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return textLine(fmt.Sprint(v.Int()))
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
		// Unnamed uints are usually bytes or words, so use hexadecimal.
		if t.PkgPath() == "" || t.Kind() == reflect.Uintptr {
			return textLine(formatHex(v.Uint()))
		}
		return textLine(fmt.Sprint(v.Uint()))
	case reflect.Float32, reflect.Float64:
		return textLine(fmt.Sprint(v.Float()))
	case reflect.Complex64, reflect.Complex128:
		return textLine(fmt.Sprint(v.Complex()))
	case reflect.String:
		return textLine(formatString(v.String()))
	case reflect.UnsafePointer, reflect.Chan, reflect.Func:
		return textLine(formatPointer(v))
	case reflect.Struct:
		var list textList
		for i := 0; i < v.NumField(); i++ {
			vv := v.Field(i)
			if value.IsZero(vv) {
				continue // Elide fields with zero values
			}
			s := opts.WithTypeMode(autoType).FormatValue(vv, m)
			list = append(list, textRecord{Key: t.Field(i).Name, Value: s})
		}
		return textWrap{"{", list, "}"}
	case reflect.Slice:
		if v.IsNil() {
			return textNil
		}
		if opts.PrintAddresses {
			ptr = formatPointer(v)
		}
		fallthrough
	case reflect.Array:
		var list textList
		for i := 0; i < v.Len(); i++ {
			vi := v.Index(i)
			if vi.CanAddr() { // Check for cyclic elements
				p := vi.Addr()
				if m.Visit(p) {
					var out textNode
					out = textLine(formatPointer(p))
					out = opts.WithTypeMode(emitType).FormatType(p.Type(), out)
					out = textWrap{"*", out, ""}
					list = append(list, textRecord{Value: out})
					continue
				}
			}
			s := opts.WithTypeMode(elideType).FormatValue(vi, m)
			list = append(list, textRecord{Value: s})
		}
		return textWrap{ptr + "{", list, "}"}
	case reflect.Map:
		if v.IsNil() {
			return textNil
		}
		if m.Visit(v) {
			return textLine(formatPointer(v))
		}

		var list textList
		for _, k := range value.SortKeys(v.MapKeys()) {
			sk := formatMapKey(k)
			sv := opts.WithTypeMode(elideType).FormatValue(v.MapIndex(k), m)
			list = append(list, textRecord{Key: sk, Value: sv})
		}
		if opts.PrintAddresses {
			ptr = formatPointer(v)
		}
		return textWrap{ptr + "{", list, "}"}
	case reflect.Ptr:
		if v.IsNil() {
			return textNil
		}
		if m.Visit(v) || opts.ShallowPointers {
			return textLine(formatPointer(v))
		}
		if opts.PrintAddresses {
			ptr = formatPointer(v)
		}
		skipType = true // Let the underlying value print the type instead
		return textWrap{"&" + ptr, opts.FormatValue(v.Elem(), m), ""}
	case reflect.Interface:
		if v.IsNil() {
			return textNil
		}
		// Interfaces accept different concrete types,
		// so configure the underlying value to explicitly print the type.
		skipType = true // Print the concrete type instead
		return opts.WithTypeMode(emitType).FormatValue(v.Elem(), m)
	default:
		panic(fmt.Sprintf("%v kind not handled", v.Kind()))
	}
}
func ( opts formatOptions ) FormatValue ( v reflect . Value , m visitedPointers ) ( out textNode ) { if ! v . IsValid ( ) { return nil } t := v . Type ( ) // Check whether there is an Error or String method to call. if ! opts . AvoidStringer && v . CanInterface ( ) { // Avoid calling Error or String methods on nil receivers since many // implementations crash when doing so. if ( t . Kind ( ) != reflect . Ptr && t . Kind ( ) != reflect . Interface ) || ! v . IsNil ( ) { switch v := v . Interface ( ) . ( type ) { case error : return textLine ( " " + formatString ( v . Error ( ) ) ) case fmt . Stringer : return textLine ( " " + formatString ( v . String ( ) ) ) } } } // Check whether to explicitly wrap the result with the type. var skipType bool defer func ( ) { if ! skipType { out = opts . FormatType ( t , out ) } } ( ) var ptr string switch t . Kind ( ) { case reflect . Bool : return textLine ( fmt . Sprint ( v . Bool ( ) ) ) case reflect . Int , reflect . Int8 , reflect . Int16 , reflect . Int32 , reflect . Int64 : return textLine ( fmt . Sprint ( v . Int ( ) ) ) case reflect . Uint , reflect . Uint8 , reflect . Uint16 , reflect . Uint32 , reflect . Uint64 , reflect . Uintptr : // Unnamed uints are usually bytes or words, so use hexadecimal. if t . PkgPath ( ) == " " || t . Kind ( ) == reflect . Uintptr { return textLine ( formatHex ( v . Uint ( ) ) ) } return textLine ( fmt . Sprint ( v . Uint ( ) ) ) case reflect . Float32 , reflect . Float64 : return textLine ( fmt . Sprint ( v . Float ( ) ) ) case reflect . Complex64 , reflect . Complex128 : return textLine ( fmt . Sprint ( v . Complex ( ) ) ) case reflect . String : return textLine ( formatString ( v . String ( ) ) ) case reflect . UnsafePointer , reflect . Chan , reflect . Func : return textLine ( formatPointer ( v ) ) case reflect . Struct : var list textList for i := 0 ; i < v . NumField ( ) ; i ++ { vv := v . Field ( i ) if value . IsZero ( vv ) { continue // Elide fields with zero values } s := opts . WithTypeMode ( autoType ) . FormatValue ( vv , m ) list = append ( list , textRecord { Key : t . Field ( i ) . Name , Value : s } ) } return textWrap { " " , list , " " } case reflect . Slice : if v . IsNil ( ) { return textNil } if opts . PrintAddresses { ptr = formatPointer ( v ) } fallthrough case reflect . Array : var list textList for i := 0 ; i < v . Len ( ) ; i ++ { vi := v . Index ( i ) if vi . CanAddr ( ) { // Check for cyclic elements p := vi . Addr ( ) if m . Visit ( p ) { var out textNode out = textLine ( formatPointer ( p ) ) out = opts . WithTypeMode ( emitType ) . FormatType ( p . Type ( ) , out ) out = textWrap { " " , out , " " } list = append ( list , textRecord { Value : out } ) continue } } s := opts . WithTypeMode ( elideType ) . FormatValue ( vi , m ) list = append ( list , textRecord { Value : s } ) } return textWrap { ptr + " " , list , " " } case reflect . Map : if v . IsNil ( ) { return textNil } if m . Visit ( v ) { return textLine ( formatPointer ( v ) ) } var list textList for _ , k := range value . SortKeys ( v . MapKeys ( ) ) { sk := formatMapKey ( k ) sv := opts . WithTypeMode ( elideType ) . FormatValue ( v . MapIndex ( k ) , m ) list = append ( list , textRecord { Key : sk , Value : sv } ) } if opts . PrintAddresses { ptr = formatPointer ( v ) } return textWrap { ptr + " " , list , " " } case reflect . Ptr : if v . IsNil ( ) { return textNil } if m . Visit ( v ) || opts . ShallowPointers { return textLine ( formatPointer ( v ) ) } if opts . PrintAddresses { ptr = formatPointer ( v ) } skipType = true // Let the underlying value print the type instead return textWrap { " " + ptr , opts . FormatValue ( v . Elem ( ) , m ) , " " } case reflect . Interface : if v . IsNil ( ) { return textNil } // Interfaces accept different concrete types, // so configure the underlying value to explicitly print the type. skipType = true // Print the concrete type instead return opts . WithTypeMode ( emitType ) . FormatValue ( v . Elem ( ) , m ) default : panic ( fmt . Sprintf ( " " , v . Kind ( ) ) ) } }
// FormatValue prints the reflect.Value, taking extra care to avoid descending
// into pointers already in m. As pointers are visited, m is also updated.
FormatValue prints the reflect . Value taking extra care to avoid descending into pointers already in m . As pointers are visited m is also updated .