cue: fix attribute parsing bugs

One check caused spurious errors and another
bug caused this error to silently drop the
corresponding field.

Change-Id: Ib38939abf190668925063fab7a96d92062af3d0a
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/2711
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/cue/ast.go b/cue/ast.go
index 267a125..df566c4 100644
--- a/cue/ast.go
+++ b/cue/ast.go
@@ -362,7 +362,7 @@
 			v.sel, _ = ast.LabelName(x)
 			attrs, err := createAttrs(v.ctx(), newNode(n), n.Attrs)
 			if err != nil {
-				return err
+				return v.errf(n, err.format, err.args)
 			}
 			f, ok := v.nodeLabel(x)
 			if !ok {
diff --git a/cue/ast_test.go b/cue/ast_test.go
index 806cce1..5fa74fb 100644
--- a/cue/ast_test.go
+++ b/cue/ast_test.go
@@ -247,6 +247,16 @@
 		`,
 		out: `<0>{a: _|_(preference mark not allowed at this position), ` +
 			`b: (*_|_(preference mark not allowed at this position) | 2)}`,
+	}, {
+		in: `
+			a: int @foo(1,"str")
+		`,
+		out: "<0>{a: int @foo(1,\"str\")}",
+	}, {
+		in: `
+			a: int @b('' ,b) // invalid
+		`,
+		out: "attribute missing ')':\n    test:2:16\nmissing ',' in file:\n    test:3:3\n<0>{}",
 	}}
 	for _, tc := range testCases {
 		t.Run("", func(t *testing.T) {
diff --git a/cue/attr.go b/cue/attr.go
index a00982d..636fa05 100644
--- a/cue/attr.go
+++ b/cue/attr.go
@@ -45,7 +45,7 @@
 	return a.text[a.offset+1 : len(a.text)-1]
 }
 
-func createAttrs(ctx *context, src source, attrs []*ast.Attribute) (a *attributes, err evaluated) {
+func createAttrs(ctx *context, src source, attrs []*ast.Attribute) (a *attributes, err *bottom) {
 	if len(attrs) == 0 {
 		return nil, nil
 	}
@@ -57,6 +57,10 @@
 			return nil, ctx.mkErr(newNode(a), "invalid attribute %q", a.Text)
 		}
 		as = append(as, attr{a.Text[:n], index})
+
+		if err := parseAttrBody(ctx, src, a.Text[index+1:n-1], nil); err != nil {
+			return nil, err
+		}
 	}
 
 	sort.Slice(as, func(i, j int) bool { return as[i].text < as[j].text })
@@ -67,11 +71,6 @@
 		}
 	}
 
-	for _, a := range attrs {
-		if err := parseAttrBody(ctx, src, a.Text, nil); err != nil {
-			return nil, err
-		}
-	}
 	return &attributes{as}, nil
 }
 
@@ -131,7 +130,7 @@
 func (kv *keyValue) key() string   { return kv.data[:kv.equal] }
 func (kv *keyValue) value() string { return kv.data[kv.equal+1:] }
 
-func parseAttrBody(ctx *context, src source, s string, a *parsedAttr) (err evaluated) {
+func parseAttrBody(ctx *context, src source, s string, a *parsedAttr) (err *bottom) {
 	i := 0
 	for {
 		// always scan at least one, possibly empty element.
@@ -150,7 +149,7 @@
 	return nil
 }
 
-func scanAttributeElem(ctx *context, src source, s string, a *parsedAttr) (n int, err evaluated) {
+func scanAttributeElem(ctx *context, src source, s string, a *parsedAttr) (n int, err *bottom) {
 	// try CUE string
 	kv := keyValue{}
 	if n, kv.data, err = scanAttributeString(ctx, src, s); n == 0 {
@@ -187,7 +186,7 @@
 	return n, err
 }
 
-func scanAttributeString(ctx *context, src source, s string) (n int, str string, err evaluated) {
+func scanAttributeString(ctx *context, src source, s string) (n int, str string, err *bottom) {
 	if s == "" || (s[0] != '#' && s[0] != '"' && s[0] != '\'') {
 		return 0, "", nil
 	}
diff --git a/cue/attr_test.go b/cue/attr_test.go
index d3f08fd..3f30f79 100644
--- a/cue/attr_test.go
+++ b/cue/attr_test.go
@@ -37,6 +37,9 @@
 		in:  "a,",
 		out: "[{a 0} { 0}]",
 	}, {
+		in:  `"a",`,
+		out: "[{a 0} { 0}]",
+	}, {
 		in:  "a,b",
 		out: "[{a 0} {b 0}]",
 	}, {
@@ -118,6 +121,18 @@
 	}, {
 		in:  "@b('' ,b)",
 		err: "invalid attribute",
+	}, {
+		in:  `@foo(,"bar")`,
+		out: `foo:,"bar"`,
+	}, {
+		in:  `@foo("bar",1)`,
+		out: `foo:"bar",1`,
+	}, {
+		in:  `@foo("bar")`,
+		out: `foo:"bar"`,
+	}, {
+		in:  `@foo(,"bar",1)`,
+		out: `foo:,"bar",1`,
 	}}
 	for _, tc := range testdata {
 		t.Run(tc.in, func(t *testing.T) {
diff --git a/cue/export_test.go b/cue/export_test.go
index 9da8857..de354eb 100644
--- a/cue/export_test.go
+++ b/cue/export_test.go
@@ -382,6 +382,26 @@
 			a: b - 100
 			b: a + 100
 		}`),
+	}, {
+		in: `A: {
+			<_>: B
+		} @protobuf(1,"test")
+
+		B: {}
+		B: {a: int} | {b: int}
+		`,
+		out: unindent(`
+		{
+			A: {
+				<_>: B
+			} @protobuf(1,"test")
+			B: {
+			} & ({
+				a: int
+			} | {
+				b: int
+			})
+		}`),
 	}}
 	for _, tc := range testCases {
 		t.Run("", func(t *testing.T) {
diff --git a/cue/parser/parser_test.go b/cue/parser/parser_test.go
index 053e9b1..89973a5 100644
--- a/cue/parser/parser_test.go
+++ b/cue/parser/parser_test.go
@@ -69,9 +69,9 @@
 		 b: 2 @foo(a,b=4) @go(Foo)
 		 c: {
 			 d: "x" @go(D) @json(,omitempty)
-			 e: "y" @ts(,type=string)
+			 e: "y" @ts(,type=string,"str")
 		 }`,
-		`a: 1 @xml(,attr), b: 2 @foo(a,b=4) @go(Foo), c: {d: "x" @go(D) @json(,omitempty), e: "y" @ts(,type=string)}`,
+		`a: 1 @xml(,attr), b: 2 @foo(a,b=4) @go(Foo), c: {d: "x" @go(D) @json(,omitempty), e: "y" @ts(,type=string,"str")}`,
 	}, {
 		"not emitted",
 		`a: true
diff --git a/cue/types.go b/cue/types.go
index cb39921..9133853 100644
--- a/cue/types.go
+++ b/cue/types.go
@@ -1522,7 +1522,7 @@
 		}
 		at := Attribute{}
 		if err := parseAttrBody(v.ctx(), nil, a.body(), &at.attr); err != nil {
-			return Attribute{err: err.(error)}
+			return Attribute{err: v.toErr(err)}
 		}
 		return at
 	}