path.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390
  1. // Copyright 2017, 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 file.
  4. package cmp
  5. import (
  6. "fmt"
  7. "reflect"
  8. "strings"
  9. "unicode"
  10. "unicode/utf8"
  11. "github.com/google/go-cmp/cmp/internal/value"
  12. )
  13. // Path is a list of [PathStep] describing the sequence of operations to get
  14. // from some root type to the current position in the value tree.
  15. // The first Path element is always an operation-less [PathStep] that exists
  16. // simply to identify the initial type.
  17. //
  18. // When traversing structs with embedded structs, the embedded struct will
  19. // always be accessed as a field before traversing the fields of the
  20. // embedded struct themselves. That is, an exported field from the
  21. // embedded struct will never be accessed directly from the parent struct.
  22. type Path []PathStep
  23. // PathStep is a union-type for specific operations to traverse
  24. // a value's tree structure. Users of this package never need to implement
  25. // these types as values of this type will be returned by this package.
  26. //
  27. // Implementations of this interface:
  28. // - [StructField]
  29. // - [SliceIndex]
  30. // - [MapIndex]
  31. // - [Indirect]
  32. // - [TypeAssertion]
  33. // - [Transform]
  34. type PathStep interface {
  35. String() string
  36. // Type is the resulting type after performing the path step.
  37. Type() reflect.Type
  38. // Values is the resulting values after performing the path step.
  39. // The type of each valid value is guaranteed to be identical to Type.
  40. //
  41. // In some cases, one or both may be invalid or have restrictions:
  42. // - For StructField, both are not interface-able if the current field
  43. // is unexported and the struct type is not explicitly permitted by
  44. // an Exporter to traverse unexported fields.
  45. // - For SliceIndex, one may be invalid if an element is missing from
  46. // either the x or y slice.
  47. // - For MapIndex, one may be invalid if an entry is missing from
  48. // either the x or y map.
  49. //
  50. // The provided values must not be mutated.
  51. Values() (vx, vy reflect.Value)
  52. }
  53. var (
  54. _ PathStep = StructField{}
  55. _ PathStep = SliceIndex{}
  56. _ PathStep = MapIndex{}
  57. _ PathStep = Indirect{}
  58. _ PathStep = TypeAssertion{}
  59. _ PathStep = Transform{}
  60. )
  61. func (pa *Path) push(s PathStep) {
  62. *pa = append(*pa, s)
  63. }
  64. func (pa *Path) pop() {
  65. *pa = (*pa)[:len(*pa)-1]
  66. }
  67. // Last returns the last [PathStep] in the Path.
  68. // If the path is empty, this returns a non-nil [PathStep]
  69. // that reports a nil [PathStep.Type].
  70. func (pa Path) Last() PathStep {
  71. return pa.Index(-1)
  72. }
  73. // Index returns the ith step in the Path and supports negative indexing.
  74. // A negative index starts counting from the tail of the Path such that -1
  75. // refers to the last step, -2 refers to the second-to-last step, and so on.
  76. // If index is invalid, this returns a non-nil [PathStep]
  77. // that reports a nil [PathStep.Type].
  78. func (pa Path) Index(i int) PathStep {
  79. if i < 0 {
  80. i = len(pa) + i
  81. }
  82. if i < 0 || i >= len(pa) {
  83. return pathStep{}
  84. }
  85. return pa[i]
  86. }
  87. // String returns the simplified path to a node.
  88. // The simplified path only contains struct field accesses.
  89. //
  90. // For example:
  91. //
  92. // MyMap.MySlices.MyField
  93. func (pa Path) String() string {
  94. var ss []string
  95. for _, s := range pa {
  96. if _, ok := s.(StructField); ok {
  97. ss = append(ss, s.String())
  98. }
  99. }
  100. return strings.TrimPrefix(strings.Join(ss, ""), ".")
  101. }
  102. // GoString returns the path to a specific node using Go syntax.
  103. //
  104. // For example:
  105. //
  106. // (*root.MyMap["key"].(*mypkg.MyStruct).MySlices)[2][3].MyField
  107. func (pa Path) GoString() string {
  108. var ssPre, ssPost []string
  109. var numIndirect int
  110. for i, s := range pa {
  111. var nextStep PathStep
  112. if i+1 < len(pa) {
  113. nextStep = pa[i+1]
  114. }
  115. switch s := s.(type) {
  116. case Indirect:
  117. numIndirect++
  118. pPre, pPost := "(", ")"
  119. switch nextStep.(type) {
  120. case Indirect:
  121. continue // Next step is indirection, so let them batch up
  122. case StructField:
  123. numIndirect-- // Automatic indirection on struct fields
  124. case nil:
  125. pPre, pPost = "", "" // Last step; no need for parenthesis
  126. }
  127. if numIndirect > 0 {
  128. ssPre = append(ssPre, pPre+strings.Repeat("*", numIndirect))
  129. ssPost = append(ssPost, pPost)
  130. }
  131. numIndirect = 0
  132. continue
  133. case Transform:
  134. ssPre = append(ssPre, s.trans.name+"(")
  135. ssPost = append(ssPost, ")")
  136. continue
  137. }
  138. ssPost = append(ssPost, s.String())
  139. }
  140. for i, j := 0, len(ssPre)-1; i < j; i, j = i+1, j-1 {
  141. ssPre[i], ssPre[j] = ssPre[j], ssPre[i]
  142. }
  143. return strings.Join(ssPre, "") + strings.Join(ssPost, "")
  144. }
  145. type pathStep struct {
  146. typ reflect.Type
  147. vx, vy reflect.Value
  148. }
  149. func (ps pathStep) Type() reflect.Type { return ps.typ }
  150. func (ps pathStep) Values() (vx, vy reflect.Value) { return ps.vx, ps.vy }
  151. func (ps pathStep) String() string {
  152. if ps.typ == nil {
  153. return "<nil>"
  154. }
  155. s := value.TypeString(ps.typ, false)
  156. if s == "" || strings.ContainsAny(s, "{}\n") {
  157. return "root" // Type too simple or complex to print
  158. }
  159. return fmt.Sprintf("{%s}", s)
  160. }
  161. // StructField is a [PathStep] that represents a struct field access
  162. // on a field called [StructField.Name].
  163. type StructField struct{ *structField }
  164. type structField struct {
  165. pathStep
  166. name string
  167. idx int
  168. // These fields are used for forcibly accessing an unexported field.
  169. // pvx, pvy, and field are only valid if unexported is true.
  170. unexported bool
  171. mayForce bool // Forcibly allow visibility
  172. paddr bool // Was parent addressable?
  173. pvx, pvy reflect.Value // Parent values (always addressable)
  174. field reflect.StructField // Field information
  175. }
  176. func (sf StructField) Type() reflect.Type { return sf.typ }
  177. func (sf StructField) Values() (vx, vy reflect.Value) {
  178. if !sf.unexported {
  179. return sf.vx, sf.vy // CanInterface reports true
  180. }
  181. // Forcibly obtain read-write access to an unexported struct field.
  182. if sf.mayForce {
  183. vx = retrieveUnexportedField(sf.pvx, sf.field, sf.paddr)
  184. vy = retrieveUnexportedField(sf.pvy, sf.field, sf.paddr)
  185. return vx, vy // CanInterface reports true
  186. }
  187. return sf.vx, sf.vy // CanInterface reports false
  188. }
  189. func (sf StructField) String() string { return fmt.Sprintf(".%s", sf.name) }
  190. // Name is the field name.
  191. func (sf StructField) Name() string { return sf.name }
  192. // Index is the index of the field in the parent struct type.
  193. // See [reflect.Type.Field].
  194. func (sf StructField) Index() int { return sf.idx }
  195. // SliceIndex is a [PathStep] that represents an index operation on
  196. // a slice or array at some index [SliceIndex.Key].
  197. type SliceIndex struct{ *sliceIndex }
  198. type sliceIndex struct {
  199. pathStep
  200. xkey, ykey int
  201. isSlice bool // False for reflect.Array
  202. }
  203. func (si SliceIndex) Type() reflect.Type { return si.typ }
  204. func (si SliceIndex) Values() (vx, vy reflect.Value) { return si.vx, si.vy }
  205. func (si SliceIndex) String() string {
  206. switch {
  207. case si.xkey == si.ykey:
  208. return fmt.Sprintf("[%d]", si.xkey)
  209. case si.ykey == -1:
  210. // [5->?] means "I don't know where X[5] went"
  211. return fmt.Sprintf("[%d->?]", si.xkey)
  212. case si.xkey == -1:
  213. // [?->3] means "I don't know where Y[3] came from"
  214. return fmt.Sprintf("[?->%d]", si.ykey)
  215. default:
  216. // [5->3] means "X[5] moved to Y[3]"
  217. return fmt.Sprintf("[%d->%d]", si.xkey, si.ykey)
  218. }
  219. }
  220. // Key is the index key; it may return -1 if in a split state
  221. func (si SliceIndex) Key() int {
  222. if si.xkey != si.ykey {
  223. return -1
  224. }
  225. return si.xkey
  226. }
  227. // SplitKeys are the indexes for indexing into slices in the
  228. // x and y values, respectively. These indexes may differ due to the
  229. // insertion or removal of an element in one of the slices, causing
  230. // all of the indexes to be shifted. If an index is -1, then that
  231. // indicates that the element does not exist in the associated slice.
  232. //
  233. // [SliceIndex.Key] is guaranteed to return -1 if and only if the indexes
  234. // returned by SplitKeys are not the same. SplitKeys will never return -1 for
  235. // both indexes.
  236. func (si SliceIndex) SplitKeys() (ix, iy int) { return si.xkey, si.ykey }
  237. // MapIndex is a [PathStep] that represents an index operation on a map at some index Key.
  238. type MapIndex struct{ *mapIndex }
  239. type mapIndex struct {
  240. pathStep
  241. key reflect.Value
  242. }
  243. func (mi MapIndex) Type() reflect.Type { return mi.typ }
  244. func (mi MapIndex) Values() (vx, vy reflect.Value) { return mi.vx, mi.vy }
  245. func (mi MapIndex) String() string { return fmt.Sprintf("[%#v]", mi.key) }
  246. // Key is the value of the map key.
  247. func (mi MapIndex) Key() reflect.Value { return mi.key }
  248. // Indirect is a [PathStep] that represents pointer indirection on the parent type.
  249. type Indirect struct{ *indirect }
  250. type indirect struct {
  251. pathStep
  252. }
  253. func (in Indirect) Type() reflect.Type { return in.typ }
  254. func (in Indirect) Values() (vx, vy reflect.Value) { return in.vx, in.vy }
  255. func (in Indirect) String() string { return "*" }
  256. // TypeAssertion is a [PathStep] that represents a type assertion on an interface.
  257. type TypeAssertion struct{ *typeAssertion }
  258. type typeAssertion struct {
  259. pathStep
  260. }
  261. func (ta TypeAssertion) Type() reflect.Type { return ta.typ }
  262. func (ta TypeAssertion) Values() (vx, vy reflect.Value) { return ta.vx, ta.vy }
  263. func (ta TypeAssertion) String() string { return fmt.Sprintf(".(%v)", value.TypeString(ta.typ, false)) }
  264. // Transform is a [PathStep] that represents a transformation
  265. // from the parent type to the current type.
  266. type Transform struct{ *transform }
  267. type transform struct {
  268. pathStep
  269. trans *transformer
  270. }
  271. func (tf Transform) Type() reflect.Type { return tf.typ }
  272. func (tf Transform) Values() (vx, vy reflect.Value) { return tf.vx, tf.vy }
  273. func (tf Transform) String() string { return fmt.Sprintf("%s()", tf.trans.name) }
  274. // Name is the name of the [Transformer].
  275. func (tf Transform) Name() string { return tf.trans.name }
  276. // Func is the function pointer to the transformer function.
  277. func (tf Transform) Func() reflect.Value { return tf.trans.fnc }
  278. // Option returns the originally constructed [Transformer] option.
  279. // The == operator can be used to detect the exact option used.
  280. func (tf Transform) Option() Option { return tf.trans }
  281. // pointerPath represents a dual-stack of pointers encountered when
  282. // recursively traversing the x and y values. This data structure supports
  283. // detection of cycles and determining whether the cycles are equal.
  284. // In Go, cycles can occur via pointers, slices, and maps.
  285. //
  286. // The pointerPath uses a map to represent a stack; where descension into a
  287. // pointer pushes the address onto the stack, and ascension from a pointer
  288. // pops the address from the stack. Thus, when traversing into a pointer from
  289. // reflect.Ptr, reflect.Slice element, or reflect.Map, we can detect cycles
  290. // by checking whether the pointer has already been visited. The cycle detection
  291. // uses a separate stack for the x and y values.
  292. //
  293. // If a cycle is detected we need to determine whether the two pointers
  294. // should be considered equal. The definition of equality chosen by Equal
  295. // requires two graphs to have the same structure. To determine this, both the
  296. // x and y values must have a cycle where the previous pointers were also
  297. // encountered together as a pair.
  298. //
  299. // Semantically, this is equivalent to augmenting Indirect, SliceIndex, and
  300. // MapIndex with pointer information for the x and y values.
  301. // Suppose px and py are two pointers to compare, we then search the
  302. // Path for whether px was ever encountered in the Path history of x, and
  303. // similarly so with py. If either side has a cycle, the comparison is only
  304. // equal if both px and py have a cycle resulting from the same PathStep.
  305. //
  306. // Using a map as a stack is more performant as we can perform cycle detection
  307. // in O(1) instead of O(N) where N is len(Path).
  308. type pointerPath struct {
  309. // mx is keyed by x pointers, where the value is the associated y pointer.
  310. mx map[value.Pointer]value.Pointer
  311. // my is keyed by y pointers, where the value is the associated x pointer.
  312. my map[value.Pointer]value.Pointer
  313. }
  314. func (p *pointerPath) Init() {
  315. p.mx = make(map[value.Pointer]value.Pointer)
  316. p.my = make(map[value.Pointer]value.Pointer)
  317. }
  318. // Push indicates intent to descend into pointers vx and vy where
  319. // visited reports whether either has been seen before. If visited before,
  320. // equal reports whether both pointers were encountered together.
  321. // Pop must be called if and only if the pointers were never visited.
  322. //
  323. // The pointers vx and vy must be a reflect.Ptr, reflect.Slice, or reflect.Map
  324. // and be non-nil.
  325. func (p pointerPath) Push(vx, vy reflect.Value) (equal, visited bool) {
  326. px := value.PointerOf(vx)
  327. py := value.PointerOf(vy)
  328. _, ok1 := p.mx[px]
  329. _, ok2 := p.my[py]
  330. if ok1 || ok2 {
  331. equal = p.mx[px] == py && p.my[py] == px // Pointers paired together
  332. return equal, true
  333. }
  334. p.mx[px] = py
  335. p.my[py] = px
  336. return false, false
  337. }
  338. // Pop ascends from pointers vx and vy.
  339. func (p pointerPath) Pop(vx, vy reflect.Value) {
  340. delete(p.mx, value.PointerOf(vx))
  341. delete(p.my, value.PointerOf(vy))
  342. }
  343. // isExported reports whether the identifier is exported.
  344. func isExported(id string) bool {
  345. r, _ := utf8.DecodeRuneInString(id)
  346. return unicode.IsUpper(r)
  347. }