]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blob - vendor/github.com/google/go-cmp/cmp/report_reflect.go
update vendor and go.mod
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / google / go-cmp / cmp / report_reflect.go
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
5 package cmp
6
7 import (
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
18 type 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.
35 func (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.
77 func (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.
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)
215 }
216
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 {
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.
238 func 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.
262 func 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
270 type 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.
274 func (m visitedPointers) Visit(v reflect.Value) bool {
275 p := value.PointerOf(v)
276 _, visited := m[p]
277 m[p] = struct{}{}
278 return visited
279 }