internal/core/export: fix definition wrapping

- use hidden definitions (for real)
- also handle recurisve cases

Change-Id: Ie44a9e5f67db36cd8bad0a255ddb8053684a1723
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/9486
Reviewed-by: CUE cueckoo <cueckoo@gmail.com>
Reviewed-by: Paul Jolly <paul@myitcv.org.uk>
diff --git a/cmd/cue/cmd/testdata/script/issue304.txt b/cmd/cue/cmd/testdata/script/issue304.txt
index 61c41a4..ef46457 100644
--- a/cmd/cue/cmd/testdata/script/issue304.txt
+++ b/cmd/cue/cmd/testdata/script/issue304.txt
@@ -3,8 +3,8 @@
 
 -- expect-stdout --
 
-#_def
-#_def: {
+_#def
+_#def: {
 	x: int
 	body?: {
 		a:  int
diff --git a/internal/core/adt/closed.go b/internal/core/adt/closed.go
index 39a00fa..045179c 100644
--- a/internal/core/adt/closed.go
+++ b/internal/core/adt/closed.go
@@ -70,6 +70,29 @@
 // TODO(errors): return a dedicated ConflictError that can track original
 // positions on demand.
 
+func (v *Vertex) IsInOneOf(t SpanType) bool {
+	for _, s := range v.Structs {
+		if s.CloseInfo.IsInOneOf(t) {
+			return true
+		}
+	}
+	return false
+}
+
+// IsRecursivelyClosed returns true if this value is either a definition or unified
+// with a definition.
+func (v *Vertex) IsRecursivelyClosed() bool {
+	if v.IsInOneOf(DefinitionSpan) {
+		return true
+	}
+	for p := v; p != nil; p = p.Parent {
+		if p.Label.IsDef() {
+			return true
+		}
+	}
+	return false
+}
+
 type closeNodeType uint8
 
 const (
diff --git a/internal/core/export/export.go b/internal/core/export/export.go
index d96ed3c..b24e7ec 100644
--- a/internal/core/export/export.go
+++ b/internal/core/export/export.go
@@ -97,18 +97,19 @@
 	e := newExporter(p, r, pkgID, v)
 	e.markUsedFeatures(v)
 
-	if v.Label.IsDef() {
+	isDef := v.IsRecursivelyClosed()
+	if isDef {
 		e.inDefinition++
 	}
 
 	expr := e.expr(v)
 
-	if v.Label.IsDef() {
+	if isDef {
 		e.inDefinition--
 		if s, ok := expr.(*ast.StructLit); ok {
 			expr = ast.NewStruct(
-				ast.Embed(ast.NewIdent("#_def")),
-				ast.NewIdent("#_def"), s,
+				ast.Embed(ast.NewIdent("_#def")),
+				ast.NewIdent("_#def"), s,
 			)
 		}
 	}
diff --git a/internal/core/export/export_test.go b/internal/core/export/export_test.go
index c61699f..3486ccf 100644
--- a/internal/core/export/export_test.go
+++ b/internal/core/export/export_test.go
@@ -156,6 +156,7 @@
 			return n, nil
 		},
 		out: `#Provider: {ID: string, notConcrete: bool, a: int, b: a+1}, providers: {foo: {ID: "12345", notConcrete: bool, a: int, b: a+1}}`,
+		p:   export.All,
 	}, {
 		// Issue #882
 		in: func(r *adt.OpContext) (adt.Expr, error) {
@@ -173,6 +174,21 @@
 		},
 		out: `#One: {version: string}, ones: {[string]: #One}`,
 		p:   export.All,
+	}, {
+		// Indicate closedness in an element that is closed and misses parent
+		// context.
+		// Issue #882
+		in: func(r *adt.OpContext) (adt.Expr, error) {
+			v := ctx.CompileString(`
+					#A: b: c: string
+				`)
+			v = v.LookupPath(cue.ParsePath("#A.b"))
+
+			_, n := value.ToInternal(v)
+			return n, nil
+		},
+		out: `_#def, _#def: {c: string}`,
+		p:   export.All,
 	}}
 	for _, tc := range testCases {
 		t.Run("", func(t *testing.T) {