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