Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

any over interface{} #330

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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