• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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