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.
4
5package cmp
6
7import (
8	"fmt"
9	"reflect"
10	"strconv"
11	"strings"
12	"unicode"
13
14	"github.com/google/go-cmp/cmp/internal/flags"
15	"github.com/google/go-cmp/cmp/internal/value"
16)
17
18type formatValueOptions struct {
19	// AvoidStringer controls whether to avoid calling custom stringer
20	// methods like error.Error or fmt.Stringer.String.
21	AvoidStringer bool
22
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.
26	ShallowPointers bool
27
28	// PrintAddresses controls whether to print the address of all pointers,
29	// slice elements, and maps.
30	PrintAddresses bool
31}
32
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.
35func (opts formatOptions) FormatType(t reflect.Type, s textNode) textNode {
36	// Check whether to emit the type or not.
37	switch opts.TypeMode {
38	case autoType:
39		switch t.Kind() {
40		case reflect.Struct, reflect.Slice, reflect.Array, reflect.Map:
41			if s.Equal(textNil) {
42				return s
43			}
44		default:
45			return s
46		}
47	case elideType:
48		return s
49	}
50
51	// Determine the type label, applying special handling for unnamed types.
52	typeName := t.String()
53	if t.Name() == "" {
54		// According to Go grammar, certain type literals contain symbols that
55		// do not strongly bind to the next lexicographical token (e.g., *T).
56		switch t.Kind() {
57		case reflect.Chan, reflect.Func, reflect.Ptr:
58			typeName = "(" + typeName + ")"
59		}
60		typeName = strings.Replace(typeName, "struct {", "struct{", -1)
61		typeName = strings.Replace(typeName, "interface {", "interface{", -1)
62	}
63
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, ""}
70		}
71	}
72	return textWrap{typeName + "(", s, ")"}
73}
74
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.
77func (opts formatOptions) FormatValue(v reflect.Value, m visitedPointers) (out textNode) {
78	if !v.IsValid() {
79		return nil
80	}
81	t := v.Type()
82
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) {
89			case error:
90				return textLine("e" + formatString(v.Error()))
91			case fmt.Stringer:
92				return textLine("s" + formatString(v.String()))
93			}
94		}
95	}
96
97	// Check whether to explicitly wrap the result with the type.
98	var skipType bool
99	defer func() {
100		if !skipType {
101			out = opts.FormatType(t, out)
102		}
103	}()
104
105	var ptr string
106	switch t.Kind() {
107	case reflect.Bool:
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()))
115		}
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()))
121	case reflect.String:
122		return textLine(formatString(v.String()))
123	case reflect.UnsafePointer, reflect.Chan, reflect.Func:
124		return textLine(formatPointer(v))
125	case reflect.Struct:
126		var list textList
127		for i := 0; i < v.NumField(); i++ {
128			vv := v.Field(i)
129			if value.IsZero(vv) {
130				continue // Elide fields with zero values
131			}
132			s := opts.WithTypeMode(autoType).FormatValue(vv, m)
133			list = append(list, textRecord{Key: t.Field(i).Name, Value: s})
134		}
135		return textWrap{"{", list, "}"}
136	case reflect.Slice:
137		if v.IsNil() {
138			return textNil
139		}
140		if opts.PrintAddresses {
141			ptr = formatPointer(v)
142		}
143		fallthrough
144	case reflect.Array:
145		var list textList
146		for i := 0; i < v.Len(); i++ {
147			vi := v.Index(i)
148			if vi.CanAddr() { // Check for cyclic elements
149				p := vi.Addr()
150				if m.Visit(p) {
151					var out textNode
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})
156					continue
157				}
158			}
159			s := opts.WithTypeMode(elideType).FormatValue(vi, m)
160			list = append(list, textRecord{Value: s})
161		}
162		return textWrap{ptr + "{", list, "}"}
163	case reflect.Map:
164		if v.IsNil() {
165			return textNil
166		}
167		if m.Visit(v) {
168			return textLine(formatPointer(v))
169		}
170
171		var list textList
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})
176		}
177		if opts.PrintAddresses {
178			ptr = formatPointer(v)
179		}
180		return textWrap{ptr + "{", list, "}"}
181	case reflect.Ptr:
182		if v.IsNil() {
183			return textNil
184		}
185		if m.Visit(v) || opts.ShallowPointers {
186			return textLine(formatPointer(v))
187		}
188		if opts.PrintAddresses {
189			ptr = formatPointer(v)
190		}
191		skipType = true // Let the underlying value print the type instead
192		return textWrap{"&" + ptr, opts.FormatValue(v.Elem(), m), ""}
193	case reflect.Interface:
194		if v.IsNil() {
195			return textNil
196		}
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)
201	default:
202		panic(fmt.Sprintf("%v kind not handled", v.Kind()))
203	}
204}
205
206// formatMapKey formats v as if it were a map key.
207// The result is guaranteed to be a single line.
208func 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)
215}
216
217// formatString prints s as a double-quoted or backtick-quoted string.
218func 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 {
223		return qs
224	}
225
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')
230	}
231	if strings.IndexFunc(s, rawInvalid) < 0 {
232		return "`" + s + "`"
233	}
234	return qs
235}
236
237// formatHex prints u as a hexadecimal integer in Go notation.
238func formatHex(u uint64) string {
239	var f string
240	switch {
241	case u <= 0xff:
242		f = "0x%02x"
243	case u <= 0xffff:
244		f = "0x%04x"
245	case u <= 0xffffff:
246		f = "0x%06x"
247	case u <= 0xffffffff:
248		f = "0x%08x"
249	case u <= 0xffffffffff:
250		f = "0x%010x"
251	case u <= 0xffffffffffff:
252		f = "0x%012x"
253	case u <= 0xffffffffffffff:
254		f = "0x%014x"
255	case u <= 0xffffffffffffffff:
256		f = "0x%016x"
257	}
258	return fmt.Sprintf(f, u)
259}
260
261// formatPointer prints the address of the pointer.
262func formatPointer(v reflect.Value) string {
263	p := v.Pointer()
264	if flags.Deterministic {
265		p = 0xdeadf00f // Only used for stable testing purposes
266	}
267	return fmt.Sprintf("⟪0x%x⟫", p)
268}
269
270type visitedPointers map[value.Pointer]struct{}
271
272// Visit inserts pointer v into the visited map and reports whether it had
273// already been visited before.
274func (m visitedPointers) Visit(v reflect.Value) bool {
275	p := value.PointerOf(v)
276	_, visited := m[p]
277	m[p] = struct{}{}
278	return visited
279}
280