internal/core/export: allow showing recursive errors.
Change-Id: I40393ce06fe3728c9a362ee68845e55a67119057
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/7848
Reviewed-by: CUE cueckoo <cueckoo@gmail.com>
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/internal/core/export/export.go b/internal/core/export/export.go
index 880f49f..ae2c0eb 100644
--- a/internal/core/export/export.go
+++ b/internal/core/export/export.go
@@ -47,7 +47,8 @@
ShowDocs bool
ShowAttributes bool
- // AllowErrorType
+ // ShowErrors treats errors as values and will not percolate errors up.
+ ShowErrors bool
// Use unevaluated conjuncts for these error types
// IgnoreRecursive
@@ -176,6 +177,7 @@
func (p *Profile) Vertex(r adt.Runtime, pkgID string, n *adt.Vertex) (*ast.File, errors.Error) {
e := exporter{
+ ctx: eval.NewContext(r, nil),
cfg: p,
index: r,
pkgID: pkgID,
diff --git a/internal/core/export/testdata/adt.txtar b/internal/core/export/testdata/adt.txtar
index c29a70f..bac856a 100644
--- a/internal/core/export/testdata/adt.txtar
+++ b/internal/core/export/testdata/adt.txtar
@@ -79,6 +79,24 @@
}
+errorStructDef: {
+ a: 1
+ b: 1 & 2
+
+ #Def: 1
+}
+
+errorList: [
+ 1,
+ 1 & 2,
+]
+
+errorListDef: {
+ errorList
+
+ #Def: 1
+}
+
-- out/definition --
import mystrings "strings"
@@ -144,6 +162,16 @@
"foo\(i)": v
}
}
+errorStructDef: {
+ a: 1
+ b: _|_ // conflicting values 2 and 1
+ #Def: 1
+}
+errorList: [1, 1 & 2]
+errorListDef: {
+ errorList
+ #Def: 1
+}
-- out/doc --
[]
[p1]
@@ -192,6 +220,17 @@
[y1 bar2]
[y1 foo1]
[y1 foo2]
+[errorStructDef]
+[errorStructDef a]
+[errorStructDef b]
+[errorStructDef #Def]
+[errorList]
+[errorList 0]
+[errorList 1]
+[errorListDef]
+[errorListDef #Def]
+[errorListDef 0]
+[errorListDef 1]
[x]
-- out/value --
== Simplified
@@ -201,4 +240,68 @@
== Final
_|_ // e3: index out of range [2] with length 2
== All
-_|_ // e3: index out of range [2] with length 2
+{
+ p1: {}
+ d1: {
+ foobar: int
+ }
+ bar: "bar"
+
+ // XXX: reference not resolving.
+ d2: {
+ foobar: {
+ name: "xx"
+ foo: "xx"
+ }
+ }
+ bytes: '\xeb \x1a\xf5\xaa\xf0\xd6\x06)'
+ c1: true
+ s1: """
+ multi
+ bar
+ line
+ """
+ l1: [3]
+ l2: []
+ l3: []
+ l4: [1, 2]
+ n1: 1.0
+ n10: 10
+
+ // t is true
+ t: true
+ e1: <1.0
+ e2: >1.0 & <10
+ e3: _|_ // e3: index out of range [2] with length 2
+ e4: _|_ // e4: index 3 out of range
+ e5: _|_ // e3: index out of range [2] with length 2
+ e6: false
+ e7: true
+ e8?: true
+ m1: {
+ // foo is an optional field
+ foo?: 3
+
+ // bar is a field
+ bar: 4
+ }
+ y1: {
+ src: [1, 2, 3]
+ foo0: 1
+ bar1: 2
+ bar2: 3
+ foo1: 2
+ foo2: 3
+ }
+ errorStructDef: {
+ a: 1
+ b: _|_ // errorStructDef.b: conflicting values 2 and 1
+ #Def: 1
+ }
+ errorList: [1, _|_]
+ x: int
+ errorListDef: {
+ #Def: 1
+ [1, _|_]
+ }
+}
diff --git a/internal/core/export/value.go b/internal/core/export/value.go
index 50e8c48..612374d 100644
--- a/internal/core/export/value.go
+++ b/internal/core/export/value.go
@@ -55,17 +55,26 @@
}
case *adt.Bottom:
- if !x.IsIncomplete() || len(n.Conjuncts) == 0 {
- result = e.bottom(x)
- break
- }
+ switch {
+ case e.cfg.ShowErrors && x.ChildError:
+ // TODO(perf): use precompiled arc statistics
+ if len(n.Arcs) > 0 && n.Arcs[0].Label.IsInt() && !e.showArcs(n) {
+ result = e.listComposite(n)
+ } else {
+ result = e.structComposite(n)
+ }
- // fall back to expression mode
- a := []ast.Expr{}
- for _, c := range n.Conjuncts {
- a = append(a, e.expr(c.Expr()))
+ case !x.IsIncomplete() || len(n.Conjuncts) == 0:
+ result = e.bottom(x)
+
+ default:
+ // fall back to expression mode
+ a := []ast.Expr{}
+ for _, c := range n.Conjuncts {
+ a = append(a, e.expr(c.Expr()))
+ }
+ result = ast.NewBinExpr(token.AND, a...)
}
- result = ast.NewBinExpr(token.AND, a...)
case adt.Value:
if e.showArcs(n) {
@@ -339,6 +348,24 @@
case *adt.ListMarker:
// As lists may be long, put them at the end.
defer e.addEmbed(e.listComposite(v))
+ case *adt.Bottom:
+ if !e.cfg.ShowErrors || !x.ChildError {
+ // Should not be reachable, but just in case. The output will be
+ // correct.
+ e.addEmbed(e.value(x))
+ return s
+ }
+ // Always also show regular fields, even when list, as we are in
+ // debugging mode.
+ showRegular = true
+ // TODO(perf): do something better
+ for _, a := range v.Arcs {
+ if a.Label.IsInt() {
+ defer e.addEmbed(e.listComposite(v))
+ break
+ }
+ }
+
case adt.Value:
e.addEmbed(e.value(x))
}
diff --git a/internal/core/export/value_test.go b/internal/core/export/value_test.go
index e22c21f..22a53a4 100644
--- a/internal/core/export/value_test.go
+++ b/internal/core/export/value_test.go
@@ -57,6 +57,9 @@
ctx := eval.NewContext(r, v)
v.Finalize(ctx)
+ all := export.All
+ all.ShowErrors = true
+
for _, tc := range []struct {
name string
fn func(r adt.Runtime, id string, v adt.Value) (ast.Expr, errors.Error)
@@ -64,7 +67,7 @@
{"Simplified", export.Simplified.Value},
{"Raw", export.Raw.Value},
{"Final", export.Final.Value},
- {"All", export.All.Value},
+ {"All", all.Value},
} {
fmt.Fprintln(t, "==", tc.name)
x, errs := tc.fn(r, pkgID, v)
@@ -99,7 +102,10 @@
ctx := eval.NewContext(r, v)
v.Finalize(ctx)
- x, errs := export.Simplified.Value(r, "main", v)
+ p := export.All
+ p.ShowErrors = true
+
+ x, errs := p.Value(r, "main", v)
if errs != nil {
t.Fatal(errs)
}