internal/core/export: support package and struct attributes
Fixes #665
Change-Id: Id2ceec41ff3fcbedb22e74a488b1909da934287f
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/8342
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/cmd/cue/cmd/testdata/script/def_jsonschema.txt b/cmd/cue/cmd/testdata/script/def_jsonschema.txt
index c1515e8..fe0d7b3 100644
--- a/cmd/cue/cmd/testdata/script/def_jsonschema.txt
+++ b/cmd/cue/cmd/testdata/script/def_jsonschema.txt
@@ -22,6 +22,10 @@
import "strings"
#Person: {
+ // Person
+ @jsonschema(schema="http://json-schema.org/draft-07/schema#")
+ @jsonschema(id="https://example.com/person.schema.json")
+
// The person's first name.
firstName?: string
diff --git a/internal/core/export/expr.go b/internal/core/export/expr.go
index 48467b5..97c63db 100644
--- a/internal/core/export/expr.go
+++ b/internal/core/export/expr.go
@@ -88,6 +88,7 @@
exporter: x,
values: &adt.Vertex{},
fields: map[adt.Feature]field{},
+ attrs: []*ast.Attribute{},
}
_, saved := e.pushFrame(orig)
@@ -101,6 +102,10 @@
s := x.top().scope
+ for _, a := range e.attrs {
+ s.Elts = append(s.Elts, a)
+ }
+
// Unify values only for one level.
if len(e.values.Conjuncts) > 0 {
e.values.Finalize(e.ctx)
@@ -133,7 +138,7 @@
switch len(e.exprs) {
case 0:
if len(e.structs) > 0 {
- return ast.NewStruct()
+ return s
}
return ast.NewIdent("_")
case 1:
@@ -207,6 +212,7 @@
exprs []ast.Expr
structs []*adt.StructInfo
fields map[adt.Feature]field
+ attrs []*ast.Attribute
hasEllipsis bool
}
@@ -237,6 +243,10 @@
case *adt.StructLit:
e.top().upCount++
+ if e.cfg.ShowAttributes {
+ e.attrs = extractDeclAttrs(e.attrs, x.Src)
+ }
+
// Only add if it only has no bulk fields or elipsis.
if isComplexStruct(x) {
_, saved := e.pushFrame(nil)
diff --git a/internal/core/export/extract.go b/internal/core/export/extract.go
index b6ccf59..4090fd9 100644
--- a/internal/core/export/extract.go
+++ b/internal/core/export/extract.go
@@ -17,6 +17,7 @@
import (
"cuelang.org/go/cue/ast"
"cuelang.org/go/cue/token"
+ "cuelang.org/go/internal"
"cuelang.org/go/internal/core/adt"
)
@@ -151,6 +152,34 @@
return attrs
}
+func ExtractDeclAttrs(a []adt.Conjunct) (attrs []*ast.Attribute) {
+ for _, c := range a {
+ attrs = extractDeclAttrs(attrs, c.Expr().Source())
+ }
+ return attrs
+}
+
+func extractDeclAttrs(attrs []*ast.Attribute, n ast.Node) []*ast.Attribute {
+ switch x := n.(type) {
+ case nil:
+ case *ast.File:
+ info := internal.GetPackageInfo(x)
+ attrs = appendDeclAttrs(attrs, x.Decls[info.Index:])
+ case *ast.StructLit:
+ attrs = appendDeclAttrs(attrs, x.Elts)
+ }
+ return attrs
+}
+
+func appendDeclAttrs(a []*ast.Attribute, decls []ast.Decl) []*ast.Attribute {
+ for _, d := range decls {
+ if attr, ok := d.(*ast.Attribute); ok && !containsAttr(a, attr) {
+ a = append(a, attr)
+ }
+ }
+ return a
+}
+
func containsAttr(a []*ast.Attribute, x *ast.Attribute) bool {
for _, e := range a {
if e.Text == x.Text {
diff --git a/internal/core/export/testdata/adt.txtar b/internal/core/export/testdata/adt.txtar
index 8668310..5b701fe 100644
--- a/internal/core/export/testdata/adt.txtar
+++ b/internal/core/export/testdata/adt.txtar
@@ -1,6 +1,8 @@
-- in.cue --
import mystrings "strings"
+@foo(bar)
+
p1: [X=string]: name: X
d1: "foo\(bar)": int
@@ -254,6 +256,7 @@
_|_ // e3: index out of range [2] with length 2
== All
{
+ @foo(bar)
p1: {}
d1: {
foobar: int
diff --git a/internal/core/export/testdata/attrs.txtar b/internal/core/export/testdata/attrs.txtar
new file mode 100644
index 0000000..665335a
--- /dev/null
+++ b/internal/core/export/testdata/attrs.txtar
@@ -0,0 +1,84 @@
+-- a.cue --
+@package("foo")
+
+package bar
+
+@file("foo")
+
+a: {
+ @decl(1)
+ @decl(2)
+} @field(2)
+
+a: {
+ @decl(1)
+ @decl(3)
+
+} @field(1) @field(4)
+
+a: {} @field(1) @field(3)
+
+-- b.cue --
+@package("b")
+
+package bar
+
+@file("bar")
+
+a: {
+ @decl(5)
+} @field(5)
+
+
+-- out/definition --
+package bar
+
+@file("foo")
+
+@file("bar")
+a: {
+ @decl(1)
+ @decl(2)
+ @decl(3)
+ @decl(5)
+} @field(2) @field(1) @field(4) @field(3) @field(5)
+-- out/doc --
+[]
+[a]
+-- out/value --
+== Simplified
+{
+ a: {}
+}
+== Raw
+{
+ a: {}
+}
+== Final
+{
+ a: {}
+}
+== All
+{
+ @file("foo")
+
+ @file("bar")
+ a: {
+ @decl(1)
+ @decl(2)
+ @decl(3)
+ @decl(5)
+ } @field(2) @field(1) @field(4) @field(3) @field(5)
+}
+== Eval
+{
+ @file("foo")
+
+ @file("bar")
+ a: {
+ @decl(1)
+ @decl(2)
+ @decl(3)
+ @decl(5)
+ } @field(2) @field(1) @field(4) @field(3) @field(5)
+}
diff --git a/internal/core/export/value.go b/internal/core/export/value.go
index fd5ff07..720c3d7 100644
--- a/internal/core/export/value.go
+++ b/internal/core/export/value.go
@@ -370,6 +370,12 @@
e.addEmbed(e.value(x))
}
+ if e.cfg.ShowAttributes {
+ for _, a := range ExtractDeclAttrs(v.Conjuncts) {
+ s.Elts = append(s.Elts, a)
+ }
+ }
+
p := e.cfg
for _, label := range VertexFeatures(v) {
show := false
diff --git a/internal/internal.go b/internal/internal.go
index 25fdb69..b6c7d89 100644
--- a/internal/internal.go
+++ b/internal/internal.go
@@ -103,8 +103,14 @@
return elts, e
}
-func PackageInfo(f *ast.File) (p *ast.Package, name string, tok token.Pos) {
- for _, d := range f.Decls {
+type PkgInfo struct {
+ Package *ast.Package
+ Index int // position in File.Decls
+ Name string
+}
+
+func GetPackageInfo(f *ast.File) PkgInfo {
+ for i, d := range f.Decls {
switch x := d.(type) {
case *ast.CommentGroup:
case *ast.Attribute:
@@ -112,9 +118,18 @@
if x.Name == nil {
break
}
- return x, x.Name.Name, x.Name.Pos()
+ return PkgInfo{x, i, x.Name.Name}
}
}
+ return PkgInfo{}
+}
+
+// Deprecated: use GetPackageInfo
+func PackageInfo(f *ast.File) (p *ast.Package, name string, tok token.Pos) {
+ x := GetPackageInfo(f)
+ if p := x.Package; p != nil {
+ return p, x.Name, p.Name.Pos()
+ }
return nil, "", f.Pos()
}