diff --git a/tpl/collections/index.go b/tpl/collections/index.go index 43fccbc6b..13dd38672 100644 --- a/tpl/collections/index.go +++ b/tpl/collections/index.go @@ -35,12 +35,9 @@ import ( func (ns *Namespace) Index(item any, args ...any) (any, error) { v := reflect.ValueOf(item) if !v.IsValid() { - return nil, errors.New("index of untyped nil") - } - - lowerm, ok := item.(maps.Params) - if ok { - return lowerm.Get(cast.ToStringSlice(args)...), nil + // See issue 10489 + // This used to be an error. + return nil, nil } var indices []any @@ -51,18 +48,25 @@ func (ns *Namespace) Index(item any, args ...any) (any, error) { for i := 0; i < v.Len(); i++ { indices = append(indices, v.Index(i).Interface()) } + } else { + indices = append(indices, args[0]) } + } else { + indices = args } - if indices == nil { - indices = args + lowerm, ok := item.(maps.Params) + if ok { + return lowerm.Get(cast.ToStringSlice(indices)...), nil } for _, i := range indices { index := reflect.ValueOf(i) var isNil bool if v, isNil = indirect(v); isNil { - return nil, errors.New("index of nil pointer") + // See issue 10489 + // This used to be an error. + return nil, nil } switch v.Kind() { case reflect.Array, reflect.Slice, reflect.String: diff --git a/tpl/collections/index_test.go b/tpl/collections/index_test.go index 662fe6f33..7c917c443 100644 --- a/tpl/collections/index_test.go +++ b/tpl/collections/index_test.go @@ -30,6 +30,11 @@ func TestIndex(t *testing.T) { c := qt.New(t) ns := New(&deps.Deps{Language: langs.NewDefaultLanguage(config.New())}) + var ( + emptyInterface any + nilPointer *int + ) + for i, test := range []struct { item any indices []any @@ -45,16 +50,23 @@ func TestIndex(t *testing.T) { {map[string]map[string]string{"a": {"b": "c"}}, []any{"a", "b"}, "c", false}, {[]map[string]map[string]string{{"a": {"b": "c"}}}, []any{0, "a", "b"}, "c", false}, {map[string]map[string]any{"a": {"b": []string{"c", "d"}}}, []any{"a", "b", 1}, "d", false}, - {map[string]map[string]string{"a": {"b": "c"}}, []any{[]string{"a", "b"}}, "c", false}, {maps.Params{"a": "av"}, []any{"A"}, "av", false}, {maps.Params{"a": map[string]any{"b": "bv"}}, []any{"A", "B"}, "bv", false}, + + // These used to be errors. + // See issue 10489. + {nil, nil, nil, false}, + {nil, []any{0}, nil, false}, + {emptyInterface, []any{0}, nil, false}, + {nilPointer, []any{0}, nil, false}, + // errors - {nil, nil, nil, true}, {[]int{0, 1}, []any{"1"}, nil, true}, {[]int{0, 1}, []any{nil}, nil, true}, {tstNoStringer{}, []any{0}, nil, true}, } { - c.Run(fmt.Sprint(i), func(c *qt.C) { + + c.Run(fmt.Sprintf("vararg %d", i), func(c *qt.C) { errMsg := qt.Commentf("[%d] %v", i, test) result, err := ns.Index(test.item, test.indices...) @@ -66,5 +78,18 @@ func TestIndex(t *testing.T) { c.Assert(err, qt.IsNil, errMsg) c.Assert(result, qt.DeepEquals, test.expect, errMsg) }) + + c.Run(fmt.Sprintf("slice %d", i), func(c *qt.C) { + errMsg := qt.Commentf("[%d] %v", i, test) + + result, err := ns.Index(test.item, test.indices) + + if test.isErr { + c.Assert(err, qt.Not(qt.IsNil), errMsg) + return + } + c.Assert(err, qt.IsNil, errMsg) + c.Assert(result, qt.DeepEquals, test.expect, errMsg) + }) } }