From 83cedbec8582347a12b4233a34b269eebab3b0e6 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Fri, 20 Nov 2015 17:01:25 -0800 Subject: [PATCH] Fix ZeroProperties bug on nested interfaces ZeroProperties was setting nested structs to their zero value, even if they contained an interface that should be recursed into, not replaced with nil. --- proptools/clone.go | 5 ++- proptools/clone_test.go | 95 +++++++++++++++++++++++++++++++++++++++-- 2 files changed, 95 insertions(+), 5 deletions(-) diff --git a/proptools/clone.go b/proptools/clone.go index 7496584..3b23a57 100644 --- a/proptools/clone.go +++ b/proptools/clone.go @@ -140,7 +140,7 @@ func ZeroProperties(structValue reflect.Value) { fieldValue := structValue.Field(i) switch fieldValue.Kind() { - case reflect.Bool, reflect.String, reflect.Struct, reflect.Slice, reflect.Int, reflect.Uint: + case reflect.Bool, reflect.String, reflect.Slice, reflect.Int, reflect.Uint: fieldValue.Set(reflect.Zero(fieldValue.Type())) case reflect.Interface: if fieldValue.IsNil() { @@ -172,7 +172,8 @@ func ZeroProperties(structValue reflect.Value) { panic(fmt.Errorf("can't zero field %q: points to a %s", field.Name, fieldValue.Elem().Kind())) } - + case reflect.Struct: + ZeroProperties(fieldValue) default: panic(fmt.Errorf("unexpected kind for property struct field %q: %s", field.Name, fieldValue.Kind())) diff --git a/proptools/clone_test.go b/proptools/clone_test.go index f7460cf..acbd50c 100644 --- a/proptools/clone_test.go +++ b/proptools/clone_test.go @@ -130,6 +130,26 @@ var clonePropertiesTestCases = []struct { }, }, { + // Clone nested interface + in: &struct { + Nested struct{ S interface{} } + }{ + Nested: struct{ S interface{} }{ + S: &struct{ S string }{ + S: "string1", + }, + }, + }, + out: &struct { + Nested struct{ S interface{} } + }{ + Nested: struct{ S interface{} }{ + S: &struct{ S string }{ + S: "string1", + }, + }, + }, + }, { // Empty struct in: &struct{}{}, out: &struct{}{}, @@ -267,6 +287,25 @@ var cloneEmptyPropertiesTestCases = []struct { S: &struct{ S string }{}, }, }, + { + // Clone nested interface + in: &struct { + Nested struct{ S interface{} } + }{ + Nested: struct{ S interface{} }{ + S: &struct{ S string }{ + S: "string1", + }, + }, + }, + out: &struct { + Nested struct{ S interface{} } + }{ + Nested: struct{ S interface{} }{ + S: &struct{ S string }{}, + }, + }, + }, { // Empty struct in: &struct{}{}, @@ -295,11 +334,61 @@ var cloneEmptyPropertiesTestCases = []struct { }, out: &struct{ S *struct{} }{}, }, + { + // Anonymous struct + in: &struct { + EmbeddedStruct + Nested struct{ EmbeddedStruct } + }{ + EmbeddedStruct: EmbeddedStruct{ + S: "string1", + }, + Nested: struct{ EmbeddedStruct }{ + EmbeddedStruct: EmbeddedStruct{ + S: "string2", + }, + }, + }, + out: &struct { + EmbeddedStruct + Nested struct{ EmbeddedStruct } + }{ + EmbeddedStruct: EmbeddedStruct{}, + Nested: struct{ EmbeddedStruct }{ + EmbeddedStruct: EmbeddedStruct{}, + }, + }, + }, + { + // Anonymous interface + in: &struct { + EmbeddedInterface + Nested struct{ EmbeddedInterface } + }{ + EmbeddedInterface: &struct{ S string }{ + S: "string1", + }, + Nested: struct{ EmbeddedInterface }{ + EmbeddedInterface: &struct{ S string }{ + S: "string2", + }, + }, + }, + out: &struct { + EmbeddedInterface + Nested struct{ EmbeddedInterface } + }{ + EmbeddedInterface: &struct{ S string }{}, + Nested: struct{ EmbeddedInterface }{ + EmbeddedInterface: &struct{ S string }{}, + }, + }, + }, } func TestCloneEmptyProperties(t *testing.T) { for _, testCase := range cloneEmptyPropertiesTestCases { - testString := fmt.Sprintf("%s", testCase.in) + testString := fmt.Sprintf("%#v", testCase.in) got := CloneEmptyProperties(reflect.ValueOf(testCase.in).Elem()).Interface() @@ -314,9 +403,9 @@ func TestCloneEmptyProperties(t *testing.T) { func TestZeroProperties(t *testing.T) { for _, testCase := range cloneEmptyPropertiesTestCases { - testString := fmt.Sprintf("%s", testCase.in) + testString := fmt.Sprintf("%#v", testCase.in) - got := CloneEmptyProperties(reflect.ValueOf(testCase.in).Elem()).Interface() + got := CloneProperties(reflect.ValueOf(testCase.in).Elem()).Interface() ZeroProperties(reflect.ValueOf(got).Elem()) if !reflect.DeepEqual(testCase.out, got) { -- GitLab