cue: introduce more explict error types
Issue #52
Change-Id: Ib0ba1f2f500ab15351cfccce57ac35e72d181983
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/2206
Reviewed-by: Marcel van Lohuizen <mpvl@google.com>
diff --git a/cmd/cue/cmd/testdata/partial/eval_conc.out b/cmd/cue/cmd/testdata/partial/eval_conc.out
index 9437c40..3c83dc1 100644
--- a/cmd/cue/cmd/testdata/partial/eval_conc.out
+++ b/cmd/cue/cmd/testdata/partial/eval_conc.out
@@ -1,2 +1,2 @@
-: invalid non-ground value string (must be concrete int|string):
- ./testdata/partial/partial.cue:7:7
+more than one element remaining (1 and 2):
+ ./testdata/partial/partial.cue:4:6
diff --git a/cue/builtin.go b/cue/builtin.go
index 8751ecb..fc22d07 100644
--- a/cue/builtin.go
+++ b/cue/builtin.go
@@ -421,7 +421,7 @@
func (c *callCtxt) error(i int) error {
x := newValueRoot(c.ctx, c.args[i])
- return x.Err()
+ return x.err()
}
func (c *callCtxt) list(i int) (a Iterator) {
diff --git a/cue/errors.go b/cue/errors.go
index afd89ae..3bf3dbb 100644
--- a/cue/errors.go
+++ b/cue/errors.go
@@ -51,7 +51,6 @@
func (v Value) toErr(b *bottom) errors.Error {
return &valueError{
- Message: errors.NewMessage(b.msg, nil),
v: v,
err: b,
}
@@ -61,14 +60,20 @@
// A valueError is returned as a result of evaluating a value.
type valueError struct {
- errors.Message
-
v Value
err *bottom
}
+func (e *valueError) Error() string {
+ return fmt.Sprint(e.err)
+}
+
func (e *valueError) Position() token.Pos {
- return e.err.pos.Pos()
+ return e.err.Pos()
+}
+
+func (e *valueError) Positions() []token.Pos {
+ return e.err.Positions()
}
func (e *valueError) Path() (a []string) {
diff --git a/cue/errors/errors.go b/cue/errors/errors.go
index 019c71d..717cc89 100644
--- a/cue/errors/errors.go
+++ b/cue/errors/errors.go
@@ -299,17 +299,17 @@
for e := err; e != nil; e = xerrors.Unwrap(e) {
switch x := e.(type) {
- case interface{ Position() token.Pos }:
- if pos := x.Position().String(); pos != "-" {
- positions = append(positions, pos)
- }
-
case interface{ Positions() []token.Pos }:
for _, p := range x.Positions() {
if p.IsValid() {
positions = append(positions, p.String())
}
}
+
+ case interface{ Position() token.Pos }:
+ if pos := x.Position().String(); pos != "-" {
+ positions = append(positions, pos)
+ }
}
}
diff --git a/cue/instance.go b/cue/instance.go
index 62c5f40..219c548 100644
--- a/cue/instance.go
+++ b/cue/instance.go
@@ -237,8 +237,7 @@
for _, k := range path {
obj, err := v.structVal(ctx)
if err != nil {
- v := err.(*bottom)
- return Value{idx, &valueData{arc: arc{cache: v, v: v}}}
+ return Value{idx, &valueData{arc: arc{cache: err, v: err}}}
}
v = obj.Lookup(k)
}
diff --git a/cue/types.go b/cue/types.go
index f168f35..046a3c5 100644
--- a/cue/types.go
+++ b/cue/types.go
@@ -788,6 +788,14 @@
// Err returns the error represented by v or nil v is not an error.
func (v Value) Err() error {
+ if err := v.checkKind(v.ctx(), bottomKind); err != nil {
+ return v.toErr(err)
+ }
+ return nil
+}
+
+// TODO: make bottom not an error and then make this return *bottom.
+func (v Value) err() error {
// TODO(incomplete): change to not return an error for incomplete.
if err := v.checkKind(v.ctx(), bottomKind); err != nil {
return err
@@ -928,7 +936,7 @@
v, _ = v.Default()
ctx := v.ctx()
if err := v.checkKind(ctx, listKind); err != nil {
- return Iterator{ctx: ctx}, err
+ return Iterator{ctx: ctx}, v.toErr(err)
}
l := v.eval(ctx).(*list)
return Iterator{ctx: ctx, val: v, iter: l, len: len(l.elem.arcs)}, nil
@@ -938,7 +946,7 @@
func (v Value) Null() error {
v, _ = v.Default()
if err := v.checkKind(v.ctx(), nullKind); err != nil {
- return err
+ return v.toErr(err)
}
return nil
}
@@ -953,7 +961,7 @@
v, _ = v.Default()
ctx := v.ctx()
if err := v.checkKind(ctx, boolKind); err != nil {
- return false, err
+ return false, v.toErr(err)
}
return v.eval(ctx).(*boolLit).b, nil
}
@@ -963,7 +971,7 @@
v, _ = v.Default()
ctx := v.ctx()
if err := v.checkKind(ctx, stringKind); err != nil {
- return "", err
+ return "", v.toErr(err)
}
return v.eval(ctx).(*stringLit).str, nil
}
@@ -979,7 +987,7 @@
case *stringLit:
return []byte(x.str), nil
}
- return nil, v.checkKind(ctx, bytesKind|stringKind)
+ return nil, v.toErr(v.checkKind(ctx, bytesKind|stringKind))
}
// Reader returns a new Reader if v is a string or bytes type and an error
@@ -993,7 +1001,7 @@
case *stringLit:
return strings.NewReader(x.str), nil
}
- return nil, v.checkKind(ctx, stringKind|bytesKind)
+ return nil, v.toErr(v.checkKind(ctx, stringKind|bytesKind))
}
// TODO: distinguish between optional, hidden, etc. Probably the best approach
@@ -1001,7 +1009,7 @@
// a structVal.
// structVal returns an structVal or an error if v is not a struct.
-func (v Value) structVal(ctx *context) (structValue, error) {
+func (v Value) structVal(ctx *context) (structValue, *bottom) {
return v.structValOpts(ctx, options{
omitHidden: true,
omitOptional: true,
@@ -1009,7 +1017,7 @@
}
// structVal returns an structVal or an error if v is not a struct.
-func (v Value) structValOpts(ctx *context, o options) (structValue, error) {
+func (v Value) structValOpts(ctx *context, o options) (structValue, *bottom) {
v, _ = v.Default()
if err := v.checkKind(ctx, structKind); err != nil {
return structValue{}, err
@@ -1062,7 +1070,7 @@
ctx := v.ctx()
obj, err := v.structValOpts(ctx, o)
if err != nil {
- return Iterator{ctx: ctx}, err
+ return Iterator{ctx: ctx}, v.toErr(err)
}
return Iterator{ctx: ctx, val: v, iter: obj.n, len: len(obj.n.arcs)}, nil
}
@@ -1076,7 +1084,8 @@
for _, k := range path {
obj, err := v.structVal(ctx)
if err != nil {
- return newValueRoot(ctx, err.(*bottom))
+ // TODO: return a Value at the same location and a new error?
+ return newValueRoot(ctx, err)
}
v = obj.Lookup(k)
}
@@ -1318,18 +1327,18 @@
o := getOptions(opts)
list := errors.List{}
v.Walk(func(v Value) bool {
- if err := v.Err(); err != nil {
+ if err := v.checkKind(v.ctx(), bottomKind); err != nil {
if !o.concrete && isIncomplete(v.eval(v.ctx())) {
return false
}
- list.Add(err)
+ list.Add(v.toErr(err))
if len(list) > 50 {
return false // mostly to avoid some hypothetical cycle issue
}
}
if o.concrete {
if err := isGroundRecursive(v.ctx(), v.eval(v.ctx())); err != nil {
- list.Add(err)
+ list.Add(v.toErr(err))
}
}
return true
@@ -1343,7 +1352,7 @@
return nil
}
-func isGroundRecursive(ctx *context, v value) error {
+func isGroundRecursive(ctx *context, v value) *bottom {
switch x := v.(type) {
case *list:
for i := 0; i < len(x.elem.arcs); i++ {
@@ -1354,7 +1363,7 @@
}
default:
if !x.kind().isGround() {
- return ctx.mkErr(v, "incomplete value %q", debugStr(ctx, v))
+ return ctx.mkErr(v, "incomplete value (%v)", debugStr(ctx, v))
}
}
return nil
diff --git a/cue/types_test.go b/cue/types_test.go
index 5e6ed27..e8a59c3 100644
--- a/cue/types_test.go
+++ b/cue/types_test.go
@@ -463,7 +463,7 @@
}}
for _, tc := range testCases {
t.Run(tc.value, func(t *testing.T) {
- err := getInstance(t, tc.value).Value().Err()
+ err := getInstance(t, tc.value).Value().err()
checkErr(t, err, tc.err, "init")
})
}
@@ -638,7 +638,7 @@
}
}
v := obj.Lookup("non-existing")
- checkErr(t, v.Err(), "not found", "non-existing")
+ checkErr(t, v.err(), "not found", "non-existing")
})
}
}