1 // Copyright 2019, 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.md file.
14 "github.com/google/go-cmp/cmp/internal/flags"
15 "github.com/google/go-cmp/cmp/internal/value"
18 type formatValueOptions struct {
19 // AvoidStringer controls whether to avoid calling custom stringer
20 // methods like error.Error or fmt.Stringer.String.
23 // ShallowPointers controls whether to avoid descending into pointers.
24 // Useful when printing map keys, where pointer comparison is performed
25 // on the pointer address rather than the pointed-at value.
28 // PrintAddresses controls whether to print the address of all pointers,
29 // slice elements, and maps.
33 // FormatType prints the type as if it were wrapping s.
34 // This may return s as-is depending on the current type and TypeMode mode.
35 func (opts formatOptions) FormatType(t reflect.Type, s textNode) textNode {
36 // Check whether to emit the type or not.
37 switch opts.TypeMode {
40 case reflect.Struct, reflect.Slice, reflect.Array, reflect.Map:
51 // Determine the type label, applying special handling for unnamed types.
52 typeName := t.String()
54 // According to Go grammar, certain type literals contain symbols that
55 // do not strongly bind to the next lexicographical token (e.g., *T).
57 case reflect.Chan, reflect.Func, reflect.Ptr:
58 typeName = "(" + typeName + ")"
60 typeName = strings.Replace(typeName, "struct {", "struct{", -1)
61 typeName = strings.Replace(typeName, "interface {", "interface{", -1)
64 // Avoid wrap the value in parenthesis if unnecessary.
65 if s, ok := s.(textWrap); ok {
66 hasParens := strings.HasPrefix(s.Prefix, "(") && strings.HasSuffix(s.Suffix, ")")
67 hasBraces := strings.HasPrefix(s.Prefix, "{") && strings.HasSuffix(s.Suffix, "}")
68 if hasParens || hasBraces {
69 return textWrap{typeName, s, ""}
72 return textWrap{typeName + "(", s, ")"}
75 // FormatValue prints the reflect.Value, taking extra care to avoid descending
76 // into pointers already in m. As pointers are visited, m is also updated.
77 func (opts formatOptions) FormatValue(v reflect.Value, m visitedPointers) (out textNode) {
83 // Check whether there is an Error or String method to call.
84 if !opts.AvoidStringer && v.CanInterface() {
85 // Avoid calling Error or String methods on nil receivers since many
86 // implementations crash when doing so.
87 if (t.Kind() != reflect.Ptr && t.Kind() != reflect.Interface) || !v.IsNil() {
88 switch v := v.Interface().(type) {
90 return textLine("e" + formatString(v.Error()))
92 return textLine("s" + formatString(v.String()))
97 // Check whether to explicitly wrap the result with the type.
101 out = opts.FormatType(t, out)
108 return textLine(fmt.Sprint(v.Bool()))
109 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
110 return textLine(fmt.Sprint(v.Int()))
111 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
112 // Unnamed uints are usually bytes or words, so use hexadecimal.
113 if t.PkgPath() == "" || t.Kind() == reflect.Uintptr {
114 return textLine(formatHex(v.Uint()))
116 return textLine(fmt.Sprint(v.Uint()))
117 case reflect.Float32, reflect.Float64:
118 return textLine(fmt.Sprint(v.Float()))
119 case reflect.Complex64, reflect.Complex128:
120 return textLine(fmt.Sprint(v.Complex()))
122 return textLine(formatString(v.String()))
123 case reflect.UnsafePointer, reflect.Chan, reflect.Func:
124 return textLine(formatPointer(v))
127 for i := 0; i < v.NumField(); i++ {
129 if value.IsZero(vv) {
130 continue // Elide fields with zero values
132 s := opts.WithTypeMode(autoType).FormatValue(vv, m)
133 list = append(list, textRecord{Key: t.Field(i).Name, Value: s})
135 return textWrap{"{", list, "}"}
140 if opts.PrintAddresses {
141 ptr = formatPointer(v)
146 for i := 0; i < v.Len(); i++ {
148 if vi.CanAddr() { // Check for cyclic elements
152 out = textLine(formatPointer(p))
153 out = opts.WithTypeMode(emitType).FormatType(p.Type(), out)
154 out = textWrap{"*", out, ""}
155 list = append(list, textRecord{Value: out})
159 s := opts.WithTypeMode(elideType).FormatValue(vi, m)
160 list = append(list, textRecord{Value: s})
162 return textWrap{ptr + "{", list, "}"}
168 return textLine(formatPointer(v))
172 for _, k := range value.SortKeys(v.MapKeys()) {
173 sk := formatMapKey(k)
174 sv := opts.WithTypeMode(elideType).FormatValue(v.MapIndex(k), m)
175 list = append(list, textRecord{Key: sk, Value: sv})
177 if opts.PrintAddresses {
178 ptr = formatPointer(v)
180 return textWrap{ptr + "{", list, "}"}
185 if m.Visit(v) || opts.ShallowPointers {
186 return textLine(formatPointer(v))
188 if opts.PrintAddresses {
189 ptr = formatPointer(v)
191 skipType = true // Let the underlying value print the type instead
192 return textWrap{"&" + ptr, opts.FormatValue(v.Elem(), m), ""}
193 case reflect.Interface:
197 // Interfaces accept different concrete types,
198 // so configure the underlying value to explicitly print the type.
199 skipType = true // Print the concrete type instead
200 return opts.WithTypeMode(emitType).FormatValue(v.Elem(), m)
202 panic(fmt.Sprintf("%v kind not handled", v.Kind()))
206 // formatMapKey formats v as if it were a map key.
207 // The result is guaranteed to be a single line.
208 func formatMapKey(v reflect.Value) string {
209 var opts formatOptions
210 opts.TypeMode = elideType
211 opts.AvoidStringer = true
212 opts.ShallowPointers = true
213 s := opts.FormatValue(v, visitedPointers{}).String()
214 return strings.TrimSpace(s)
217 // formatString prints s as a double-quoted or backtick-quoted string.
218 func formatString(s string) string {
219 // Use quoted string if it the same length as a raw string literal.
220 // Otherwise, attempt to use the raw string form.
221 qs := strconv.Quote(s)
222 if len(qs) == 1+len(s)+1 {
226 // Disallow newlines to ensure output is a single line.
227 // Only allow printable runes for readability purposes.
228 rawInvalid := func(r rune) bool {
229 return r == '`' || r == '\n' || !(unicode.IsPrint(r) || r == '\t')
231 if strings.IndexFunc(s, rawInvalid) < 0 {
237 // formatHex prints u as a hexadecimal integer in Go notation.
238 func formatHex(u uint64) string {
247 case u <= 0xffffffff:
249 case u <= 0xffffffffff:
251 case u <= 0xffffffffffff:
253 case u <= 0xffffffffffffff:
255 case u <= 0xffffffffffffffff:
258 return fmt.Sprintf(f, u)
261 // formatPointer prints the address of the pointer.
262 func formatPointer(v reflect.Value) string {
264 if flags.Deterministic {
265 p = 0xdeadf00f // Only used for stable testing purposes
267 return fmt.Sprintf("⟪0x%x⟫", p)
270 type visitedPointers map[value.Pointer]struct{}
272 // Visit inserts pointer v into the visited map and reports whether it had
273 // already been visited before.
274 func (m visitedPointers) Visit(v reflect.Value) bool {
275 p := value.PointerOf(v)