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)
 	}