diff --git a/vendor.conf b/vendor.conf index 2fab83499..2d0bc82aa 100644 --- a/vendor.conf +++ b/vendor.conf @@ -39,8 +39,8 @@ google.golang.org/genproto d80a6e20e776b0b17a324d0ba1ab50a39c8e8944 golang.org/x/text 19e51611da83d6be54ddafce4a4af510cb3e9ea4 github.com/containerd/ttrpc f02858b1457c5ca3aaec3a0803eb0d59f96e41d6 github.com/syndtr/gocapability db04d3cc01c8b54962a58ec7e491717d06cfcc16 -gotest.tools v2.1.0 -github.com/google/go-cmp v0.1.0 +gotest.tools v2.3.0 +github.com/google/go-cmp v0.2.0 go.etcd.io/bbolt v1.3.2 # cri dependencies diff --git a/vendor/github.com/google/go-cmp/README.md b/vendor/github.com/google/go-cmp/README.md index d82f10bfc..61c9c4cd9 100644 --- a/vendor/github.com/google/go-cmp/README.md +++ b/vendor/github.com/google/go-cmp/README.md @@ -21,7 +21,7 @@ The primary features of `cmp` are: equality is determined by recursively comparing the primitive kinds on both values, much like `reflect.DeepEqual`. Unlike `reflect.DeepEqual`, unexported fields are not compared by default; they result in panics unless suppressed - by using an `Ignore` option (see `cmpopts.IgnoreUnexported`) or explictly + by using an `Ignore` option (see `cmpopts.IgnoreUnexported`) or explicitly compared using the `AllowUnexported` option. See the [GoDoc documentation][godoc] for more information. diff --git a/vendor/github.com/google/go-cmp/cmp/cmpopts/equate.go b/vendor/github.com/google/go-cmp/cmp/cmpopts/equate.go index cc39492cf..41bbddc61 100644 --- a/vendor/github.com/google/go-cmp/cmp/cmpopts/equate.go +++ b/vendor/github.com/google/go-cmp/cmp/cmpopts/equate.go @@ -17,7 +17,7 @@ func equateAlways(_, _ interface{}) bool { return true } // EquateEmpty returns a Comparer option that determines all maps and slices // with a length of zero to be equal, regardless of whether they are nil. // -// EquateEmpty can be used in conjuction with SortSlices and SortMaps. +// EquateEmpty can be used in conjunction with SortSlices and SortMaps. func EquateEmpty() cmp.Option { return cmp.FilterValues(isEmpty, cmp.Comparer(equateAlways)) } @@ -42,7 +42,7 @@ func isEmpty(x, y interface{}) bool { // The mathematical expression used is equivalent to: // |x-y| ≤ max(fraction*min(|x|, |y|), margin) // -// EquateApprox can be used in conjuction with EquateNaNs. +// EquateApprox can be used in conjunction with EquateNaNs. func EquateApprox(fraction, margin float64) cmp.Option { if margin < 0 || fraction < 0 || math.IsNaN(margin) || math.IsNaN(fraction) { panic("margin or fraction must be a non-negative number") @@ -73,7 +73,7 @@ func (a approximator) compareF32(x, y float32) bool { // EquateNaNs returns a Comparer option that determines float32 and float64 // NaN values to be equal. // -// EquateNaNs can be used in conjuction with EquateApprox. +// EquateNaNs can be used in conjunction with EquateApprox. func EquateNaNs() cmp.Option { return cmp.Options{ cmp.FilterValues(areNaNsF64s, cmp.Comparer(equateAlways)), diff --git a/vendor/github.com/google/go-cmp/cmp/cmpopts/ignore.go b/vendor/github.com/google/go-cmp/cmp/cmpopts/ignore.go index 016891da3..e86554b92 100644 --- a/vendor/github.com/google/go-cmp/cmp/cmpopts/ignore.go +++ b/vendor/github.com/google/go-cmp/cmp/cmpopts/ignore.go @@ -50,7 +50,7 @@ func (tf typeFilter) filter(p cmp.Path) bool { if len(p) < 1 { return false } - t := p[len(p)-1].Type() + t := p.Last().Type() for _, ti := range tf { if t.AssignableTo(ti) { return true @@ -95,7 +95,7 @@ func (tf ifaceFilter) filter(p cmp.Path) bool { if len(p) < 1 { return false } - t := p[len(p)-1].Type() + t := p.Last().Type() for _, ti := range tf { if t.AssignableTo(ti) { return true @@ -131,14 +131,11 @@ func newUnexportedFilter(typs ...interface{}) unexportedFilter { return ux } func (xf unexportedFilter) filter(p cmp.Path) bool { - if len(p) < 2 { - return false - } - sf, ok := p[len(p)-1].(cmp.StructField) + sf, ok := p.Index(-1).(cmp.StructField) if !ok { return false } - return xf.m[p[len(p)-2].Type()] && !isExported(sf.Name()) + return xf.m[p.Index(-2).Type()] && !isExported(sf.Name()) } // isExported reports whether the identifier is exported. diff --git a/vendor/github.com/google/go-cmp/cmp/cmpopts/sort.go b/vendor/github.com/google/go-cmp/cmp/cmpopts/sort.go index a566d240b..da17d7469 100644 --- a/vendor/github.com/google/go-cmp/cmp/cmpopts/sort.go +++ b/vendor/github.com/google/go-cmp/cmp/cmpopts/sort.go @@ -24,7 +24,7 @@ import ( // The less function does not have to be "total". That is, if !less(x, y) and // !less(y, x) for two elements x and y, their relative order is maintained. // -// SortSlices can be used in conjuction with EquateEmpty. +// SortSlices can be used in conjunction with EquateEmpty. func SortSlices(less interface{}) cmp.Option { vf := reflect.ValueOf(less) if !function.IsType(vf.Type(), function.Less) || vf.IsNil() { @@ -95,7 +95,7 @@ func (ss sliceSorter) less(v reflect.Value, i, j int) bool { // • Transitive: if !less(x, y) and !less(y, z), then !less(x, z) // • Total: if x != y, then either less(x, y) or less(y, x) // -// SortMaps can be used in conjuction with EquateEmpty. +// SortMaps can be used in conjunction with EquateEmpty. func SortMaps(less interface{}) cmp.Option { vf := reflect.ValueOf(less) if !function.IsType(vf.Type(), function.Less) || vf.IsNil() { diff --git a/vendor/github.com/google/go-cmp/cmp/compare.go b/vendor/github.com/google/go-cmp/cmp/compare.go index 5527f0149..7e215f220 100644 --- a/vendor/github.com/google/go-cmp/cmp/compare.go +++ b/vendor/github.com/google/go-cmp/cmp/compare.go @@ -22,7 +22,7 @@ // equality is determined by recursively comparing the primitive kinds on both // values, much like reflect.DeepEqual. Unlike reflect.DeepEqual, unexported // fields are not compared by default; they result in panics unless suppressed -// by using an Ignore option (see cmpopts.IgnoreUnexported) or explictly compared +// by using an Ignore option (see cmpopts.IgnoreUnexported) or explicitly compared // using the AllowUnexported option. package cmp @@ -35,7 +35,7 @@ import ( "github.com/google/go-cmp/cmp/internal/value" ) -// BUG: Maps with keys containing NaN values cannot be properly compared due to +// BUG(dsnet): Maps with keys containing NaN values cannot be properly compared due to // the reflection package's inability to retrieve such entries. Equal will panic // anytime it comes across a NaN key, but this behavior may change. // @@ -61,8 +61,8 @@ var nothing = reflect.Value{} // // • If the values have an Equal method of the form "(T) Equal(T) bool" or // "(T) Equal(I) bool" where T is assignable to I, then use the result of -// x.Equal(y). Otherwise, no such method exists and evaluation proceeds to -// the next rule. +// x.Equal(y) even if x or y is nil. +// Otherwise, no such method exists and evaluation proceeds to the next rule. // // • Lastly, try to compare x and y based on their basic kinds. // Simple kinds like booleans, integers, floats, complex numbers, strings, and @@ -304,7 +304,8 @@ func (s *state) tryOptions(vx, vy reflect.Value, t reflect.Type) bool { // Evaluate all filters and apply the remaining options. if opt := opts.filter(s, vx, vy, t); opt != nil { - return opt.apply(s, vx, vy) + opt.apply(s, vx, vy) + return true } return false } @@ -322,6 +323,7 @@ func (s *state) tryMethod(vx, vy reflect.Value, t reflect.Type) bool { } func (s *state) callTRFunc(f, v reflect.Value) reflect.Value { + v = sanitizeValue(v, f.Type().In(0)) if !s.dynChecker.Next() { return f.Call([]reflect.Value{v})[0] } @@ -345,6 +347,8 @@ func (s *state) callTRFunc(f, v reflect.Value) reflect.Value { } func (s *state) callTTBFunc(f, x, y reflect.Value) bool { + x = sanitizeValue(x, f.Type().In(0)) + y = sanitizeValue(y, f.Type().In(1)) if !s.dynChecker.Next() { return f.Call([]reflect.Value{x, y})[0].Bool() } @@ -372,20 +376,40 @@ func detectRaces(c chan<- reflect.Value, f reflect.Value, vs ...reflect.Value) { ret = f.Call(vs)[0] } +// sanitizeValue converts nil interfaces of type T to those of type R, +// assuming that T is assignable to R. +// Otherwise, it returns the input value as is. +func sanitizeValue(v reflect.Value, t reflect.Type) reflect.Value { + // TODO(dsnet): Remove this hacky workaround. + // See https://golang.org/issue/22143 + if v.Kind() == reflect.Interface && v.IsNil() && v.Type() != t { + return reflect.New(t).Elem() + } + return v +} + func (s *state) compareArray(vx, vy reflect.Value, t reflect.Type) { step := &sliceIndex{pathStep{t.Elem()}, 0, 0} s.curPath.push(step) // Compute an edit-script for slices vx and vy. - eq, es := diff.Difference(vx.Len(), vy.Len(), func(ix, iy int) diff.Result { + es := diff.Difference(vx.Len(), vy.Len(), func(ix, iy int) diff.Result { step.xkey, step.ykey = ix, iy return s.statelessCompare(vx.Index(ix), vy.Index(iy)) }) - // Equal or no edit-script, so report entire slices as is. - if eq || es == nil { + // Report the entire slice as is if the arrays are of primitive kind, + // and the arrays are different enough. + isPrimitive := false + switch t.Elem().Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, + reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr, + reflect.Bool, reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128: + isPrimitive = true + } + if isPrimitive && es.Dist() > (vx.Len()+vy.Len())/4 { s.curPath.pop() // Pop first since we are reporting the whole slice - s.report(eq, vx, vy) + s.report(false, vx, vy) return } diff --git a/vendor/github.com/google/go-cmp/cmp/internal/diff/debug_enable.go b/vendor/github.com/google/go-cmp/cmp/internal/diff/debug_enable.go index ba46c62e1..fd9f7f177 100644 --- a/vendor/github.com/google/go-cmp/cmp/internal/diff/debug_enable.go +++ b/vendor/github.com/google/go-cmp/cmp/internal/diff/debug_enable.go @@ -50,7 +50,7 @@ import ( // // The series of '.', 'X', 'Y', and 'M' characters at the bottom represents // the currently established path from the forward and reverse searches, -// seperated by a '|' character. +// separated by a '|' character. const ( updateDelay = 100 * time.Millisecond diff --git a/vendor/github.com/google/go-cmp/cmp/internal/diff/diff.go b/vendor/github.com/google/go-cmp/cmp/internal/diff/diff.go index baa41fd23..260befea2 100644 --- a/vendor/github.com/google/go-cmp/cmp/internal/diff/diff.go +++ b/vendor/github.com/google/go-cmp/cmp/internal/diff/diff.go @@ -106,9 +106,9 @@ func (r Result) Similar() bool { // Difference reports whether two lists of lengths nx and ny are equal // given the definition of equality provided as f. // -// This function may return a edit-script, which is a sequence of operations -// needed to convert one list into the other. If non-nil, the following -// invariants for the edit-script are maintained: +// This function returns an edit-script, which is a sequence of operations +// needed to convert one list into the other. The following invariants for +// the edit-script are maintained: // • eq == (es.Dist()==0) // • nx == es.LenX() // • ny == es.LenY() @@ -117,17 +117,7 @@ func (r Result) Similar() bool { // produces an edit-script with a minimal Levenshtein distance). This algorithm // favors performance over optimality. The exact output is not guaranteed to // be stable and may change over time. -func Difference(nx, ny int, f EqualFunc) (eq bool, es EditScript) { - es = searchGraph(nx, ny, f) - st := es.stats() - eq = len(es) == st.NI - if !eq && st.NI < (nx+ny)/4 { - return eq, nil // Edit-script more distracting than helpful - } - return eq, es -} - -func searchGraph(nx, ny int, f EqualFunc) EditScript { +func Difference(nx, ny int, f EqualFunc) (es EditScript) { // This algorithm is based on traversing what is known as an "edit-graph". // See Figure 1 from "An O(ND) Difference Algorithm and Its Variations" // by Eugene W. Myers. Since D can be as large as N itself, this is diff --git a/vendor/github.com/google/go-cmp/cmp/internal/value/format.go b/vendor/github.com/google/go-cmp/cmp/internal/value/format.go index abaeca89e..657e50877 100644 --- a/vendor/github.com/google/go-cmp/cmp/internal/value/format.go +++ b/vendor/github.com/google/go-cmp/cmp/internal/value/format.go @@ -8,15 +8,11 @@ package value import ( "fmt" "reflect" + "strconv" "strings" "unicode" - "unicode/utf8" ) -// formatFakePointers controls whether to substitute pointer addresses with nil. -// This is used for deterministic testing. -var formatFakePointers = false - var stringerIface = reflect.TypeOf((*fmt.Stringer)(nil)).Elem() // Format formats the value v as a string. @@ -26,28 +22,35 @@ var stringerIface = reflect.TypeOf((*fmt.Stringer)(nil)).Elem() // * Avoids printing struct fields that are zero // * Prints a nil-slice as being nil, not empty // * Prints map entries in deterministic order -func Format(v reflect.Value, useStringer bool) string { - return formatAny(v, formatConfig{useStringer, true, true, !formatFakePointers}, nil) +func Format(v reflect.Value, conf FormatConfig) string { + conf.printType = true + conf.followPointers = true + conf.realPointers = true + return formatAny(v, conf, nil) } -type formatConfig struct { - useStringer bool // Should the String method be used if available? - printType bool // Should we print the type before the value? - followPointers bool // Should we recursively follow pointers? - realPointers bool // Should we print the real address of pointers? +type FormatConfig struct { + UseStringer bool // Should the String method be used if available? + printType bool // Should we print the type before the value? + PrintPrimitiveType bool // Should we print the type of primitives? + followPointers bool // Should we recursively follow pointers? + realPointers bool // Should we print the real address of pointers? } -func formatAny(v reflect.Value, conf formatConfig, visited map[uintptr]bool) string { +func formatAny(v reflect.Value, conf FormatConfig, visited map[uintptr]bool) string { // TODO: Should this be a multi-line printout in certain situations? if !v.IsValid() { return "" } - if conf.useStringer && v.Type().Implements(stringerIface) && v.CanInterface() { + if conf.UseStringer && v.Type().Implements(stringerIface) && v.CanInterface() { if (v.Kind() == reflect.Ptr || v.Kind() == reflect.Interface) && v.IsNil() { return "" } - return fmt.Sprintf("%q", v.Interface().(fmt.Stringer).String()) + + const stringerPrefix = "s" // Indicates that the String method was used + s := v.Interface().(fmt.Stringer).String() + return stringerPrefix + formatString(s) } switch v.Kind() { @@ -66,7 +69,7 @@ func formatAny(v reflect.Value, conf formatConfig, visited map[uintptr]bool) str case reflect.Complex64, reflect.Complex128: return formatPrimitive(v.Type(), v.Complex(), conf) case reflect.String: - return formatPrimitive(v.Type(), fmt.Sprintf("%q", v), conf) + return formatPrimitive(v.Type(), formatString(v.String()), conf) case reflect.UnsafePointer, reflect.Chan, reflect.Func: return formatPointer(v, conf) case reflect.Ptr: @@ -127,11 +130,13 @@ func formatAny(v reflect.Value, conf formatConfig, visited map[uintptr]bool) str visited = insertPointer(visited, v.Pointer()) var ss []string - subConf := conf - subConf.printType = v.Type().Elem().Kind() == reflect.Interface + keyConf, valConf := conf, conf + keyConf.printType = v.Type().Key().Kind() == reflect.Interface + keyConf.followPointers = false + valConf.printType = v.Type().Elem().Kind() == reflect.Interface for _, k := range SortKeys(v.MapKeys()) { - sk := formatAny(k, formatConfig{realPointers: conf.realPointers}, visited) - sv := formatAny(v.MapIndex(k), subConf, visited) + sk := formatAny(k, keyConf, visited) + sv := formatAny(v.MapIndex(k), valConf, visited) ss = append(ss, fmt.Sprintf("%s: %s", sk, sv)) } s := fmt.Sprintf("{%s}", strings.Join(ss, ", ")) @@ -149,7 +154,7 @@ func formatAny(v reflect.Value, conf formatConfig, visited map[uintptr]bool) str continue // Elide zero value fields } name := v.Type().Field(i).Name - subConf.useStringer = conf.useStringer && isExported(name) + subConf.UseStringer = conf.UseStringer s := formatAny(vv, subConf, visited) ss = append(ss, fmt.Sprintf("%s: %s", name, s)) } @@ -163,14 +168,33 @@ func formatAny(v reflect.Value, conf formatConfig, visited map[uintptr]bool) str } } -func formatPrimitive(t reflect.Type, v interface{}, conf formatConfig) string { - if conf.printType && t.PkgPath() != "" { +func formatString(s string) string { + // Use quoted string if it the same length as a raw string literal. + // Otherwise, attempt to use the raw string form. + qs := strconv.Quote(s) + if len(qs) == 1+len(s)+1 { + return qs + } + + // Disallow newlines to ensure output is a single line. + // Only allow printable runes for readability purposes. + rawInvalid := func(r rune) bool { + return r == '`' || r == '\n' || !unicode.IsPrint(r) + } + if strings.IndexFunc(s, rawInvalid) < 0 { + return "`" + s + "`" + } + return qs +} + +func formatPrimitive(t reflect.Type, v interface{}, conf FormatConfig) string { + if conf.printType && (conf.PrintPrimitiveType || t.PkgPath() != "") { return fmt.Sprintf("%v(%v)", t, v) } return fmt.Sprintf("%v", v) } -func formatPointer(v reflect.Value, conf formatConfig) string { +func formatPointer(v reflect.Value, conf FormatConfig) string { p := v.Pointer() if !conf.realPointers { p = 0 // For deterministic printing purposes @@ -251,9 +275,3 @@ func isZero(v reflect.Value) bool { } return false } - -// isExported reports whether the identifier is exported. -func isExported(id string) bool { - r, _ := utf8.DecodeRuneInString(id) - return unicode.IsUpper(r) -} diff --git a/vendor/github.com/google/go-cmp/cmp/internal/value/sort.go b/vendor/github.com/google/go-cmp/cmp/internal/value/sort.go index ea73cf143..fe8aa27a0 100644 --- a/vendor/github.com/google/go-cmp/cmp/internal/value/sort.go +++ b/vendor/github.com/google/go-cmp/cmp/internal/value/sort.go @@ -24,7 +24,7 @@ func SortKeys(vs []reflect.Value) []reflect.Value { // Deduplicate keys (fails for NaNs). vs2 := vs[:1] for _, v := range vs[1:] { - if v.Interface() != vs2[len(vs2)-1].Interface() { + if isLess(vs2[len(vs2)-1], v) { vs2 = append(vs2, v) } } diff --git a/vendor/github.com/google/go-cmp/cmp/options.go b/vendor/github.com/google/go-cmp/cmp/options.go index a4e159ac4..91d4b066e 100644 --- a/vendor/github.com/google/go-cmp/cmp/options.go +++ b/vendor/github.com/google/go-cmp/cmp/options.go @@ -38,9 +38,8 @@ type Option interface { type applicableOption interface { Option - // apply executes the option and reports whether the option was applied. - // Each option may mutate s. - apply(s *state, vx, vy reflect.Value) bool + // apply executes the option, which may mutate s or panic. + apply(s *state, vx, vy reflect.Value) } // coreOption represents the following types: @@ -85,7 +84,7 @@ func (opts Options) filter(s *state, vx, vy reflect.Value, t reflect.Type) (out return out } -func (opts Options) apply(s *state, _, _ reflect.Value) bool { +func (opts Options) apply(s *state, _, _ reflect.Value) { const warning = "ambiguous set of applicable options" const help = "consider using filters to ensure at most one Comparer or Transformer may apply" var ss []string @@ -196,7 +195,7 @@ 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) bool { return true } +func (ignore) apply(_ *state, _, _ reflect.Value) { return } func (ignore) String() string { return "Ignore()" } // invalid is a sentinel Option type to indicate that some options could not @@ -204,7 +203,7 @@ func (ignore) String() string type invalid struct{ core } func (invalid) filter(_ *state, _, _ reflect.Value, _ reflect.Type) applicableOption { return invalid{} } -func (invalid) apply(s *state, _, _ reflect.Value) bool { +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)) } @@ -215,9 +214,12 @@ func (invalid) apply(s *state, _, _ reflect.Value) bool { // The transformer f must be a function "func(T) R" that converts values of // type T to those of type R and is implicitly filtered to input values // assignable to T. The transformer must not mutate T in any way. -// If T and R are the same type, an additional filter must be applied to -// act as the base case to prevent an infinite recursion applying the same -// transform to itself (see the SortedSlice example). +// +// To help prevent some cases of infinite recursive cycles applying the +// same transform to the output of itself (e.g., in the case where the +// 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. // // 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. @@ -248,14 +250,21 @@ type transformer struct { func (tr *transformer) isFiltered() bool { return tr.typ != nil } -func (tr *transformer) filter(_ *state, _, _ reflect.Value, t reflect.Type) applicableOption { +func (tr *transformer) filter(s *state, _, _ reflect.Value, t reflect.Type) applicableOption { + for i := len(s.curPath) - 1; i >= 0; i-- { + 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 + } + } if tr.typ == nil || t.AssignableTo(tr.typ) { return tr } return nil } -func (tr *transformer) apply(s *state, vx, vy reflect.Value) bool { +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}) @@ -264,7 +273,6 @@ func (tr *transformer) apply(s *state, vx, vy reflect.Value) bool { vx = s.callTRFunc(tr.fnc, vx) vy = s.callTRFunc(tr.fnc, vy) s.compareAny(vx, vy) - return true } func (tr transformer) String() string { @@ -310,10 +318,9 @@ func (cm *comparer) filter(_ *state, _, _ reflect.Value, t reflect.Type) applica return nil } -func (cm *comparer) apply(s *state, vx, vy reflect.Value) bool { +func (cm *comparer) apply(s *state, vx, vy reflect.Value) { eq := s.callTTBFunc(cm.fnc, vx, vy) s.report(eq, vx, vy) - return true } func (cm comparer) String() string { @@ -348,7 +355,7 @@ func (cm comparer) String() string { // all unexported fields on specified struct types. func AllowUnexported(types ...interface{}) Option { if !supportAllowUnexported { - panic("AllowUnexported is not supported on App Engine Classic or GopherJS") + panic("AllowUnexported is not supported on purego builds, Google App Engine Standard, or GopherJS") } m := make(map[reflect.Type]bool) for _, typ := range types { diff --git a/vendor/github.com/google/go-cmp/cmp/path.go b/vendor/github.com/google/go-cmp/cmp/path.go index 0c2eb333f..c08a3cf80 100644 --- a/vendor/github.com/google/go-cmp/cmp/path.go +++ b/vendor/github.com/google/go-cmp/cmp/path.go @@ -79,6 +79,11 @@ type ( PathStep Name() string Func() reflect.Value + + // Option returns the originally constructed Transformer option. + // The == operator can be used to detect the exact option used. + Option() Option + isTransform() } ) @@ -94,10 +99,21 @@ func (pa *Path) pop() { // Last returns the last PathStep in the Path. // If the path is empty, this returns a non-nil PathStep that reports a nil Type. func (pa Path) Last() PathStep { - if len(pa) > 0 { - return pa[len(pa)-1] + return pa.Index(-1) +} + +// Index returns the ith step in the Path and supports negative indexing. +// A negative index starts counting from the tail of the Path such that -1 +// refers to the last step, -2 refers to the second-to-last step, and so on. +// If index is invalid, this returns a non-nil PathStep that reports a nil Type. +func (pa Path) Index(i int) PathStep { + if i < 0 { + i = len(pa) + i } - return pathStep{} + if i < 0 || i >= len(pa) { + return pathStep{} + } + return pa[i] } // String returns the simplified path to a node. @@ -150,13 +166,12 @@ func (pa Path) GoString() string { ssPost = append(ssPost, ")") continue case *typeAssertion: - // Elide type assertions immediately following a transform to - // prevent overly verbose path printouts. - // Some transforms return interface{} because of Go's lack of - // generics, but typically take in and return the exact same - // concrete type. Other times, the transform creates an anonymous - // struct, which will be very verbose to print. - if _, ok := nextStep.(*transform); ok { + // As a special-case, elide type assertions on anonymous types + // since they are typically generated dynamically and can be very + // verbose. For example, some transforms return interface{} because + // of Go's lack of generics, but typically take in and return the + // exact same concrete type. + if s.Type().PkgPath() == "" { continue } } @@ -250,6 +265,7 @@ func (sf structField) Name() string { return sf.name } func (sf structField) Index() int { return sf.idx } func (tf transform) Name() string { return tf.trans.name } func (tf transform) Func() reflect.Value { return tf.trans.fnc } +func (tf transform) Option() Option { return tf.trans } func (pathStep) isPathStep() {} func (sliceIndex) isSliceIndex() {} diff --git a/vendor/github.com/google/go-cmp/cmp/reporter.go b/vendor/github.com/google/go-cmp/cmp/reporter.go index a21d0cded..20e9f18e0 100644 --- a/vendor/github.com/google/go-cmp/cmp/reporter.go +++ b/vendor/github.com/google/go-cmp/cmp/reporter.go @@ -30,12 +30,12 @@ func (r *defaultReporter) Report(x, y reflect.Value, eq bool, p Path) { const maxLines = 256 r.ndiffs++ if r.nbytes < maxBytes && r.nlines < maxLines { - sx := value.Format(x, true) - sy := value.Format(y, true) + sx := value.Format(x, value.FormatConfig{UseStringer: true}) + sy := value.Format(y, value.FormatConfig{UseStringer: true}) if sx == sy { - // Stringer is not helpful, so rely on more exact formatting. - sx = value.Format(x, false) - sy = value.Format(y, false) + // Unhelpful output, so use more exact formatting. + sx = value.Format(x, value.FormatConfig{PrintPrimitiveType: true}) + sy = value.Format(y, value.FormatConfig{PrintPrimitiveType: true}) } s := fmt.Sprintf("%#v:\n\t-: %s\n\t+: %s\n", p, sx, sy) r.diffs = append(r.diffs, s) @@ -49,5 +49,5 @@ func (r *defaultReporter) String() string { if r.ndiffs == len(r.diffs) { return s } - return fmt.Sprintf("%s... %d more differences ...", s, len(r.diffs)-r.ndiffs) + return fmt.Sprintf("%s... %d more differences ...", s, r.ndiffs-len(r.diffs)) } diff --git a/vendor/github.com/google/go-cmp/cmp/unsafe_panic.go b/vendor/github.com/google/go-cmp/cmp/unsafe_panic.go index 0d44987f5..d1518eb3a 100644 --- a/vendor/github.com/google/go-cmp/cmp/unsafe_panic.go +++ b/vendor/github.com/google/go-cmp/cmp/unsafe_panic.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE.md file. -// +build appengine js +// +build purego appengine js package cmp diff --git a/vendor/github.com/google/go-cmp/cmp/unsafe_reflect.go b/vendor/github.com/google/go-cmp/cmp/unsafe_reflect.go index 81fb82632..579b65507 100644 --- a/vendor/github.com/google/go-cmp/cmp/unsafe_reflect.go +++ b/vendor/github.com/google/go-cmp/cmp/unsafe_reflect.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE.md file. -// +build !appengine,!js +// +build !purego,!appengine,!js package cmp diff --git a/vendor/gotest.tools/LICENSE b/vendor/gotest.tools/LICENSE index d64569567..aeaa2fac3 100644 --- a/vendor/gotest.tools/LICENSE +++ b/vendor/gotest.tools/LICENSE @@ -1,202 +1,13 @@ +Copyright 2018 gotest.tools authors - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + http://www.apache.org/licenses/LICENSE-2.0 - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/vendor/gotest.tools/README.md b/vendor/gotest.tools/README.md index f5df20431..3155723dd 100644 --- a/vendor/gotest.tools/README.md +++ b/vendor/gotest.tools/README.md @@ -3,7 +3,7 @@ A collection of packages to augment `testing` and support common patterns. [![GoDoc](https://godoc.org/gotest.tools?status.svg)](https://godoc.org/gotest.tools) -[![CircleCI](https://circleci.com/gh/gotestyourself/gotestyourself/tree/master.svg?style=shield)](https://circleci.com/gh/gotestyourself/gotestyourself/tree/master) +[![CircleCI](https://circleci.com/gh/gotestyourself/gotest.tools/tree/master.svg?style=shield)](https://circleci.com/gh/gotestyourself/gotest.tools/tree/master) [![Go Reportcard](https://goreportcard.com/badge/gotest.tools)](https://goreportcard.com/report/gotest.tools) @@ -29,3 +29,7 @@ A collection of packages to augment `testing` and support common patterns. * [gotest.tools/gotestsum](https://github.com/gotestyourself/gotestsum) - go test runner with custom output * [maxbrunsfeld/counterfeiter](https://github.com/maxbrunsfeld/counterfeiter) - generate fakes for interfaces * [jonboulle/clockwork](https://github.com/jonboulle/clockwork) - a fake clock for testing code that uses `time` + +## Contributing + +See [CONTRIBUTING.md](CONTRIBUTING.md). diff --git a/vendor/gotest.tools/assert/cmp/compare.go b/vendor/gotest.tools/assert/cmp/compare.go index ae03749e2..cf48d887a 100644 --- a/vendor/gotest.tools/assert/cmp/compare.go +++ b/vendor/gotest.tools/assert/cmp/compare.go @@ -4,6 +4,7 @@ package cmp // import "gotest.tools/assert/cmp" import ( "fmt" "reflect" + "regexp" "strings" "github.com/google/go-cmp/cmp" @@ -58,6 +59,39 @@ func toResult(success bool, msg string) Result { return ResultFailure(msg) } +// RegexOrPattern may be either a *regexp.Regexp or a string that is a valid +// regexp pattern. +type RegexOrPattern interface{} + +// Regexp succeeds if value v matches regular expression re. +// +// Example: +// assert.Assert(t, cmp.Regexp("^[0-9a-f]{32}$", str)) +// r := regexp.MustCompile("^[0-9a-f]{32}$") +// assert.Assert(t, cmp.Regexp(r, str)) +func Regexp(re RegexOrPattern, v string) Comparison { + match := func(re *regexp.Regexp) Result { + return toResult( + re.MatchString(v), + fmt.Sprintf("value %q does not match regexp %q", v, re.String())) + } + + return func() Result { + switch regex := re.(type) { + case *regexp.Regexp: + return match(regex) + case string: + re, err := regexp.Compile(regex) + if err != nil { + return ResultFailure(err.Error()) + } + return match(re) + default: + return ResultFailure(fmt.Sprintf("invalid type %T for regex pattern", regex)) + } + } +} + // Equal succeeds if x == y. See assert.Equal for full documentation. func Equal(x, y interface{}) Comparison { return func() Result { @@ -186,7 +220,7 @@ func Error(err error, message string) Comparison { return ResultFailure("expected an error, got nil") case err.Error() != message: return ResultFailure(fmt.Sprintf( - "expected error %q, got %+v", message, err)) + "expected error %q, got %s", message, formatErrorMessage(err))) } return ResultSuccess } @@ -201,12 +235,22 @@ func ErrorContains(err error, substring string) Comparison { return ResultFailure("expected an error, got nil") case !strings.Contains(err.Error(), substring): return ResultFailure(fmt.Sprintf( - "expected error to contain %q, got %+v", substring, err)) + "expected error to contain %q, got %s", substring, formatErrorMessage(err))) } return ResultSuccess } } +func formatErrorMessage(err error) string { + if _, ok := err.(interface { + Cause() error + }); ok { + return fmt.Sprintf("%q\n%+v", err, err) + } + // This error was not wrapped with github.com/pkg/errors + return fmt.Sprintf("%q", err) +} + // Nil succeeds if obj is a nil interface, pointer, or function. // // Use NilError() for comparing errors. Use Len(obj, 0) for comparing slices, diff --git a/vendor/gotest.tools/assert/cmp/result.go b/vendor/gotest.tools/assert/cmp/result.go index 7c3c37dd7..204edda4b 100644 --- a/vendor/gotest.tools/assert/cmp/result.go +++ b/vendor/gotest.tools/assert/cmp/result.go @@ -9,31 +9,37 @@ import ( "gotest.tools/internal/source" ) -// Result of a Comparison. +// A Result of a Comparison. type Result interface { Success() bool } -type result struct { +// StringResult is an implementation of Result that reports the error message +// string verbatim and does not provide any templating or formatting of the +// message. +type StringResult struct { success bool message string } -func (r result) Success() bool { +// Success returns true if the comparison was successful. +func (r StringResult) Success() bool { return r.success } -func (r result) FailureMessage() string { +// FailureMessage returns the message used to provide additional information +// about the failure. +func (r StringResult) FailureMessage() string { return r.message } // ResultSuccess is a constant which is returned by a ComparisonWithResult to // indicate success. -var ResultSuccess = result{success: true} +var ResultSuccess = StringResult{success: true} // ResultFailure returns a failed Result with a failure message. -func ResultFailure(message string) Result { - return result{message: message} +func ResultFailure(message string) StringResult { + return StringResult{message: message} } // ResultFromError returns ResultSuccess if err is nil. Otherwise ResultFailure diff --git a/vendor/gotest.tools/assert/result.go b/vendor/gotest.tools/assert/result.go index 3900264d0..949d93961 100644 --- a/vendor/gotest.tools/assert/result.go +++ b/vendor/gotest.tools/assert/result.go @@ -70,7 +70,6 @@ func filterPrintableExpr(args []ast.Expr) []ast.Expr { result[i] = starExpr.X continue } - result[i] = nil } return result } diff --git a/vendor/gotest.tools/internal/difflib/difflib.go b/vendor/gotest.tools/internal/difflib/difflib.go index 5efa99c1d..b6f486b9c 100644 --- a/vendor/gotest.tools/internal/difflib/difflib.go +++ b/vendor/gotest.tools/internal/difflib/difflib.go @@ -1,4 +1,4 @@ -/* Package difflib is a partial port of Python difflib module. +/*Package difflib is a partial port of Python difflib module. Original source: https://github.com/pmezard/go-difflib @@ -20,12 +20,14 @@ func max(a, b int) int { return b } +// Match stores line numbers of size of match type Match struct { A int B int Size int } +// OpCode identifies the type of diff type OpCode struct { Tag byte I1 int @@ -73,19 +75,20 @@ type SequenceMatcher struct { opCodes []OpCode } +// NewMatcher returns a new SequenceMatcher func NewMatcher(a, b []string) *SequenceMatcher { m := SequenceMatcher{autoJunk: true} m.SetSeqs(a, b) return &m } -// Set two sequences to be compared. +// SetSeqs sets two sequences to be compared. func (m *SequenceMatcher) SetSeqs(a, b []string) { m.SetSeq1(a) m.SetSeq2(b) } -// Set the first sequence to be compared. The second sequence to be compared is +// SetSeq1 sets the first sequence to be compared. The second sequence to be compared is // not changed. // // SequenceMatcher computes and caches detailed information about the second @@ -103,7 +106,7 @@ func (m *SequenceMatcher) SetSeq1(a []string) { m.opCodes = nil } -// Set the second sequence to be compared. The first sequence to be compared is +// SetSeq2 sets the second sequence to be compared. The first sequence to be compared is // not changed. func (m *SequenceMatcher) SetSeq2(b []string) { if &b == &m.b { @@ -129,12 +132,12 @@ func (m *SequenceMatcher) chainB() { m.bJunk = map[string]struct{}{} if m.IsJunk != nil { junk := m.bJunk - for s, _ := range b2j { + for s := range b2j { if m.IsJunk(s) { junk[s] = struct{}{} } } - for s, _ := range junk { + for s := range junk { delete(b2j, s) } } @@ -149,7 +152,7 @@ func (m *SequenceMatcher) chainB() { popular[s] = struct{}{} } } - for s, _ := range popular { + for s := range popular { delete(b2j, s) } } @@ -259,7 +262,7 @@ func (m *SequenceMatcher) findLongestMatch(alo, ahi, blo, bhi int) Match { return Match{A: besti, B: bestj, Size: bestsize} } -// Return list of triples describing matching subsequences. +// GetMatchingBlocks returns a list of triples describing matching subsequences. // // Each triple is of the form (i, j, n), and means that // a[i:i+n] == b[j:j+n]. The triples are monotonically increasing in @@ -323,7 +326,7 @@ func (m *SequenceMatcher) GetMatchingBlocks() []Match { return m.matchingBlocks } -// Return list of 5-tuples describing how to turn a into b. +// GetOpCodes returns a list of 5-tuples describing how to turn a into b. // // Each tuple is of the form (tag, i1, i2, j1, j2). The first tuple // has i1 == j1 == 0, and remaining tuples have i1 == the i2 from the @@ -374,7 +377,7 @@ func (m *SequenceMatcher) GetOpCodes() []OpCode { return m.opCodes } -// Isolate change clusters by eliminating ranges with no changes. +// GetGroupedOpCodes isolates change clusters by eliminating ranges with no changes. // // Return a generator of groups with up to n lines of context. // Each group is in the same format as returned by GetOpCodes(). @@ -384,7 +387,7 @@ func (m *SequenceMatcher) GetGroupedOpCodes(n int) [][]OpCode { } codes := m.GetOpCodes() if len(codes) == 0 { - codes = []OpCode{OpCode{'e', 0, 1, 0, 1}} + codes = []OpCode{{'e', 0, 1, 0, 1}} } // Fixup leading and trailing groups if they show no changes. if codes[0].Tag == 'e' { diff --git a/vendor/gotest.tools/internal/source/defers.go b/vendor/gotest.tools/internal/source/defers.go new file mode 100644 index 000000000..66cfafbb6 --- /dev/null +++ b/vendor/gotest.tools/internal/source/defers.go @@ -0,0 +1,53 @@ +package source + +import ( + "go/ast" + "go/token" + + "github.com/pkg/errors" +) + +func scanToDeferLine(fileset *token.FileSet, node ast.Node, lineNum int) ast.Node { + var matchedNode ast.Node + ast.Inspect(node, func(node ast.Node) bool { + switch { + case node == nil || matchedNode != nil: + return false + case fileset.Position(node.End()).Line == lineNum: + if funcLit, ok := node.(*ast.FuncLit); ok { + matchedNode = funcLit + return false + } + } + return true + }) + debug("defer line node: %s", debugFormatNode{matchedNode}) + return matchedNode +} + +func guessDefer(node ast.Node) (ast.Node, error) { + defers := collectDefers(node) + switch len(defers) { + case 0: + return nil, errors.New("failed to expression in defer") + case 1: + return defers[0].Call, nil + default: + return nil, errors.Errorf( + "ambiguous call expression: multiple (%d) defers in call block", + len(defers)) + } +} + +func collectDefers(node ast.Node) []*ast.DeferStmt { + var defers []*ast.DeferStmt + ast.Inspect(node, func(node ast.Node) bool { + if d, ok := node.(*ast.DeferStmt); ok { + defers = append(defers, d) + debug("defer: %s", debugFormatNode{d}) + return false + } + return true + }) + return defers +} diff --git a/vendor/gotest.tools/internal/source/source.go b/vendor/gotest.tools/internal/source/source.go index a05933cc3..8a5d0e8d3 100644 --- a/vendor/gotest.tools/internal/source/source.go +++ b/vendor/gotest.tools/internal/source/source.go @@ -24,106 +24,12 @@ func FormattedCallExprArg(stackIndex int, argPos int) (string, error) { if err != nil { return "", err } + if argPos >= len(args) { + return "", errors.New("failed to find expression") + } return FormatNode(args[argPos]) } -func getNodeAtLine(filename string, lineNum int) (ast.Node, error) { - fileset := token.NewFileSet() - astFile, err := parser.ParseFile(fileset, filename, nil, parser.AllErrors) - if err != nil { - return nil, errors.Wrapf(err, "failed to parse source file: %s", filename) - } - - node := scanToLine(fileset, astFile, lineNum) - if node == nil { - return nil, errors.Errorf( - "failed to find an expression on line %d in %s", lineNum, filename) - } - return node, nil -} - -func scanToLine(fileset *token.FileSet, node ast.Node, lineNum int) ast.Node { - v := &scanToLineVisitor{lineNum: lineNum, fileset: fileset} - ast.Walk(v, node) - return v.matchedNode -} - -type scanToLineVisitor struct { - lineNum int - matchedNode ast.Node - fileset *token.FileSet -} - -func (v *scanToLineVisitor) Visit(node ast.Node) ast.Visitor { - if node == nil || v.matchedNode != nil { - return nil - } - if v.nodePosition(node).Line == v.lineNum { - v.matchedNode = node - return nil - } - return v -} - -// In golang 1.9 the line number changed from being the line where the statement -// ended to the line where the statement began. -func (v *scanToLineVisitor) nodePosition(node ast.Node) token.Position { - if goVersionBefore19 { - return v.fileset.Position(node.End()) - } - return v.fileset.Position(node.Pos()) -} - -var goVersionBefore19 = isGOVersionBefore19() - -func isGOVersionBefore19() bool { - version := runtime.Version() - // not a release version - if !strings.HasPrefix(version, "go") { - return false - } - version = strings.TrimPrefix(version, "go") - parts := strings.Split(version, ".") - if len(parts) < 2 { - return false - } - minor, err := strconv.ParseInt(parts[1], 10, 32) - return err == nil && parts[0] == "1" && minor < 9 -} - -func getCallExprArgs(node ast.Node) ([]ast.Expr, error) { - visitor := &callExprVisitor{} - ast.Walk(visitor, node) - if visitor.expr == nil { - return nil, errors.New("failed to find call expression") - } - return visitor.expr.Args, nil -} - -type callExprVisitor struct { - expr *ast.CallExpr -} - -func (v *callExprVisitor) Visit(node ast.Node) ast.Visitor { - if v.expr != nil || node == nil { - return nil - } - debug("visit (%T): %s", node, debugFormatNode{node}) - - if callExpr, ok := node.(*ast.CallExpr); ok { - v.expr = callExpr - return nil - } - return v -} - -// FormatNode using go/format.Node and return the result as a string -func FormatNode(node ast.Node) (string, error) { - buf := new(bytes.Buffer) - err := format.Node(buf, token.NewFileSet(), node) - return buf.String(), err -} - // CallExprArgs returns the ast.Expr slice for the args of an ast.CallExpr at // the index in the call stack. func CallExprArgs(stackIndex int) ([]ast.Expr, error) { @@ -137,12 +43,109 @@ func CallExprArgs(stackIndex int) ([]ast.Expr, error) { if err != nil { return nil, err } - debug("found node (%T): %s", node, debugFormatNode{node}) + debug("found node: %s", debugFormatNode{node}) return getCallExprArgs(node) } -var debugEnabled = os.Getenv("GOTESTYOURSELF_DEBUG") != "" +func getNodeAtLine(filename string, lineNum int) (ast.Node, error) { + fileset := token.NewFileSet() + astFile, err := parser.ParseFile(fileset, filename, nil, parser.AllErrors) + if err != nil { + return nil, errors.Wrapf(err, "failed to parse source file: %s", filename) + } + + if node := scanToLine(fileset, astFile, lineNum); node != nil { + return node, nil + } + if node := scanToDeferLine(fileset, astFile, lineNum); node != nil { + node, err := guessDefer(node) + if err != nil || node != nil { + return node, err + } + } + return nil, errors.Errorf( + "failed to find an expression on line %d in %s", lineNum, filename) +} + +func scanToLine(fileset *token.FileSet, node ast.Node, lineNum int) ast.Node { + var matchedNode ast.Node + ast.Inspect(node, func(node ast.Node) bool { + switch { + case node == nil || matchedNode != nil: + return false + case nodePosition(fileset, node).Line == lineNum: + matchedNode = node + return false + } + return true + }) + return matchedNode +} + +// In golang 1.9 the line number changed from being the line where the statement +// ended to the line where the statement began. +func nodePosition(fileset *token.FileSet, node ast.Node) token.Position { + if goVersionBefore19 { + return fileset.Position(node.End()) + } + return fileset.Position(node.Pos()) +} + +var goVersionBefore19 = func() bool { + version := runtime.Version() + // not a release version + if !strings.HasPrefix(version, "go") { + return false + } + version = strings.TrimPrefix(version, "go") + parts := strings.Split(version, ".") + if len(parts) < 2 { + return false + } + minor, err := strconv.ParseInt(parts[1], 10, 32) + return err == nil && parts[0] == "1" && minor < 9 +}() + +func getCallExprArgs(node ast.Node) ([]ast.Expr, error) { + visitor := &callExprVisitor{} + ast.Walk(visitor, node) + if visitor.expr == nil { + return nil, errors.New("failed to find call expression") + } + debug("callExpr: %s", debugFormatNode{visitor.expr}) + return visitor.expr.Args, nil +} + +type callExprVisitor struct { + expr *ast.CallExpr +} + +func (v *callExprVisitor) Visit(node ast.Node) ast.Visitor { + if v.expr != nil || node == nil { + return nil + } + debug("visit: %s", debugFormatNode{node}) + + switch typed := node.(type) { + case *ast.CallExpr: + v.expr = typed + return nil + case *ast.DeferStmt: + ast.Walk(v, typed.Call.Fun) + return nil + } + return v +} + +// FormatNode using go/format.Node and return the result as a string +func FormatNode(node ast.Node) (string, error) { + buf := new(bytes.Buffer) + err := format.Node(buf, token.NewFileSet(), node) + return buf.String(), err +} + +var debugEnabled = os.Getenv("GOTESTTOOLS_DEBUG") != "" func debug(format string, args ...interface{}) { if debugEnabled { @@ -159,5 +162,5 @@ func (n debugFormatNode) String() string { if err != nil { return fmt.Sprintf("failed to format %s: %s", n.Node, err) } - return out + return fmt.Sprintf("(%T) %s", n.Node, out) }