diff --git a/common/collections/append.go b/common/collections/append.go index a9c14c1aa1d..4657cd50e1e 100644 --- a/common/collections/append.go +++ b/common/collections/append.go @@ -60,6 +60,27 @@ func Append(to any, from ...any) (any, error) { return Slice(from...), nil } + // See issue #11004. + isToASliceOfSlices := tov.Len() > 0 + for i := 0; i < tov.Len(); i++ { + v, isNil := indirect(tov.Index(i)) + if isNil || v.Kind() != reflect.Slice { + isToASliceOfSlices = false + break + } + } + + if isToASliceOfSlices { + fv := reflect.ValueOf(from) + if !fv.Type().AssignableTo(tot) { + // Fall back to a []interface{} slice. + tov, _ := indirect(reflect.ValueOf(to)) + return appendToInterfaceSlice(tov, from) + } + + return reflect.Append(tov, fv).Interface(), nil + } + for _, f := range from { fv := reflect.ValueOf(f) if !fv.Type().AssignableTo(tot) { @@ -67,6 +88,7 @@ func Append(to any, from ...any) (any, error) { tov, _ := indirect(reflect.ValueOf(to)) return appendToInterfaceSlice(tov, from...) } + tov = reflect.Append(tov, fv) } diff --git a/common/collections/append_test.go b/common/collections/append_test.go index 6df32fee68d..878355e091e 100644 --- a/common/collections/append_test.go +++ b/common/collections/append_test.go @@ -24,7 +24,7 @@ func TestAppend(t *testing.T) { t.Parallel() c := qt.New(t) - for _, test := range []struct { + for i, test := range []struct { start any addend []any expected any @@ -66,6 +66,40 @@ func TestAppend(t *testing.T) { []any{&tstSlicer{"a"}}, []any{"a", "b", &tstSlicer{"a"}}, }, + // Issue ##11004 + { + testSlicerInterfaces{testSlicerInterfaces{&tstSlicerIn1{"a"}, &tstSlicerIn1{"b"}}}, + []any{testSlicerInterfaces{testSlicerInterfaces{&tstSlicerIn1{"c"}}}}, + testSlicerInterfaces{ + testSlicerInterfaces{ + &tstSlicerIn1{TheName: "a"}, + &tstSlicerIn1{TheName: "b"}, + }, + testSlicerInterfaces{ + &tstSlicerIn1{TheName: "c"}, + }, + }, + }, + { + []any{[]string{"a", "b"}}, + []any{&tstSlicer{"a"}}, + []interface{}{ + []string{"a", "b"}, + []interface{}{ + &tstSlicer{TheName: "a"}, + }, + }, + }, + { + []any{[]string{"a", "b"}}, + []any{"c"}, + []interface{}{ + []string{"a", "b"}, + []interface{}{ + "c", + }, + }, + }, // Errors {"", []any{[]string{"a", "b"}}, false}, // No string concatenation. @@ -85,6 +119,6 @@ func TestAppend(t *testing.T) { } c.Assert(err, qt.IsNil) - c.Assert(result, qt.DeepEquals, test.expected) + c.Assert(result, qt.DeepEquals, test.expected, qt.Commentf("[%d] %v", i, test.start)) } } diff --git a/common/collections/slice_test.go b/common/collections/slice_test.go index 5788b9161c8..f40d7f97ce6 100644 --- a/common/collections/slice_test.go +++ b/common/collections/slice_test.go @@ -34,6 +34,10 @@ type testSlicerInterface interface { type testSlicerInterfaces []testSlicerInterface +func (t testSlicerInterfaces) Name() string { + return "testSlicerInterfaces" +} + type tstSlicerIn1 struct { TheName string } diff --git a/tpl/collections/append_test.go b/tpl/collections/append_test.go index 78cdcdd849c..831ce8c0e9a 100644 --- a/tpl/collections/append_test.go +++ b/tpl/collections/append_test.go @@ -34,6 +34,11 @@ func TestAppend(t *testing.T) { {[]string{"a", "b"}, []any{"c"}, []string{"a", "b", "c"}}, {[]string{"a", "b"}, []any{"c", "d", "e"}, []string{"a", "b", "c", "d", "e"}}, {[]string{"a", "b"}, []any{[]string{"c", "d", "e"}}, []string{"a", "b", "c", "d", "e"}}, + // Issue #11004 + {[]any{[]any{"a"}}, []any{"b"}, []any{[]any{"a"}, []any{"b"}}}, + {[]any{[]string{"a"}}, []any{"b"}, []any{[]string{"a"}, []any{"b"}}}, + {[]any{[]any{"a"}, "b"}, []any{"c"}, []any{[]any{"a"}, "b", "c"}}, + // Errors {"", []any{[]string{"a", "b"}}, false}, {[]string{"a", "b"}, []any{}, false},