cue/errors: correct handling of wrapping list errors
This cleans up several kinds of error reporting.
It also creates more consistency in error reporting
between the different cue tools.
Fixes #553
Change-Id: Ie511e3df1c88225b5f05281790baf00df8b3ba01
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/7402
Reviewed-by: CUE cueckoo <cueckoo@gmail.com>
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/cmd/cue/cmd/eval.go b/cmd/cue/cmd/eval.go
index 8d94aa8..0d90deb 100644
--- a/cmd/cue/cmd/eval.go
+++ b/cmd/cue/cmd/eval.go
@@ -20,7 +20,6 @@
"github.com/spf13/cobra"
"cuelang.org/go/cue"
- "cuelang.org/go/cue/ast"
"cuelang.org/go/cue/format"
"cuelang.org/go/internal"
"cuelang.org/go/internal/encoding"
@@ -141,7 +140,10 @@
errHeader()
return err
}
- if flagConcrete.Bool(cmd) && !flagIgnore.Bool(cmd) {
+
+ // TODO(#553): this can be removed once v.Syntax() below retains line
+ // information.
+ if (e.IsConcrete() || flagConcrete.Bool(cmd)) && !flagIgnore.Bool(cmd) {
if err := v.Validate(cue.Concrete(true)); err != nil {
errHeader()
exitOnErr(cmd, err, false)
@@ -149,7 +151,7 @@
}
}
- f := getSyntax(v, syn)
+ f := internal.ToFile(v.Syntax(syn...))
f.Filename = id
err := e.EncodeFile(f)
if err != nil {
@@ -160,7 +162,3 @@
exitOnErr(cmd, iter.err(), true)
return nil
}
-
-func getSyntax(v cue.Value, opts []cue.Option) *ast.File {
- return internal.ToFile(v.Syntax(opts...))
-}
diff --git a/cmd/cue/cmd/testdata/script/export_err.txt b/cmd/cue/cmd/testdata/script/export_err.txt
index 79206ab..6e6a415 100644
--- a/cmd/cue/cmd/testdata/script/export_err.txt
+++ b/cmd/cue/cmd/testdata/script/export_err.txt
@@ -1,13 +1,25 @@
+! cue eval ./exporterr --out json
+cmp stdout expect-stdout
+cmp stderr expect-stderr
+
+! cue eval ./exporterr -c
+cmp stdout expect-stdout
+cmp stderr expect-stderr
+
! cue export ./exporterr
cmp stdout expect-stdout
cmp stderr expect-stderr
-- expect-stderr --
-a.b.2.c: cannot convert incomplete value "int" to JSON
+a.b.2.c: incomplete value int
+out: invalid interpolation: undefined field d:
+ ./exporterr/export_err.cue:7:6
-- expect-stdout --
-- exporterr/export_err.cue --
package exporterr
-a: {
- b: [0, 1, {c: int}, 3]
-}
+a: b: [0, 1, {c: int}, 3]
+
+// Issue #553
+b: c: "hello"
+out: "d is \(b.d)"
-- exporterr/cue.mod --
diff --git a/cue/errors/errors.go b/cue/errors/errors.go
index 7a28c25..6914985 100644
--- a/cue/errors/errors.go
+++ b/cue/errors/errors.go
@@ -143,11 +143,23 @@
// Wrapf creates an Error with the associated position and message. The provided
// error is added for inspection context.
func Wrapf(err error, p token.Pos, format string, args ...interface{}) Error {
- return &posError{
- pos: p,
- Message: NewMessage(format, args),
- err: err,
+ a, ok := err.(list)
+ if !ok {
+ return &posError{
+ pos: p,
+ Message: NewMessage(format, args),
+ err: err,
+ }
}
+ b := make([]Error, len(a))
+ for i, err := range a {
+ b[i] = &posError{
+ pos: p,
+ Message: NewMessage(format, args),
+ err: err,
+ }
+ }
+ return list(b)
}
// Promote converts a regular Go error to an Error if it isn't already one.
diff --git a/cue/testdata/interpolation/incomplete.txtar b/cue/testdata/interpolation/incomplete.txtar
new file mode 100644
index 0000000..29a5343
--- /dev/null
+++ b/cue/testdata/interpolation/incomplete.txtar
@@ -0,0 +1,47 @@
+Issue #553
+
+-- in.cue --
+a: "foo"
+b: "boo"
+commands: {
+ #c: {
+ help: "help!"
+ }
+}
+out: """
+ a is \(a)
+ b is \(b)
+
+ c is \(commands.#c.help)
+ d is \(commands.#d.help)
+ """
+-- out/eval --
+(struct){
+ a: (string){ "foo" }
+ b: (string){ "boo" }
+ commands: (struct){
+ #c: (#struct){
+ help: (string){ "help!" }
+ }
+ }
+ out: (_|_){
+ // [incomplete] out: invalid interpolation: undefined field #d:
+ // ./in.cue:8:6
+ }
+}
+-- out/compile --
+--- in.cue
+{
+ a: "foo"
+ b: "boo"
+ commands: {
+ #c: {
+ help: "help!"
+ }
+ }
+ out: "a is \(〈0;a〉)
+ b is \(〈0;b〉)
+
+ c is \(〈0;commands〉.#c.help)
+ d is \(〈0;commands〉.#d.help)"
+}
diff --git a/internal/encoding/encoder.go b/internal/encoding/encoder.go
index fbb019f..b6e09ae 100644
--- a/internal/encoding/encoder.go
+++ b/internal/encoding/encoder.go
@@ -42,6 +42,16 @@
encFile func(*ast.File) error
encValue func(cue.Value) error
autoSimplify bool
+ concrete bool
+}
+
+// IsConcrete reports whether the output is required to be concrete.
+//
+// INTERNAL ONLY: this is just to work around a problem related to issue #553
+// of catching errors ony after syntax generation, dropping line number
+// information.
+func (e *Encoder) IsConcrete() bool {
+ return e.concrete
}
func (e Encoder) Close() error {
@@ -86,6 +96,7 @@
if err != nil {
return nil, err
}
+ e.concrete = !fi.Incomplete
synOpts := []cue.Option{}
if !fi.KeepDefaults || !fi.Incomplete {
@@ -135,6 +146,7 @@
e.encFile = func(f *ast.File) error { return format(f.Filename, f) }
case build.JSON, build.JSONL:
+ e.concrete = true
d := json.NewEncoder(w)
d.SetIndent("", " ")
d.SetEscapeHTML(cfg.EscapeHTML)
@@ -147,6 +159,7 @@
}
case build.YAML:
+ e.concrete = true
streamed := false
e.encValue = func(v cue.Value) error {
if streamed {
@@ -163,6 +176,7 @@
}
case build.Text:
+ e.concrete = true
e.encValue = func(v cue.Value) error {
s, err := v.String()
if err != nil {
@@ -197,6 +211,10 @@
}
return e.encodeFile(f, nil)
}
+ v := inst.Value()
+ if err := v.Validate(cue.Concrete(e.concrete)); err != nil {
+ return err
+ }
if e.encValue != nil {
return e.encValue(inst.Value())
}
@@ -216,6 +234,10 @@
if interpret != nil {
return e.Encode(inst)
}
+ v := inst.Value()
+ if err := v.Validate(cue.Concrete(e.concrete)); err != nil {
+ return err
+ }
return e.encValue(inst.Value())
}