From 626dcefca87d5d18f5f3ea9ab03b123997c679ff Mon Sep 17 00:00:00 2001 From: Joe Tsai Date: Mon, 17 Aug 2020 23:02:56 -0700 Subject: [PATCH] Suggest use of cmpopts.EquateErrors If cmp panics because it is trying to access an unexported field, specially suggest the use of cmpopts.EquateErrors if the parent type implements the error interface. Fixes #233 --- cmp/compare_test.go | 13 +++++++++++++ cmp/options.go | 5 ++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/cmp/compare_test.go b/cmp/compare_test.go index ba39bde..bdcc06b 100644 --- a/cmp/compare_test.go +++ b/cmp/compare_test.go @@ -696,6 +696,19 @@ func comparerTests() []test { }, wantEqual: true, reason: "verify that exporter does not leak implementation details", + }, { + label: label + "/ErrorPanic", + x: io.EOF, + y: io.EOF, + wantPanic: "consider using cmpopts.EquateErrors", + reason: "suggest cmpopts.EquateErrors when accessing unexported fields of error types", + }, { + label: label + "/ErrorEqual", + x: io.EOF, + y: io.EOF, + opts: []cmp.Option{cmpopts.EquateErrors()}, + wantEqual: true, + reason: "cmpopts.EquateErrors should equate these two errors as sentinel values", }} } diff --git a/cmp/options.go b/cmp/options.go index abbd2a6..4b0407a 100644 --- a/cmp/options.go +++ b/cmp/options.go @@ -225,11 +225,14 @@ func (validator) apply(s *state, vx, vy reflect.Value) { // 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 using an Exporter, AllowUnexported, or cmpopts.IgnoreUnexported" + help := "consider using a custom Comparer; if you control the implementation of type, you can also consider using an Exporter, AllowUnexported, or cmpopts.IgnoreUnexported" var name string if t := s.curPath.Index(-2).Type(); t.Name() != "" { // Named type with unexported fields. name = fmt.Sprintf("%q.%v", t.PkgPath(), t.Name()) // e.g., "path/to/package".MyType + if _, ok := reflect.New(t).Interface().(error); ok { + help = "consider using cmpopts.EquateErrors to compare error values" + } } else { // Unnamed type with unexported fields. Derive PkgPath from field. var pkgPath string