]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blobdiff - vendor/github.com/google/go-cmp/cmp/options.go
update vendor and go.mod
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / google / go-cmp / cmp / options.go
index 91d4b066e0520f68ea48ecaf5c84ab34b2c2574b..793448160ee7121cf45eddfbc09d512e6bf2c5a6 100644 (file)
@@ -7,7 +7,7 @@ package cmp
 import (
        "fmt"
        "reflect"
-       "runtime"
+       "regexp"
        "strings"
 
        "github.com/google/go-cmp/cmp/internal/function"
@@ -29,11 +29,11 @@ type Option interface {
        // An Options is returned only if multiple comparers or transformers
        // can apply simultaneously and will only contain values of those types
        // or sub-Options containing values of those types.
-       filter(s *state, vx, vy reflect.Value, t reflect.Type) applicableOption
+       filter(s *state, t reflect.Type, vx, vy reflect.Value) applicableOption
 }
 
 // applicableOption represents the following types:
-//     Fundamental: ignore | invalid | *comparer | *transformer
+//     Fundamental: ignore | validator | *comparer | *transformer
 //     Grouping:    Options
 type applicableOption interface {
        Option
@@ -43,7 +43,7 @@ type applicableOption interface {
 }
 
 // coreOption represents the following types:
-//     Fundamental: ignore | invalid | *comparer | *transformer
+//     Fundamental: ignore | validator | *comparer | *transformer
 //     Filters:     *pathFilter | *valuesFilter
 type coreOption interface {
        Option
@@ -63,19 +63,19 @@ func (core) isCore() {}
 // on all individual options held within.
 type Options []Option
 
-func (opts Options) filter(s *state, vx, vy reflect.Value, t reflect.Type) (out applicableOption) {
+func (opts Options) filter(s *state, t reflect.Type, vx, vy reflect.Value) (out applicableOption) {
        for _, opt := range opts {
-               switch opt := opt.filter(s, vx, vy, t); opt.(type) {
+               switch opt := opt.filter(s, t, vx, vy); opt.(type) {
                case ignore:
                        return ignore{} // Only ignore can short-circuit evaluation
-               case invalid:
-                       out = invalid{} // Takes precedence over comparer or transformer
+               case validator:
+                       out = validator{} // Takes precedence over comparer or transformer
                case *comparer, *transformer, Options:
                        switch out.(type) {
                        case nil:
                                out = opt
-                       case invalid:
-                               // Keep invalid
+                       case validator:
+                               // Keep validator
                        case *comparer, *transformer, Options:
                                out = Options{out, opt} // Conflicting comparers or transformers
                        }
@@ -106,6 +106,11 @@ func (opts Options) String() string {
 // FilterPath returns a new Option where opt is only evaluated if filter f
 // returns true for the current Path in the value tree.
 //
+// This filter is called even if a slice element or map entry is missing and
+// provides an opportunity to ignore such cases. The filter function must be
+// symmetric such that the filter result is identical regardless of whether the
+// missing value is from x or y.
+//
 // The option passed in may be an Ignore, Transformer, Comparer, Options, or
 // a previously filtered Option.
 func FilterPath(f func(Path) bool, opt Option) Option {
@@ -124,22 +129,22 @@ type pathFilter struct {
        opt Option
 }
 
-func (f pathFilter) filter(s *state, vx, vy reflect.Value, t reflect.Type) applicableOption {
+func (f pathFilter) filter(s *state, t reflect.Type, vx, vy reflect.Value) applicableOption {
        if f.fnc(s.curPath) {
-               return f.opt.filter(s, vx, vy, t)
+               return f.opt.filter(s, t, vx, vy)
        }
        return nil
 }
 
 func (f pathFilter) String() string {
-       fn := getFuncName(reflect.ValueOf(f.fnc).Pointer())
-       return fmt.Sprintf("FilterPath(%s, %v)", fn, f.opt)
+       return fmt.Sprintf("FilterPath(%s, %v)", function.NameOf(reflect.ValueOf(f.fnc)), f.opt)
 }
 
 // FilterValues returns a new Option where opt is only evaluated if filter f,
 // which is a function of the form "func(T, T) bool", returns true for the
-// current pair of values being compared. If the type of the values is not
-// assignable to T, then this filter implicitly returns false.
+// current pair of values being compared. If either value is invalid or
+// the type of the values is not assignable to T, then this filter implicitly
+// returns false.
 //
 // The filter function must be
 // symmetric (i.e., agnostic to the order of the inputs) and
@@ -171,19 +176,18 @@ type valuesFilter struct {
        opt Option
 }
 
-func (f valuesFilter) filter(s *state, vx, vy reflect.Value, t reflect.Type) applicableOption {
-       if !vx.IsValid() || !vy.IsValid() {
-               return invalid{}
+func (f valuesFilter) filter(s *state, t reflect.Type, vx, vy reflect.Value) applicableOption {
+       if !vx.IsValid() || !vx.CanInterface() || !vy.IsValid() || !vy.CanInterface() {
+               return nil
        }
        if (f.typ == nil || t.AssignableTo(f.typ)) && s.callTTBFunc(f.fnc, vx, vy) {
-               return f.opt.filter(s, vx, vy, t)
+               return f.opt.filter(s, t, vx, vy)
        }
        return nil
 }
 
 func (f valuesFilter) String() string {
-       fn := getFuncName(f.fnc.Pointer())
-       return fmt.Sprintf("FilterValues(%s, %v)", fn, f.opt)
+       return fmt.Sprintf("FilterValues(%s, %v)", function.NameOf(f.fnc), f.opt)
 }
 
 // Ignore is an Option that causes all comparisons to be ignored.
@@ -194,20 +198,45 @@ func Ignore() Option { return ignore{} }
 type ignore struct{ core }
 
 func (ignore) isFiltered() bool                                                     { return false }
-func (ignore) filter(_ *state, _, _ reflect.Value, _ reflect.Type) applicableOption { return ignore{} }
-func (ignore) apply(_ *state, _, _ reflect.Value)                                   { return }
+func (ignore) filter(_ *state, _ reflect.Type, _, _ reflect.Value) applicableOption { return ignore{} }
+func (ignore) apply(s *state, _, _ reflect.Value)                                   { s.report(true, reportByIgnore) }
 func (ignore) String() string                                                       { return "Ignore()" }
 
-// invalid is a sentinel Option type to indicate that some options could not
-// be evaluated due to unexported fields.
-type invalid struct{ core }
+// validator is a sentinel Option type to indicate that some options could not
+// be evaluated due to unexported fields, missing slice elements, or
+// missing map entries. Both values are validator only for unexported fields.
+type validator struct{ core }
+
+func (validator) filter(_ *state, _ reflect.Type, vx, vy reflect.Value) applicableOption {
+       if !vx.IsValid() || !vy.IsValid() {
+               return validator{}
+       }
+       if !vx.CanInterface() || !vy.CanInterface() {
+               return validator{}
+       }
+       return nil
+}
+func (validator) apply(s *state, vx, vy reflect.Value) {
+       // Implies missing slice element or map entry.
+       if !vx.IsValid() || !vy.IsValid() {
+               s.report(vx.IsValid() == vy.IsValid(), 0)
+               return
+       }
+
+       // Unable to Interface implies unexported field without visibility access.
+       if !vx.CanInterface() || !vy.CanInterface() {
+               const help = "consider using a custom Comparer; if you control the implementation of type, you can also consider AllowUnexported or cmpopts.IgnoreUnexported"
+               panic(fmt.Sprintf("cannot handle unexported field: %#v\n%s", s.curPath, help))
+       }
 
-func (invalid) filter(_ *state, _, _ reflect.Value, _ reflect.Type) applicableOption { return invalid{} }
-func (invalid) apply(s *state, _, _ reflect.Value) {
-       const help = "consider using AllowUnexported or cmpopts.IgnoreUnexported"
-       panic(fmt.Sprintf("cannot handle unexported field: %#v\n%s", s.curPath, help))
+       panic("not reachable")
 }
 
+// identRx represents a valid identifier according to the Go specification.
+const identRx = `[_\p{L}][_\p{L}\p{N}]*`
+
+var identsRx = regexp.MustCompile(`^` + identRx + `(\.` + identRx + `)*$`)
+
 // Transformer returns an Option that applies a transformation function that
 // converts values of a certain type into that of another.
 //
@@ -220,18 +249,25 @@ func (invalid) apply(s *state, _, _ reflect.Value) {
 // input and output types are the same), an implicit filter is added such that
 // a transformer is applicable only if that exact transformer is not already
 // in the tail of the Path since the last non-Transform step.
+// For situations where the implicit filter is still insufficient,
+// consider using cmpopts.AcyclicTransformer, which adds a filter
+// to prevent the transformer from being recursively applied upon itself.
 //
 // The name is a user provided label that is used as the Transform.Name in the
-// transformation PathStep. If empty, an arbitrary name is used.
+// transformation PathStep (and eventually shown in the Diff output).
+// The name must be a valid identifier or qualified identifier in Go syntax.
+// If empty, an arbitrary name is used.
 func Transformer(name string, f interface{}) Option {
        v := reflect.ValueOf(f)
        if !function.IsType(v.Type(), function.Transformer) || v.IsNil() {
                panic(fmt.Sprintf("invalid transformer function: %T", f))
        }
        if name == "" {
-               name = "λ" // Lambda-symbol as place-holder for anonymous transformer
-       }
-       if !isValid(name) {
+               name = function.NameOf(v)
+               if !identsRx.MatchString(name) {
+                       name = "λ" // Lambda-symbol as placeholder name
+               }
+       } else if !identsRx.MatchString(name) {
                panic(fmt.Sprintf("invalid name: %q", name))
        }
        tr := &transformer{name: name, fnc: reflect.ValueOf(f)}
@@ -250,9 +286,9 @@ type transformer struct {
 
 func (tr *transformer) isFiltered() bool { return tr.typ != nil }
 
-func (tr *transformer) filter(s *state, _, _ reflect.Value, t reflect.Type) applicableOption {
+func (tr *transformer) filter(s *state, t reflect.Type, _, _ reflect.Value) applicableOption {
        for i := len(s.curPath) - 1; i >= 0; i-- {
-               if t, ok := s.curPath[i].(*transform); !ok {
+               if t, ok := s.curPath[i].(Transform); !ok {
                        break // Hit most recent non-Transform step
                } else if tr == t.trans {
                        return nil // Cannot directly use same Transform
@@ -265,18 +301,15 @@ func (tr *transformer) filter(s *state, _, _ reflect.Value, t reflect.Type) appl
 }
 
 func (tr *transformer) apply(s *state, vx, vy reflect.Value) {
-       // Update path before calling the Transformer so that dynamic checks
-       // will use the updated path.
-       s.curPath.push(&transform{pathStep{tr.fnc.Type().Out(0)}, tr})
-       defer s.curPath.pop()
-
-       vx = s.callTRFunc(tr.fnc, vx)
-       vy = s.callTRFunc(tr.fnc, vy)
-       s.compareAny(vx, vy)
+       step := Transform{&transform{pathStep{typ: tr.fnc.Type().Out(0)}, tr}}
+       vvx := s.callTRFunc(tr.fnc, vx, step)
+       vvy := s.callTRFunc(tr.fnc, vy, step)
+       step.vx, step.vy = vvx, vvy
+       s.compareAny(step)
 }
 
 func (tr transformer) String() string {
-       return fmt.Sprintf("Transformer(%s, %s)", tr.name, getFuncName(tr.fnc.Pointer()))
+       return fmt.Sprintf("Transformer(%s, %s)", tr.name, function.NameOf(tr.fnc))
 }
 
 // Comparer returns an Option that determines whether two values are equal
@@ -311,7 +344,7 @@ type comparer struct {
 
 func (cm *comparer) isFiltered() bool { return cm.typ != nil }
 
-func (cm *comparer) filter(_ *state, _, _ reflect.Value, t reflect.Type) applicableOption {
+func (cm *comparer) filter(_ *state, t reflect.Type, _, _ reflect.Value) applicableOption {
        if cm.typ == nil || t.AssignableTo(cm.typ) {
                return cm
        }
@@ -320,11 +353,11 @@ func (cm *comparer) filter(_ *state, _, _ reflect.Value, t reflect.Type) applica
 
 func (cm *comparer) apply(s *state, vx, vy reflect.Value) {
        eq := s.callTTBFunc(cm.fnc, vx, vy)
-       s.report(eq, vx, vy)
+       s.report(eq, reportByFunc)
 }
 
 func (cm comparer) String() string {
-       return fmt.Sprintf("Comparer(%s)", getFuncName(cm.fnc.Pointer()))
+       return fmt.Sprintf("Comparer(%s)", function.NameOf(cm.fnc))
 }
 
 // AllowUnexported returns an Option that forcibly allows operations on
@@ -338,7 +371,7 @@ func (cm comparer) String() string {
 // defined in an internal package where the semantic meaning of an unexported
 // field is in the control of the user.
 //
-// For some cases, a custom Comparer should be used instead that defines
+// In many cases, a custom Comparer should be used instead that defines
 // equality as a function of the public API of a type rather than the underlying
 // unexported implementation.
 //
@@ -370,27 +403,92 @@ func AllowUnexported(types ...interface{}) Option {
 
 type visibleStructs map[reflect.Type]bool
 
-func (visibleStructs) filter(_ *state, _, _ reflect.Value, _ reflect.Type) applicableOption {
+func (visibleStructs) filter(_ *state, _ reflect.Type, _, _ reflect.Value) applicableOption {
        panic("not implemented")
 }
 
-// reporter is an Option that configures how differences are reported.
-type reporter interface {
-       // TODO: Not exported yet.
+// Result represents the comparison result for a single node and
+// is provided by cmp when calling Result (see Reporter).
+type Result struct {
+       _     [0]func() // Make Result incomparable
+       flags resultFlags
+}
+
+// Equal reports whether the node was determined to be equal or not.
+// As a special case, ignored nodes are considered equal.
+func (r Result) Equal() bool {
+       return r.flags&(reportEqual|reportByIgnore) != 0
+}
+
+// ByIgnore reports whether the node is equal because it was ignored.
+// This never reports true if Equal reports false.
+func (r Result) ByIgnore() bool {
+       return r.flags&reportByIgnore != 0
+}
+
+// ByMethod reports whether the Equal method determined equality.
+func (r Result) ByMethod() bool {
+       return r.flags&reportByMethod != 0
+}
+
+// ByFunc reports whether a Comparer function determined equality.
+func (r Result) ByFunc() bool {
+       return r.flags&reportByFunc != 0
+}
+
+type resultFlags uint
+
+const (
+       _ resultFlags = (1 << iota) / 2
+
+       reportEqual
+       reportUnequal
+       reportByIgnore
+       reportByMethod
+       reportByFunc
+)
+
+// Reporter is an Option that can be passed to Equal. When Equal traverses
+// the value trees, it calls PushStep as it descends into each node in the
+// tree and PopStep as it ascend out of the node. The leaves of the tree are
+// either compared (determined to be equal or not equal) or ignored and reported
+// as such by calling the Report method.
+func Reporter(r interface {
+       // PushStep is called when a tree-traversal operation is performed.
+       // The PathStep itself is only valid until the step is popped.
+       // The PathStep.Values are valid for the duration of the entire traversal
+       // and must not be mutated.
+       //
+       // Equal always calls PushStep at the start to provide an operation-less
+       // PathStep used to report the root values.
        //
-       // Perhaps add PushStep and PopStep and change Report to only accept
-       // a PathStep instead of the full-path? Adding a PushStep and PopStep makes
-       // it clear that we are traversing the value tree in a depth-first-search
-       // manner, which has an effect on how values are printed.
+       // Within a slice, the exact set of inserted, removed, or modified elements
+       // is unspecified and may change in future implementations.
+       // The entries of a map are iterated through in an unspecified order.
+       PushStep(PathStep)
+
+       // Report is called exactly once on leaf nodes to report whether the
+       // comparison identified the node as equal, unequal, or ignored.
+       // A leaf node is one that is immediately preceded by and followed by
+       // a pair of PushStep and PopStep calls.
+       Report(Result)
+
+       // PopStep ascends back up the value tree.
+       // There is always a matching pop call for every push call.
+       PopStep()
+}) Option {
+       return reporter{r}
+}
 
-       Option
+type reporter struct{ reporterIface }
+type reporterIface interface {
+       PushStep(PathStep)
+       Report(Result)
+       PopStep()
+}
 
-       // Report is called for every comparison made and will be provided with
-       // the two values being compared, the equality result, and the
-       // current path in the value tree. It is possible for x or y to be an
-       // invalid reflect.Value if one of the values is non-existent;
-       // which is possible with maps and slices.
-       Report(x, y reflect.Value, eq bool, p Path)
+func (reporter) filter(_ *state, _ reflect.Type, _, _ reflect.Value) applicableOption {
+       panic("not implemented")
 }
 
 // normalizeOption normalizes the input options such that all Options groups
@@ -424,30 +522,3 @@ func flattenOptions(dst, src Options) Options {
        }
        return dst
 }
-
-// getFuncName returns a short function name from the pointer.
-// The string parsing logic works up until Go1.9.
-func getFuncName(p uintptr) string {
-       fnc := runtime.FuncForPC(p)
-       if fnc == nil {
-               return "<unknown>"
-       }
-       name := fnc.Name() // E.g., "long/path/name/mypkg.(mytype).(long/path/name/mypkg.myfunc)-fm"
-       if strings.HasSuffix(name, ")-fm") || strings.HasSuffix(name, ")·fm") {
-               // Strip the package name from method name.
-               name = strings.TrimSuffix(name, ")-fm")
-               name = strings.TrimSuffix(name, ")·fm")
-               if i := strings.LastIndexByte(name, '('); i >= 0 {
-                       methodName := name[i+1:] // E.g., "long/path/name/mypkg.myfunc"
-                       if j := strings.LastIndexByte(methodName, '.'); j >= 0 {
-                               methodName = methodName[j+1:] // E.g., "myfunc"
-                       }
-                       name = name[:i] + methodName // E.g., "long/path/name/mypkg.(mytype)." + "myfunc"
-               }
-       }
-       if i := strings.LastIndexByte(name, '/'); i >= 0 {
-               // Strip the package name.
-               name = name[i+1:] // E.g., "mypkg.(mytype).myfunc"
-       }
-       return name
-}