aboutsummaryrefslogtreecommitdiff
path: root/cmp/report_slices.go
diff options
context:
space:
mode:
authorJoe Tsai <joetsai@digital-static.net>2021-03-03 09:41:45 -0800
committerGitHub <noreply@github.com>2021-03-03 09:41:45 -0800
commitdc6435e426906757e7c944f74758028d3d2edc3c (patch)
tree2b6613be1b2619f379cfa87ca461889f7865cc4f /cmp/report_slices.go
parente9947a2e1dee9e355ae5d2f794787ad215aff039 (diff)
downloadgo-cmp-dc6435e426906757e7c944f74758028d3d2edc3c.tar.gz
De-virtualize interfaces for specialized diffing (#254)
Specialized diffing strings and slices should occur for interface types where both values have the same concrete type. This is especially relevant for protocmp.Transform, which transforms every proto.Message as a map[string]interface{}.
Diffstat (limited to 'cmp/report_slices.go')
-rw-r--r--cmp/report_slices.go25
1 files changed, 21 insertions, 4 deletions
diff --git a/cmp/report_slices.go b/cmp/report_slices.go
index da04caf..168f92f 100644
--- a/cmp/report_slices.go
+++ b/cmp/report_slices.go
@@ -26,8 +26,6 @@ func (opts formatOptions) CanFormatDiffSlice(v *valueNode) bool {
return false // No differences detected
case !v.ValueX.IsValid() || !v.ValueY.IsValid():
return false // Both values must be valid
- case v.Type.Kind() == reflect.Slice && (v.ValueX.Len() == 0 || v.ValueY.Len() == 0):
- return false // Both slice values have to be non-empty
case v.NumIgnored > 0:
return false // Some ignore option was used
case v.NumTransformed > 0:
@@ -45,7 +43,16 @@ func (opts formatOptions) CanFormatDiffSlice(v *valueNode) bool {
return false
}
- switch t := v.Type; t.Kind() {
+ // Check whether this is an interface with the same concrete types.
+ t := v.Type
+ vx, vy := v.ValueX, v.ValueY
+ if t.Kind() == reflect.Interface && !vx.IsNil() && !vy.IsNil() && vx.Elem().Type() == vy.Elem().Type() {
+ vx, vy = vx.Elem(), vy.Elem()
+ t = vx.Type()
+ }
+
+ // Check whether we provide specialized diffing for this type.
+ switch t.Kind() {
case reflect.String:
case reflect.Array, reflect.Slice:
// Only slices of primitive types have specialized handling.
@@ -57,6 +64,11 @@ func (opts formatOptions) CanFormatDiffSlice(v *valueNode) bool {
return false
}
+ // Both slice values have to be non-empty.
+ if t.Kind() == reflect.Slice && (vx.Len() == 0 || vy.Len() == 0) {
+ return false
+ }
+
// If a sufficient number of elements already differ,
// use specialized formatting even if length requirement is not met.
if v.NumDiff > v.NumSame {
@@ -68,7 +80,7 @@ func (opts formatOptions) CanFormatDiffSlice(v *valueNode) bool {
// Use specialized string diffing for longer slices or strings.
const minLength = 64
- return v.ValueX.Len() >= minLength && v.ValueY.Len() >= minLength
+ return vx.Len() >= minLength && vy.Len() >= minLength
}
// FormatDiffSlice prints a diff for the slices (or strings) represented by v.
@@ -77,6 +89,11 @@ func (opts formatOptions) CanFormatDiffSlice(v *valueNode) bool {
func (opts formatOptions) FormatDiffSlice(v *valueNode) textNode {
assert(opts.DiffMode == diffUnknown)
t, vx, vy := v.Type, v.ValueX, v.ValueY
+ if t.Kind() == reflect.Interface {
+ vx, vy = vx.Elem(), vy.Elem()
+ t = vx.Type()
+ opts = opts.WithTypeMode(emitType)
+ }
// Auto-detect the type of the data.
var isLinedText, isText, isBinary bool