cue: track incompleteness across marshal errors
Fixes #235
Change-Id: Iafee5b2d584a79ea738e6b60ce6872b22a3c70a5
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/4423
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/cue/builtin.go b/cue/builtin.go
index bdad738..e1c161e 100644
--- a/cue/builtin.go
+++ b/cue/builtin.go
@@ -18,6 +18,7 @@
package cue
import (
+ "encoding/json"
"fmt"
"io"
"math/big"
@@ -289,6 +290,10 @@
case nil:
case *callError:
ret = err.b
+ case *json.MarshalerError:
+ if err, ok := err.Err.(*marshalError); ok && err.b != nil {
+ ret = err.b
+ }
default:
// TODO: store the underlying error explicitly
ret = ctx.mkErr(src, x, "error in call to %s: %v", x.name(ctx), err)
diff --git a/cue/resolve_test.go b/cue/resolve_test.go
index 3733af9..6637245 100644
--- a/cue/resolve_test.go
+++ b/cue/resolve_test.go
@@ -16,6 +16,7 @@
import (
"flag"
+ "strings"
"testing"
)
@@ -2886,17 +2887,30 @@
`B :: <11>C{foo: string, a: <12>C{foo: <13>C{[string]: <14>(_: string)-><15>.foo, }}}, ` +
`b: <16>C{foo: "key", a: <17>C{foo: <18>C{["key"]: <19>(_: string)-><20>.foo, }}}` +
`}`,
+ }, {
+ desc: "json Marshaling detects incomplete",
+ in: `
+ import "encoding/json"
+ a: json.Marshal({ a: string} )
+
+ foo: { a: 3, b: foo.c }
+ b: json.Marshal(foo)
+ `,
+ out: `<0>{` +
+ `a: <1>.Marshal (<2>{a: string}), ` +
+ `foo: <3>{a: 3, b: <4>.foo.c}, ` +
+ `b: <1>.Marshal (<4>.foo)}`,
}}
rewriteHelper(t, testCases, evalFull)
}
func TestX(t *testing.T) {
- t.Skip()
-
// Don't remove. For debugging.
- testCases := []testCase{{
- in: `
- `,
- }}
- rewriteHelper(t, testCases, evalFull)
+ in := `
+ `
+
+ if strings.TrimSpace(in) == "" {
+ t.Skip()
+ }
+ rewriteHelper(t, []testCase{{in: in}}, evalFull)
}
diff --git a/cue/types.go b/cue/types.go
index 9976180..02db68c 100644
--- a/cue/types.go
+++ b/cue/types.go
@@ -205,14 +205,15 @@
type marshalError struct {
err errors.Error
+ b *bottom
}
func toMarshalErr(v Value, b *bottom) error {
- return &marshalError{v.toErr(b)}
+ return &marshalError{v.toErr(b), b}
}
-func marshalErrf(v Value, src source, msg string, args ...interface{}) error {
- arguments := append([]interface{}{msg}, args...)
+func marshalErrf(v Value, src source, code errCode, msg string, args ...interface{}) error {
+ arguments := append([]interface{}{code, msg}, args...)
b := v.idx.mkErr(src, arguments...)
return toMarshalErr(v, b)
}
@@ -240,9 +241,9 @@
case *marshalError:
return x
case errors.Error:
- return &marshalError{x}
+ return &marshalError{x, nil}
default:
- return &marshalError{errors.Wrapf(err, token.NoPos, "json error")}
+ return &marshalError{errors.Wrapf(err, token.NoPos, "json error"), nil}
}
}
@@ -805,12 +806,12 @@
return nil, toMarshalErr(v, x.(*bottom))
default:
if k.hasReferences() {
- return nil, marshalErrf(v, x, "value %q contains unresolved references", ctx.str(x))
+ return nil, marshalErrf(v, x, codeIncomplete, "value %q contains unresolved references", ctx.str(x))
}
if !k.isGround() {
- return nil, marshalErrf(v, x, "cannot convert incomplete value %q to JSON", ctx.str(x))
+ return nil, marshalErrf(v, x, codeIncomplete, "cannot convert incomplete value %q to JSON", ctx.str(x))
}
- return nil, marshalErrf(v, x, "cannot convert value %q of type %T to JSON", ctx.str(x), x)
+ return nil, marshalErrf(v, x, 0, "cannot convert value %q of type %T to JSON", ctx.str(x), x)
}
}