diff --git a/cue/builtin.go b/cue/builtin.go index 54367ba07..d6716ebc2 100644 --- a/cue/builtin.go +++ b/cue/builtin.go @@ -27,6 +27,7 @@ import ( "cuelang.org/go/cue/errors" "cuelang.org/go/cue/parser" + "cuelang.org/go/cue/token" "cuelang.org/go/internal" "github.com/cockroachdb/apd/v2" ) @@ -118,22 +119,41 @@ var lenBuiltin = &builtin{ Result: intKind, Func: func(c *callCtxt) { v := c.value(0) - switch v.Kind() { + switch k := v.IncompleteKind(); k { case StructKind: - s, _ := v.structValData(c.ctx) + s, err := v.structValData(c.ctx) + if err != nil { + c.ret = err + break + } c.ret = s.Len() case ListKind: i := 0 - iter, _ := v.List() + iter, err := v.List() + if err != nil { + c.ret = err + break + } for ; iter.Next(); i++ { } c.ret = i case BytesKind: - b, _ := v.Bytes() + b, err := v.Bytes() + if err != nil { + c.ret = err + break + } c.ret = len(b) case StringKind: - s, _ := v.String() + s, err := v.String() + if err != nil { + c.ret = err + break + } c.ret = len(s) + default: + c.ret = errors.Newf(token.NoPos, + "invalid argument type %v", k) } }, } @@ -268,8 +288,11 @@ func (x *builtin) call(ctx *context, src source, args ...evaluated) (ret value) } }() x.Func(&call) - if e, ok := call.ret.(value); ok { - return e + switch v := call.ret.(type) { + case value: + return v + case *valueError: + return v.err } return convert(ctx, x, false, call.ret) } diff --git a/cue/resolve_test.go b/cue/resolve_test.go index 92c781cd9..1e5cbd48d 100644 --- a/cue/resolve_test.go +++ b/cue/resolve_test.go @@ -2182,6 +2182,43 @@ func TestFullEval(t *testing.T) { `str: string, ` + `s2: strings.ContainsAny ("dd"), ` + `s3: <4>.ContainsAny (<3>.str,"dd")}`, + }, { + desc: "len of incomplete types", + in: ` + args: *[] | [...string] + v1: len(args) + v2: len([]) + v3: len({}) + v4: len({a: 3}) + v5: len({a: 3} | {a: 4}) + v6: len('sf' | 'dd') + v7: len([2] | *[1, 2]) + v8: len([2] | [1, 2]) + v9: len("😂") + v10: len("") + `, + out: `<0>{` + + `args: [], ` + + `v1: 0, ` + + `v2: 0, ` + + `v3: 0, ` + + `v4: 1, ` + + `v5: len ((<1>{a: 3} | <2>{a: 4})), ` + + `v6: len (('sf' | 'dd')), ` + + `v7: 2, ` + + `v8: len (([2] | [1,2])), ` + + `v9: 4, ` + + `v10: 0}`, + }, { + desc: "slice rewrite bug", + in: ` + fn: { + arg: [...int] & [1] + out: arg[1:] + } + fn1: fn & {arg: [1]} + `, + out: `<0>{fn: <1>{arg: [1], out: []}, fn1: <2>{arg: [1], out: []}}`, }} rewriteHelper(t, testCases, evalFull) } @@ -2192,6 +2229,16 @@ func TestX(t *testing.T) { // Don't remove. For debugging. testCases := []testCase{{ in: ` + fnRec: {nn: [...int], out: (fn & {arg: nn}).out} + fn: { + arg: [...int] + + out: arg[0] + (fnRec & {nn: arg[1:]}).out if len(arg) > 0 + out: 0 if len(arg) == 0 + } + fn7: (fn & {arg: [1, 2, 3]}).out + + `, }} rewriteHelper(t, testCases, evalFull) diff --git a/cue/rewrite.go b/cue/rewrite.go index 06171e0fd..ff4e7ba2c 100644 --- a/cue/rewrite.go +++ b/cue/rewrite.go @@ -130,8 +130,13 @@ func (x *list) rewrite(ctx *context, fn rewriteFunc) value { func (x *sliceExpr) rewrite(ctx *context, fn rewriteFunc) value { v := rewrite(ctx, x.x, fn) - lo := rewrite(ctx, x.lo, fn) - hi := rewrite(ctx, x.hi, fn) + var lo, hi value + if x.lo != nil { + lo = rewrite(ctx, x.lo, fn) + } + if x.hi != nil { + hi = rewrite(ctx, x.hi, fn) + } if v == x.x && lo == x.lo && hi == x.hi { return x }