Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / github.com / google / go-cmp@v0.5.1 / 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         "bytes"
9         "fmt"
10         "reflect"
11         "strconv"
12         "strings"
13         "unicode"
14         "unicode/utf8"
15
16         "github.com/google/go-cmp/cmp/internal/value"
17 )
18
19 type formatValueOptions struct {
20         // AvoidStringer controls whether to avoid calling custom stringer
21         // methods like error.Error or fmt.Stringer.String.
22         AvoidStringer bool
23
24         // PrintAddresses controls whether to print the address of all pointers,
25         // slice elements, and maps.
26         PrintAddresses bool
27
28         // QualifiedNames controls whether FormatType uses the fully qualified name
29         // (including the full package path as opposed to just the package name).
30         QualifiedNames bool
31
32         // VerbosityLevel controls the amount of output to produce.
33         // A higher value produces more output. A value of zero or lower produces
34         // no output (represented using an ellipsis).
35         // If LimitVerbosity is false, then the level is treated as infinite.
36         VerbosityLevel int
37
38         // LimitVerbosity specifies that formatting should respect VerbosityLevel.
39         LimitVerbosity bool
40 }
41
42 // FormatType prints the type as if it were wrapping s.
43 // This may return s as-is depending on the current type and TypeMode mode.
44 func (opts formatOptions) FormatType(t reflect.Type, s textNode) textNode {
45         // Check whether to emit the type or not.
46         switch opts.TypeMode {
47         case autoType:
48                 switch t.Kind() {
49                 case reflect.Struct, reflect.Slice, reflect.Array, reflect.Map:
50                         if s.Equal(textNil) {
51                                 return s
52                         }
53                 default:
54                         return s
55                 }
56                 if opts.DiffMode == diffIdentical {
57                         return s // elide type for identical nodes
58                 }
59         case elideType:
60                 return s
61         }
62
63         // Determine the type label, applying special handling for unnamed types.
64         typeName := value.TypeString(t, opts.QualifiedNames)
65         if t.Name() == "" {
66                 // According to Go grammar, certain type literals contain symbols that
67                 // do not strongly bind to the next lexicographical token (e.g., *T).
68                 switch t.Kind() {
69                 case reflect.Chan, reflect.Func, reflect.Ptr:
70                         typeName = "(" + typeName + ")"
71                 }
72         }
73         return &textWrap{Prefix: typeName, Value: wrapParens(s)}
74 }
75
76 // wrapParens wraps s with a set of parenthesis, but avoids it if the
77 // wrapped node itself is already surrounded by a pair of parenthesis or braces.
78 // It handles unwrapping one level of pointer-reference nodes.
79 func wrapParens(s textNode) textNode {
80         var refNode *textWrap
81         if s2, ok := s.(*textWrap); ok {
82                 // Unwrap a single pointer reference node.
83                 switch s2.Metadata.(type) {
84                 case leafReference, trunkReference, trunkReferences:
85                         refNode = s2
86                         if s3, ok := refNode.Value.(*textWrap); ok {
87                                 s2 = s3
88                         }
89                 }
90
91                 // Already has delimiters that make parenthesis unnecessary.
92                 hasParens := strings.HasPrefix(s2.Prefix, "(") && strings.HasSuffix(s2.Suffix, ")")
93                 hasBraces := strings.HasPrefix(s2.Prefix, "{") && strings.HasSuffix(s2.Suffix, "}")
94                 if hasParens || hasBraces {
95                         return s
96                 }
97         }
98         if refNode != nil {
99                 refNode.Value = &textWrap{Prefix: "(", Value: refNode.Value, Suffix: ")"}
100                 return s
101         }
102         return &textWrap{Prefix: "(", Value: s, Suffix: ")"}
103 }
104
105 // FormatValue prints the reflect.Value, taking extra care to avoid descending
106 // into pointers already in ptrs. As pointers are visited, ptrs is also updated.
107 func (opts formatOptions) FormatValue(v reflect.Value, parentKind reflect.Kind, ptrs *pointerReferences) (out textNode) {
108         if !v.IsValid() {
109                 return nil
110         }
111         t := v.Type()
112
113         // Check slice element for cycles.
114         if parentKind == reflect.Slice {
115                 ptrRef, visited := ptrs.Push(v.Addr())
116                 if visited {
117                         return makeLeafReference(ptrRef, false)
118                 }
119                 defer ptrs.Pop()
120                 defer func() { out = wrapTrunkReference(ptrRef, false, out) }()
121         }
122
123         // Check whether there is an Error or String method to call.
124         if !opts.AvoidStringer && v.CanInterface() {
125                 // Avoid calling Error or String methods on nil receivers since many
126                 // implementations crash when doing so.
127                 if (t.Kind() != reflect.Ptr && t.Kind() != reflect.Interface) || !v.IsNil() {
128                         var prefix, strVal string
129                         func() {
130                                 // Swallow and ignore any panics from String or Error.
131                                 defer func() { recover() }()
132                                 switch v := v.Interface().(type) {
133                                 case error:
134                                         strVal = v.Error()
135                                         prefix = "e"
136                                 case fmt.Stringer:
137                                         strVal = v.String()
138                                         prefix = "s"
139                                 }
140                         }()
141                         if prefix != "" {
142                                 return opts.formatString(prefix, strVal)
143                         }
144                 }
145         }
146
147         // Check whether to explicitly wrap the result with the type.
148         var skipType bool
149         defer func() {
150                 if !skipType {
151                         out = opts.FormatType(t, out)
152                 }
153         }()
154
155         switch t.Kind() {
156         case reflect.Bool:
157                 return textLine(fmt.Sprint(v.Bool()))
158         case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
159                 return textLine(fmt.Sprint(v.Int()))
160         case reflect.Uint, reflect.Uint16, reflect.Uint32, reflect.Uint64:
161                 return textLine(fmt.Sprint(v.Uint()))
162         case reflect.Uint8:
163                 if parentKind == reflect.Slice || parentKind == reflect.Array {
164                         return textLine(formatHex(v.Uint()))
165                 }
166                 return textLine(fmt.Sprint(v.Uint()))
167         case reflect.Uintptr:
168                 return textLine(formatHex(v.Uint()))
169         case reflect.Float32, reflect.Float64:
170                 return textLine(fmt.Sprint(v.Float()))
171         case reflect.Complex64, reflect.Complex128:
172                 return textLine(fmt.Sprint(v.Complex()))
173         case reflect.String:
174                 return opts.formatString("", v.String())
175         case reflect.UnsafePointer, reflect.Chan, reflect.Func:
176                 return textLine(formatPointer(value.PointerOf(v), true))
177         case reflect.Struct:
178                 var list textList
179                 v := makeAddressable(v) // needed for retrieveUnexportedField
180                 maxLen := v.NumField()
181                 if opts.LimitVerbosity {
182                         maxLen = ((1 << opts.verbosity()) >> 1) << 2 // 0, 4, 8, 16, 32, etc...
183                         opts.VerbosityLevel--
184                 }
185                 for i := 0; i < v.NumField(); i++ {
186                         vv := v.Field(i)
187                         if value.IsZero(vv) {
188                                 continue // Elide fields with zero values
189                         }
190                         if len(list) == maxLen {
191                                 list.AppendEllipsis(diffStats{})
192                                 break
193                         }
194                         sf := t.Field(i)
195                         if supportExporters && !isExported(sf.Name) {
196                                 vv = retrieveUnexportedField(v, sf, true)
197                         }
198                         s := opts.WithTypeMode(autoType).FormatValue(vv, t.Kind(), ptrs)
199                         list = append(list, textRecord{Key: sf.Name, Value: s})
200                 }
201                 return &textWrap{Prefix: "{", Value: list, Suffix: "}"}
202         case reflect.Slice:
203                 if v.IsNil() {
204                         return textNil
205                 }
206
207                 // Check whether this is a []byte of text data.
208                 if t.Elem() == reflect.TypeOf(byte(0)) {
209                         b := v.Bytes()
210                         isPrintSpace := func(r rune) bool { return unicode.IsPrint(r) && unicode.IsSpace(r) }
211                         if len(b) > 0 && utf8.Valid(b) && len(bytes.TrimFunc(b, isPrintSpace)) == 0 {
212                                 out = opts.formatString("", string(b))
213                                 return opts.WithTypeMode(emitType).FormatType(t, out)
214                         }
215                 }
216
217                 fallthrough
218         case reflect.Array:
219                 maxLen := v.Len()
220                 if opts.LimitVerbosity {
221                         maxLen = ((1 << opts.verbosity()) >> 1) << 2 // 0, 4, 8, 16, 32, etc...
222                         opts.VerbosityLevel--
223                 }
224                 var list textList
225                 for i := 0; i < v.Len(); i++ {
226                         if len(list) == maxLen {
227                                 list.AppendEllipsis(diffStats{})
228                                 break
229                         }
230                         s := opts.WithTypeMode(elideType).FormatValue(v.Index(i), t.Kind(), ptrs)
231                         list = append(list, textRecord{Value: s})
232                 }
233
234                 out = &textWrap{Prefix: "{", Value: list, Suffix: "}"}
235                 if t.Kind() == reflect.Slice && opts.PrintAddresses {
236                         header := fmt.Sprintf("ptr:%v, len:%d, cap:%d", formatPointer(value.PointerOf(v), false), v.Len(), v.Cap())
237                         out = &textWrap{Prefix: pointerDelimPrefix + header + pointerDelimSuffix, Value: out}
238                 }
239                 return out
240         case reflect.Map:
241                 if v.IsNil() {
242                         return textNil
243                 }
244
245                 // Check pointer for cycles.
246                 ptrRef, visited := ptrs.Push(v)
247                 if visited {
248                         return makeLeafReference(ptrRef, opts.PrintAddresses)
249                 }
250                 defer ptrs.Pop()
251
252                 maxLen := v.Len()
253                 if opts.LimitVerbosity {
254                         maxLen = ((1 << opts.verbosity()) >> 1) << 2 // 0, 4, 8, 16, 32, etc...
255                         opts.VerbosityLevel--
256                 }
257                 var list textList
258                 for _, k := range value.SortKeys(v.MapKeys()) {
259                         if len(list) == maxLen {
260                                 list.AppendEllipsis(diffStats{})
261                                 break
262                         }
263                         sk := formatMapKey(k, false, ptrs)
264                         sv := opts.WithTypeMode(elideType).FormatValue(v.MapIndex(k), t.Kind(), ptrs)
265                         list = append(list, textRecord{Key: sk, Value: sv})
266                 }
267
268                 out = &textWrap{Prefix: "{", Value: list, Suffix: "}"}
269                 out = wrapTrunkReference(ptrRef, opts.PrintAddresses, out)
270                 return out
271         case reflect.Ptr:
272                 if v.IsNil() {
273                         return textNil
274                 }
275
276                 // Check pointer for cycles.
277                 ptrRef, visited := ptrs.Push(v)
278                 if visited {
279                         out = makeLeafReference(ptrRef, opts.PrintAddresses)
280                         return &textWrap{Prefix: "&", Value: out}
281                 }
282                 defer ptrs.Pop()
283
284                 skipType = true // Let the underlying value print the type instead
285                 out = opts.FormatValue(v.Elem(), t.Kind(), ptrs)
286                 out = wrapTrunkReference(ptrRef, opts.PrintAddresses, out)
287                 out = &textWrap{Prefix: "&", Value: out}
288                 return out
289         case reflect.Interface:
290                 if v.IsNil() {
291                         return textNil
292                 }
293                 // Interfaces accept different concrete types,
294                 // so configure the underlying value to explicitly print the type.
295                 skipType = true // Print the concrete type instead
296                 return opts.WithTypeMode(emitType).FormatValue(v.Elem(), t.Kind(), ptrs)
297         default:
298                 panic(fmt.Sprintf("%v kind not handled", v.Kind()))
299         }
300 }
301
302 func (opts formatOptions) formatString(prefix, s string) textNode {
303         maxLen := len(s)
304         maxLines := strings.Count(s, "\n") + 1
305         if opts.LimitVerbosity {
306                 maxLen = (1 << opts.verbosity()) << 5   // 32, 64, 128, 256, etc...
307                 maxLines = (1 << opts.verbosity()) << 2 //  4, 8, 16, 32, 64, etc...
308         }
309
310         // For multiline strings, use the triple-quote syntax,
311         // but only use it when printing removed or inserted nodes since
312         // we only want the extra verbosity for those cases.
313         lines := strings.Split(strings.TrimSuffix(s, "\n"), "\n")
314         isTripleQuoted := len(lines) >= 4 && (opts.DiffMode == '-' || opts.DiffMode == '+')
315         for i := 0; i < len(lines) && isTripleQuoted; i++ {
316                 lines[i] = strings.TrimPrefix(strings.TrimSuffix(lines[i], "\r"), "\r") // trim leading/trailing carriage returns for legacy Windows endline support
317                 isPrintable := func(r rune) bool {
318                         return unicode.IsPrint(r) || r == '\t' // specially treat tab as printable
319                 }
320                 line := lines[i]
321                 isTripleQuoted = !strings.HasPrefix(strings.TrimPrefix(line, prefix), `"""`) && !strings.HasPrefix(line, "...") && strings.TrimFunc(line, isPrintable) == "" && len(line) <= maxLen
322         }
323         if isTripleQuoted {
324                 var list textList
325                 list = append(list, textRecord{Diff: opts.DiffMode, Value: textLine(prefix + `"""`), ElideComma: true})
326                 for i, line := range lines {
327                         if numElided := len(lines) - i; i == maxLines-1 && numElided > 1 {
328                                 comment := commentString(fmt.Sprintf("%d elided lines", numElided))
329                                 list = append(list, textRecord{Diff: opts.DiffMode, Value: textEllipsis, ElideComma: true, Comment: comment})
330                                 break
331                         }
332                         list = append(list, textRecord{Diff: opts.DiffMode, Value: textLine(line), ElideComma: true})
333                 }
334                 list = append(list, textRecord{Diff: opts.DiffMode, Value: textLine(prefix + `"""`), ElideComma: true})
335                 return &textWrap{Prefix: "(", Value: list, Suffix: ")"}
336         }
337
338         // Format the string as a single-line quoted string.
339         if len(s) > maxLen+len(textEllipsis) {
340                 return textLine(prefix + formatString(s[:maxLen]) + string(textEllipsis))
341         }
342         return textLine(prefix + formatString(s))
343 }
344
345 // formatMapKey formats v as if it were a map key.
346 // The result is guaranteed to be a single line.
347 func formatMapKey(v reflect.Value, disambiguate bool, ptrs *pointerReferences) string {
348         var opts formatOptions
349         opts.DiffMode = diffIdentical
350         opts.TypeMode = elideType
351         opts.PrintAddresses = disambiguate
352         opts.AvoidStringer = disambiguate
353         opts.QualifiedNames = disambiguate
354         s := opts.FormatValue(v, reflect.Map, ptrs).String()
355         return strings.TrimSpace(s)
356 }
357
358 // formatString prints s as a double-quoted or backtick-quoted string.
359 func formatString(s string) string {
360         // Use quoted string if it the same length as a raw string literal.
361         // Otherwise, attempt to use the raw string form.
362         qs := strconv.Quote(s)
363         if len(qs) == 1+len(s)+1 {
364                 return qs
365         }
366
367         // Disallow newlines to ensure output is a single line.
368         // Only allow printable runes for readability purposes.
369         rawInvalid := func(r rune) bool {
370                 return r == '`' || r == '\n' || !(unicode.IsPrint(r) || r == '\t')
371         }
372         if utf8.ValidString(s) && strings.IndexFunc(s, rawInvalid) < 0 {
373                 return "`" + s + "`"
374         }
375         return qs
376 }
377
378 // formatHex prints u as a hexadecimal integer in Go notation.
379 func formatHex(u uint64) string {
380         var f string
381         switch {
382         case u <= 0xff:
383                 f = "0x%02x"
384         case u <= 0xffff:
385                 f = "0x%04x"
386         case u <= 0xffffff:
387                 f = "0x%06x"
388         case u <= 0xffffffff:
389                 f = "0x%08x"
390         case u <= 0xffffffffff:
391                 f = "0x%010x"
392         case u <= 0xffffffffffff:
393                 f = "0x%012x"
394         case u <= 0xffffffffffffff:
395                 f = "0x%014x"
396         case u <= 0xffffffffffffffff:
397                 f = "0x%016x"
398         }
399         return fmt.Sprintf(f, u)
400 }