Skip to content

Commit

Permalink
any over interface{}
Browse files Browse the repository at this point in the history
  • Loading branch information
mrwormhole committed Sep 18, 2023
1 parent c3ad843 commit 8b6733e
Show file tree
Hide file tree
Showing 20 changed files with 190 additions and 189 deletions.
16 changes: 8 additions & 8 deletions cmp/cmpopts/equate.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import (
"github.com/google/go-cmp/cmp"
)

func equateAlways(_, _ interface{}) bool { return true }
func equateAlways(_, _ any) bool { return true }

// EquateEmpty returns a [cmp.Comparer] option that determines all maps and slices
// with a length of zero to be equal, regardless of whether they are nil.
Expand All @@ -25,7 +25,7 @@ func EquateEmpty() cmp.Option {
return cmp.FilterValues(isEmpty, cmp.Comparer(equateAlways))
}

func isEmpty(x, y interface{}) bool {
func isEmpty(x, y any) bool {
vx, vy := reflect.ValueOf(x), reflect.ValueOf(y)
return (x != nil && y != nil && vx.Type() == vy.Type()) &&
(vx.Kind() == reflect.Slice || vx.Kind() == reflect.Map) &&
Expand Down Expand Up @@ -140,17 +140,17 @@ func EquateErrors() cmp.Option {
}

// areConcreteErrors reports whether x and y are types that implement error.
// The input types are deliberately of the interface{} type rather than the
// The input types are deliberately of the any type rather than the
// error type so that we can handle situations where the current type is an
// interface{}, but the underlying concrete types both happen to implement
// any, but the underlying concrete types both happen to implement
// the error interface.
func areConcreteErrors(x, y interface{}) bool {
func areConcreteErrors(x, y any) bool {
_, ok1 := x.(error)
_, ok2 := y.(error)
return ok1 && ok2
}

func compareErrors(x, y interface{}) bool {
func compareErrors(x, y any) bool {
xe := x.(error)
ye := y.(error)
return errors.Is(xe, ye) || errors.Is(ye, xe)
Expand All @@ -163,7 +163,7 @@ func compareErrors(x, y interface{}) bool {
// safe for direct == comparison. For example, [net/netip.Addr] is documented
// as being semantically safe to use with ==, while [time.Time] is documented
// to discourage the use of == on time values.
func EquateComparable(typs ...interface{}) cmp.Option {
func EquateComparable(typs ...any) cmp.Option {
types := make(typesFilter)
for _, typ := range typs {
switch t := reflect.TypeOf(typ); {
Expand All @@ -182,4 +182,4 @@ type typesFilter map[reflect.Type]bool

func (tf typesFilter) filter(p cmp.Path) bool { return tf[p.Last().Type()] }

func equateAny(x, y interface{}) bool { return x == y }
func equateAny(x, y any) bool { return x == y }
2 changes: 1 addition & 1 deletion cmp/cmpopts/example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,4 +127,4 @@ var t fakeT

type fakeT struct{}

func (t fakeT) Errorf(format string, args ...interface{}) { fmt.Printf(format+"\n", args...) }
func (t fakeT) Errorf(format string, args ...any) { fmt.Printf(format+"\n", args...) }
18 changes: 9 additions & 9 deletions cmp/cmpopts/ignore.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,21 +21,21 @@ import (
//
// The name may be a dot-delimited string (e.g., "Foo.Bar") to ignore a
// specific sub-field that is embedded or nested within the parent struct.
func IgnoreFields(typ interface{}, names ...string) cmp.Option {
func IgnoreFields(typ any, names ...string) cmp.Option {
sf := newStructFilter(typ, names...)
return cmp.FilterPath(sf.filter, cmp.Ignore())
}

// IgnoreTypes returns an [cmp.Option] that ignores all values assignable to
// certain types, which are specified by passing in a value of each type.
func IgnoreTypes(typs ...interface{}) cmp.Option {
func IgnoreTypes(typs ...any) cmp.Option {
tf := newTypeFilter(typs...)
return cmp.FilterPath(tf.filter, cmp.Ignore())
}

type typeFilter []reflect.Type

func newTypeFilter(typs ...interface{}) (tf typeFilter) {
func newTypeFilter(typs ...any) (tf typeFilter) {
for _, typ := range typs {
t := reflect.TypeOf(typ)
if t == nil {
Expand Down Expand Up @@ -63,14 +63,14 @@ func (tf typeFilter) filter(p cmp.Path) bool {
// values assignable to certain interface types. These interfaces are specified
// by passing in an anonymous struct with the interface types embedded in it.
// For example, to ignore [sync.Locker], pass in struct{sync.Locker}{}.
func IgnoreInterfaces(ifaces interface{}) cmp.Option {
func IgnoreInterfaces(ifaces any) cmp.Option {
tf := newIfaceFilter(ifaces)
return cmp.FilterPath(tf.filter, cmp.Ignore())
}

type ifaceFilter []reflect.Type

func newIfaceFilter(ifaces interface{}) (tf ifaceFilter) {
func newIfaceFilter(ifaces any) (tf ifaceFilter) {
t := reflect.TypeOf(ifaces)
if ifaces == nil || t.Name() != "" || t.Kind() != reflect.Struct {
panic("input must be an anonymous struct")
Expand Down Expand Up @@ -116,14 +116,14 @@ func (tf ifaceFilter) filter(p cmp.Path) bool {
// Avoid ignoring unexported fields of a type which you do not control (i.e. a
// type from another repository), as changes to the implementation of such types
// may change how the comparison behaves. Prefer a custom [cmp.Comparer] instead.
func IgnoreUnexported(typs ...interface{}) cmp.Option {
func IgnoreUnexported(typs ...any) cmp.Option {
ux := newUnexportedFilter(typs...)
return cmp.FilterPath(ux.filter, cmp.Ignore())
}

type unexportedFilter struct{ m map[reflect.Type]bool }

func newUnexportedFilter(typs ...interface{}) unexportedFilter {
func newUnexportedFilter(typs ...any) unexportedFilter {
ux := unexportedFilter{m: make(map[reflect.Type]bool)}
for _, typ := range typs {
t := reflect.TypeOf(typ)
Expand Down Expand Up @@ -152,7 +152,7 @@ func isExported(id string) bool {
// The discard function must be of the form "func(T) bool" which is used to
// ignore slice elements of type V, where V is assignable to T.
// Elements are ignored if the function reports true.
func IgnoreSliceElements(discardFunc interface{}) cmp.Option {
func IgnoreSliceElements(discardFunc any) cmp.Option {
vf := reflect.ValueOf(discardFunc)
if !function.IsType(vf.Type(), function.ValuePredicate) || vf.IsNil() {
panic(fmt.Sprintf("invalid discard function: %T", discardFunc))
Expand Down Expand Up @@ -180,7 +180,7 @@ func IgnoreSliceElements(discardFunc interface{}) cmp.Option {
// The discard function must be of the form "func(T, R) bool" which is used to
// ignore map entries of type K and V, where K and V are assignable to T and R.
// Entries are ignored if the function reports true.
func IgnoreMapEntries(discardFunc interface{}) cmp.Option {
func IgnoreMapEntries(discardFunc any) cmp.Option {
vf := reflect.ValueOf(discardFunc)
if !function.IsType(vf.Type(), function.KeyValuePredicate) || vf.IsNil() {
panic(fmt.Sprintf("invalid discard function: %T", discardFunc))
Expand Down
12 changes: 6 additions & 6 deletions cmp/cmpopts/sort.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import (
// !less(y, x) for two elements x and y, their relative order is maintained.
//
// SortSlices can be used in conjunction with [EquateEmpty].
func SortSlices(lessFunc interface{}) cmp.Option {
func SortSlices(lessFunc any) cmp.Option {
vf := reflect.ValueOf(lessFunc)
if !function.IsType(vf.Type(), function.Less) || vf.IsNil() {
panic(fmt.Sprintf("invalid less function: %T", lessFunc))
Expand All @@ -40,7 +40,7 @@ type sliceSorter struct {
fnc reflect.Value // func(T, T) bool
}

func (ss sliceSorter) filter(x, y interface{}) bool {
func (ss sliceSorter) filter(x, y any) bool {
vx, vy := reflect.ValueOf(x), reflect.ValueOf(y)
if !(x != nil && y != nil && vx.Type() == vy.Type()) ||
!(vx.Kind() == reflect.Slice && vx.Type().Elem().AssignableTo(ss.in)) ||
Expand All @@ -53,7 +53,7 @@ func (ss sliceSorter) filter(x, y interface{}) bool {
ok2 := sort.SliceIsSorted(y, func(i, j int) bool { return ss.less(vy, i, j) })
return !ok1 || !ok2
}
func (ss sliceSorter) sort(x interface{}) interface{} {
func (ss sliceSorter) sort(x any) any {
src := reflect.ValueOf(x)
dst := reflect.MakeSlice(src.Type(), src.Len(), src.Len())
for i := 0; i < src.Len(); i++ {
Expand Down Expand Up @@ -97,7 +97,7 @@ func (ss sliceSorter) less(v reflect.Value, i, j int) bool {
// - Total: if x != y, then either less(x, y) or less(y, x)
//
// SortMaps can be used in conjunction with [EquateEmpty].
func SortMaps(lessFunc interface{}) cmp.Option {
func SortMaps(lessFunc any) cmp.Option {
vf := reflect.ValueOf(lessFunc)
if !function.IsType(vf.Type(), function.Less) || vf.IsNil() {
panic(fmt.Sprintf("invalid less function: %T", lessFunc))
Expand All @@ -111,13 +111,13 @@ type mapSorter struct {
fnc reflect.Value // func(T, T) bool
}

func (ms mapSorter) filter(x, y interface{}) bool {
func (ms mapSorter) filter(x, y any) bool {
vx, vy := reflect.ValueOf(x), reflect.ValueOf(y)
return (x != nil && y != nil && vx.Type() == vy.Type()) &&
(vx.Kind() == reflect.Map && vx.Type().Key().AssignableTo(ms.in)) &&
(vx.Len() != 0 || vy.Len() != 0)
}
func (ms mapSorter) sort(x interface{}) interface{} {
func (ms mapSorter) sort(x any) any {
src := reflect.ValueOf(x)
outType := reflect.StructOf([]reflect.StructField{
{Name: "K", Type: src.Type().Key()},
Expand Down
4 changes: 2 additions & 2 deletions cmp/cmpopts/struct_filter.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import (
//
// The name may be a dot-delimited string (e.g., "Foo.Bar") to select a
// specific sub-field that is embedded or nested within the parent struct.
func filterField(typ interface{}, name string, opt cmp.Option) cmp.Option {
func filterField(typ any, name string, opt cmp.Option) cmp.Option {
// TODO: This is currently unexported over concerns of how helper filters
// can be composed together easily.
// TODO: Add tests for FilterField.
Expand All @@ -32,7 +32,7 @@ type structFilter struct {
ft fieldTree // Tree of fields to match on
}

func newStructFilter(typ interface{}, names ...string) structFilter {
func newStructFilter(typ any, names ...string) structFilter {
// TODO: Perhaps allow * as a special identifier to allow ignoring any
// number of path steps until the next field match?
// This could be useful when a concrete struct gets transformed into
Expand Down
36 changes: 18 additions & 18 deletions cmp/cmpopts/util_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ type (
ParentStruct
}

EmptyInterface interface{}
EmptyInterface any
)

func TestOptions(t *testing.T) {
Expand Down Expand Up @@ -99,7 +99,7 @@ func TestOptions(t *testing.T) {

tests := []struct {
label string // Test name
x, y interface{} // Input values to compare
x, y any // Input values to compare
opts []cmp.Option // Input options
wantEqual bool // Whether the inputs are equal
wantPanic bool // Whether Equal should panic
Expand Down Expand Up @@ -836,28 +836,28 @@ func TestOptions(t *testing.T) {
reason: "equal because mismatching unexported fields are ignored",
}, {
label: "IgnoreTypes",
x: []interface{}{5, "same"},
y: []interface{}{6, "same"},
x: []any{5, "same"},
y: []any{6, "same"},
wantEqual: false,
reason: "not equal because 5 != 6",
}, {
label: "IgnoreTypes",
x: []interface{}{5, "same"},
y: []interface{}{6, "same"},
x: []any{5, "same"},
y: []any{6, "same"},
opts: []cmp.Option{IgnoreTypes(0)},
wantEqual: true,
reason: "equal because ints are ignored",
}, {
label: "IgnoreTypes+IgnoreInterfaces",
x: []interface{}{5, "same", new(bytes.Buffer)},
y: []interface{}{6, "same", new(bytes.Buffer)},
x: []any{5, "same", new(bytes.Buffer)},
y: []any{6, "same", new(bytes.Buffer)},
opts: []cmp.Option{IgnoreTypes(0)},
wantPanic: true,
reason: "panics because bytes.Buffer has unexported fields",
}, {
label: "IgnoreTypes+IgnoreInterfaces",
x: []interface{}{5, "same", new(bytes.Buffer)},
y: []interface{}{6, "diff", new(bytes.Buffer)},
x: []any{5, "same", new(bytes.Buffer)},
y: []any{6, "diff", new(bytes.Buffer)},
opts: []cmp.Option{
IgnoreTypes(0, ""),
IgnoreInterfaces(struct{ io.Reader }{}),
Expand All @@ -866,8 +866,8 @@ func TestOptions(t *testing.T) {
reason: "equal because bytes.Buffer is ignored by match on interface type",
}, {
label: "IgnoreTypes+IgnoreInterfaces",
x: []interface{}{5, "same", new(bytes.Buffer)},
y: []interface{}{6, "same", new(bytes.Buffer)},
x: []any{5, "same", new(bytes.Buffer)},
y: []any{6, "same", new(bytes.Buffer)},
opts: []cmp.Option{
IgnoreTypes(0, ""),
IgnoreInterfaces(struct {
Expand Down Expand Up @@ -1138,13 +1138,13 @@ func TestOptions(t *testing.T) {
}

func TestPanic(t *testing.T) {
args := func(x ...interface{}) []interface{} { return x }
args := func(x ...any) []any { return x }
tests := []struct {
label string // Test name
fnc interface{} // Option function to call
args []interface{} // Arguments to pass in
wantPanic string // Expected panic message
reason string // The reason for the expected outcome
label string // Test name
fnc any // Option function to call
args []any // Arguments to pass in
wantPanic string // Expected panic message
reason string // The reason for the expected outcome
}{{
label: "EquateApprox",
fnc: EquateApprox,
Expand Down
2 changes: 1 addition & 1 deletion cmp/cmpopts/xform.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ func (xf xformFilter) filter(p cmp.Path) bool {
//
// Had this been an unfiltered [cmp.Transformer] instead, this would result in an
// infinite cycle converting a string to []string to [][]string and so on.
func AcyclicTransformer(name string, xformFunc interface{}) cmp.Option {
func AcyclicTransformer(name string, xformFunc any) cmp.Option {
xf := xformFilter{cmp.Transformer(name, xformFunc)}
return cmp.FilterPath(xf.filter, xf.xform)
}
8 changes: 3 additions & 5 deletions cmp/compare.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,6 @@ import (
"github.com/google/go-cmp/cmp/internal/value"
)

// TODO(≥go1.18): Use any instead of interface{}.

// Equal reports whether x and y are equal by recursively applying the
// following rules in the given order to x and y and all of their sub-values:
//
Expand Down Expand Up @@ -92,7 +90,7 @@ import (
// is checked to detect whether the address has already been visited.
// If there is a cycle, then the pointed at values are considered equal
// only if both addresses were previously visited in the same path step.
func Equal(x, y interface{}, opts ...Option) bool {
func Equal(x, y any, opts ...Option) bool {
s := newState(opts)
s.compareAny(rootStep(x, y))
return s.result.Equal()
Expand All @@ -112,7 +110,7 @@ func Equal(x, y interface{}, opts ...Option) bool {
//
// Do not depend on this output being stable. If you need the ability to
// programmatically interpret the difference, consider using a custom Reporter.
func Diff(x, y interface{}, opts ...Option) string {
func Diff(x, y any, opts ...Option) string {
s := newState(opts)

// Optimization: If there are no other reporters, we can optimize for the
Expand All @@ -138,7 +136,7 @@ func Diff(x, y interface{}, opts ...Option) string {

// rootStep constructs the first path step. If x and y have differing types,
// then they are stored within an empty interface type.
func rootStep(x, y interface{}) PathStep {
func rootStep(x, y any) PathStep {
vx := reflect.ValueOf(x)
vy := reflect.ValueOf(y)

Expand Down

0 comments on commit 8b6733e

Please sign in to comment.