cue: fix exporting of label aliases
These could sometimes be dropped when exported.
This also changes the default expression (if none is
specified) from '_' to 'string'.
Change-Id: I5713ffec945aab1c289f551bf814e0123b85647b
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/3961
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/cue/ast.go b/cue/ast.go
index 2794987..ec77515 100644
--- a/cue/ast.go
+++ b/cue/ast.go
@@ -538,6 +538,9 @@
n2 := v.mapScope(n.Scope)
ret = &nodeRef{baseValue: newExpr(n), node: n2}
+ // Allow different names to refer to the same field in unification. We
+ // do this by anonymizing the the reference. This then has to be
+ // resolved again when refering to lambdas.
l, lambda := n2.(*lambdaExpr)
if lambda && len(l.params.arcs) == 1 {
f = 0
diff --git a/cue/export.go b/cue/export.go
index f9aee37..c3d2c84 100644
--- a/cue/export.go
+++ b/cue/export.go
@@ -205,12 +205,12 @@
if v != nil {
expr = p.expr(v)
} else {
- expr = ast.NewIdent("_")
+ expr = ast.NewIdent("string")
}
switch n.Name {
case "", "_":
default:
- expr = &ast.Alias{Ident: n, Expr: ast.NewIdent("_")}
+ expr = &ast.Alias{Ident: n, Expr: ast.NewIdent("string")}
}
return ast.NewList(expr)
}
@@ -312,6 +312,16 @@
return x.closeStatus.shouldClose()
}
+func (p *exporter) badf(msg string, args ...interface{}) ast.Expr {
+ msg = fmt.Sprintf(msg, args...)
+ bad := &ast.BadExpr{}
+ bad.AddComment(&ast.CommentGroup{
+ Doc: true,
+ List: []*ast.Comment{{Text: "// " + msg}},
+ })
+ return bad
+}
+
func (p *exporter) expr(v value) ast.Expr {
// TODO: use the raw expression for convert incomplete errors downstream
// as well.
@@ -365,17 +375,24 @@
if n != nil {
return ast.NewSel(n, p.ctx.labelStr(x.feature))
}
- ident := p.identifier(x.feature)
+ f := x.feature
+ ident := p.identifier(f)
node, ok := x.x.(*nodeRef)
if !ok {
- // TODO: should not happen: report error
+ return p.badf("selector without node")
+ }
+ if l, ok := node.node.(*lambdaExpr); ok && len(l.arcs) == 1 {
+ f = l.params.arcs[0].feature
+ // TODO: ensure it is shadowed.
+ ident = p.identifier(f)
return ident
}
- // TODO: nodes may have changed. Use different algorithm.
+
+ // TODO: nodes may have been shadowed. Use different algorithm.
conflict := false
for i := len(p.stack) - 1; i >= 0; i-- {
e := &p.stack[i]
- if e.from != x.feature {
+ if e.from != f {
continue
}
if e.key != node.node {
@@ -385,18 +402,17 @@
if conflict {
ident = e.to
if e.to == nil {
- name := p.unique(p.ctx.labelStr(x.feature))
+ name := p.unique(p.ctx.labelStr(f))
e.syn.Elts = append(e.syn.Elts, &ast.Alias{
Ident: p.ident(name),
- Expr: p.identifier(x.feature),
+ Expr: p.identifier(f),
})
ident = p.ident(name)
e.to = ident
}
}
- return ident
+ break
}
- // TODO: should not happen: report error
return ident
case *indexExpr:
diff --git a/cue/export_test.go b/cue/export_test.go
index eefbfa2..688f6f2 100644
--- a/cue/export_test.go
+++ b/cue/export_test.go
@@ -365,8 +365,8 @@
emb
}
e :: {
- [_]: <100
- b: int
+ [string]: <100
+ b: int
f
}
}`),
@@ -429,7 +429,7 @@
out: unindent(`
{
b: [{
- [X=_]: int
+ [X=string]: int
if a > 4 {
f: 4
}
@@ -686,7 +686,7 @@
out: unindent(`
{
A: {
- [_]: B
+ [string]: B
} @protobuf(1,"test")
B: {
} & ({
@@ -715,7 +715,7 @@
eval: true,
in: `
A :: { b: int }
- a: A & { [_]: <10 }
+ a: A & { [string]: <10 }
B :: a
`,
out: unindent(`
@@ -780,15 +780,15 @@
out: unindent(`
{
T :: {
- [_]: int64
+ [string]: int64
}
X :: {
- [_]: int64
- x: int64
+ [string]: int64
+ x: int64
}
x: {
- [_]: int64
- x: int64
+ [string]: int64
+ x: int64
}
}`),
}, {
@@ -910,6 +910,25 @@
})
}
}`),
+ }, {
+ eval: true,
+ in: `
+ x: [string]: int
+ a: [P=string]: {
+ b: x[P]
+ c: P
+ e: len(P)
+ }
+ `,
+ out: unindent(`
+ {
+ x: [string]: int
+ a: [P=string]: {
+ b: x[P]
+ c: string
+ e: len(P)
+ }
+ }`),
}}
for _, tc := range testCases {
t.Run("", func(t *testing.T) {