internal/core/export: fix decl export printing and extraction
Fixes #894
A few related fixes:
- Extract from all evaluated structs, not just unevaluated
conjuncts, which results in dropping merged values.
(Note that unlike with fields attributes, the the declaration
attributes are value-bound, and thus need to be included.)
- Decl attrs were not always printed in export. This has now
been fixed.
- Changed the (internal) API to accept a Vertex, instead
of a list of conjuncts, which is a better abstraction.
Change-Id: Id598fefa88aff88aefd126fb80f1637b3f50c7fb
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/9382
Reviewed-by: CUE cueckoo <cueckoo@gmail.com>
Reviewed-by: Paul Jolly <paul@myitcv.org.uk>
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/cue/types.go b/cue/types.go
index 2038a70..30efd1e 100644
--- a/cue/types.go
+++ b/cue/types.go
@@ -2111,7 +2111,7 @@
return nonExistAttr(key)
}
// look up the attributes
- for _, a := range export.ExtractFieldAttrs(v.v.Conjuncts) {
+ for _, a := range export.ExtractFieldAttrs(v.v) {
k, _ := a.Split()
if key != k {
continue
@@ -2149,13 +2149,13 @@
attrs := []Attribute{}
if mask&FieldAttr != 0 {
- for _, a := range export.ExtractFieldAttrs(v.v.Conjuncts) {
+ for _, a := range export.ExtractFieldAttrs(v.v) {
attrs = append(attrs, newAttr(internal.FieldAttr, a))
}
}
if mask&DeclAttr != 0 {
- for _, a := range export.ExtractDeclAttrs(v.v.Conjuncts) {
+ for _, a := range export.ExtractDeclAttrs(v.v) {
attrs = append(attrs, newAttr(internal.DeclAttr, a))
}
}
diff --git a/internal/core/export/expr.go b/internal/core/export/expr.go
index 4af83ac..cdd5b28 100644
--- a/internal/core/export/expr.go
+++ b/internal/core/export/expr.go
@@ -137,16 +137,31 @@
if len(e.fields) == 0 && !e.hasEllipsis {
switch len(e.embed) + len(e.conjuncts) {
case 0:
+ if len(e.attrs) > 0 {
+ break
+ }
if len(e.structs) > 0 {
return s
}
return ast.NewIdent("_")
case 1:
+ var x ast.Expr
if len(e.conjuncts) == 1 {
- return e.conjuncts[0]
+ x = e.conjuncts[0]
+ } else {
+ x = e.embed[0]
}
- return e.embed[0]
+ if len(e.attrs) == 0 {
+ return x
+ }
+ if st, ok := x.(*ast.StructLit); ok {
+ s.Elts = append(s.Elts, st.Elts...)
+ return s
+ }
case 2:
+ if len(e.attrs) > 0 {
+ break
+ }
// Simplify.
e.conjuncts = append(e.conjuncts, e.embed...)
return ast.NewBinExpr(token.AND, e.conjuncts...)
@@ -195,7 +210,9 @@
ast.SetComments(d, docs)
}
if x.cfg.ShowAttributes {
- d.Attrs = ExtractFieldAttrs(a)
+ for _, c := range a {
+ d.Attrs = extractFieldAttrs(d.Attrs, c)
+ }
}
s.Elts = append(s.Elts, d)
}
diff --git a/internal/core/export/extract.go b/internal/core/export/extract.go
index 4090fd9..792563c 100644
--- a/internal/core/export/extract.go
+++ b/internal/core/export/extract.go
@@ -137,12 +137,15 @@
return false
}
-func ExtractFieldAttrs(a []adt.Conjunct) (attrs []*ast.Attribute) {
- for _, x := range a {
- f, ok := x.Source().(*ast.Field)
- if !ok {
- continue
- }
+func ExtractFieldAttrs(v *adt.Vertex) (attrs []*ast.Attribute) {
+ for _, x := range v.Conjuncts {
+ attrs = extractFieldAttrs(attrs, x)
+ }
+ return attrs
+}
+
+func extractFieldAttrs(attrs []*ast.Attribute, c adt.Conjunct) []*ast.Attribute {
+ if f, ok := c.Source().(*ast.Field); ok {
for _, a := range f.Attrs {
if !containsAttr(attrs, a) {
attrs = append(attrs, a)
@@ -152,9 +155,11 @@
return attrs
}
-func ExtractDeclAttrs(a []adt.Conjunct) (attrs []*ast.Attribute) {
- for _, c := range a {
- attrs = extractDeclAttrs(attrs, c.Expr().Source())
+func ExtractDeclAttrs(v *adt.Vertex) (attrs []*ast.Attribute) {
+ for _, st := range v.Structs {
+ if src := st.StructLit; src != nil {
+ attrs = extractDeclAttrs(attrs, src.Src)
+ }
}
return attrs
}
diff --git a/internal/core/export/testdata/adt.txtar b/internal/core/export/testdata/adt.txtar
index 9adb227..3c65168 100644
--- a/internal/core/export/testdata/adt.txtar
+++ b/internal/core/export/testdata/adt.txtar
@@ -120,6 +120,7 @@
-- out/definition --
import mystrings "strings"
+@foo(bar)
p1: {
[X=string]: {
name: X
diff --git a/internal/core/export/testdata/attrs.txtar b/internal/core/export/testdata/attrs.txtar
index 665335a..e51eb7f 100644
--- a/internal/core/export/testdata/attrs.txtar
+++ b/internal/core/export/testdata/attrs.txtar
@@ -18,6 +18,33 @@
a: {} @field(1) @field(3)
+doNotPropagate: {
+ #A: { } @attr1()
+ a: #A
+
+ // Do not accumulated field attributes in embedding.
+ #B: { } @attr1()
+ b: { #B }
+}
+
+embedScalarField: {
+ a: { 2 } @attr1()
+ a: { _ } @attr2()
+}
+
+embedScalarDecl: {
+ b0: { 2, @attr1() }
+ b1: b0
+ b2: { 2, b0, @attr2() }
+}
+
+dontMergeForDef: {
+ a: { @decl1() }
+ b: a & { x: 1, @decl2() }
+ c: a & { @decl2() }
+ d: { a, @decl2() }
+}
+
-- b.cue --
@package("b")
@@ -42,21 +69,143 @@
@decl(3)
@decl(5)
} @field(2) @field(1) @field(4) @field(3) @field(5)
+doNotPropagate: {
+ #A: {} @attr1()
+ a: #A
+
+ // Do not accumulated field attributes in embedding.
+ #B: {} @attr1()
+ b: #B
+}
+embedScalarField: {
+ a: 2 @attr1() @attr2()
+}
+embedScalarDecl: {
+ b0: {
+ @attr1()
+ 2
+ }
+ b1: b0
+ b2: {
+ @attr2()
+ b0
+ 2
+ }
+}
+dontMergeForDef: {
+ a: {
+ @decl1()
+ }
+ b: a & {
+ @decl2()
+ x: 1
+ }
+ c: a & {
+ @decl2()
+ }
+ d: {
+ @decl2()
+ a
+ }
+}
-- out/doc --
[]
[a]
+[doNotPropagate]
+[doNotPropagate #A]
+[doNotPropagate a]
+[doNotPropagate #B]
+- Do not accumulated field attributes in embedding.
+
+[doNotPropagate b]
+[embedScalarField]
+[embedScalarField a]
+[embedScalarDecl]
+[embedScalarDecl b0]
+[embedScalarDecl b1]
+[embedScalarDecl b2]
+[dontMergeForDef]
+[dontMergeForDef a]
+[dontMergeForDef b]
+[dontMergeForDef b x]
+[dontMergeForDef c]
+[dontMergeForDef d]
-- out/value --
== Simplified
{
a: {}
+ doNotPropagate: {
+ a: {}
+ b: {}
+ }
+ embedScalarField: {
+ a: 2
+ }
+ embedScalarDecl: {
+ b0: 2
+ b1: 2
+ b2: 2
+ }
+ dontMergeForDef: {
+ a: {}
+ b: {
+ x: 1
+ }
+ c: {}
+ d: {}
+ }
}
== Raw
{
a: {}
+ doNotPropagate: {
+ #A: {}
+ a: {}
+
+ // Do not accumulated field attributes in embedding.
+ #B: {}
+ b: {}
+ }
+ embedScalarField: {
+ a: 2
+ }
+ embedScalarDecl: {
+ b0: 2
+ b1: 2
+ b2: 2
+ }
+ dontMergeForDef: {
+ a: {}
+ b: {
+ x: 1
+ }
+ c: {}
+ d: {}
+ }
}
== Final
{
a: {}
+ doNotPropagate: {
+ a: {}
+ b: {}
+ }
+ embedScalarField: {
+ a: 2
+ }
+ embedScalarDecl: {
+ b0: 2
+ b1: 2
+ b2: 2
+ }
+ dontMergeForDef: {
+ a: {}
+ b: {
+ x: 1
+ }
+ c: {}
+ d: {}
+ }
}
== All
{
@@ -69,6 +218,43 @@
@decl(3)
@decl(5)
} @field(2) @field(1) @field(4) @field(3) @field(5)
+ doNotPropagate: {
+ #A: {} @attr1()
+ a: {}
+
+ // Do not accumulated field attributes in embedding.
+ #B: {} @attr1()
+ b: {}
+ }
+ embedScalarField: {
+ a: 2 @attr1() @attr2()
+ }
+ embedScalarDecl: {
+ b0: {
+ 2, @attr1()
+ }
+ b1: {
+ 2, @attr1()
+ }
+ b2: {
+ 2, @attr2(), @attr1()
+ }
+ }
+ dontMergeForDef: {
+ a: {
+ @decl1()
+ }
+ b: {
+ @decl1(), @decl2()
+ x: 1
+ }
+ c: {
+ @decl1(), @decl2()
+ }
+ d: {
+ @decl2(), @decl1()
+ }
+ }
}
== Eval
{
@@ -81,4 +267,39 @@
@decl(3)
@decl(5)
} @field(2) @field(1) @field(4) @field(3) @field(5)
+ doNotPropagate: {
+ #A: {} @attr1()
+ a: {}
+ #B: {} @attr1()
+ b: {}
+ }
+ embedScalarField: {
+ a: 2 @attr1() @attr2()
+ }
+ embedScalarDecl: {
+ b0: {
+ 2, @attr1()
+ }
+ b1: {
+ 2, @attr1()
+ }
+ b2: {
+ 2, @attr2(), @attr1()
+ }
+ }
+ dontMergeForDef: {
+ a: {
+ @decl1()
+ }
+ b: {
+ @decl1(), @decl2()
+ x: 1
+ }
+ c: {
+ @decl1(), @decl2()
+ }
+ d: {
+ @decl2(), @decl1()
+ }
+ }
}
diff --git a/internal/core/export/value.go b/internal/core/export/value.go
index 1508581..91aeacd 100644
--- a/internal/core/export/value.go
+++ b/internal/core/export/value.go
@@ -42,15 +42,19 @@
// value with a reference in graph mode.
func (e *exporter) vertex(n *adt.Vertex) (result ast.Expr) {
+ var attrs []*ast.Attribute
+ if e.cfg.ShowAttributes {
+ attrs = ExtractDeclAttrs(n)
+ }
switch x := n.BaseValue.(type) {
case nil:
// bare
case *adt.StructMarker:
- result = e.structComposite(n)
+ result = e.structComposite(n, attrs)
case *adt.ListMarker:
- if e.showArcs(n) {
- result = e.structComposite(n)
+ if e.showArcs(n) || attrs != nil {
+ result = e.structComposite(n, attrs)
} else {
result = e.listComposite(n)
}
@@ -59,10 +63,10 @@
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) {
+ if len(n.Arcs) > 0 && n.Arcs[0].Label.IsInt() && !e.showArcs(n) && attrs == nil {
result = e.listComposite(n)
} else {
- result = e.structComposite(n)
+ result = e.structComposite(n, attrs)
}
case !x.IsIncomplete() || len(n.Conjuncts) == 0:
@@ -70,8 +74,8 @@
}
case adt.Value:
- if e.showArcs(n) {
- result = e.structComposite(n)
+ if e.showArcs(n) || attrs != nil {
+ result = e.structComposite(n, attrs)
} else {
result = e.value(x, n.Conjuncts...)
}
@@ -337,7 +341,7 @@
return false
}
-func (e *exporter) structComposite(v *adt.Vertex) ast.Expr {
+func (e *exporter) structComposite(v *adt.Vertex, attrs []*ast.Attribute) ast.Expr {
s, saved := e.pushFrame(v.Conjuncts)
e.top().upCount++
defer func() {
@@ -374,10 +378,8 @@
e.addEmbed(e.value(x))
}
- if e.cfg.ShowAttributes {
- for _, a := range ExtractDeclAttrs(v.Conjuncts) {
- s.Elts = append(s.Elts, a)
- }
+ for _, a := range attrs {
+ s.Elts = append(s.Elts, a)
}
p := e.cfg
@@ -434,7 +436,7 @@
}
if p.ShowAttributes {
- f.Attrs = ExtractFieldAttrs(arc.Conjuncts)
+ f.Attrs = ExtractFieldAttrs(arc)
}
if p.ShowDocs {
diff --git a/internal/encoding/yaml/encode.go b/internal/encoding/yaml/encode.go
index d9742e7..8fd0a61 100644
--- a/internal/encoding/yaml/encode.go
+++ b/internal/encoding/yaml/encode.go
@@ -183,6 +183,9 @@
docForNext.WriteString("\n\n")
continue
+ case *ast.Attribute:
+ continue
+
case *ast.Field:
if x.Token == token.ISA {
return nil, errors.Newf(x.TokenPos, "yaml: definition not allowed")