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.md file. 4 5package cmp 6 7import ( 8 "fmt" 9 "reflect" 10 "strings" 11 "unicode" 12 "unicode/utf8" 13) 14 15type ( 16 // Path is a list of PathSteps describing the sequence of operations to get 17 // from some root type to the current position in the value tree. 18 // The first Path element is always an operation-less PathStep that exists 19 // simply to identify the initial type. 20 // 21 // When traversing structs with embedded structs, the embedded struct will 22 // always be accessed as a field before traversing the fields of the 23 // embedded struct themselves. That is, an exported field from the 24 // embedded struct will never be accessed directly from the parent struct. 25 Path []PathStep 26 27 // PathStep is a union-type for specific operations to traverse 28 // a value's tree structure. Users of this package never need to implement 29 // these types as values of this type will be returned by this package. 30 PathStep interface { 31 String() string 32 Type() reflect.Type // Resulting type after performing the path step 33 isPathStep() 34 } 35 36 // SliceIndex is an index operation on a slice or array at some index Key. 37 SliceIndex interface { 38 PathStep 39 Key() int // May return -1 if in a split state 40 41 // SplitKeys returns the indexes for indexing into slices in the 42 // x and y values, respectively. These indexes may differ due to the 43 // insertion or removal of an element in one of the slices, causing 44 // all of the indexes to be shifted. If an index is -1, then that 45 // indicates that the element does not exist in the associated slice. 46 // 47 // Key is guaranteed to return -1 if and only if the indexes returned 48 // by SplitKeys are not the same. SplitKeys will never return -1 for 49 // both indexes. 50 SplitKeys() (x int, y int) 51 52 isSliceIndex() 53 } 54 // MapIndex is an index operation on a map at some index Key. 55 MapIndex interface { 56 PathStep 57 Key() reflect.Value 58 isMapIndex() 59 } 60 // TypeAssertion represents a type assertion on an interface. 61 TypeAssertion interface { 62 PathStep 63 isTypeAssertion() 64 } 65 // StructField represents a struct field access on a field called Name. 66 StructField interface { 67 PathStep 68 Name() string 69 Index() int 70 isStructField() 71 } 72 // Indirect represents pointer indirection on the parent type. 73 Indirect interface { 74 PathStep 75 isIndirect() 76 } 77 // Transform is a transformation from the parent type to the current type. 78 Transform interface { 79 PathStep 80 Name() string 81 Func() reflect.Value 82 isTransform() 83 } 84) 85 86func (pa *Path) push(s PathStep) { 87 *pa = append(*pa, s) 88} 89 90func (pa *Path) pop() { 91 *pa = (*pa)[:len(*pa)-1] 92} 93 94// Last returns the last PathStep in the Path. 95// If the path is empty, this returns a non-nil PathStep that reports a nil Type. 96func (pa Path) Last() PathStep { 97 if len(pa) > 0 { 98 return pa[len(pa)-1] 99 } 100 return pathStep{} 101} 102 103// String returns the simplified path to a node. 104// The simplified path only contains struct field accesses. 105// 106// For example: 107// MyMap.MySlices.MyField 108func (pa Path) String() string { 109 var ss []string 110 for _, s := range pa { 111 if _, ok := s.(*structField); ok { 112 ss = append(ss, s.String()) 113 } 114 } 115 return strings.TrimPrefix(strings.Join(ss, ""), ".") 116} 117 118// GoString returns the path to a specific node using Go syntax. 119// 120// For example: 121// (*root.MyMap["key"].(*mypkg.MyStruct).MySlices)[2][3].MyField 122func (pa Path) GoString() string { 123 var ssPre, ssPost []string 124 var numIndirect int 125 for i, s := range pa { 126 var nextStep PathStep 127 if i+1 < len(pa) { 128 nextStep = pa[i+1] 129 } 130 switch s := s.(type) { 131 case *indirect: 132 numIndirect++ 133 pPre, pPost := "(", ")" 134 switch nextStep.(type) { 135 case *indirect: 136 continue // Next step is indirection, so let them batch up 137 case *structField: 138 numIndirect-- // Automatic indirection on struct fields 139 case nil: 140 pPre, pPost = "", "" // Last step; no need for parenthesis 141 } 142 if numIndirect > 0 { 143 ssPre = append(ssPre, pPre+strings.Repeat("*", numIndirect)) 144 ssPost = append(ssPost, pPost) 145 } 146 numIndirect = 0 147 continue 148 case *transform: 149 ssPre = append(ssPre, s.trans.name+"(") 150 ssPost = append(ssPost, ")") 151 continue 152 case *typeAssertion: 153 // Elide type assertions immediately following a transform to 154 // prevent overly verbose path printouts. 155 // Some transforms return interface{} because of Go's lack of 156 // generics, but typically take in and return the exact same 157 // concrete type. Other times, the transform creates an anonymous 158 // struct, which will be very verbose to print. 159 if _, ok := nextStep.(*transform); ok { 160 continue 161 } 162 } 163 ssPost = append(ssPost, s.String()) 164 } 165 for i, j := 0, len(ssPre)-1; i < j; i, j = i+1, j-1 { 166 ssPre[i], ssPre[j] = ssPre[j], ssPre[i] 167 } 168 return strings.Join(ssPre, "") + strings.Join(ssPost, "") 169} 170 171type ( 172 pathStep struct { 173 typ reflect.Type 174 } 175 176 sliceIndex struct { 177 pathStep 178 xkey, ykey int 179 } 180 mapIndex struct { 181 pathStep 182 key reflect.Value 183 } 184 typeAssertion struct { 185 pathStep 186 } 187 structField struct { 188 pathStep 189 name string 190 idx int 191 192 // These fields are used for forcibly accessing an unexported field. 193 // pvx, pvy, and field are only valid if unexported is true. 194 unexported bool 195 force bool // Forcibly allow visibility 196 pvx, pvy reflect.Value // Parent values 197 field reflect.StructField // Field information 198 } 199 indirect struct { 200 pathStep 201 } 202 transform struct { 203 pathStep 204 trans *transformer 205 } 206) 207 208func (ps pathStep) Type() reflect.Type { return ps.typ } 209func (ps pathStep) String() string { 210 if ps.typ == nil { 211 return "<nil>" 212 } 213 s := ps.typ.String() 214 if s == "" || strings.ContainsAny(s, "{}\n") { 215 return "root" // Type too simple or complex to print 216 } 217 return fmt.Sprintf("{%s}", s) 218} 219 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} 235func (mi mapIndex) String() string { return fmt.Sprintf("[%#v]", mi.key) } 236func (ta typeAssertion) String() string { return fmt.Sprintf(".(%v)", ta.typ) } 237func (sf structField) String() string { return fmt.Sprintf(".%s", sf.name) } 238func (in indirect) String() string { return "*" } 239func (tf transform) String() string { return fmt.Sprintf("%s()", tf.trans.name) } 240 241func (si sliceIndex) Key() int { 242 if si.xkey != si.ykey { 243 return -1 244 } 245 return si.xkey 246} 247func (si sliceIndex) SplitKeys() (x, y int) { return si.xkey, si.ykey } 248func (mi mapIndex) Key() reflect.Value { return mi.key } 249func (sf structField) Name() string { return sf.name } 250func (sf structField) Index() int { return sf.idx } 251func (tf transform) Name() string { return tf.trans.name } 252func (tf transform) Func() reflect.Value { return tf.trans.fnc } 253 254func (pathStep) isPathStep() {} 255func (sliceIndex) isSliceIndex() {} 256func (mapIndex) isMapIndex() {} 257func (typeAssertion) isTypeAssertion() {} 258func (structField) isStructField() {} 259func (indirect) isIndirect() {} 260func (transform) isTransform() {} 261 262var ( 263 _ SliceIndex = sliceIndex{} 264 _ MapIndex = mapIndex{} 265 _ TypeAssertion = typeAssertion{} 266 _ StructField = structField{} 267 _ Indirect = indirect{} 268 _ Transform = transform{} 269 270 _ PathStep = sliceIndex{} 271 _ PathStep = mapIndex{} 272 _ PathStep = typeAssertion{} 273 _ PathStep = structField{} 274 _ PathStep = indirect{} 275 _ PathStep = transform{} 276) 277 278// isExported reports whether the identifier is exported. 279func isExported(id string) bool { 280 r, _ := utf8.DecodeRuneInString(id) 281 return unicode.IsUpper(r) 282} 283 284// isValid reports whether the identifier is valid. 285// Empty and underscore-only strings are not valid. 286func isValid(id string) bool { 287 ok := id != "" && id != "_" 288 for j, c := range id { 289 ok = ok && (j > 0 || !unicode.IsDigit(c)) 290 ok = ok && (c == '_' || unicode.IsLetter(c) || unicode.IsDigit(c)) 291 } 292 return ok 293} 294