cue: improve error messages
- more Go-style error messages
- report original value and type level
- remove struct IDs from printed structs
- more context information overall
Note: this is only a partial update. Errors
relating to disjunctions, for instance, need
a lot more love.
Change-Id: I9025a2cace8b38a987e4de3e73620bea734b7414
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/2580
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/cmd/cue/cmd/testdata/partial/eval_conc.out b/cmd/cue/cmd/testdata/partial/eval_conc.out
index f26c68d..006c7ca 100644
--- a/cmd/cue/cmd/testdata/partial/eval_conc.out
+++ b/cmd/cue/cmd/testdata/partial/eval_conc.out
@@ -1,6 +1,6 @@
-sum:more than one element remaining (1 and 2):
+sum: more than one element remaining (1 and 2):
./testdata/partial/partial.cue:4:6
-b.idx:invalid non-ground value string (must be concrete int|string):
+b.idx: invalid non-ground value string (must be concrete int|string):
./testdata/partial/partial.cue:7:9
-b.str:incomplete value (string):
+b.str: incomplete value (string):
./testdata/partial/partial.cue:8:7
diff --git a/cmd/cue/cmd/testdata/tasks/cmd_baddisplay.out b/cmd/cue/cmd/testdata/tasks/cmd_baddisplay.out
index fa756db..780db31 100644
--- a/cmd/cue/cmd/testdata/tasks/cmd_baddisplay.out
+++ b/cmd/cue/cmd/testdata/tasks/cmd_baddisplay.out
@@ -1,3 +1,3 @@
-text:unsupported op &(int, string):
+text: conflicting values 42 and string (mismatched types int and string):
./testdata/tasks/task_tool.cue:29:9
tool/cli:4:9
diff --git a/cue/binop.go b/cue/binop.go
index 791bfe4..1e487ab 100644
--- a/cue/binop.go
+++ b/cue/binop.go
@@ -16,6 +16,7 @@
import (
"bytes"
+ "fmt"
"math/big"
"regexp"
"sort"
@@ -54,9 +55,27 @@
leftKind := left.kind()
rightKind := right.kind()
- kind, invert := matchBinOpKind(op, leftKind, rightKind)
+ kind, invert, msg := matchBinOpKind(op, leftKind, rightKind)
if kind == bottomKind {
- return ctx.mkIncompatible(src, op, left, right)
+ simplify := func(v, orig value) value {
+ switch x := v.(type) {
+ case *disjunction:
+ return orig
+ case *binaryExpr:
+ if x.op == opDisjunction {
+ return orig
+ }
+ default:
+ return x
+ }
+ return v
+ }
+ var l, r value = left, right
+ if x, ok := src.(*binaryExpr); ok {
+ l = simplify(x.left, left)
+ r = simplify(x.right, right)
+ }
+ return ctx.mkErr(src, msg, op, ctx.str(l), ctx.str(r), leftKind, rightKind)
}
if kind.hasReferences() {
panic("unexpected references in expression")
@@ -274,14 +293,14 @@
pos = r.Pos()
}
e := mkBin(ctx, pos, opUnify, r, v)
- msg := "%v not within bound %v"
+ msg := "invalid value %v (out of bound %v)"
switch r.op {
case opNeq, opNMat:
- msg = "%v excluded by %v"
+ msg = "invalid value %v (excluded by %v)"
case opMat:
- msg = "%v does not match %v"
+ msg = "invalid value %v (does not match %v)"
}
- return ctx.mkErr(e, msg, debugStr(ctx, v), debugStr(ctx, r))
+ return ctx.mkErr(e, msg, ctx.str(v), ctx.str(r))
}
func opInfo(op op) (cmp op, norm int) {
@@ -310,8 +329,9 @@
newSrc := binSrc(src.Pos(), op, x, other)
switch op {
case opUnify:
- k, _ := matchBinOpKind(opUnify, x.kind(), other.kind())
+ k, _, msg := matchBinOpKind(opUnify, x.kind(), other.kind())
if k == bottomKind {
+ return ctx.mkErr(src, msg, opUnify, ctx.str(x), ctx.str(other), x.kind(), other.kind())
break
}
switch y := other.(type) {
@@ -433,8 +453,8 @@
fallthrough
case d.Negative:
- return ctx.mkErr(newSrc, "incompatible bounds %v and %v",
- debugStr(ctx, x), debugStr(ctx, y))
+ return ctx.mkErr(newSrc, "conflicting bounds %v and %v",
+ ctx.str(x), ctx.str(y))
}
case x.op == opNeq:
@@ -477,8 +497,9 @@
newSrc := binSrc(src.Pos(), op, x, other)
switch op {
case opUnify:
- k, _ := matchBinOpKind(opUnify, x.kind(), other.kind())
+ k, _, msg := matchBinOpKind(opUnify, x.kind(), other.kind())
if k == bottomKind {
+ return ctx.mkErr(src, msg, op, ctx.str(x), ctx.str(other), x.kind(), other.kind())
break
}
switch y := other.(type) {
@@ -513,7 +534,7 @@
return y
}
}
- return ctx.mkIncompatible(src, op, x, other)
+ return ctx.mkErr(src, "invalid operation %v and %v (operator not defined for custom validator)")
}
func (x *customValidator) check(ctx *context, v evaluated) evaluated {
@@ -531,13 +552,13 @@
return ctx.mkErr(x, "invalid custom validator")
} else if !b.b {
var buf bytes.Buffer
- buf.WriteString(x.call.Name)
+ fmt.Fprintf(&buf, "%s.%s", ctx.labelStr(x.call.pkg), x.call.Name)
buf.WriteString("(")
for _, a := range x.args {
- buf.WriteString(debugStr(ctx, a))
+ buf.WriteString(ctx.str(a))
}
buf.WriteString(")")
- return ctx.mkErr(x, "value %v not in %v", debugStr(ctx, v), buf.String())
+ return ctx.mkErr(x, "invalid value %s (does not satisfy %s)", ctx.str(v), buf.String())
}
return nil
}
@@ -681,7 +702,7 @@
switch op {
case opUnify:
if x.b != y.b {
- return ctx.mkErr(x, "conflicting values: %v != %v", x.b, y.b)
+ return ctx.mkErr(x, "conflicting values %v and %v", x.b, y.b)
}
return x
case opLand:
@@ -711,7 +732,8 @@
str := other.strValue()
if x.str != str {
src := mkBin(ctx, src.Pos(), op, x, other)
- return ctx.mkErr(src, "conflicting values: %v != %v", x.str, str)
+ return ctx.mkErr(src, "conflicting values %v and %v",
+ ctx.str(x), ctx.str(y))
}
return x
case opLss, opLeq, opEql, opNeq, opGeq, opGtr:
@@ -762,7 +784,8 @@
switch op {
case opUnify:
if !bytes.Equal(x.b, b) {
- return ctx.mkErr(x, "conflicting values: %v != %v", x.b, b)
+ return ctx.mkErr(x, "conflicting values %v and %v",
+ ctx.str(x), ctx.str(y))
}
return x
case opLss, opLeq, opEql, opNeq, opGeq, opGtr:
@@ -867,7 +890,8 @@
case opUnify:
if x.v.Cmp(&y.v) != 0 {
src = mkBin(ctx, src.Pos(), op, x, other)
- return ctx.mkErr(src, "conflicting values: %v != %v", x.strValue(), y.strValue())
+ return ctx.mkErr(src, "conflicting values %v and %v",
+ ctx.str(x), ctx.str(y))
}
if k != x.k {
n.v = x.v
@@ -1010,7 +1034,7 @@
n := unify(ctx, src, x.len.(evaluated), y.len.(evaluated))
if isBottom(n) {
src = mkBin(ctx, src.Pos(), op, x, other)
- return ctx.mkErr(src, "incompatible list lengths: %v", n)
+ return ctx.mkErr(src, "conflicting list lengths: %v", n)
}
sx := x.elem.arcs
xa := sx
@@ -1026,10 +1050,10 @@
typ := x.typ
max, ok := n.(*numLit)
if !ok || len(xa) < max.intValue(ctx) {
+ src := mkBin(ctx, src.Pos(), op, x.typ, y.typ)
typ = unify(ctx, src, x.typ.(evaluated), y.typ.(evaluated))
if isBottom(typ) {
- src = mkBin(ctx, src.Pos(), op, x, other)
- return ctx.mkErr(src, "incompatible list types: %v: ", typ)
+ return ctx.mkErr(src, "conflicting list element types: %v", typ)
}
}
diff --git a/cue/build_test.go b/cue/build_test.go
index da37549..cb3607c 100644
--- a/cue/build_test.go
+++ b/cue/build_test.go
@@ -79,7 +79,7 @@
emit string
}{{
insts(&bimport{"", files(`test: "ok"`)}),
- `<0>{test: "ok"}`,
+ `{test: "ok"}`,
// }, {
// insts(pkg1, &bimport{"",
// files(
diff --git a/cue/builtin.go b/cue/builtin.go
index 95282ef..dccb441 100644
--- a/cue/builtin.go
+++ b/cue/builtin.go
@@ -167,7 +167,7 @@
}
c.ret = &disjunction{baseValue{c.src}, d, false}
if len(d) == 0 {
- c.ret = errors.New("empty or")
+ c.ret = errors.New("empty list in call to or")
}
},
}
@@ -187,26 +187,38 @@
return false
}
+func (x *builtin) name(ctx *context) string {
+ if x.pkg == 0 {
+ return x.Name
+ }
+ return fmt.Sprintf("%s.%s", ctx.labelStr(x.pkg), x.Name)
+}
+
func (x *builtin) call(ctx *context, src source, args ...evaluated) (ret value) {
if x.Func == nil {
- return ctx.mkErr(x, "Builtin %q is not a function", x.Name)
+ return ctx.mkErr(x, "builtin %s is not a function", x.name(ctx))
}
if len(x.Params)-1 == len(args) && x.Result == boolKind {
// We have a custom builtin
return &customValidator{src.base(), args, x}
}
- if len(x.Params) != len(args) {
- return ctx.mkErr(src, x, "number of arguments does not match (%d vs %d)",
- len(x.Params), len(args))
+ switch {
+ case len(x.Params) < len(args):
+ return ctx.mkErr(src, x, "too many arguments in call to %s (have %d, want %d)",
+ x.name(ctx), len(args), len(x.Params))
+ case len(x.Params) > len(args):
+ return ctx.mkErr(src, x, "not enough arguments in call to %s (have %d, want %d)",
+ x.name(ctx), len(args), len(x.Params))
}
for i, a := range args {
if x.Params[i] != bottomKind {
if unifyType(x.Params[i], a.kind()) == bottomKind {
- return ctx.mkErr(src, x, "argument %d requires type %v, found %v", i+1, x.Params[i], a.kind())
+ const msg = "cannot use %s (type %s) as %s in argument %d to %s"
+ return ctx.mkErr(src, x, msg, ctx.str(a), a.kind(), x.Params[i], i+1, x.name(ctx))
}
}
}
- call := callCtxt{src: src, ctx: ctx, args: args}
+ call := callCtxt{src: src, ctx: ctx, builtin: x, args: args}
defer func() {
var errVal interface{} = call.err
if err := recover(); err != nil {
@@ -218,7 +230,7 @@
ret = err.b
default:
// TODO: store the underlying error explicitly
- ret = ctx.mkErr(src, x, "call error: %v", err)
+ ret = ctx.mkErr(src, x, "error in call to %s: %v", x.name(ctx), err)
}
}()
x.Func(&call)
@@ -230,11 +242,16 @@
// callCtxt is passed to builtin implementations.
type callCtxt struct {
- src source
- ctx *context
- args []evaluated
- err error
- ret interface{}
+ src source
+ ctx *context
+ builtin *builtin
+ args []evaluated
+ err error
+ ret interface{}
+}
+
+func (c *callCtxt) name() string {
+ return c.builtin.name(c.ctx)
}
var builtins = map[string]*structLit{}
@@ -321,47 +338,56 @@
return newValueRoot(c.ctx, c.args[i])
}
-func (c *callCtxt) int(i int) int { return int(c.intValue(i, 64)) }
-func (c *callCtxt) int8(i int) int8 { return int8(c.intValue(i, 8)) }
-func (c *callCtxt) int16(i int) int16 { return int16(c.intValue(i, 16)) }
-func (c *callCtxt) int32(i int) int32 { return int32(c.intValue(i, 32)) }
-func (c *callCtxt) rune(i int) rune { return rune(c.intValue(i, 32)) }
-func (c *callCtxt) int64(i int) int64 { return int64(c.intValue(i, 64)) }
+func (c *callCtxt) invalidArgType(arg value, i int, typ string, err error) {
+ if err != nil {
+ c.errf(c.src, err, "cannot use %s (type %s) as %s in argument %d to %s: %v",
+ c.ctx.str(arg), arg.kind(), typ, i, c.name(), err)
+ } else {
+ c.errf(c.src, nil, "cannot use %s (type %s) as %s in argument %d to %s",
+ c.ctx.str(arg), arg.kind(), typ, i, c.name())
+ }
+}
-func (c *callCtxt) intValue(i, bits int) int64 {
- x := newValueRoot(c.ctx, c.args[i])
+func (c *callCtxt) int(i int) int { return int(c.intValue(i, 64, "int64")) }
+func (c *callCtxt) int8(i int) int8 { return int8(c.intValue(i, 8, "int8")) }
+func (c *callCtxt) int16(i int) int16 { return int16(c.intValue(i, 16, "int16")) }
+func (c *callCtxt) int32(i int) int32 { return int32(c.intValue(i, 32, "int32")) }
+func (c *callCtxt) rune(i int) rune { return rune(c.intValue(i, 32, "rune")) }
+func (c *callCtxt) int64(i int) int64 { return int64(c.intValue(i, 64, "int64")) }
+
+func (c *callCtxt) intValue(i, bits int, typ string) int64 {
+ arg := c.args[i]
+ x := newValueRoot(c.ctx, arg)
n, err := x.Int(nil)
if err != nil {
- c.errf(c.src, err, "argument %d must be in int, found number", i)
+ c.invalidArgType(arg, i, typ, err)
return 0
}
if n.BitLen() > bits {
- c.errf(c.src, err, "argument %d out of range: has %d > %d bits", n.BitLen(), bits)
+ c.errf(c.src, err, "int %s overflows %s in argument %d in call to %s",
+ n, typ, i, c.name())
}
res, _ := x.Int64()
return res
}
-func (c *callCtxt) uint(i int) uint { return uint(c.uintValue(i, 64)) }
-func (c *callCtxt) uint8(i int) uint8 { return uint8(c.uintValue(i, 8)) }
-func (c *callCtxt) byte(i int) uint8 { return byte(c.uintValue(i, 8)) }
-func (c *callCtxt) uint16(i int) uint16 { return uint16(c.uintValue(i, 16)) }
-func (c *callCtxt) uint32(i int) uint32 { return uint32(c.uintValue(i, 32)) }
-func (c *callCtxt) uint64(i int) uint64 { return uint64(c.uintValue(i, 64)) }
+func (c *callCtxt) uint(i int) uint { return uint(c.uintValue(i, 64, "uint64")) }
+func (c *callCtxt) uint8(i int) uint8 { return uint8(c.uintValue(i, 8, "uint8")) }
+func (c *callCtxt) byte(i int) uint8 { return byte(c.uintValue(i, 8, "byte")) }
+func (c *callCtxt) uint16(i int) uint16 { return uint16(c.uintValue(i, 16, "uint16")) }
+func (c *callCtxt) uint32(i int) uint32 { return uint32(c.uintValue(i, 32, "uint32")) }
+func (c *callCtxt) uint64(i int) uint64 { return uint64(c.uintValue(i, 64, "uint64")) }
-func (c *callCtxt) uintValue(i, bits int) uint64 {
+func (c *callCtxt) uintValue(i, bits int, typ string) uint64 {
x := newValueRoot(c.ctx, c.args[i])
n, err := x.Int(nil)
- if err != nil {
- c.errf(c.src, err, "argument %d must be an integer", i)
- return 0
- }
- if n.Sign() < 0 {
- c.errf(c.src, nil, "argument %d must be a positive integer", i)
+ if err != nil || n.Sign() < 0 {
+ c.invalidArgType(c.args[i], i, typ, err)
return 0
}
if n.BitLen() > bits {
- c.errf(c.src, nil, "argument %d out of range: has %d > %d bits", i, n.BitLen(), bits)
+ c.errf(c.src, err, "int %s overflows %s in argument %d in call to %s",
+ n, typ, i, c.name())
}
res, _ := x.Uint64()
return res
@@ -371,7 +397,7 @@
x := newValueRoot(c.ctx, c.args[i])
res, err := x.Float64()
if err != nil {
- c.errf(c.src, err, "invalid argument %d: %v", i, err)
+ c.invalidArgType(c.args[i], i, "float64", err)
return 0
}
return res
@@ -381,7 +407,7 @@
x := newValueRoot(c.ctx, c.args[i])
n, err := x.Int(nil)
if err != nil {
- c.errf(c.src, err, "argument %d must be in int, found number", i)
+ c.invalidArgType(c.args[i], i, "int", err)
return nil
}
return n
@@ -392,7 +418,7 @@
var mant big.Int
exp, err := x.MantExp(&mant)
if err != nil {
- c.errf(c.src, err, "invalid argument %d: %v", i, err)
+ c.invalidArgType(c.args[i], i, "float", err)
return nil
}
f := &big.Float{}
@@ -409,7 +435,7 @@
x := newValueRoot(c.ctx, c.args[i])
v, err := x.String()
if err != nil {
- c.errf(c.src, err, "invalid argument %d: %v", i, err)
+ c.invalidArgType(c.args[i], i, "string", err)
return ""
}
return v
@@ -419,7 +445,7 @@
x := newValueRoot(c.ctx, c.args[i])
v, err := x.Bytes()
if err != nil {
- c.errf(c.src, err, "invalid argument %d: %v", i, err)
+ c.invalidArgType(c.args[i], i, "bytes", err)
return nil
}
return v
@@ -430,7 +456,7 @@
// TODO: optimize for string and bytes cases
r, err := x.Reader()
if err != nil {
- c.errf(c.src, err, "invalid argument %d: %v", i, err)
+ c.invalidArgType(c.args[i], i, "bytes|string", err)
return nil
}
return r
@@ -440,33 +466,37 @@
x := newValueRoot(c.ctx, c.args[i])
b, err := x.Bool()
if err != nil {
- c.errf(c.src, err, "invalid argument %d: %v", i, err)
+ c.invalidArgType(c.args[i], i, "bool", err)
return false
}
return b
}
func (c *callCtxt) list(i int) (a Iterator) {
- x := newValueRoot(c.ctx, c.args[i])
+ arg := c.args[i]
+ x := newValueRoot(c.ctx, arg)
v, err := x.List()
if err != nil {
- c.errf(c.src, err, "invalid argument %d: %v", i, err)
+ c.invalidArgType(c.args[i], i, "list", err)
return Iterator{ctx: c.ctx}
}
return v
}
func (c *callCtxt) strList(i int) (a []string) {
- x := newValueRoot(c.ctx, c.args[i])
+ arg := c.args[i]
+ x := newValueRoot(c.ctx, arg)
v, err := x.List()
if err != nil {
- c.errf(c.src, err, "invalid argument %d: %v", i, err)
+ c.invalidArgType(c.args[i], i, "list", err)
return nil
}
- for i := 0; v.Next(); i++ {
+ for j := 0; v.Next(); j++ {
str, err := v.Value().String()
if err != nil {
- c.errf(c.src, err, "list element %d: %v", i, err)
+ c.errf(c.src, err, "invalid list element %d in argument %d to %s: %v",
+ j, i, c.name(), err)
+ break
}
a = append(a, str)
}
diff --git a/cue/builtin_test.go b/cue/builtin_test.go
index a560b08..3ebd34e 100644
--- a/cue/builtin_test.go
+++ b/cue/builtin_test.go
@@ -48,13 +48,13 @@
`3`,
}, {
test("math", "math.Pi(3)"),
- `_|_(<0>.Pi:cannot call non-function 3.14159265358979323846264338327950288419716939937510582097494459 (type float))`,
+ `_|_(cannot call non-function Pi (type float))`,
}, {
test("math", "math.Floor(3, 5)"),
- `_|_(<0>.Floor (3,5):number of arguments does not match (1 vs 2))`,
+ `_|_(too many arguments in call to math.Floor (have 2, want 1))`,
}, {
test("math", `math.Floor("foo")`),
- `_|_(<0>.Floor ("foo"):argument 1 requires type number, found string)`,
+ `_|_(cannot use "foo" (type string) as number in argument 1 to math.Floor)`,
}, {
test("encoding/hex", `hex.Encode("foo")`),
`"666f6f"`,
@@ -63,32 +63,32 @@
`'foo'`,
}, {
test("encoding/hex", `hex.Decode("foo")`),
- `_|_(<0>.Decode ("foo"):call error: encoding/hex: invalid byte: U+006F 'o')`,
+ `_|_(error in call to encoding/hex.Decode: encoding/hex: invalid byte: U+006F 'o')`,
}, {
test("strconv", `strconv.FormatUint(64, 16)`),
`"40"`,
}, {
// Find a better alternative, as this call should go.
test("strconv", `strconv.FormatFloat(3.02, 300, 4, 64)`),
- `_|_(<0>.FormatFloat (3.02,300,4,64):argument 1 out of range: has 9 > 8 bits)`,
+ `_|_(int 300 overflows byte in argument 1 in call to strconv.FormatFloat)`,
}, {
// Find a better alternative, as this call should go.
test("strconv", `strconv.FormatFloat(3.02, -1, 4, 64)`),
- `_|_(<0>.FormatFloat (3.02,-1,4,64):argument 1 must be a positive integer)`,
+ `_|_(cannot use -1 (type int) as byte in argument 1 to strconv.FormatFloat)`,
}, {
// Find a better alternative, as this call should go.
test("strconv", `strconv.FormatFloat(3.02, 1.0, 4, 64)`),
- `_|_(<0>.FormatFloat (3.02,1.0,4,64):argument 2 requires type int, found float)`,
+ `_|_(cannot use 1.0 (type float) as int in argument 2 to strconv.FormatFloat)`,
}, {
// Panics
test("math", `math.Jacobi(1000, 2000)`),
- `_|_(<0>.Jacobi (1000,2000):call error: big: invalid 2nd argument to Int.Jacobi: need odd integer but got 2000)`,
+ `_|_(error in call to math.Jacobi: big: invalid 2nd argument to Int.Jacobi: need odd integer but got 2000)`,
}, {
test("math", `math.Jacobi(1000, 201)`),
`1`,
}, {
test("math", `math.Asin(2.0e400)`),
- `_|_(<0>.Asin (2.0e+400):invalid argument 0: cue: value was rounded up)`,
+ `_|_(cannot use 2.0e+400 (type float) as float64 in argument 0 to math.Asin: value was rounded up)`,
}, {
test("encoding/csv", `csv.Decode("1,2,3\n4,5,6")`),
`[["1","2","3"],["4","5","6"]]`,
@@ -100,7 +100,7 @@
`"Hello World!"`,
}, {
test("strings", `strings.Join([1, 2], " ")`),
- `_|_(<0>.Join ([1,2]," "):list element 1: not of right kind (int vs string))`,
+ `_|_(invalid list element 0 in argument 0 to strings.Join: cannot use value 1 (type int) as string)`,
}, {
test("math/bits", `bits.Or(0x8, 0x1)`),
`9`,
@@ -133,7 +133,7 @@
`2`,
}, {
testExpr(`or([])`),
- `_|_(builtin:or:empty or)`,
+ `_|_(empty list in call to or)`,
}, {
test("encoding/csv", `csv.Encode([[1,2,3],[4,5],[7,8,9]])`),
`"1,2,3\n4,5\n7,8,9\n"`,
diff --git a/cue/debug.go b/cue/debug.go
index d3d1700..25199f4 100644
--- a/cue/debug.go
+++ b/cue/debug.go
@@ -23,7 +23,14 @@
func debugStr(ctx *context, v value) string {
p := newPrinter(ctx)
- p.debugStr(v)
+ p.showNodeRef = true
+ p.str(v)
+ return p.w.String()
+}
+
+func (c *context) str(v value) string {
+ p := newPrinter(c)
+ p.str(v)
return p.w.String()
}
@@ -123,8 +130,9 @@
}
type printer struct {
- ctx *context
- w *bytes.Buffer
+ ctx *context
+ w *bytes.Buffer
+ showNodeRef bool
}
func (p *printer) label(f label) string {
@@ -154,7 +162,7 @@
return f
}
-func (p *printer) debugStr(v interface{}) {
+func (p *printer) str(v interface{}) {
writef := p.writef
write := p.write
switch x := v.(type) {
@@ -164,45 +172,50 @@
write(x)
case *builtin:
write("builtin:")
- p.debugStr(x.Name)
+ p.str(x.Name)
case *nodeRef:
- writef("<%s>", p.ctx.ref(x.node))
- // p.debugStr(x.node)
+ if p.showNodeRef {
+ writef("<%s>", p.ctx.ref(x.node))
+ }
case *selectorExpr:
- p.debugStr(x.x)
f := lambdaName(x.feature, x.x)
- writef(".%v", p.label(f))
+ if _, ok := x.x.(*nodeRef); ok && !p.showNodeRef {
+ write(p.label(f))
+ } else {
+ p.str(x.x)
+ writef(".%v", p.label(f))
+ }
case *indexExpr:
- p.debugStr(x.x)
+ p.str(x.x)
write("[")
- p.debugStr(x.index)
+ p.str(x.index)
write("]")
case *sliceExpr:
- p.debugStr(x.x)
+ p.str(x.x)
write("[")
if x.lo != nil {
- p.debugStr(x.lo)
+ p.str(x.lo)
}
write(":")
if x.hi != nil {
- p.debugStr(x.hi)
+ p.str(x.hi)
}
write("]")
case *callExpr:
- p.debugStr(x.x)
+ p.str(x.x)
write(" (")
for i, a := range x.args {
- p.debugStr(a)
+ p.str(a)
if i < len(x.args)-1 {
write(",")
}
}
write(")")
case *customValidator:
- p.debugStr(x.call)
+ p.str(x.call)
write(" (")
for i, a := range x.args {
- p.debugStr(a)
+ p.str(a)
if i < len(x.args)-1 {
write(",")
}
@@ -210,12 +223,12 @@
write(")")
case *unaryExpr:
write(x.op)
- p.debugStr(x.x)
+ p.str(x.x)
case *binaryExpr:
write("(")
- p.debugStr(x.left)
+ p.str(x.left)
writef(" %v ", x.op)
- p.debugStr(x.right)
+ p.str(x.right)
write(")")
case *unification:
write("(")
@@ -223,7 +236,7 @@
if i != 0 {
writef(" & ")
}
- p.debugStr(v)
+ p.str(v)
}
write(")")
case *disjunction:
@@ -235,30 +248,35 @@
if v.marked {
writef("*")
}
- p.debugStr(v.val)
+ p.str(v.val)
}
write(")")
case *lambdaExpr:
- writef("<%s>(", p.ctx.ref(x))
- p.debugStr(x.params.arcs)
+ if p.showNodeRef {
+ writef("<%s>", p.ctx.ref(x))
+ }
+ write("(")
+ p.str(x.params.arcs)
write(")->")
- p.debugStr(x.value)
+ p.str(x.value)
case *structLit:
if x == nil {
write("*nil node*")
break
}
- p.writef("<%s>", p.ctx.ref(x))
+ if p.showNodeRef {
+ p.writef("<%s>", p.ctx.ref(x))
+ }
writef("{")
if x.template != nil {
write("<>: ")
- p.debugStr(x.template)
+ p.str(x.template)
write(", ")
}
- p.debugStr(x.arcs)
+ p.str(x.arcs)
for i, c := range x.comprehensions {
- p.debugStr(c)
+ p.str(c)
if i < len(x.comprehensions)-1 {
p.write(", ")
}
@@ -267,7 +285,7 @@
case []arc:
for i, a := range x {
- p.debugStr(a)
+ p.str(a)
if i < len(x)-1 {
p.write(", ")
@@ -286,7 +304,7 @@
p.write("?")
}
p.write(": ")
- p.debugStr(n)
+ p.str(n)
if x.attrs != nil {
for _, a := range x.attrs.attr {
p.write(" ", a.text)
@@ -294,22 +312,22 @@
}
case *fieldComprehension:
- p.debugStr(x.clauses)
+ p.str(x.clauses)
case *listComprehension:
writef("[")
- p.debugStr(x.clauses)
+ p.str(x.clauses)
write(" ]")
case *yield:
writef(" yield ")
writef("(")
- p.debugStr(x.key)
+ p.str(x.key)
if x.opt {
writef("?")
}
writef("): ")
- p.debugStr(x.value)
+ p.str(x.value)
case *feed:
writef(" <%s>for ", p.ctx.ref(x.fn))
@@ -319,13 +337,13 @@
a = x.fn.params.arcs[1]
p.writef(p.label(a.feature))
writef(" in ")
- p.debugStr(x.source)
- p.debugStr(x.fn.value)
+ p.str(x.source)
+ p.str(x.fn.value)
case *guard:
writef(" if ")
- p.debugStr(x.condition)
- p.debugStr(x.value)
+ p.str(x.condition)
+ p.str(x.value)
case *nullLit:
write("null")
@@ -353,13 +371,13 @@
p.writef("float & ")
}
p.writef("%v", x.op)
- p.debugStr(x.value)
+ p.str(x.value)
case *interpolation:
for i, e := range x.parts {
if i != 0 {
write("+")
}
- p.debugStr(e)
+ p.str(e)
}
case *list:
// TODO: do not evaluate
@@ -384,9 +402,9 @@
}
if !ok || ln > len(x.elem.arcs) {
if !open && !isTop(x.typ) {
- p.debugStr(x.len)
+ p.str(x.len)
write("*[")
- p.debugStr(x.typ)
+ p.str(x.typ)
write("]")
if len(x.elem.arcs) == 0 {
break
@@ -398,7 +416,7 @@
}
write("[")
for i, a := range x.elem.arcs {
- p.debugStr(a.v)
+ p.str(a.v)
if i < len(x.elem.arcs)-1 {
write(",")
}
@@ -406,7 +424,7 @@
if ellipsis {
write(", ...")
if !isTop(x.typ) {
- p.debugStr(x.typ)
+ p.str(x.typ)
}
}
write("]")
@@ -418,8 +436,9 @@
write("_|_")
if x.value != nil || x.format != "" {
write("(")
- if x.value != nil {
- writef("%s:", debugStr(p.ctx, x.value))
+ if x.value != nil && p.showNodeRef {
+ p.str(x.value)
+ p.write(":")
}
write(x.msg())
write(")")
diff --git a/cue/errors.go b/cue/errors.go
index 3bfa563..73b54ae 100644
--- a/cue/errors.go
+++ b/cue/errors.go
@@ -193,7 +193,8 @@
return err
}
e := mkBin(c, src.Pos(), op, a, b)
- return c.mkErr(e, "unsupported op %s(%s, %s)", op, a.kind(), b.kind())
+ return c.mkErr(e, "invalid operation %s %s %s (mismatched types %s and %s)",
+ c.str(a), op, c.str(b), a.kind(), b.kind())
}
func (idx *index) mkErr(src source, args ...interface{}) *bottom {
@@ -249,7 +250,7 @@
case value:
// TODO: convert to Go values so that localization frameworks
// can format values accordingly.
- args[i] = debugStr(ctx, v)
+ args[i] = ctx.str(v)
}
}
}
diff --git a/cue/errors/errors.go b/cue/errors/errors.go
index f5c5619..93dd7e7 100644
--- a/cue/errors/errors.go
+++ b/cue/errors/errors.go
@@ -477,7 +477,7 @@
}
if path := Path(err); path != nil {
- fprintf(w, "%s:", strings.Join(path, "."))
+ fprintf(w, "%s: ", strings.Join(path, "."))
}
if len(positions) == 0 {
diff --git a/cue/eval.go b/cue/eval.go
index 96edf07..44db675 100644
--- a/cue/eval.go
+++ b/cue/eval.go
@@ -60,6 +60,7 @@
if n.val() == nil {
field := ctx.labelStr(x.feature)
// m.foo undefined (type map[string]bool has no field or method foo)
+ // TODO: mention x.x in error message?
return ctx.mkErr(x, "undefined field %q", field)
}
// TODO: do we need to evaluate here?
@@ -150,7 +151,7 @@
e := newEval(ctx, true)
- fn := e.eval(x.x, lambdaKind, "cannot call non-function %[1]s (type %[3]s)")
+ fn := e.eval(x.x, lambdaKind, "cannot call non-function %[2]s (type %[3]s)")
args := make([]evaluated, len(x.args))
for i, a := range x.args {
args[i] = e.evalPartial(a, typeKinds, "never triggers")
@@ -328,7 +329,7 @@
case d.marked:
if marked != nil {
// TODO: allow disjunctions to be returned as is.
- return ctx.mkErr(x, codeIncomplete, "more than one default remaining (%v and %v)", debugStr(ctx, marked), debugStr(ctx, d.val))
+ return ctx.mkErr(x, codeIncomplete, "more than one default remaining (%v and %v)", ctx.str(marked), ctx.str(d.val))
}
marked = d.val.(evaluated)
case unmarked1 == nil:
@@ -343,7 +344,7 @@
case unmarked2 != nil:
return ctx.mkErr(x, codeIncomplete, "more than one element remaining (%v and %v)",
- debugStr(ctx, unmarked1), debugStr(ctx, unmarked2))
+ ctx.str(unmarked1), ctx.str(unmarked2))
case unmarked1 != nil:
return unmarked1
@@ -418,7 +419,7 @@
switch op {
case opSub:
if kind&numeric == bottomKind {
- return ctx.mkErr(src, "unary '-' requires numeric value, found %s", kind)
+ return ctx.mkErr(src, "invalid operation -%s (- %s)", ctx.str(x), kind)
}
switch v := v.(type) {
case *numLit:
@@ -434,7 +435,7 @@
case opAdd:
if kind&numeric == bottomKind {
- return ctx.mkErr(src, "unary '+' requires numeric value, found %s", kind)
+ return ctx.mkErr(src, "invalid operation +%s (+ %s)", ctx.str(x), kind)
}
if kind&^(numeric|nonGround|referenceKind) == bottomKind {
return v
@@ -450,7 +451,7 @@
case opNot:
if kind&boolKind == bottomKind {
- return ctx.mkErr(src, "unary '!' requires bool value, found %s", kind)
+ return ctx.mkErr(src, "invalid operation !%s (! %s)", ctx.str(x), kind)
}
switch v := v.(type) {
case *top:
@@ -461,5 +462,5 @@
return &boolLit{src.base(), !v.b}
}
}
- return ctx.mkErr(src, "invalid operand type %v for unary operator %v", v, op)
+ return ctx.mkErr(src, "invalid operation %s%s (%s %s)", op, ctx.str(x), op, kind)
}
diff --git a/cue/evaluator.go b/cue/evaluator.go
index 529e509..f8c7bb4 100644
--- a/cue/evaluator.go
+++ b/cue/evaluator.go
@@ -69,7 +69,7 @@
for i := 3; i < len(args); i++ {
switch v := args[i].(type) {
case value:
- args[i] = debugStr(e.ctx, v)
+ args[i] = e.ctx.str(v)
}
}
err = e.ctx.mkErr(orig, args...)
diff --git a/cue/go.go b/cue/go.go
index 5daeb2c..adf67bc 100644
--- a/cue/go.go
+++ b/cue/go.go
@@ -211,7 +211,10 @@
case *big.Rat:
// should we represent this as a binary operation?
n := newNum(src, numKind)
- ctx.Quo(&n.v, apd.NewWithBigInt(v.Num(), 0), apd.NewWithBigInt(v.Denom(), 0))
+ _, err := ctx.Quo(&n.v, apd.NewWithBigInt(v.Num(), 0), apd.NewWithBigInt(v.Denom(), 0))
+ if err != nil {
+ return ctx.mkErr(src, err)
+ }
if !v.IsInt() {
n.k = floatKind
}
@@ -219,7 +222,7 @@
case *big.Float:
n := newNum(src, floatKind)
- n.v.SetString(v.String())
+ _, _, _ = n.v.SetString(v.String())
return n
case *apd.Decimal:
diff --git a/cue/kind.go b/cue/kind.go
index fd6382d..e078153 100644
--- a/cue/kind.go
+++ b/cue/kind.go
@@ -159,9 +159,9 @@
// - keep type compatibility mapped at a central place
// - reduce the amount op type switching.
// - simplifies testing
-func matchBinOpKind(op op, a, b kind) (k kind, swap bool) {
+func matchBinOpKind(op op, a, b kind) (k kind, swap bool, msg string) {
if op == opDisjunction {
- return a | b, false
+ return a | b, false, ""
}
u := unifyType(a, b)
valBits := u & typeKinds
@@ -171,57 +171,72 @@
a = a & typeKinds
b = b & typeKinds
if valBits == bottomKind {
+ msg := "invalid operation %[2]s %[1]s %[3]s (mismatched types %[4]s and %[5]s)"
k := nullKind
switch op {
case opLss, opLeq, opGtr, opGeq:
if a.isAnyOf(numKind) && b.isAnyOf(numKind) {
- return boolKind, false
+ return boolKind, false, ""
}
case opEql, opNeq:
if a.isAnyOf(numKind) && b.isAnyOf(numKind) {
- return boolKind, false
+ return boolKind, false, ""
}
- fallthrough
- case opUnify:
if a&nullKind != 0 {
- return k, false
+ return k, false, ""
}
if b&nullKind != 0 {
- return k, true
+ return k, true, ""
}
- return bottomKind, false
+ return bottomKind, false, msg
+ case opUnify:
+ if a&nullKind != 0 {
+ return k, false, ""
+ }
+ if b&nullKind != 0 {
+ return k, true, ""
+ }
+ switch {
+ case a.isGround() && !b.isGround():
+ msg = "invalid value %[2]s (must be %[5]s)"
+ case !a.isGround() && b.isGround():
+ msg = "invalid value %[3]s (must be %[4]s)"
+ default:
+ msg = "conflicting values %[2]s and %[3]s (mismatched types %[4]s and %[5]s)"
+ }
+ return bottomKind, false, msg
case opRem, opQuo, opMul, opAdd, opSub:
if a.isAnyOf(numKind) && b.isAnyOf(numKind) {
- return floatKind, false
+ return floatKind, false, ""
}
}
if op == opMul {
if a.isAnyOf(listKind|stringKind|bytesKind) && b.isAnyOf(intKind) {
- return a | catBits, false
+ return a | catBits, false, ""
}
if b.isAnyOf(listKind|stringKind|bytesKind) && a.isAnyOf(intKind) {
- return b | catBits, true
+ return b | catBits, true, ""
}
}
// non-overlapping types
if a&scalarKinds == 0 || b&scalarKinds == 0 {
- return bottomKind, false
+ return bottomKind, false, msg
}
// a and b have different numeric types.
switch {
case b.isAnyOf(durationKind):
// a must be a numeric, non-duration type.
if op == opMul {
- return durationKind | catBits, true
+ return durationKind | catBits, true, msg
}
case a.isAnyOf(durationKind):
if opIn(op, opMul, opQuo, opRem) {
- return durationKind | catBits, false
+ return durationKind | catBits, false, msg
}
case op.isCmp():
- return boolKind, false
+ return boolKind, false, ""
}
- return bottomKind, false
+ return bottomKind, false, msg
}
switch {
case a&nonGround == 0 && b&nonGround == 0:
@@ -236,50 +251,52 @@
switch op {
case opUnify:
// Increase likelihood of unification succeeding on first try.
- return u, swap
+ return u, swap, ""
case opLand, opLor:
if u.isAnyOf(boolKind) {
- return boolKind | catBits, swap
+ return boolKind | catBits, swap, ""
}
case opEql, opNeq, opMat, opNMat:
if u.isAnyOf(fixedKinds) {
- return boolKind | catBits, false
+ return boolKind | catBits, false, ""
}
case opLss, opLeq, opGeq, opGtr:
if u.isAnyOf(fixedKinds) {
- return boolKind | catBits, false
+ return boolKind | catBits, false, ""
}
case opAdd:
if u.isAnyOf(addableKind) {
- return u&(addableKind) | catBits, false
+ return u&(addableKind) | catBits, false, ""
}
case opSub:
if u.isAnyOf(scalarKinds) {
- return u&scalarKinds | catBits, false
+ return u&scalarKinds | catBits, false, ""
}
case opRem:
if u.isAnyOf(numKind) {
- return floatKind | catBits, false
+ return floatKind | catBits, false, ""
}
case opQuo:
if u.isAnyOf(numKind) {
- return floatKind | catBits, false
+ return floatKind | catBits, false, ""
}
case opIRem, opIMod:
if u.isAnyOf(intKind) {
- return u&(intKind) | catBits, false
+ return u&(intKind) | catBits, false, ""
}
case opIQuo, opIDiv:
if u.isAnyOf(intKind) {
- return intKind | catBits, false
+ return intKind | catBits, false, ""
}
case opMul:
if u.isAnyOf(numKind) {
- return u&numKind | catBits, false
+ return u&numKind | catBits, false, ""
}
default:
panic("unimplemented")
}
- return bottomKind, false
+ msg = "invalid operation %[2]s %[1]s %[3]s"
+ msg += fmt.Sprintf(" (operator not defined on %s)", valBits)
+ return bottomKind, false, msg
}
diff --git a/cue/kind_test.go b/cue/kind_test.go
index ac5a76a..48f48db 100644
--- a/cue/kind_test.go
+++ b/cue/kind_test.go
@@ -54,7 +54,7 @@
for _, tc := range testCases {
key := fmt.Sprintf("%s(%v, %v)", tc.op, tc.a, tc.b)
t.Run(key, func(t *testing.T) {
- got, _ := matchBinOpKind(tc.op, tc.a, tc.b)
+ got, _, _ := matchBinOpKind(tc.op, tc.a, tc.b)
if got != tc.want {
t.Errorf("got %v, want %v", got, tc.want)
}
diff --git a/cue/resolve_test.go b/cue/resolve_test.go
index 6125c7a..0db51ed 100644
--- a/cue/resolve_test.go
+++ b/cue/resolve_test.go
@@ -123,15 +123,15 @@
`b1: "a", ` +
`b2: "foo", ` +
- `b3: _|_((=~"[a-z]{4}" & "foo"):"foo" does not match =~"[a-z]{4}"), ` +
+ `b3: _|_((=~"[a-z]{4}" & "foo"):invalid value "foo" (does not match =~"[a-z]{4}")), ` +
`b4: "foo", ` +
`s1: =~"c", ` +
`s2: (!="b" & =~"[a-z]"), ` +
- `e1: _|_(("foo" =~ 1):unsupported op =~(string, int)), ` +
- `e2: _|_(("foo" !~ true):unsupported op !~(string, bool)), ` +
- `e3: _|_((!="a" & <5):unsupported op &(string, number))}`,
+ `e1: _|_(("foo" =~ 1):invalid operation "foo" =~ 1 (mismatched types string and int)), ` +
+ `e2: _|_(("foo" !~ true):invalid operation "foo" !~ true (mismatched types string and bool)), ` +
+ `e3: _|_((!="a" & <5):conflicting values !="a" and <5 (mismatched types string and number))}`,
}, {
desc: "arithmetic",
in: `
@@ -173,15 +173,15 @@
`v4: 2.0, ` +
`v5: 0, ` +
- `e0: _|_((2 + "a"):unsupported op +(int, string)), ` +
+ `e0: _|_((2 + "a"):invalid operation 2 + "a" (mismatched types int and string)), ` +
// `e1: _|_((2.0 / 1):unsupported op /(float, int)), ` +
// `e2: _|_((1 / 2.0):unsupported op /(int, float)), ` +
// `e3: _|_((3.0 % 2):unsupported op %(float, int)), ` +
// `e4: _|_((1 % 2.0):unsupported op %(int, float)), ` +
- `e5: _|_((1.0 div 2):unsupported op div(float, int)), ` +
- `e6: _|_((2 rem 2.0):unsupported op rem(int, float)), ` +
- `e7: _|_((2 quo 2.0):unsupported op quo(int, float)), ` +
- `e8: _|_((1.0 mod 1):unsupported op mod(float, int))}`,
+ `e5: _|_((1.0 div 2):invalid operation 1.0 div 2 (mismatched types float and int)), ` +
+ `e6: _|_((2 rem 2.0):invalid operation 2 rem 2.0 (mismatched types int and float)), ` +
+ `e7: _|_((2 quo 2.0):invalid operation 2 quo 2.0 (mismatched types int and float)), ` +
+ `e8: _|_((1.0 mod 1):invalid operation 1.0 mod 1 (mismatched types float and int))}`,
}, {
desc: "integer-specific arithmetic",
in: `
@@ -216,17 +216,17 @@
// TODO: handle divide by zero
`,
out: `<0>{q1: 2, q2: -2, q3: -2, q4: 2, ` +
- `qe1: _|_((2.0 quo 1):unsupported op quo(float, int)), ` +
- `qe2: _|_((2 quo 1.0):unsupported op quo(int, float)), ` +
- `r1: 1, r2: 1, r3: -1, r4: -1, re1: ` +
- `_|_((2.0 rem 1):unsupported op rem(float, int)), ` +
- `re2: _|_((2 rem 1.0):unsupported op rem(int, float)), ` +
+ `qe1: _|_((2.0 quo 1):invalid operation 2.0 quo 1 (mismatched types float and int)), ` +
+ `qe2: _|_((2 quo 1.0):invalid operation 2 quo 1.0 (mismatched types int and float)), ` +
+ `r1: 1, r2: 1, r3: -1, r4: -1, ` +
+ `re1: _|_((2.0 rem 1):invalid operation 2.0 rem 1 (mismatched types float and int)), ` +
+ `re2: _|_((2 rem 1.0):invalid operation 2 rem 1.0 (mismatched types int and float)), ` +
`d1: 2, d2: -2, d3: -3, d4: 3, ` +
- `de1: _|_((2.0 div 1):unsupported op div(float, int)), ` +
- `de2: _|_((2 div 1.0):unsupported op div(int, float)), ` +
+ `de1: _|_((2.0 div 1):invalid operation 2.0 div 1 (mismatched types float and int)), ` +
+ `de2: _|_((2 div 1.0):invalid operation 2 div 1.0 (mismatched types int and float)), ` +
`m1: 1, m2: 1, m3: 1, m4: 1, ` +
- `me1: _|_((2.0 mod 1):unsupported op mod(float, int)), ` +
- `me2: _|_((2 mod 1.0):unsupported op mod(int, float))}`,
+ `me1: _|_((2.0 mod 1):invalid operation 2.0 mod 1 (mismatched types float and int)), ` +
+ `me2: _|_((2 mod 1.0):invalid operation 2 mod 1.0 (mismatched types int and float))}`,
}, {
desc: "booleans",
in: `
@@ -237,7 +237,7 @@
e: true
e: !true
`,
- out: "<0>{t: true, f: false, e: _|_(true:conflicting values: true != false)}",
+ out: "<0>{t: true, f: false, e: _|_(true:conflicting values true and false)}",
}, {
desc: "boolean arithmetic",
in: `
@@ -248,7 +248,7 @@
e: true & true
f: true & false
`,
- out: "<0>{a: true, b: true, c: false, d: true, e: true, f: _|_(true:conflicting values: true != false)}",
+ out: "<0>{a: true, b: true, c: false, d: true, e: true, f: _|_(true:conflicting values true and false)}",
}, {
desc: "basic type",
in: `
@@ -261,7 +261,7 @@
f: true
f: bool
`,
- out: `<0>{a: 1, b: 1, c: 1.0, d: _|_((int & float):unsupported op &(int, float)), e: "4", f: true}`,
+ out: `<0>{a: 1, b: 1, c: 1.0, d: _|_((int & float):conflicting values int and float (mismatched types int and float)), e: "4", f: true}`, // TODO: eliminate redundancy
}, {
desc: "strings and bytes",
in: `
@@ -285,8 +285,8 @@
`b1: 'abcabcabc', ` +
`b2: 'abcabc', ` +
- `e0: _|_(("a" + ''):unsupported op +(string, bytes)), ` +
- `e1: _|_(('b' + "c"):unsupported op +(bytes, string))` +
+ `e0: _|_(("a" + ''):invalid operation "a" + '' (mismatched types string and bytes)), ` +
+ `e1: _|_(('b' + "c"):invalid operation 'b' + "c" (mismatched types bytes and string))` +
`}`,
}, {
desc: "escaping",
@@ -330,7 +330,7 @@
e4: [1, 2, ...>=4 & <=5] & [1, 2, 4, 8]
e5: [1, 2, 4, 8] & [1, 2, ...>=4 & <=5]
`,
- out: `<0>{list: [1,2,3], index: 2, unify: [1,2,3], e: _|_(([] & 4):unsupported op &(list, int)), e2: _|_("d":invalid list index "d" (type string)), e3: _|_(-1:invalid list index -1 (index must be non-negative)), e4: [1,2,4,_|_((<=5 & 8):8 not within bound <=5)], e5: [1,2,4,_|_((<=5 & 8):8 not within bound <=5)]}`,
+ out: `<0>{list: [1,2,3], index: 2, unify: [1,2,3], e: _|_(([] & 4):conflicting values [] and 4 (mismatched types list and int)), e2: _|_("d":invalid list index "d" (type string)), e3: _|_(-1:invalid list index -1 (index must be non-negative)), e4: [1,2,4,_|_((<=5 & 8):invalid value 8 (out of bound <=5))], e5: [1,2,4,_|_((<=5 & 8):invalid value 8 (out of bound <=5))]}`,
}, {
desc: "list arithmetic",
in: `
@@ -377,7 +377,7 @@
e: 1 // 1 & {a:3}
e: {a:3}
`,
- out: "<0>{o1: <1>{a: 1, b: 2}, o2: <2>{a: 1, b: 2}, o3: <3>{a: 1, b: 2}, o4: <4>{a: 1, b: 2}, e: _|_((1 & <5>{a: 3}):unsupported op &(int, struct))}",
+ out: "<0>{o1: <1>{a: 1, b: 2}, o2: <2>{a: 1, b: 2}, o3: <3>{a: 1, b: 2}, o4: <4>{a: 1, b: 2}, e: _|_((1 & <5>{a: 3}):conflicting values 1 and {a: 3} (mismatched types int and struct))}",
}, {
desc: "disjunctions",
in: `
@@ -429,7 +429,7 @@
p: +true
m: -false
`,
- out: `<0>{i: int, j: 3, s: string, t: "s", e: _|_((int & string):unsupported op &(int, string)), e2: _|_((1 & string):unsupported op &(int, string)), b: _|_(!int:unary '!' requires bool value, found int), p: _|_(+true:unary '+' requires numeric value, found bool), m: _|_(-false:unary '-' requires numeric value, found bool)}`,
+ out: `<0>{i: int, j: 3, s: string, t: "s", e: _|_((int & string):conflicting values int and string (mismatched types int and string)), e2: _|_((1 & string):conflicting values 1 and string (mismatched types int and string)), b: _|_(!int:invalid operation !int (! int)), p: _|_(+true:invalid operation +true (+ bool)), m: _|_(-false:invalid operation -false (- bool))}`,
}, {
desc: "comparison",
in: `
@@ -443,7 +443,7 @@
seq: "a" + "b" == "ab"
err: 2 == "s"
`,
- out: `<0>{lss: true, leq: true, eql: true, neq: true, gtr: true, geq: true, seq: true, err: _|_((2 == "s"):unsupported op ==(int, string))}`,
+ out: `<0>{lss: true, leq: true, eql: true, neq: true, gtr: true, geq: true, seq: true, err: _|_((2 == "s"):invalid operation 2 == "s" (mismatched types int and string))}`,
}, {
desc: "null",
in: `
@@ -510,9 +510,9 @@
x: x + 1
`,
out: `<0>{` +
- `a: _|_((210 & 200):conflicting values: 210 != 200), ` +
- `b: _|_((210 & 200):conflicting values: 210 != 200), ` +
- `x: _|_((100 & 101):conflicting values: 100 != 101)}`,
+ `a: _|_((210 & 200):conflicting values 210 and 200), ` +
+ `b: _|_((210 & 200):conflicting values 210 and 200), ` +
+ `x: _|_((100 & 101):conflicting values 100 and 101)}`,
// TODO: find a way to mark error in data.
}}
rewriteHelper(t, testCases, evalPartial)
@@ -588,7 +588,7 @@
e1: 2.0 % (3&int)
e2: int & 4.0/2.0
`,
- out: `<0>{v1: 5e+11, v2: true, n1: 1, v5: 2, e1: 2.0, e2: _|_((int & 2):unsupported op &(int, float))}`,
+ out: `<0>{v1: 5e+11, v2: true, n1: 1, v5: 2, e1: 2.0, e2: _|_((int & (4.0 / 2.0)):conflicting values int and (4.0 / 2.0) (mismatched types int and float))}`,
}, {
desc: "inequality",
in: `
@@ -719,15 +719,15 @@
`s30: int & >0, ` +
- `e1: _|_((!=null & null):null excluded by !=null), ` +
- `e2: _|_((!=null & null):null excluded by !=null), ` +
- `e3: _|_((>1 & 1):1 not within bound >1), ` +
- `e4: _|_((<0 & 0):0 not within bound <0), ` +
- `e5: _|_(incompatible bounds >1 and <0), ` +
- `e6: _|_(incompatible bounds >11 and <11), ` +
- `e7: _|_(incompatible bounds >=11 and <11), ` +
- `e8: _|_(incompatible bounds >11 and <=11), ` +
- `e9: _|_((>"a" & <1):unsupported op &(string, number))}`,
+ `e1: _|_((!=null & null):invalid value null (excluded by !=null)), ` +
+ `e2: _|_((!=null & null):invalid value null (excluded by !=null)), ` +
+ `e3: _|_((>1 & 1):invalid value 1 (out of bound >1)), ` +
+ `e4: _|_((<0 & 0):invalid value 0 (out of bound <0)), ` +
+ `e5: _|_(conflicting bounds >1 and <0), ` +
+ `e6: _|_(conflicting bounds >11 and <11), ` +
+ `e7: _|_(conflicting bounds >=11 and <11), ` +
+ `e8: _|_(conflicting bounds >11 and <=11), ` +
+ `e9: _|_((>"a" & <1):conflicting values >"a" and <1 (mismatched types string and number))}`,
}, {
desc: "custom validators",
in: `
@@ -741,7 +741,7 @@
`,
out: `<0>{` +
`a: "after", ` +
- `b: _|_(builtin:ContainsAny ("c"):value "dog" not in ContainsAny("c"))` +
+ `b: _|_(builtin:ContainsAny ("c"):invalid value "dog" (does not satisfy strings.ContainsAny("c")))` +
`}`,
}, {
desc: "null coalescing",
@@ -750,7 +750,7 @@
b: a.x | "b"
c: a["x"] | "c"
`,
- out: `<1>{a: null, b: "b", c: "c"}`,
+ out: `<0>{a: null, b: "b", c: "c"}`,
}, {
desc: "reference across tuples and back",
// Tests that it is okay to partially evaluate structs.
@@ -867,8 +867,8 @@
`i2: 3, ` +
`t0: [<3>{a: 8}], ` +
`t1: [, ...int], ` +
- `e0: _|_(([<4>{},<4>{}] & [<5>{}]):incompatible list lengths: conflicting values: 2 != 1), ` +
- `e1: _|_(([, ...int] & [, ...float]):incompatible list types: unsupported op &(int, float): )` +
+ `e0: _|_(([<4>{},<4>{}] & [<5>{}]):conflicting list lengths: conflicting values 2 and 1), ` +
+ `e1: _|_((int & float):conflicting list element types: conflicting values int and float (mismatched types int and float))` +
`}`,
}, {
// TODO: consider removing list arithmetic altogether. It is no longer
@@ -1057,7 +1057,7 @@
in: `
a: "a" & 1
`,
- out: `<0>{a: _|_(("a" & 1):unsupported op &(string, int))}`,
+ out: `<0>{a: _|_(("a" & 1):conflicting values "a" and 1 (mismatched types string and int))}`,
}, {
desc: "structs",
in: `
@@ -1260,15 +1260,14 @@
`a1: 3, ` +
`a2: 1, ` +
`a3: 5, ` +
- `a4: _|_((<=5 & 6):6 not within bound <=5), ` +
- `a5: _|_((>=1 & 0):0 not within bound >=1), ` +
+ `a4: _|_((<=5 & 6):invalid value 6 (out of bound <=5)), ` +
+ `a5: _|_((>=1 & 0):invalid value 0 (out of bound >=1)), ` +
`a6: 3, ` +
`a7: 1, ` +
`a8: 5, ` +
- // TODO: improve error
- `a9: _|_((<=5 & 6):6 not within bound <=5), ` +
- `a10: _|_((>=1 & 0):0 not within bound >=1), ` +
+ `a9: _|_((<=5 & 6):invalid value 6 (out of bound <=5)), ` +
+ `a10: _|_((>=1 & 0):invalid value 0 (out of bound >=1)), ` +
`b1: (>=1 & <=5), ` +
`b2: 1, ` +
@@ -1276,18 +1275,18 @@
`b4: (>=2 & <=3), ` +
`b5: (>=3 & <=5), ` +
`b6: 5, ` +
- `b7: _|_(incompatible bounds >=6 and <=5), ` +
+ `b7: _|_(conflicting bounds >=6 and <=5), ` +
`b8: (>=1 & <=5), ` +
`b9: 1, ` +
`b10: 5, ` +
`b11: (>=2 & <=3), ` +
`b12: (>=3 & <=5), ` +
`b13: 5, ` +
- `b14: _|_(incompatible bounds >=6 and <=5), ` +
+ `b14: _|_(conflicting bounds >=6 and <=5), ` +
`c1: (int & >=1 & <=5), ` +
`c2: (<=5 & int & >=1), ` +
- `c3: _|_((string & >=1):unsupported op &(string, number)), ` +
- `c4: _|_(((>=1 & <=5) & string):unsupported op &(number, string)), ` +
+ `c3: _|_((string & >=1):conflicting values string and >=1 (mismatched types string and number)), ` +
+ `c4: _|_(((>=1 & <=5) & string):conflicting values (>=1 & <=5) and string (mismatched types number and string)), ` +
`s1: "e", ` +
`s2: "ee", ` +
`n1: (>=1 & <=2), ` +
@@ -1308,7 +1307,7 @@
e1: 100_000
`,
out: `<0>{k1: 44, k2: -8000000000, ` +
- `e1: _|_((int & <=32767 & 100000):100000 not within bound int & <=32767)}`,
+ `e1: _|_((int & <=32767 & 100000):invalid value 100000 (out of bound int & <=32767))}`,
}, {
desc: "field comprehensions",
in: `
@@ -1342,10 +1341,10 @@
out: `<0>{` +
`a1: <1>{a: (=~"oo" & =~"fo"), b: =~"oo", c: =~"fo"}, ` +
`a2: <2>{a: "foo", b: =~"oo", c: =~"fo"}, ` +
- `a3: <3>{a: _|_((=~"oo" & "bar"):"bar" does not match =~"oo"), b: =~"oo", c: =~"fo"}, ` +
+ `a3: <3>{a: _|_((=~"oo" & "bar"):invalid value "bar" (does not match =~"oo")), b: =~"oo", c: =~"fo"}, ` +
`o1: <4>{a: string, b: string, c: "bar"}, ` +
`o2: <5>{a: "foo", b: string, c: "bar"}, ` +
- `o3: <6>{a: _|_((builtin:or ([<7>.b,<7>.c]) & "foo"):empty disjunction: conflicting values: baz != foo), b: "baz", c: "bar"}}`,
+ `o3: <6>{a: _|_((builtin:or ([<7>.b,<7>.c]) & "foo"):empty disjunction: conflicting values "baz" and "foo"), b: "baz", c: "bar"}}`,
}, {
desc: "self-reference cycles conflicts with strings",
in: `
@@ -1355,7 +1354,7 @@
}
a x: "hey"
`,
- out: `<0>{a: <1>{x: _|_(("hey!?" & "hey"):conflicting values: hey!? != hey), y: "hey!"}}`,
+ out: `<0>{a: <1>{x: _|_(("hey!?" & "hey"):conflicting values "hey!?" and "hey"), y: "hey!"}}`,
}, {
desc: "resolved self-reference cycles with disjunctions",
in: `
@@ -1453,11 +1452,11 @@
`xd5: 10, ` +
`xd3: 6, ` +
- `xe1: _|_((6 & 7):conflicting values: 6 != 7), ` +
- `xe2: _|_((6 & 7):conflicting values: 6 != 7), ` +
- `xe4: _|_((6 & 7):conflicting values: 6 != 7), ` +
- `xe5: _|_((6 & 7):conflicting values: 6 != 7), ` +
- `xe3: _|_((6 & 7):conflicting values: 6 != 7), ` +
+ `xe1: _|_((6 & 7):conflicting values 6 and 7), ` +
+ `xe2: _|_((6 & 7):conflicting values 6 and 7), ` +
+ `xe4: _|_((6 & 7):conflicting values 6 and 7), ` +
+ `xe5: _|_((6 & 7):conflicting values 6 and 7), ` +
+ `xe3: _|_((6 & 7):conflicting values 6 and 7), ` +
`xf1: 8, ` +
`xf2: 8, ` +
@@ -1543,11 +1542,11 @@
`xd5: 10, ` +
`xd3: 6, ` +
- `xe1: _|_((6 & 7):conflicting values: 6 != 7), ` +
- `xe2: _|_((6 & 7):conflicting values: 6 != 7), ` +
- `xe4: _|_((6 & 7):conflicting values: 6 != 7), ` +
- `xe5: _|_((6 & 7):conflicting values: 6 != 7), ` +
- `xe3: _|_((6 & 7):conflicting values: 6 != 7), ` +
+ `xe1: _|_((6 & 7):conflicting values 6 and 7), ` +
+ `xe2: _|_((6 & 7):conflicting values 6 and 7), ` +
+ `xe4: _|_((6 & 7):conflicting values 6 and 7), ` +
+ `xe5: _|_((6 & 7):conflicting values 6 and 7), ` +
+ `xe3: _|_((6 & 7):conflicting values 6 and 7), ` +
`z1: (*11 | 13), ` + // 13 is eliminated with evalFull
`z2: 10, ` +
@@ -1562,7 +1561,7 @@
in: `
a: 8000.9
a: 7080 | int`,
- out: `<0>{a: _|_((8000.9 & int):unsupported op &(float, int))}`,
+ out: `<0>{a: _|_((8000.9 & (int | int)):conflicting values 8000.9 and int (mismatched types float and int))}`, // TODO: fix repetition
}, {
desc: "resolve all disjunctions",
in: `
@@ -1907,7 +1906,7 @@
x: {a:1}|{a:2}
y: x & {a:3}
`,
- out: `<3>{x: _|_((<0>{a: 1} | <1>{a: 2}):more than one element remaining (<0>{a: 1} and <1>{a: 2})), y: _|_((<4>.x & <5>{a: 3}):empty disjunction: <2>{a: (1 & 3)})}`,
+ out: `<0>{x: _|_((<1>{a: 1} | <2>{a: 2}):more than one element remaining ({a: 1} and {a: 2})), y: _|_((<3>.x & <4>{a: 3}):empty disjunction: {a: (1 & 3)})}`,
}, {
desc: "cannot resolve references that would be ambiguous",
in: `
@@ -1923,14 +1922,14 @@
c1: (*{a:1} | {b:1}) & c2
c2: (*{a:2} | {b:2}) & c1
`,
- out: `<4>{` +
- `a1: _|_(((*0 | 1) & (<5>.a3 - <5>.a2)):cycle detected), ` +
+ out: `<0>{` +
+ `a1: _|_(((*0 | 1) & (<1>.a3 - <1>.a2)):cycle detected), ` +
`a3: 1, ` +
- `a2: _|_(((*0 | 1) & (<5>.a3 - <5>.a1)):cycle detected), ` +
+ `a2: _|_(((*0 | 1) & (<1>.a3 - <1>.a1)):cycle detected), ` +
`b1: _|_((0 | 1 | *_|_):more than one element remaining (0 and 1)), ` +
`b2: _|_((0 | 1 | *_|_):more than one element remaining (0 and 1)), ` +
- `c1: _|_((<0>{a: 1, b: 2} | <1>{a: 2, b: 1} | *_|_):more than one element remaining (<0>{a: 1, b: 2} and <1>{a: 2, b: 1})), ` +
- `c2: _|_((<2>{a: 2, b: 1} | <3>{a: 1, b: 2} | *_|_):more than one element remaining (<2>{a: 2, b: 1} and <3>{a: 1, b: 2}))}`,
+ `c1: _|_((<2>{a: 1, b: 2} | <3>{a: 2, b: 1} | *_|_):more than one element remaining ({a: 1, b: 2} and {a: 2, b: 1})), ` +
+ `c2: _|_((<4>{a: 2, b: 1} | <5>{a: 1, b: 2} | *_|_):more than one element remaining ({a: 2, b: 1} and {a: 1, b: 2}))}`,
}}
rewriteHelper(t, testCases, evalFull)
}
diff --git a/cue/types.go b/cue/types.go
index be07f26..ca3bc09 100644
--- a/cue/types.go
+++ b/cue/types.go
@@ -330,13 +330,13 @@
var (
// ErrBelow indicates that a value was rounded down in a conversion.
- ErrBelow = errors.New("cue: value was rounded down")
+ ErrBelow = errors.New("value was rounded down")
// ErrAbove indicates that a value was rounded up in a conversion.
- ErrAbove = errors.New("cue: value was rounded up")
+ ErrAbove = errors.New("value was rounded up")
// ErrInfinite indicates that a value is infinite.
- ErrInfinite = errors.New("cue: infinite")
+ ErrInfinite = errors.New("infinite")
)
// Int converts the underlying integral number to an big.Int. It reports an
@@ -711,12 +711,12 @@
return nil, toMarshalErr(v, x.(*bottom))
default:
if k.hasReferences() {
- return nil, marshalErrf(v, x, "value %q contains unresolved references", debugStr(ctx, x))
+ return nil, marshalErrf(v, x, "value %q contains unresolved references", ctx.str(x))
}
if !k.isGround() {
- return nil, marshalErrf(v, x, "cannot convert incomplete value %q to JSON", debugStr(ctx, x))
+ return nil, marshalErrf(v, x, "cannot convert incomplete value %q to JSON", ctx.str(x))
}
- return nil, marshalErrf(v, x, "cannot convert value %q of type %T to JSON", debugStr(ctx, x), x)
+ return nil, marshalErrf(v, x, "cannot convert value %q of type %T to JSON", ctx.str(x), x)
}
}
@@ -880,7 +880,8 @@
got := x.kind()
if want != bottomKind {
if got&want&concreteKind == bottomKind {
- return ctx.mkErr(x, "not of right kind (%v vs %v)", got, want)
+ return ctx.mkErr(x, "cannot use value %v (type %s) as %s",
+ v.ctx().str(x), got, want)
}
if !got.isGround() {
return ctx.mkErr(x, codeIncomplete,
@@ -1171,7 +1172,7 @@
fmt.Fprint(state, "<nil>")
return
}
- io.WriteString(state, debugStr(ctx, v.path.cache))
+ _, _ = io.WriteString(state, ctx.str(v.path.cache))
}
// Reference returns path from the root of the instance referred to by this
@@ -1387,7 +1388,7 @@
}
default:
if !x.kind().isGround() {
- return ctx.mkErr(v, "incomplete value (%v)", debugStr(ctx, v))
+ return ctx.mkErr(v, "incomplete value (%v)", ctx.str(v))
}
}
return nil
@@ -1429,9 +1430,10 @@
// The returned attribute will return an error for any of its methods if there
// is no attribute for the requested key.
func (v Value) Attribute(key string) Attribute {
+ const msgNotExist = "attribute %q does not exist"
// look up the attributes
if v.path == nil || v.path.attrs == nil {
- return Attribute{err: v.toErr(errNotExists)}
+ return Attribute{err: errors.Newf(token.NoPos, msgNotExist, key)}
}
for _, a := range v.path.attrs.attr {
if a.key() != key {
@@ -1443,7 +1445,7 @@
}
return at
}
- return Attribute{err: v.toErr(errNotExists)}
+ return Attribute{err: errors.Newf(token.NoPos, msgNotExist, key)}
}
// An Attribute contains meta data about a field.
diff --git a/cue/types_test.go b/cue/types_test.go
index c715819..d4d6dd1 100644
--- a/cue/types_test.go
+++ b/cue/types_test.go
@@ -220,8 +220,8 @@
errU: ErrBelow.Error(),
}, {
value: "1.0",
- err: "not of right kind (float vs int)",
- errU: "not of right kind (float vs int)",
+ err: "cannot use value 1.0 (type float) as int",
+ errU: "cannot use value 1.0 (type float) as int",
notInt: true,
}, {
value: "int",
@@ -478,7 +478,7 @@
err: "from source",
}, {
value: `"str"`,
- err: "not of right kind (string vs null)",
+ err: "cannot use value \"str\" (type string) as null",
}, {
value: `null`,
}, {
@@ -503,7 +503,7 @@
err: "from source",
}, {
value: `"str"`,
- err: "not of right kind (string vs bool)",
+ err: "cannot use value \"str\" (type string) as bool",
}, {
value: `true`,
bool: true,
@@ -535,7 +535,7 @@
err: "from source",
}, {
value: `"str"`,
- err: "not of right kind (string vs list)",
+ err: "cannot use value \"str\" (type string) as list",
}, {
value: `[]`,
res: "[]",
@@ -583,7 +583,7 @@
err: "from source",
}, {
value: `"str"`,
- err: "not of right kind (string vs struct)",
+ err: "cannot use value \"str\" (type string) as struct",
}, {
value: `{}`,
res: "{}",
@@ -697,12 +697,12 @@
ok: true,
}, {
value: `*{a:1,b:2}|{a:1}|{b:2}`,
- def: "<0>{a: 1, b: 2}",
- val: "<0>{a: 1}|<0>{b: 2}",
+ def: "{a: 1, b: 2}",
+ val: "{a: 1}|{b: 2}",
ok: true,
}, {
value: `{a:1}&{b:2}`,
- def: `<0>{a: 1, b: 2}`,
+ def: `{a: 1, b: 2}`,
val: ``,
ok: false,
}}
@@ -756,7 +756,7 @@
// length: "2",
}, {
input: "3",
- length: "_|_(3:len not supported for type 8)",
+ length: "_|_(len not supported for type 8)", // TODO: fix kind name
}}
for _, tc := range testCases {
t.Run(tc.input, func(t *testing.T) {
@@ -1145,7 +1145,7 @@
}, {
config: config,
path: strList(),
- str: "<0>{a: <1>{a: 0, b: 1, c: 2}, b: <2>{d: <0>.a.a, e: int}",
+ str: "{a: {a: 0, b: 1, c: 2}, b: {d: a.a, e: int}",
}, {
config: config,
path: strList("a", "a"),
@@ -1153,7 +1153,7 @@
}, {
config: config,
path: strList("a"),
- str: "<0>{a: 0, b: 1, c: 2}",
+ str: "{a: 0, b: 1, c: 2}",
}, {
config: config,
path: strList("b", "d"),
@@ -1166,7 +1166,7 @@
}, {
config: config,
path: strList("b", "d", "lookup in non-struct"),
- str: "not of right kind (int vs struct)",
+ str: "cannot use value 0 (type int) as struct",
}}
for _, tc := range testCases {
t.Run(tc.str, func(t *testing.T) {
@@ -1214,15 +1214,15 @@
}, {
path: "a",
attr: "bar",
- err: errors.New("undefined value"),
+ err: errors.New(`attribute "bar" does not exist`),
}, {
path: "xx",
attr: "bar",
- err: errors.New("undefined value"),
+ err: errors.New(`attribute "bar" does not exist`),
}, {
path: "e",
attr: "bar",
- err: errors.New("undefined value"),
+ err: errors.New(`attribute "bar" does not exist`),
}}
for _, tc := range testCases {
t.Run(tc.path+"-"+tc.attr, func(t *testing.T) {
@@ -1267,7 +1267,7 @@
}, {
path: "e",
attr: "bar",
- err: errors.New("undefined value"),
+ err: errors.New(`attribute "bar" does not exist`),
}, {
path: "b",
attr: "foo",
@@ -1315,7 +1315,7 @@
}, {
path: "e",
attr: "bar",
- err: errors.New("undefined value"),
+ err: errors.New(`attribute "bar" does not exist`),
}, {
path: "b",
attr: "foo",
@@ -1377,7 +1377,7 @@
}, {
path: "e",
attr: "bar",
- err: errors.New("undefined value"),
+ err: errors.New(`attribute "bar" does not exist`),
}, {
path: "b",
attr: "foo",
@@ -1452,7 +1452,7 @@
}, {
path: "e",
attr: "bar",
- err: errors.New("undefined value"),
+ err: errors.New(`attribute "bar" does not exist`),
}, {
path: "b",
attr: "foo",
@@ -1688,7 +1688,7 @@
}, {
value: `(a.b)
a: {}`,
- out: `_|_(<0>.a.b:undefined field "b")`,
+ out: `_|_(undefined field "b")`,
}, {
value: `true`,
out: `true`,
diff --git a/cue/value.go b/cue/value.go
index 88a8af2..859c930 100644
--- a/cue/value.go
+++ b/cue/value.go
@@ -76,7 +76,7 @@
}
got := x.kind()
if got&want&concreteKind == bottomKind && want != bottomKind {
- return ctx.mkErr(x, "not of right kind (%v vs %v)", got, want)
+ return ctx.mkErr(x, "cannot use value %v (type %s) as %s", x, got, want)
}
if !got.isGround() {
return ctx.mkErr(x, codeIncomplete,
@@ -1161,7 +1161,7 @@
func (x *binaryExpr) kind() kind {
// TODO: cache results
- kind, _ := matchBinOpKind(x.op, x.left.kind(), x.right.kind())
+ kind, _, _ := matchBinOpKind(x.op, x.left.kind(), x.right.kind())
return kind | nonGround
}
@@ -1286,7 +1286,7 @@
err := x.values[0].val
if !isBottom(err) {
// TODO: use format instead of debugStr.
- err = ctx.mkErr(src, debugStr(ctx, err))
+ err = ctx.mkErr(src, ctx.str(err))
}
return mVal{ctx.mkErr(src, "empty disjunction: %v", err), false}
case 1:
diff --git a/cuego/examples_test.go b/cuego/examples_test.go
index 0a0c0f4..9e160a9 100644
--- a/cuego/examples_test.go
+++ b/cuego/examples_test.go
@@ -42,7 +42,7 @@
//Output:
// completed: cuego_test.Sum{A:1, B:5, C:6} (err: <nil>)
// completed: cuego_test.Sum{A:2, B:6, C:8} (err: <nil>)
- // empty disjunction: unsupported op &(null, struct)
+ // empty disjunction: invalid operation null & {A: 2, B: 3, C: 8} (mismatched types null and struct)
}
func ExampleConstrain() {
@@ -91,6 +91,6 @@
//Output:
// error: <nil>
// validate: <nil>
- // validate: 39 not within bound <=12
- // validate: "foo.jso" does not match =~".json$"
+ // validate: invalid value 39 (out of bound <=12)
+ // validate: invalid value "foo.jso" (does not match =~".json$")
}
diff --git a/doc/tutorial/basics/bottom.md b/doc/tutorial/basics/bottom.md
index 46515da..2a36afa 100644
--- a/doc/tutorial/basics/bottom.md
+++ b/doc/tutorial/basics/bottom.md
@@ -7,7 +7,7 @@
Specifying duplicate fields with conflicting values results in an error
or bottom.
_Bottom_ is a special value in CUE, denoted `_|_`, that indicates an
-error such as incompatible values.
+error such as conflicting values.
Any error in CUE results in `_|_`.
Logically all errors are equal, although errors may be associated with
metadata such as an error message.
@@ -31,8 +31,8 @@
<!-- result -->
`$ cue eval -i bottom.cue`
```
-a: _|_ /* conflicting values: 4 != 5 */
-l: [1, _|_ /* conflicting values: 2 != 3 */]
+a: _|_ /* conflicting values 4 and 5 */
+l: [1, _|_ /* conflicting values 2 and 3 */]
list: [0, 1, 2]
val: _|_ /* index 3 out of bounds */
```
diff --git a/doc/tutorial/basics/numbers.md b/doc/tutorial/basics/numbers.md
index d2fb464..deb8329 100644
--- a/doc/tutorial/basics/numbers.md
+++ b/doc/tutorial/basics/numbers.md
@@ -34,6 +34,6 @@
```
a: 4
b: 4
-c: _|_ /* unsupported op &(int, float) */
+c: _|_ /* conflicting values int and 4.0 (mismatched types int and float) */
d: 4
```
\ No newline at end of file
diff --git a/doc/tutorial/basics/rangedef.md b/doc/tutorial/basics/rangedef.md
index d0939c3..f7fe8ab 100644
--- a/doc/tutorial/basics/rangedef.md
+++ b/doc/tutorial/basics/rangedef.md
@@ -43,7 +43,7 @@
<!-- result -->
`$ cue eval -i range.cue`
```
-a: _|_ /* -1 not within bound int & >=0 */
+a: _|_ /* invalid value -1 (out of bound int & >=0) */
b: 128
c: 2000000000
```
\ No newline at end of file
diff --git a/doc/tutorial/basics/ranges.md b/doc/tutorial/basics/ranges.md
index e7677fe..b552bf4 100644
--- a/doc/tutorial/basics/ranges.md
+++ b/doc/tutorial/basics/ranges.md
@@ -35,9 +35,9 @@
`$ cue eval -i bounds.cue`
```
a: 3.5
-b: _|_ /* unsupported op &(int, float) */
+b: _|_ /* conflicting values ri and 3.5 (mismatched types int and float) */
c: 3
d: "ma"
-e: _|_ /* "mu" not within bound <"mo" */
+e: _|_ /* invalid value "mu" (out of bound <"mo") */
r1: >=5 & <8
```
diff --git a/doc/tutorial/basics/regexp.md b/doc/tutorial/basics/regexp.md
index 504f40d..3f0e6de 100644
--- a/doc/tutorial/basics/regexp.md
+++ b/doc/tutorial/basics/regexp.md
@@ -35,5 +35,5 @@
b: true
c: =~"^[a-z]{3}$"
d: "foo"
-e: _|_ /* "foo bar" does not match =~"^[a-z]{3}$" */
+e: _|_ /* invalid value "foo bar" (does not match =~"^[a-z]{3}$") */
```
\ No newline at end of file
diff --git a/doc/tutorial/basics/unification.md b/doc/tutorial/basics/unification.md
index e631768..f8254ac 100644
--- a/doc/tutorial/basics/unification.md
+++ b/doc/tutorial/basics/unification.md
@@ -45,16 +45,16 @@
q: {
x: 1
y: 2
- z: _|_ /* conflicting values: 3 != 4 */
+ z: _|_ /* conflicting values 3 and 4 */
}
r: {
x: 1
y: 2
- z: _|_ /* conflicting values: 3 != 4 */
+ z: _|_ /* conflicting values 3 and 4 */
}
s: {
x: 1
y: 2
- z: _|_ /* conflicting values: 4 != 3 */
+ z: _|_ /* conflicting values 4 and 3 */
}
```
\ No newline at end of file