Nie możesz wybrać więcej, niż 25 tematów Tematy muszą się zaczynać od litery lub cyfry, mogą zawierać myślniki ('-') i mogą mieć do 35 znaków.
 
 
 

280 wiersze
7.9 KiB

  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. package cmp
  5. import (
  6. "fmt"
  7. "reflect"
  8. "strconv"
  9. "strings"
  10. "unicode"
  11. "github.com/google/go-cmp/cmp/internal/flags"
  12. "github.com/google/go-cmp/cmp/internal/value"
  13. )
  14. type formatValueOptions struct {
  15. // AvoidStringer controls whether to avoid calling custom stringer
  16. // methods like error.Error or fmt.Stringer.String.
  17. AvoidStringer bool
  18. // ShallowPointers controls whether to avoid descending into pointers.
  19. // Useful when printing map keys, where pointer comparison is performed
  20. // on the pointer address rather than the pointed-at value.
  21. ShallowPointers bool
  22. // PrintAddresses controls whether to print the address of all pointers,
  23. // slice elements, and maps.
  24. PrintAddresses bool
  25. }
  26. // FormatType prints the type as if it were wrapping s.
  27. // This may return s as-is depending on the current type and TypeMode mode.
  28. func (opts formatOptions) FormatType(t reflect.Type, s textNode) textNode {
  29. // Check whether to emit the type or not.
  30. switch opts.TypeMode {
  31. case autoType:
  32. switch t.Kind() {
  33. case reflect.Struct, reflect.Slice, reflect.Array, reflect.Map:
  34. if s.Equal(textNil) {
  35. return s
  36. }
  37. default:
  38. return s
  39. }
  40. case elideType:
  41. return s
  42. }
  43. // Determine the type label, applying special handling for unnamed types.
  44. typeName := t.String()
  45. if t.Name() == "" {
  46. // According to Go grammar, certain type literals contain symbols that
  47. // do not strongly bind to the next lexicographical token (e.g., *T).
  48. switch t.Kind() {
  49. case reflect.Chan, reflect.Func, reflect.Ptr:
  50. typeName = "(" + typeName + ")"
  51. }
  52. typeName = strings.Replace(typeName, "struct {", "struct{", -1)
  53. typeName = strings.Replace(typeName, "interface {", "interface{", -1)
  54. }
  55. // Avoid wrap the value in parenthesis if unnecessary.
  56. if s, ok := s.(textWrap); ok {
  57. hasParens := strings.HasPrefix(s.Prefix, "(") && strings.HasSuffix(s.Suffix, ")")
  58. hasBraces := strings.HasPrefix(s.Prefix, "{") && strings.HasSuffix(s.Suffix, "}")
  59. if hasParens || hasBraces {
  60. return textWrap{typeName, s, ""}
  61. }
  62. }
  63. return textWrap{typeName + "(", s, ")"}
  64. }
  65. // FormatValue prints the reflect.Value, taking extra care to avoid descending
  66. // into pointers already in m. As pointers are visited, m is also updated.
  67. func (opts formatOptions) FormatValue(v reflect.Value, m visitedPointers) (out textNode) {
  68. if !v.IsValid() {
  69. return nil
  70. }
  71. t := v.Type()
  72. // Check whether there is an Error or String method to call.
  73. if !opts.AvoidStringer && v.CanInterface() {
  74. // Avoid calling Error or String methods on nil receivers since many
  75. // implementations crash when doing so.
  76. if (t.Kind() != reflect.Ptr && t.Kind() != reflect.Interface) || !v.IsNil() {
  77. switch v := v.Interface().(type) {
  78. case error:
  79. return textLine("e" + formatString(v.Error()))
  80. case fmt.Stringer:
  81. return textLine("s" + formatString(v.String()))
  82. }
  83. }
  84. }
  85. // Check whether to explicitly wrap the result with the type.
  86. var skipType bool
  87. defer func() {
  88. if !skipType {
  89. out = opts.FormatType(t, out)
  90. }
  91. }()
  92. var ptr string
  93. switch t.Kind() {
  94. case reflect.Bool:
  95. return textLine(fmt.Sprint(v.Bool()))
  96. case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
  97. return textLine(fmt.Sprint(v.Int()))
  98. case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
  99. // Unnamed uints are usually bytes or words, so use hexadecimal.
  100. if t.PkgPath() == "" || t.Kind() == reflect.Uintptr {
  101. return textLine(formatHex(v.Uint()))
  102. }
  103. return textLine(fmt.Sprint(v.Uint()))
  104. case reflect.Float32, reflect.Float64:
  105. return textLine(fmt.Sprint(v.Float()))
  106. case reflect.Complex64, reflect.Complex128:
  107. return textLine(fmt.Sprint(v.Complex()))
  108. case reflect.String:
  109. return textLine(formatString(v.String()))
  110. case reflect.UnsafePointer, reflect.Chan, reflect.Func:
  111. return textLine(formatPointer(v))
  112. case reflect.Struct:
  113. var list textList
  114. for i := 0; i < v.NumField(); i++ {
  115. vv := v.Field(i)
  116. if value.IsZero(vv) {
  117. continue // Elide fields with zero values
  118. }
  119. s := opts.WithTypeMode(autoType).FormatValue(vv, m)
  120. list = append(list, textRecord{Key: t.Field(i).Name, Value: s})
  121. }
  122. return textWrap{"{", list, "}"}
  123. case reflect.Slice:
  124. if v.IsNil() {
  125. return textNil
  126. }
  127. if opts.PrintAddresses {
  128. ptr = formatPointer(v)
  129. }
  130. fallthrough
  131. case reflect.Array:
  132. var list textList
  133. for i := 0; i < v.Len(); i++ {
  134. vi := v.Index(i)
  135. if vi.CanAddr() { // Check for cyclic elements
  136. p := vi.Addr()
  137. if m.Visit(p) {
  138. var out textNode
  139. out = textLine(formatPointer(p))
  140. out = opts.WithTypeMode(emitType).FormatType(p.Type(), out)
  141. out = textWrap{"*", out, ""}
  142. list = append(list, textRecord{Value: out})
  143. continue
  144. }
  145. }
  146. s := opts.WithTypeMode(elideType).FormatValue(vi, m)
  147. list = append(list, textRecord{Value: s})
  148. }
  149. return textWrap{ptr + "{", list, "}"}
  150. case reflect.Map:
  151. if v.IsNil() {
  152. return textNil
  153. }
  154. if m.Visit(v) {
  155. return textLine(formatPointer(v))
  156. }
  157. var list textList
  158. for _, k := range value.SortKeys(v.MapKeys()) {
  159. sk := formatMapKey(k)
  160. sv := opts.WithTypeMode(elideType).FormatValue(v.MapIndex(k), m)
  161. list = append(list, textRecord{Key: sk, Value: sv})
  162. }
  163. if opts.PrintAddresses {
  164. ptr = formatPointer(v)
  165. }
  166. return textWrap{ptr + "{", list, "}"}
  167. case reflect.Ptr:
  168. if v.IsNil() {
  169. return textNil
  170. }
  171. if m.Visit(v) || opts.ShallowPointers {
  172. return textLine(formatPointer(v))
  173. }
  174. if opts.PrintAddresses {
  175. ptr = formatPointer(v)
  176. }
  177. skipType = true // Let the underlying value print the type instead
  178. return textWrap{"&" + ptr, opts.FormatValue(v.Elem(), m), ""}
  179. case reflect.Interface:
  180. if v.IsNil() {
  181. return textNil
  182. }
  183. // Interfaces accept different concrete types,
  184. // so configure the underlying value to explicitly print the type.
  185. skipType = true // Print the concrete type instead
  186. return opts.WithTypeMode(emitType).FormatValue(v.Elem(), m)
  187. default:
  188. panic(fmt.Sprintf("%v kind not handled", v.Kind()))
  189. }
  190. }
  191. // formatMapKey formats v as if it were a map key.
  192. // The result is guaranteed to be a single line.
  193. func formatMapKey(v reflect.Value) string {
  194. var opts formatOptions
  195. opts.TypeMode = elideType
  196. opts.AvoidStringer = true
  197. opts.ShallowPointers = true
  198. s := opts.FormatValue(v, visitedPointers{}).String()
  199. return strings.TrimSpace(s)
  200. }
  201. // formatString prints s as a double-quoted or backtick-quoted string.
  202. func formatString(s string) string {
  203. // Use quoted string if it the same length as a raw string literal.
  204. // Otherwise, attempt to use the raw string form.
  205. qs := strconv.Quote(s)
  206. if len(qs) == 1+len(s)+1 {
  207. return qs
  208. }
  209. // Disallow newlines to ensure output is a single line.
  210. // Only allow printable runes for readability purposes.
  211. rawInvalid := func(r rune) bool {
  212. return r == '`' || r == '\n' || !(unicode.IsPrint(r) || r == '\t')
  213. }
  214. if strings.IndexFunc(s, rawInvalid) < 0 {
  215. return "`" + s + "`"
  216. }
  217. return qs
  218. }
  219. // formatHex prints u as a hexadecimal integer in Go notation.
  220. func formatHex(u uint64) string {
  221. var f string
  222. switch {
  223. case u <= 0xff:
  224. f = "0x%02x"
  225. case u <= 0xffff:
  226. f = "0x%04x"
  227. case u <= 0xffffff:
  228. f = "0x%06x"
  229. case u <= 0xffffffff:
  230. f = "0x%08x"
  231. case u <= 0xffffffffff:
  232. f = "0x%010x"
  233. case u <= 0xffffffffffff:
  234. f = "0x%012x"
  235. case u <= 0xffffffffffffff:
  236. f = "0x%014x"
  237. case u <= 0xffffffffffffffff:
  238. f = "0x%016x"
  239. }
  240. return fmt.Sprintf(f, u)
  241. }
  242. // formatPointer prints the address of the pointer.
  243. func formatPointer(v reflect.Value) string {
  244. p := v.Pointer()
  245. if flags.Deterministic {
  246. p = 0xdeadf00f // Only used for stable testing purposes
  247. }
  248. return fmt.Sprintf("⟪0x%x⟫", p)
  249. }
  250. type visitedPointers map[value.Pointer]struct{}
  251. // Visit inserts pointer v into the visited map and reports whether it had
  252. // already been visited before.
  253. func (m visitedPointers) Visit(v reflect.Value) bool {
  254. p := value.PointerOf(v)
  255. _, visited := m[p]
  256. m[p] = struct{}{}
  257. return visited
  258. }