xref: /aosp_15_r20/external/go-cmp/cmp/compare.go (revision 88d15eac089d7f20c739ff1001d56b91872b21a1)
1*88d15eacSSasha Smundak// Copyright 2017, The Go Authors. All rights reserved.
2*88d15eacSSasha Smundak// Use of this source code is governed by a BSD-style
3*88d15eacSSasha Smundak// license that can be found in the LICENSE file.
4*88d15eacSSasha Smundak
5*88d15eacSSasha Smundak// Package cmp determines equality of values.
6*88d15eacSSasha Smundak//
7*88d15eacSSasha Smundak// This package is intended to be a more powerful and safer alternative to
8*88d15eacSSasha Smundak// reflect.DeepEqual for comparing whether two values are semantically equal.
9*88d15eacSSasha Smundak// It is intended to only be used in tests, as performance is not a goal and
10*88d15eacSSasha Smundak// it may panic if it cannot compare the values. Its propensity towards
11*88d15eacSSasha Smundak// panicking means that its unsuitable for production environments where a
12*88d15eacSSasha Smundak// spurious panic may be fatal.
13*88d15eacSSasha Smundak//
14*88d15eacSSasha Smundak// The primary features of cmp are:
15*88d15eacSSasha Smundak//
16*88d15eacSSasha Smundak//   - When the default behavior of equality does not suit the test's needs,
17*88d15eacSSasha Smundak//     custom equality functions can override the equality operation.
18*88d15eacSSasha Smundak//     For example, an equality function may report floats as equal so long as
19*88d15eacSSasha Smundak//     they are within some tolerance of each other.
20*88d15eacSSasha Smundak//
21*88d15eacSSasha Smundak//   - Types with an Equal method may use that method to determine equality.
22*88d15eacSSasha Smundak//     This allows package authors to determine the equality operation
23*88d15eacSSasha Smundak//     for the types that they define.
24*88d15eacSSasha Smundak//
25*88d15eacSSasha Smundak//   - If no custom equality functions are used and no Equal method is defined,
26*88d15eacSSasha Smundak//     equality is determined by recursively comparing the primitive kinds on
27*88d15eacSSasha Smundak//     both values, much like reflect.DeepEqual. Unlike reflect.DeepEqual,
28*88d15eacSSasha Smundak//     unexported fields are not compared by default; they result in panics
29*88d15eacSSasha Smundak//     unless suppressed by using an Ignore option (see cmpopts.IgnoreUnexported)
30*88d15eacSSasha Smundak//     or explicitly compared using the Exporter option.
31*88d15eacSSasha Smundakpackage cmp
32*88d15eacSSasha Smundak
33*88d15eacSSasha Smundakimport (
34*88d15eacSSasha Smundak	"fmt"
35*88d15eacSSasha Smundak	"reflect"
36*88d15eacSSasha Smundak	"strings"
37*88d15eacSSasha Smundak
38*88d15eacSSasha Smundak	"github.com/google/go-cmp/cmp/internal/diff"
39*88d15eacSSasha Smundak	"github.com/google/go-cmp/cmp/internal/function"
40*88d15eacSSasha Smundak	"github.com/google/go-cmp/cmp/internal/value"
41*88d15eacSSasha Smundak)
42*88d15eacSSasha Smundak
43*88d15eacSSasha Smundak// TODO(≥go1.18): Use any instead of interface{}.
44*88d15eacSSasha Smundak
45*88d15eacSSasha Smundak// Equal reports whether x and y are equal by recursively applying the
46*88d15eacSSasha Smundak// following rules in the given order to x and y and all of their sub-values:
47*88d15eacSSasha Smundak//
48*88d15eacSSasha Smundak//   - Let S be the set of all Ignore, Transformer, and Comparer options that
49*88d15eacSSasha Smundak//     remain after applying all path filters, value filters, and type filters.
50*88d15eacSSasha Smundak//     If at least one Ignore exists in S, then the comparison is ignored.
51*88d15eacSSasha Smundak//     If the number of Transformer and Comparer options in S is non-zero,
52*88d15eacSSasha Smundak//     then Equal panics because it is ambiguous which option to use.
53*88d15eacSSasha Smundak//     If S contains a single Transformer, then use that to transform
54*88d15eacSSasha Smundak//     the current values and recursively call Equal on the output values.
55*88d15eacSSasha Smundak//     If S contains a single Comparer, then use that to compare the current values.
56*88d15eacSSasha Smundak//     Otherwise, evaluation proceeds to the next rule.
57*88d15eacSSasha Smundak//
58*88d15eacSSasha Smundak//   - If the values have an Equal method of the form "(T) Equal(T) bool" or
59*88d15eacSSasha Smundak//     "(T) Equal(I) bool" where T is assignable to I, then use the result of
60*88d15eacSSasha Smundak//     x.Equal(y) even if x or y is nil. Otherwise, no such method exists and
61*88d15eacSSasha Smundak//     evaluation proceeds to the next rule.
62*88d15eacSSasha Smundak//
63*88d15eacSSasha Smundak//   - Lastly, try to compare x and y based on their basic kinds.
64*88d15eacSSasha Smundak//     Simple kinds like booleans, integers, floats, complex numbers, strings,
65*88d15eacSSasha Smundak//     and channels are compared using the equivalent of the == operator in Go.
66*88d15eacSSasha Smundak//     Functions are only equal if they are both nil, otherwise they are unequal.
67*88d15eacSSasha Smundak//
68*88d15eacSSasha Smundak// Structs are equal if recursively calling Equal on all fields report equal.
69*88d15eacSSasha Smundak// If a struct contains unexported fields, Equal panics unless an Ignore option
70*88d15eacSSasha Smundak// (e.g., cmpopts.IgnoreUnexported) ignores that field or the Exporter option
71*88d15eacSSasha Smundak// explicitly permits comparing the unexported field.
72*88d15eacSSasha Smundak//
73*88d15eacSSasha Smundak// Slices are equal if they are both nil or both non-nil, where recursively
74*88d15eacSSasha Smundak// calling Equal on all non-ignored slice or array elements report equal.
75*88d15eacSSasha Smundak// Empty non-nil slices and nil slices are not equal; to equate empty slices,
76*88d15eacSSasha Smundak// consider using cmpopts.EquateEmpty.
77*88d15eacSSasha Smundak//
78*88d15eacSSasha Smundak// Maps are equal if they are both nil or both non-nil, where recursively
79*88d15eacSSasha Smundak// calling Equal on all non-ignored map entries report equal.
80*88d15eacSSasha Smundak// Map keys are equal according to the == operator.
81*88d15eacSSasha Smundak// To use custom comparisons for map keys, consider using cmpopts.SortMaps.
82*88d15eacSSasha Smundak// Empty non-nil maps and nil maps are not equal; to equate empty maps,
83*88d15eacSSasha Smundak// consider using cmpopts.EquateEmpty.
84*88d15eacSSasha Smundak//
85*88d15eacSSasha Smundak// Pointers and interfaces are equal if they are both nil or both non-nil,
86*88d15eacSSasha Smundak// where they have the same underlying concrete type and recursively
87*88d15eacSSasha Smundak// calling Equal on the underlying values reports equal.
88*88d15eacSSasha Smundak//
89*88d15eacSSasha Smundak// Before recursing into a pointer, slice element, or map, the current path
90*88d15eacSSasha Smundak// is checked to detect whether the address has already been visited.
91*88d15eacSSasha Smundak// If there is a cycle, then the pointed at values are considered equal
92*88d15eacSSasha Smundak// only if both addresses were previously visited in the same path step.
93*88d15eacSSasha Smundakfunc Equal(x, y interface{}, opts ...Option) bool {
94*88d15eacSSasha Smundak	s := newState(opts)
95*88d15eacSSasha Smundak	s.compareAny(rootStep(x, y))
96*88d15eacSSasha Smundak	return s.result.Equal()
97*88d15eacSSasha Smundak}
98*88d15eacSSasha Smundak
99*88d15eacSSasha Smundak// Diff returns a human-readable report of the differences between two values:
100*88d15eacSSasha Smundak// y - x. It returns an empty string if and only if Equal returns true for the
101*88d15eacSSasha Smundak// same input values and options.
102*88d15eacSSasha Smundak//
103*88d15eacSSasha Smundak// The output is displayed as a literal in pseudo-Go syntax.
104*88d15eacSSasha Smundak// At the start of each line, a "-" prefix indicates an element removed from x,
105*88d15eacSSasha Smundak// a "+" prefix to indicates an element added from y, and the lack of a prefix
106*88d15eacSSasha Smundak// indicates an element common to both x and y. If possible, the output
107*88d15eacSSasha Smundak// uses fmt.Stringer.String or error.Error methods to produce more humanly
108*88d15eacSSasha Smundak// readable outputs. In such cases, the string is prefixed with either an
109*88d15eacSSasha Smundak// 's' or 'e' character, respectively, to indicate that the method was called.
110*88d15eacSSasha Smundak//
111*88d15eacSSasha Smundak// Do not depend on this output being stable. If you need the ability to
112*88d15eacSSasha Smundak// programmatically interpret the difference, consider using a custom Reporter.
113*88d15eacSSasha Smundakfunc Diff(x, y interface{}, opts ...Option) string {
114*88d15eacSSasha Smundak	s := newState(opts)
115*88d15eacSSasha Smundak
116*88d15eacSSasha Smundak	// Optimization: If there are no other reporters, we can optimize for the
117*88d15eacSSasha Smundak	// common case where the result is equal (and thus no reported difference).
118*88d15eacSSasha Smundak	// This avoids the expensive construction of a difference tree.
119*88d15eacSSasha Smundak	if len(s.reporters) == 0 {
120*88d15eacSSasha Smundak		s.compareAny(rootStep(x, y))
121*88d15eacSSasha Smundak		if s.result.Equal() {
122*88d15eacSSasha Smundak			return ""
123*88d15eacSSasha Smundak		}
124*88d15eacSSasha Smundak		s.result = diff.Result{} // Reset results
125*88d15eacSSasha Smundak	}
126*88d15eacSSasha Smundak
127*88d15eacSSasha Smundak	r := new(defaultReporter)
128*88d15eacSSasha Smundak	s.reporters = append(s.reporters, reporter{r})
129*88d15eacSSasha Smundak	s.compareAny(rootStep(x, y))
130*88d15eacSSasha Smundak	d := r.String()
131*88d15eacSSasha Smundak	if (d == "") != s.result.Equal() {
132*88d15eacSSasha Smundak		panic("inconsistent difference and equality results")
133*88d15eacSSasha Smundak	}
134*88d15eacSSasha Smundak	return d
135*88d15eacSSasha Smundak}
136*88d15eacSSasha Smundak
137*88d15eacSSasha Smundak// rootStep constructs the first path step. If x and y have differing types,
138*88d15eacSSasha Smundak// then they are stored within an empty interface type.
139*88d15eacSSasha Smundakfunc rootStep(x, y interface{}) PathStep {
140*88d15eacSSasha Smundak	vx := reflect.ValueOf(x)
141*88d15eacSSasha Smundak	vy := reflect.ValueOf(y)
142*88d15eacSSasha Smundak
143*88d15eacSSasha Smundak	// If the inputs are different types, auto-wrap them in an empty interface
144*88d15eacSSasha Smundak	// so that they have the same parent type.
145*88d15eacSSasha Smundak	var t reflect.Type
146*88d15eacSSasha Smundak	if !vx.IsValid() || !vy.IsValid() || vx.Type() != vy.Type() {
147*88d15eacSSasha Smundak		t = anyType
148*88d15eacSSasha Smundak		if vx.IsValid() {
149*88d15eacSSasha Smundak			vvx := reflect.New(t).Elem()
150*88d15eacSSasha Smundak			vvx.Set(vx)
151*88d15eacSSasha Smundak			vx = vvx
152*88d15eacSSasha Smundak		}
153*88d15eacSSasha Smundak		if vy.IsValid() {
154*88d15eacSSasha Smundak			vvy := reflect.New(t).Elem()
155*88d15eacSSasha Smundak			vvy.Set(vy)
156*88d15eacSSasha Smundak			vy = vvy
157*88d15eacSSasha Smundak		}
158*88d15eacSSasha Smundak	} else {
159*88d15eacSSasha Smundak		t = vx.Type()
160*88d15eacSSasha Smundak	}
161*88d15eacSSasha Smundak
162*88d15eacSSasha Smundak	return &pathStep{t, vx, vy}
163*88d15eacSSasha Smundak}
164*88d15eacSSasha Smundak
165*88d15eacSSasha Smundaktype state struct {
166*88d15eacSSasha Smundak	// These fields represent the "comparison state".
167*88d15eacSSasha Smundak	// Calling statelessCompare must not result in observable changes to these.
168*88d15eacSSasha Smundak	result    diff.Result // The current result of comparison
169*88d15eacSSasha Smundak	curPath   Path        // The current path in the value tree
170*88d15eacSSasha Smundak	curPtrs   pointerPath // The current set of visited pointers
171*88d15eacSSasha Smundak	reporters []reporter  // Optional reporters
172*88d15eacSSasha Smundak
173*88d15eacSSasha Smundak	// recChecker checks for infinite cycles applying the same set of
174*88d15eacSSasha Smundak	// transformers upon the output of itself.
175*88d15eacSSasha Smundak	recChecker recChecker
176*88d15eacSSasha Smundak
177*88d15eacSSasha Smundak	// dynChecker triggers pseudo-random checks for option correctness.
178*88d15eacSSasha Smundak	// It is safe for statelessCompare to mutate this value.
179*88d15eacSSasha Smundak	dynChecker dynChecker
180*88d15eacSSasha Smundak
181*88d15eacSSasha Smundak	// These fields, once set by processOption, will not change.
182*88d15eacSSasha Smundak	exporters []exporter // List of exporters for structs with unexported fields
183*88d15eacSSasha Smundak	opts      Options    // List of all fundamental and filter options
184*88d15eacSSasha Smundak}
185*88d15eacSSasha Smundak
186*88d15eacSSasha Smundakfunc newState(opts []Option) *state {
187*88d15eacSSasha Smundak	// Always ensure a validator option exists to validate the inputs.
188*88d15eacSSasha Smundak	s := &state{opts: Options{validator{}}}
189*88d15eacSSasha Smundak	s.curPtrs.Init()
190*88d15eacSSasha Smundak	s.processOption(Options(opts))
191*88d15eacSSasha Smundak	return s
192*88d15eacSSasha Smundak}
193*88d15eacSSasha Smundak
194*88d15eacSSasha Smundakfunc (s *state) processOption(opt Option) {
195*88d15eacSSasha Smundak	switch opt := opt.(type) {
196*88d15eacSSasha Smundak	case nil:
197*88d15eacSSasha Smundak	case Options:
198*88d15eacSSasha Smundak		for _, o := range opt {
199*88d15eacSSasha Smundak			s.processOption(o)
200*88d15eacSSasha Smundak		}
201*88d15eacSSasha Smundak	case coreOption:
202*88d15eacSSasha Smundak		type filtered interface {
203*88d15eacSSasha Smundak			isFiltered() bool
204*88d15eacSSasha Smundak		}
205*88d15eacSSasha Smundak		if fopt, ok := opt.(filtered); ok && !fopt.isFiltered() {
206*88d15eacSSasha Smundak			panic(fmt.Sprintf("cannot use an unfiltered option: %v", opt))
207*88d15eacSSasha Smundak		}
208*88d15eacSSasha Smundak		s.opts = append(s.opts, opt)
209*88d15eacSSasha Smundak	case exporter:
210*88d15eacSSasha Smundak		s.exporters = append(s.exporters, opt)
211*88d15eacSSasha Smundak	case reporter:
212*88d15eacSSasha Smundak		s.reporters = append(s.reporters, opt)
213*88d15eacSSasha Smundak	default:
214*88d15eacSSasha Smundak		panic(fmt.Sprintf("unknown option %T", opt))
215*88d15eacSSasha Smundak	}
216*88d15eacSSasha Smundak}
217*88d15eacSSasha Smundak
218*88d15eacSSasha Smundak// statelessCompare compares two values and returns the result.
219*88d15eacSSasha Smundak// This function is stateless in that it does not alter the current result,
220*88d15eacSSasha Smundak// or output to any registered reporters.
221*88d15eacSSasha Smundakfunc (s *state) statelessCompare(step PathStep) diff.Result {
222*88d15eacSSasha Smundak	// We do not save and restore curPath and curPtrs because all of the
223*88d15eacSSasha Smundak	// compareX methods should properly push and pop from them.
224*88d15eacSSasha Smundak	// It is an implementation bug if the contents of the paths differ from
225*88d15eacSSasha Smundak	// when calling this function to when returning from it.
226*88d15eacSSasha Smundak
227*88d15eacSSasha Smundak	oldResult, oldReporters := s.result, s.reporters
228*88d15eacSSasha Smundak	s.result = diff.Result{} // Reset result
229*88d15eacSSasha Smundak	s.reporters = nil        // Remove reporters to avoid spurious printouts
230*88d15eacSSasha Smundak	s.compareAny(step)
231*88d15eacSSasha Smundak	res := s.result
232*88d15eacSSasha Smundak	s.result, s.reporters = oldResult, oldReporters
233*88d15eacSSasha Smundak	return res
234*88d15eacSSasha Smundak}
235*88d15eacSSasha Smundak
236*88d15eacSSasha Smundakfunc (s *state) compareAny(step PathStep) {
237*88d15eacSSasha Smundak	// Update the path stack.
238*88d15eacSSasha Smundak	s.curPath.push(step)
239*88d15eacSSasha Smundak	defer s.curPath.pop()
240*88d15eacSSasha Smundak	for _, r := range s.reporters {
241*88d15eacSSasha Smundak		r.PushStep(step)
242*88d15eacSSasha Smundak		defer r.PopStep()
243*88d15eacSSasha Smundak	}
244*88d15eacSSasha Smundak	s.recChecker.Check(s.curPath)
245*88d15eacSSasha Smundak
246*88d15eacSSasha Smundak	// Cycle-detection for slice elements (see NOTE in compareSlice).
247*88d15eacSSasha Smundak	t := step.Type()
248*88d15eacSSasha Smundak	vx, vy := step.Values()
249*88d15eacSSasha Smundak	if si, ok := step.(SliceIndex); ok && si.isSlice && vx.IsValid() && vy.IsValid() {
250*88d15eacSSasha Smundak		px, py := vx.Addr(), vy.Addr()
251*88d15eacSSasha Smundak		if eq, visited := s.curPtrs.Push(px, py); visited {
252*88d15eacSSasha Smundak			s.report(eq, reportByCycle)
253*88d15eacSSasha Smundak			return
254*88d15eacSSasha Smundak		}
255*88d15eacSSasha Smundak		defer s.curPtrs.Pop(px, py)
256*88d15eacSSasha Smundak	}
257*88d15eacSSasha Smundak
258*88d15eacSSasha Smundak	// Rule 1: Check whether an option applies on this node in the value tree.
259*88d15eacSSasha Smundak	if s.tryOptions(t, vx, vy) {
260*88d15eacSSasha Smundak		return
261*88d15eacSSasha Smundak	}
262*88d15eacSSasha Smundak
263*88d15eacSSasha Smundak	// Rule 2: Check whether the type has a valid Equal method.
264*88d15eacSSasha Smundak	if s.tryMethod(t, vx, vy) {
265*88d15eacSSasha Smundak		return
266*88d15eacSSasha Smundak	}
267*88d15eacSSasha Smundak
268*88d15eacSSasha Smundak	// Rule 3: Compare based on the underlying kind.
269*88d15eacSSasha Smundak	switch t.Kind() {
270*88d15eacSSasha Smundak	case reflect.Bool:
271*88d15eacSSasha Smundak		s.report(vx.Bool() == vy.Bool(), 0)
272*88d15eacSSasha Smundak	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
273*88d15eacSSasha Smundak		s.report(vx.Int() == vy.Int(), 0)
274*88d15eacSSasha Smundak	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
275*88d15eacSSasha Smundak		s.report(vx.Uint() == vy.Uint(), 0)
276*88d15eacSSasha Smundak	case reflect.Float32, reflect.Float64:
277*88d15eacSSasha Smundak		s.report(vx.Float() == vy.Float(), 0)
278*88d15eacSSasha Smundak	case reflect.Complex64, reflect.Complex128:
279*88d15eacSSasha Smundak		s.report(vx.Complex() == vy.Complex(), 0)
280*88d15eacSSasha Smundak	case reflect.String:
281*88d15eacSSasha Smundak		s.report(vx.String() == vy.String(), 0)
282*88d15eacSSasha Smundak	case reflect.Chan, reflect.UnsafePointer:
283*88d15eacSSasha Smundak		s.report(vx.Pointer() == vy.Pointer(), 0)
284*88d15eacSSasha Smundak	case reflect.Func:
285*88d15eacSSasha Smundak		s.report(vx.IsNil() && vy.IsNil(), 0)
286*88d15eacSSasha Smundak	case reflect.Struct:
287*88d15eacSSasha Smundak		s.compareStruct(t, vx, vy)
288*88d15eacSSasha Smundak	case reflect.Slice, reflect.Array:
289*88d15eacSSasha Smundak		s.compareSlice(t, vx, vy)
290*88d15eacSSasha Smundak	case reflect.Map:
291*88d15eacSSasha Smundak		s.compareMap(t, vx, vy)
292*88d15eacSSasha Smundak	case reflect.Ptr:
293*88d15eacSSasha Smundak		s.comparePtr(t, vx, vy)
294*88d15eacSSasha Smundak	case reflect.Interface:
295*88d15eacSSasha Smundak		s.compareInterface(t, vx, vy)
296*88d15eacSSasha Smundak	default:
297*88d15eacSSasha Smundak		panic(fmt.Sprintf("%v kind not handled", t.Kind()))
298*88d15eacSSasha Smundak	}
299*88d15eacSSasha Smundak}
300*88d15eacSSasha Smundak
301*88d15eacSSasha Smundakfunc (s *state) tryOptions(t reflect.Type, vx, vy reflect.Value) bool {
302*88d15eacSSasha Smundak	// Evaluate all filters and apply the remaining options.
303*88d15eacSSasha Smundak	if opt := s.opts.filter(s, t, vx, vy); opt != nil {
304*88d15eacSSasha Smundak		opt.apply(s, vx, vy)
305*88d15eacSSasha Smundak		return true
306*88d15eacSSasha Smundak	}
307*88d15eacSSasha Smundak	return false
308*88d15eacSSasha Smundak}
309*88d15eacSSasha Smundak
310*88d15eacSSasha Smundakfunc (s *state) tryMethod(t reflect.Type, vx, vy reflect.Value) bool {
311*88d15eacSSasha Smundak	// Check if this type even has an Equal method.
312*88d15eacSSasha Smundak	m, ok := t.MethodByName("Equal")
313*88d15eacSSasha Smundak	if !ok || !function.IsType(m.Type, function.EqualAssignable) {
314*88d15eacSSasha Smundak		return false
315*88d15eacSSasha Smundak	}
316*88d15eacSSasha Smundak
317*88d15eacSSasha Smundak	eq := s.callTTBFunc(m.Func, vx, vy)
318*88d15eacSSasha Smundak	s.report(eq, reportByMethod)
319*88d15eacSSasha Smundak	return true
320*88d15eacSSasha Smundak}
321*88d15eacSSasha Smundak
322*88d15eacSSasha Smundakfunc (s *state) callTRFunc(f, v reflect.Value, step Transform) reflect.Value {
323*88d15eacSSasha Smundak	if !s.dynChecker.Next() {
324*88d15eacSSasha Smundak		return f.Call([]reflect.Value{v})[0]
325*88d15eacSSasha Smundak	}
326*88d15eacSSasha Smundak
327*88d15eacSSasha Smundak	// Run the function twice and ensure that we get the same results back.
328*88d15eacSSasha Smundak	// We run in goroutines so that the race detector (if enabled) can detect
329*88d15eacSSasha Smundak	// unsafe mutations to the input.
330*88d15eacSSasha Smundak	c := make(chan reflect.Value)
331*88d15eacSSasha Smundak	go detectRaces(c, f, v)
332*88d15eacSSasha Smundak	got := <-c
333*88d15eacSSasha Smundak	want := f.Call([]reflect.Value{v})[0]
334*88d15eacSSasha Smundak	if step.vx, step.vy = got, want; !s.statelessCompare(step).Equal() {
335*88d15eacSSasha Smundak		// To avoid false-positives with non-reflexive equality operations,
336*88d15eacSSasha Smundak		// we sanity check whether a value is equal to itself.
337*88d15eacSSasha Smundak		if step.vx, step.vy = want, want; !s.statelessCompare(step).Equal() {
338*88d15eacSSasha Smundak			return want
339*88d15eacSSasha Smundak		}
340*88d15eacSSasha Smundak		panic(fmt.Sprintf("non-deterministic function detected: %s", function.NameOf(f)))
341*88d15eacSSasha Smundak	}
342*88d15eacSSasha Smundak	return want
343*88d15eacSSasha Smundak}
344*88d15eacSSasha Smundak
345*88d15eacSSasha Smundakfunc (s *state) callTTBFunc(f, x, y reflect.Value) bool {
346*88d15eacSSasha Smundak	if !s.dynChecker.Next() {
347*88d15eacSSasha Smundak		return f.Call([]reflect.Value{x, y})[0].Bool()
348*88d15eacSSasha Smundak	}
349*88d15eacSSasha Smundak
350*88d15eacSSasha Smundak	// Swapping the input arguments is sufficient to check that
351*88d15eacSSasha Smundak	// f is symmetric and deterministic.
352*88d15eacSSasha Smundak	// We run in goroutines so that the race detector (if enabled) can detect
353*88d15eacSSasha Smundak	// unsafe mutations to the input.
354*88d15eacSSasha Smundak	c := make(chan reflect.Value)
355*88d15eacSSasha Smundak	go detectRaces(c, f, y, x)
356*88d15eacSSasha Smundak	got := <-c
357*88d15eacSSasha Smundak	want := f.Call([]reflect.Value{x, y})[0].Bool()
358*88d15eacSSasha Smundak	if !got.IsValid() || got.Bool() != want {
359*88d15eacSSasha Smundak		panic(fmt.Sprintf("non-deterministic or non-symmetric function detected: %s", function.NameOf(f)))
360*88d15eacSSasha Smundak	}
361*88d15eacSSasha Smundak	return want
362*88d15eacSSasha Smundak}
363*88d15eacSSasha Smundak
364*88d15eacSSasha Smundakfunc detectRaces(c chan<- reflect.Value, f reflect.Value, vs ...reflect.Value) {
365*88d15eacSSasha Smundak	var ret reflect.Value
366*88d15eacSSasha Smundak	defer func() {
367*88d15eacSSasha Smundak		recover() // Ignore panics, let the other call to f panic instead
368*88d15eacSSasha Smundak		c <- ret
369*88d15eacSSasha Smundak	}()
370*88d15eacSSasha Smundak	ret = f.Call(vs)[0]
371*88d15eacSSasha Smundak}
372*88d15eacSSasha Smundak
373*88d15eacSSasha Smundakfunc (s *state) compareStruct(t reflect.Type, vx, vy reflect.Value) {
374*88d15eacSSasha Smundak	var addr bool
375*88d15eacSSasha Smundak	var vax, vay reflect.Value // Addressable versions of vx and vy
376*88d15eacSSasha Smundak
377*88d15eacSSasha Smundak	var mayForce, mayForceInit bool
378*88d15eacSSasha Smundak	step := StructField{&structField{}}
379*88d15eacSSasha Smundak	for i := 0; i < t.NumField(); i++ {
380*88d15eacSSasha Smundak		step.typ = t.Field(i).Type
381*88d15eacSSasha Smundak		step.vx = vx.Field(i)
382*88d15eacSSasha Smundak		step.vy = vy.Field(i)
383*88d15eacSSasha Smundak		step.name = t.Field(i).Name
384*88d15eacSSasha Smundak		step.idx = i
385*88d15eacSSasha Smundak		step.unexported = !isExported(step.name)
386*88d15eacSSasha Smundak		if step.unexported {
387*88d15eacSSasha Smundak			if step.name == "_" {
388*88d15eacSSasha Smundak				continue
389*88d15eacSSasha Smundak			}
390*88d15eacSSasha Smundak			// Defer checking of unexported fields until later to give an
391*88d15eacSSasha Smundak			// Ignore a chance to ignore the field.
392*88d15eacSSasha Smundak			if !vax.IsValid() || !vay.IsValid() {
393*88d15eacSSasha Smundak				// For retrieveUnexportedField to work, the parent struct must
394*88d15eacSSasha Smundak				// be addressable. Create a new copy of the values if
395*88d15eacSSasha Smundak				// necessary to make them addressable.
396*88d15eacSSasha Smundak				addr = vx.CanAddr() || vy.CanAddr()
397*88d15eacSSasha Smundak				vax = makeAddressable(vx)
398*88d15eacSSasha Smundak				vay = makeAddressable(vy)
399*88d15eacSSasha Smundak			}
400*88d15eacSSasha Smundak			if !mayForceInit {
401*88d15eacSSasha Smundak				for _, xf := range s.exporters {
402*88d15eacSSasha Smundak					mayForce = mayForce || xf(t)
403*88d15eacSSasha Smundak				}
404*88d15eacSSasha Smundak				mayForceInit = true
405*88d15eacSSasha Smundak			}
406*88d15eacSSasha Smundak			step.mayForce = mayForce
407*88d15eacSSasha Smundak			step.paddr = addr
408*88d15eacSSasha Smundak			step.pvx = vax
409*88d15eacSSasha Smundak			step.pvy = vay
410*88d15eacSSasha Smundak			step.field = t.Field(i)
411*88d15eacSSasha Smundak		}
412*88d15eacSSasha Smundak		s.compareAny(step)
413*88d15eacSSasha Smundak	}
414*88d15eacSSasha Smundak}
415*88d15eacSSasha Smundak
416*88d15eacSSasha Smundakfunc (s *state) compareSlice(t reflect.Type, vx, vy reflect.Value) {
417*88d15eacSSasha Smundak	isSlice := t.Kind() == reflect.Slice
418*88d15eacSSasha Smundak	if isSlice && (vx.IsNil() || vy.IsNil()) {
419*88d15eacSSasha Smundak		s.report(vx.IsNil() && vy.IsNil(), 0)
420*88d15eacSSasha Smundak		return
421*88d15eacSSasha Smundak	}
422*88d15eacSSasha Smundak
423*88d15eacSSasha Smundak	// NOTE: It is incorrect to call curPtrs.Push on the slice header pointer
424*88d15eacSSasha Smundak	// since slices represents a list of pointers, rather than a single pointer.
425*88d15eacSSasha Smundak	// The pointer checking logic must be handled on a per-element basis
426*88d15eacSSasha Smundak	// in compareAny.
427*88d15eacSSasha Smundak	//
428*88d15eacSSasha Smundak	// A slice header (see reflect.SliceHeader) in Go is a tuple of a starting
429*88d15eacSSasha Smundak	// pointer P, a length N, and a capacity C. Supposing each slice element has
430*88d15eacSSasha Smundak	// a memory size of M, then the slice is equivalent to the list of pointers:
431*88d15eacSSasha Smundak	//	[P+i*M for i in range(N)]
432*88d15eacSSasha Smundak	//
433*88d15eacSSasha Smundak	// For example, v[:0] and v[:1] are slices with the same starting pointer,
434*88d15eacSSasha Smundak	// but they are clearly different values. Using the slice pointer alone
435*88d15eacSSasha Smundak	// violates the assumption that equal pointers implies equal values.
436*88d15eacSSasha Smundak
437*88d15eacSSasha Smundak	step := SliceIndex{&sliceIndex{pathStep: pathStep{typ: t.Elem()}, isSlice: isSlice}}
438*88d15eacSSasha Smundak	withIndexes := func(ix, iy int) SliceIndex {
439*88d15eacSSasha Smundak		if ix >= 0 {
440*88d15eacSSasha Smundak			step.vx, step.xkey = vx.Index(ix), ix
441*88d15eacSSasha Smundak		} else {
442*88d15eacSSasha Smundak			step.vx, step.xkey = reflect.Value{}, -1
443*88d15eacSSasha Smundak		}
444*88d15eacSSasha Smundak		if iy >= 0 {
445*88d15eacSSasha Smundak			step.vy, step.ykey = vy.Index(iy), iy
446*88d15eacSSasha Smundak		} else {
447*88d15eacSSasha Smundak			step.vy, step.ykey = reflect.Value{}, -1
448*88d15eacSSasha Smundak		}
449*88d15eacSSasha Smundak		return step
450*88d15eacSSasha Smundak	}
451*88d15eacSSasha Smundak
452*88d15eacSSasha Smundak	// Ignore options are able to ignore missing elements in a slice.
453*88d15eacSSasha Smundak	// However, detecting these reliably requires an optimal differencing
454*88d15eacSSasha Smundak	// algorithm, for which diff.Difference is not.
455*88d15eacSSasha Smundak	//
456*88d15eacSSasha Smundak	// Instead, we first iterate through both slices to detect which elements
457*88d15eacSSasha Smundak	// would be ignored if standing alone. The index of non-discarded elements
458*88d15eacSSasha Smundak	// are stored in a separate slice, which diffing is then performed on.
459*88d15eacSSasha Smundak	var indexesX, indexesY []int
460*88d15eacSSasha Smundak	var ignoredX, ignoredY []bool
461*88d15eacSSasha Smundak	for ix := 0; ix < vx.Len(); ix++ {
462*88d15eacSSasha Smundak		ignored := s.statelessCompare(withIndexes(ix, -1)).NumDiff == 0
463*88d15eacSSasha Smundak		if !ignored {
464*88d15eacSSasha Smundak			indexesX = append(indexesX, ix)
465*88d15eacSSasha Smundak		}
466*88d15eacSSasha Smundak		ignoredX = append(ignoredX, ignored)
467*88d15eacSSasha Smundak	}
468*88d15eacSSasha Smundak	for iy := 0; iy < vy.Len(); iy++ {
469*88d15eacSSasha Smundak		ignored := s.statelessCompare(withIndexes(-1, iy)).NumDiff == 0
470*88d15eacSSasha Smundak		if !ignored {
471*88d15eacSSasha Smundak			indexesY = append(indexesY, iy)
472*88d15eacSSasha Smundak		}
473*88d15eacSSasha Smundak		ignoredY = append(ignoredY, ignored)
474*88d15eacSSasha Smundak	}
475*88d15eacSSasha Smundak
476*88d15eacSSasha Smundak	// Compute an edit-script for slices vx and vy (excluding ignored elements).
477*88d15eacSSasha Smundak	edits := diff.Difference(len(indexesX), len(indexesY), func(ix, iy int) diff.Result {
478*88d15eacSSasha Smundak		return s.statelessCompare(withIndexes(indexesX[ix], indexesY[iy]))
479*88d15eacSSasha Smundak	})
480*88d15eacSSasha Smundak
481*88d15eacSSasha Smundak	// Replay the ignore-scripts and the edit-script.
482*88d15eacSSasha Smundak	var ix, iy int
483*88d15eacSSasha Smundak	for ix < vx.Len() || iy < vy.Len() {
484*88d15eacSSasha Smundak		var e diff.EditType
485*88d15eacSSasha Smundak		switch {
486*88d15eacSSasha Smundak		case ix < len(ignoredX) && ignoredX[ix]:
487*88d15eacSSasha Smundak			e = diff.UniqueX
488*88d15eacSSasha Smundak		case iy < len(ignoredY) && ignoredY[iy]:
489*88d15eacSSasha Smundak			e = diff.UniqueY
490*88d15eacSSasha Smundak		default:
491*88d15eacSSasha Smundak			e, edits = edits[0], edits[1:]
492*88d15eacSSasha Smundak		}
493*88d15eacSSasha Smundak		switch e {
494*88d15eacSSasha Smundak		case diff.UniqueX:
495*88d15eacSSasha Smundak			s.compareAny(withIndexes(ix, -1))
496*88d15eacSSasha Smundak			ix++
497*88d15eacSSasha Smundak		case diff.UniqueY:
498*88d15eacSSasha Smundak			s.compareAny(withIndexes(-1, iy))
499*88d15eacSSasha Smundak			iy++
500*88d15eacSSasha Smundak		default:
501*88d15eacSSasha Smundak			s.compareAny(withIndexes(ix, iy))
502*88d15eacSSasha Smundak			ix++
503*88d15eacSSasha Smundak			iy++
504*88d15eacSSasha Smundak		}
505*88d15eacSSasha Smundak	}
506*88d15eacSSasha Smundak}
507*88d15eacSSasha Smundak
508*88d15eacSSasha Smundakfunc (s *state) compareMap(t reflect.Type, vx, vy reflect.Value) {
509*88d15eacSSasha Smundak	if vx.IsNil() || vy.IsNil() {
510*88d15eacSSasha Smundak		s.report(vx.IsNil() && vy.IsNil(), 0)
511*88d15eacSSasha Smundak		return
512*88d15eacSSasha Smundak	}
513*88d15eacSSasha Smundak
514*88d15eacSSasha Smundak	// Cycle-detection for maps.
515*88d15eacSSasha Smundak	if eq, visited := s.curPtrs.Push(vx, vy); visited {
516*88d15eacSSasha Smundak		s.report(eq, reportByCycle)
517*88d15eacSSasha Smundak		return
518*88d15eacSSasha Smundak	}
519*88d15eacSSasha Smundak	defer s.curPtrs.Pop(vx, vy)
520*88d15eacSSasha Smundak
521*88d15eacSSasha Smundak	// We combine and sort the two map keys so that we can perform the
522*88d15eacSSasha Smundak	// comparisons in a deterministic order.
523*88d15eacSSasha Smundak	step := MapIndex{&mapIndex{pathStep: pathStep{typ: t.Elem()}}}
524*88d15eacSSasha Smundak	for _, k := range value.SortKeys(append(vx.MapKeys(), vy.MapKeys()...)) {
525*88d15eacSSasha Smundak		step.vx = vx.MapIndex(k)
526*88d15eacSSasha Smundak		step.vy = vy.MapIndex(k)
527*88d15eacSSasha Smundak		step.key = k
528*88d15eacSSasha Smundak		if !step.vx.IsValid() && !step.vy.IsValid() {
529*88d15eacSSasha Smundak			// It is possible for both vx and vy to be invalid if the
530*88d15eacSSasha Smundak			// key contained a NaN value in it.
531*88d15eacSSasha Smundak			//
532*88d15eacSSasha Smundak			// Even with the ability to retrieve NaN keys in Go 1.12,
533*88d15eacSSasha Smundak			// there still isn't a sensible way to compare the values since
534*88d15eacSSasha Smundak			// a NaN key may map to multiple unordered values.
535*88d15eacSSasha Smundak			// The most reasonable way to compare NaNs would be to compare the
536*88d15eacSSasha Smundak			// set of values. However, this is impossible to do efficiently
537*88d15eacSSasha Smundak			// since set equality is provably an O(n^2) operation given only
538*88d15eacSSasha Smundak			// an Equal function. If we had a Less function or Hash function,
539*88d15eacSSasha Smundak			// this could be done in O(n*log(n)) or O(n), respectively.
540*88d15eacSSasha Smundak			//
541*88d15eacSSasha Smundak			// Rather than adding complex logic to deal with NaNs, make it
542*88d15eacSSasha Smundak			// the user's responsibility to compare such obscure maps.
543*88d15eacSSasha Smundak			const help = "consider providing a Comparer to compare the map"
544*88d15eacSSasha Smundak			panic(fmt.Sprintf("%#v has map key with NaNs\n%s", s.curPath, help))
545*88d15eacSSasha Smundak		}
546*88d15eacSSasha Smundak		s.compareAny(step)
547*88d15eacSSasha Smundak	}
548*88d15eacSSasha Smundak}
549*88d15eacSSasha Smundak
550*88d15eacSSasha Smundakfunc (s *state) comparePtr(t reflect.Type, vx, vy reflect.Value) {
551*88d15eacSSasha Smundak	if vx.IsNil() || vy.IsNil() {
552*88d15eacSSasha Smundak		s.report(vx.IsNil() && vy.IsNil(), 0)
553*88d15eacSSasha Smundak		return
554*88d15eacSSasha Smundak	}
555*88d15eacSSasha Smundak
556*88d15eacSSasha Smundak	// Cycle-detection for pointers.
557*88d15eacSSasha Smundak	if eq, visited := s.curPtrs.Push(vx, vy); visited {
558*88d15eacSSasha Smundak		s.report(eq, reportByCycle)
559*88d15eacSSasha Smundak		return
560*88d15eacSSasha Smundak	}
561*88d15eacSSasha Smundak	defer s.curPtrs.Pop(vx, vy)
562*88d15eacSSasha Smundak
563*88d15eacSSasha Smundak	vx, vy = vx.Elem(), vy.Elem()
564*88d15eacSSasha Smundak	s.compareAny(Indirect{&indirect{pathStep{t.Elem(), vx, vy}}})
565*88d15eacSSasha Smundak}
566*88d15eacSSasha Smundak
567*88d15eacSSasha Smundakfunc (s *state) compareInterface(t reflect.Type, vx, vy reflect.Value) {
568*88d15eacSSasha Smundak	if vx.IsNil() || vy.IsNil() {
569*88d15eacSSasha Smundak		s.report(vx.IsNil() && vy.IsNil(), 0)
570*88d15eacSSasha Smundak		return
571*88d15eacSSasha Smundak	}
572*88d15eacSSasha Smundak	vx, vy = vx.Elem(), vy.Elem()
573*88d15eacSSasha Smundak	if vx.Type() != vy.Type() {
574*88d15eacSSasha Smundak		s.report(false, 0)
575*88d15eacSSasha Smundak		return
576*88d15eacSSasha Smundak	}
577*88d15eacSSasha Smundak	s.compareAny(TypeAssertion{&typeAssertion{pathStep{vx.Type(), vx, vy}}})
578*88d15eacSSasha Smundak}
579*88d15eacSSasha Smundak
580*88d15eacSSasha Smundakfunc (s *state) report(eq bool, rf resultFlags) {
581*88d15eacSSasha Smundak	if rf&reportByIgnore == 0 {
582*88d15eacSSasha Smundak		if eq {
583*88d15eacSSasha Smundak			s.result.NumSame++
584*88d15eacSSasha Smundak			rf |= reportEqual
585*88d15eacSSasha Smundak		} else {
586*88d15eacSSasha Smundak			s.result.NumDiff++
587*88d15eacSSasha Smundak			rf |= reportUnequal
588*88d15eacSSasha Smundak		}
589*88d15eacSSasha Smundak	}
590*88d15eacSSasha Smundak	for _, r := range s.reporters {
591*88d15eacSSasha Smundak		r.Report(Result{flags: rf})
592*88d15eacSSasha Smundak	}
593*88d15eacSSasha Smundak}
594*88d15eacSSasha Smundak
595*88d15eacSSasha Smundak// recChecker tracks the state needed to periodically perform checks that
596*88d15eacSSasha Smundak// user provided transformers are not stuck in an infinitely recursive cycle.
597*88d15eacSSasha Smundaktype recChecker struct{ next int }
598*88d15eacSSasha Smundak
599*88d15eacSSasha Smundak// Check scans the Path for any recursive transformers and panics when any
600*88d15eacSSasha Smundak// recursive transformers are detected. Note that the presence of a
601*88d15eacSSasha Smundak// recursive Transformer does not necessarily imply an infinite cycle.
602*88d15eacSSasha Smundak// As such, this check only activates after some minimal number of path steps.
603*88d15eacSSasha Smundakfunc (rc *recChecker) Check(p Path) {
604*88d15eacSSasha Smundak	const minLen = 1 << 16
605*88d15eacSSasha Smundak	if rc.next == 0 {
606*88d15eacSSasha Smundak		rc.next = minLen
607*88d15eacSSasha Smundak	}
608*88d15eacSSasha Smundak	if len(p) < rc.next {
609*88d15eacSSasha Smundak		return
610*88d15eacSSasha Smundak	}
611*88d15eacSSasha Smundak	rc.next <<= 1
612*88d15eacSSasha Smundak
613*88d15eacSSasha Smundak	// Check whether the same transformer has appeared at least twice.
614*88d15eacSSasha Smundak	var ss []string
615*88d15eacSSasha Smundak	m := map[Option]int{}
616*88d15eacSSasha Smundak	for _, ps := range p {
617*88d15eacSSasha Smundak		if t, ok := ps.(Transform); ok {
618*88d15eacSSasha Smundak			t := t.Option()
619*88d15eacSSasha Smundak			if m[t] == 1 { // Transformer was used exactly once before
620*88d15eacSSasha Smundak				tf := t.(*transformer).fnc.Type()
621*88d15eacSSasha Smundak				ss = append(ss, fmt.Sprintf("%v: %v => %v", t, tf.In(0), tf.Out(0)))
622*88d15eacSSasha Smundak			}
623*88d15eacSSasha Smundak			m[t]++
624*88d15eacSSasha Smundak		}
625*88d15eacSSasha Smundak	}
626*88d15eacSSasha Smundak	if len(ss) > 0 {
627*88d15eacSSasha Smundak		const warning = "recursive set of Transformers detected"
628*88d15eacSSasha Smundak		const help = "consider using cmpopts.AcyclicTransformer"
629*88d15eacSSasha Smundak		set := strings.Join(ss, "\n\t")
630*88d15eacSSasha Smundak		panic(fmt.Sprintf("%s:\n\t%s\n%s", warning, set, help))
631*88d15eacSSasha Smundak	}
632*88d15eacSSasha Smundak}
633*88d15eacSSasha Smundak
634*88d15eacSSasha Smundak// dynChecker tracks the state needed to periodically perform checks that
635*88d15eacSSasha Smundak// user provided functions are symmetric and deterministic.
636*88d15eacSSasha Smundak// The zero value is safe for immediate use.
637*88d15eacSSasha Smundaktype dynChecker struct{ curr, next int }
638*88d15eacSSasha Smundak
639*88d15eacSSasha Smundak// Next increments the state and reports whether a check should be performed.
640*88d15eacSSasha Smundak//
641*88d15eacSSasha Smundak// Checks occur every Nth function call, where N is a triangular number:
642*88d15eacSSasha Smundak//
643*88d15eacSSasha Smundak//	0 1 3 6 10 15 21 28 36 45 55 66 78 91 105 120 136 153 171 190 ...
644*88d15eacSSasha Smundak//
645*88d15eacSSasha Smundak// See https://en.wikipedia.org/wiki/Triangular_number
646*88d15eacSSasha Smundak//
647*88d15eacSSasha Smundak// This sequence ensures that the cost of checks drops significantly as
648*88d15eacSSasha Smundak// the number of functions calls grows larger.
649*88d15eacSSasha Smundakfunc (dc *dynChecker) Next() bool {
650*88d15eacSSasha Smundak	ok := dc.curr == dc.next
651*88d15eacSSasha Smundak	if ok {
652*88d15eacSSasha Smundak		dc.curr = 0
653*88d15eacSSasha Smundak		dc.next++
654*88d15eacSSasha Smundak	}
655*88d15eacSSasha Smundak	dc.curr++
656*88d15eacSSasha Smundak	return ok
657*88d15eacSSasha Smundak}
658*88d15eacSSasha Smundak
659*88d15eacSSasha Smundak// makeAddressable returns a value that is always addressable.
660*88d15eacSSasha Smundak// It returns the input verbatim if it is already addressable,
661*88d15eacSSasha Smundak// otherwise it creates a new value and returns an addressable copy.
662*88d15eacSSasha Smundakfunc makeAddressable(v reflect.Value) reflect.Value {
663*88d15eacSSasha Smundak	if v.CanAddr() {
664*88d15eacSSasha Smundak		return v
665*88d15eacSSasha Smundak	}
666*88d15eacSSasha Smundak	vc := reflect.New(v.Type()).Elem()
667*88d15eacSSasha Smundak	vc.Set(v)
668*88d15eacSSasha Smundak	return vc
669*88d15eacSSasha Smundak}
670