From d2fcc899bdc2d134b7c00e36137260db963e193c Mon Sep 17 00:00:00 2001 From: Joe Tsai Date: Tue, 18 Aug 2020 12:37:11 -0700 Subject: [PATCH] Suggest use of cmpopts.EquateErrors (#234) 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