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