Skip to content

Commit 8dab444

Browse files
authored
protoparse: match the way protoc populates aggregate values in uninterpreted options (#526)
1 parent cd98cc0 commit 8dab444

File tree

4 files changed

+46
-61
lines changed

4 files changed

+46
-61
lines changed

desc/protoparse/descriptor_protos.go

+31-6
Original file line numberDiff line numberDiff line change
@@ -105,16 +105,41 @@ func (r *parseResult) asUninterpretedOption(node *ast.OptionNode) *dpb.Uninterpr
105105
opt.StringValue = []byte(val)
106106
case ast.Identifier:
107107
opt.IdentifierValue = proto.String(string(val))
108-
case []*ast.MessageFieldNode:
109-
var buf bytes.Buffer
110-
aggToString(val, &buf)
111-
aggStr := buf.String()
112-
opt.AggregateValue = proto.String(aggStr)
113-
//the grammar does not allow arrays here, so no case for []ast.ValueNode
108+
default:
109+
// the grammar does not allow arrays here, so the only possible case
110+
// left should be []*ast.MessageFieldNode, which corresponds to an
111+
// *ast.MessageLiteralNode
112+
if n, ok := node.Val.(*ast.MessageLiteralNode); ok {
113+
var buf bytes.Buffer
114+
for i, el := range n.Elements {
115+
flattenNode(r.root, el, &buf)
116+
if len(n.Seps) > i && n.Seps[i] != nil {
117+
buf.WriteRune(' ')
118+
buf.WriteRune(n.Seps[i].Rune)
119+
}
120+
}
121+
aggStr := buf.String()
122+
opt.AggregateValue = proto.String(aggStr)
123+
}
124+
// TODO: else that reports an error or panics??
114125
}
115126
return opt
116127
}
117128

129+
func flattenNode(f *ast.FileNode, n ast.Node, buf *bytes.Buffer) {
130+
if cn, ok := n.(ast.CompositeNode); ok {
131+
for _, ch := range cn.Children() {
132+
flattenNode(f, ch, buf)
133+
}
134+
return
135+
}
136+
137+
if buf.Len() > 0 {
138+
buf.WriteRune(' ')
139+
}
140+
buf.WriteString(n.(ast.TerminalNode).RawText())
141+
}
142+
118143
func (r *parseResult) asUninterpretedOptionName(parts []*ast.FieldReferenceNode) []*dpb.UninterpretedOption_NamePart {
119144
ret := make([]*dpb.UninterpretedOption_NamePart, len(parts))
120145
for i, part := range parts {

desc/protoparse/options_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ func TestOptionsInUnlinkedFiles(t *testing.T) {
5959
// field where default is uninterpretable
6060
contents: `enum TestEnum{ ZERO = 0; ONE = 1; } message Test { optional TestEnum uid = 1 [(must.link) = {foo: bar}, default = ONE, json_name = "UID", deprecated = true]; }`,
6161
uninterpreted: map[string]interface{}{
62-
"Test.uid:(must.link)": aggregate("{ foo: bar }"),
62+
"Test.uid:(must.link)": aggregate("foo : bar"),
6363
"Test.uid:default": ident("ONE"),
6464
},
6565
checkInterpreted: func(t *testing.T, fd *dpb.FileDescriptorProto) {
@@ -108,7 +108,7 @@ func TestOptionsInUnlinkedFiles(t *testing.T) {
108108
// service options
109109
contents: `service Test { option deprecated = true; option (must.link) = {foo:1, foo:2, bar:3}; }`,
110110
uninterpreted: map[string]interface{}{
111-
"Test:(must.link)": aggregate("{ foo: 1 foo: 2 bar: 3 }"),
111+
"Test:(must.link)": aggregate("foo : 1 , foo : 2 , bar : 3"),
112112
},
113113
checkInterpreted: func(t *testing.T, fd *dpb.FileDescriptorProto) {
114114
testutil.Require(t, fd.GetService()[0].GetOptions().GetDeprecated())

desc/protoparse/parser.go

-51
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import (
66
"fmt"
77
"io"
88
"io/ioutil"
9-
"math"
109
"os"
1110
"path/filepath"
1211
"sort"
@@ -817,56 +816,6 @@ func checkTag(pos *SourcePos, v uint64, maxTag int32) error {
817816
return nil
818817
}
819818

820-
func aggToString(agg []*ast.MessageFieldNode, buf *bytes.Buffer) {
821-
buf.WriteString("{")
822-
for _, a := range agg {
823-
buf.WriteString(" ")
824-
buf.WriteString(a.Name.Value())
825-
if v, ok := a.Val.(*ast.MessageLiteralNode); ok {
826-
aggToString(v.Elements, buf)
827-
} else {
828-
buf.WriteString(": ")
829-
elementToString(a.Val.Value(), buf)
830-
}
831-
}
832-
buf.WriteString(" }")
833-
}
834-
835-
func elementToString(v interface{}, buf *bytes.Buffer) {
836-
switch v := v.(type) {
837-
case bool, int64, uint64, ast.Identifier:
838-
_, _ = fmt.Fprintf(buf, "%v", v)
839-
case float64:
840-
if math.IsInf(v, 1) {
841-
buf.WriteString(": inf")
842-
} else if math.IsInf(v, -1) {
843-
buf.WriteString(": -inf")
844-
} else if math.IsNaN(v) {
845-
buf.WriteString(": nan")
846-
} else {
847-
_, _ = fmt.Fprintf(buf, ": %v", v)
848-
}
849-
case string:
850-
buf.WriteRune('"')
851-
writeEscapedBytes(buf, []byte(v))
852-
buf.WriteRune('"')
853-
case []ast.ValueNode:
854-
buf.WriteString(": [")
855-
first := true
856-
for _, e := range v {
857-
if first {
858-
first = false
859-
} else {
860-
buf.WriteString(", ")
861-
}
862-
elementToString(e.Value(), buf)
863-
}
864-
buf.WriteString("]")
865-
case []*ast.MessageFieldNode:
866-
aggToString(v, buf)
867-
}
868-
}
869-
870819
func writeEscapedBytes(buf *bytes.Buffer, b []byte) {
871820
for _, c := range b {
872821
switch c {

desc/protoparse/parser_test.go

+13-2
Original file line numberDiff line numberDiff line change
@@ -226,11 +226,22 @@ func TestAggregateValueInUninterpretedOptions(t *testing.T) {
226226
testutil.Ok(t, err)
227227
fd := res.fd
228228

229+
// service TestTestService, method UserAuth; first option
229230
aggregateValue1 := *fd.Service[0].Method[0].Options.UninterpretedOption[0].AggregateValue
230-
testutil.Eq(t, "{ authenticated: true permission{ action: LOGIN entity: \"client\" } }", aggregateValue1)
231+
testutil.Eq(t, `authenticated : true permission : { action : LOGIN entity : "client" }`, aggregateValue1)
231232

233+
// service TestTestService, method Get; first option
232234
aggregateValue2 := *fd.Service[0].Method[1].Options.UninterpretedOption[0].AggregateValue
233-
testutil.Eq(t, "{ authenticated: true permission{ action: READ entity: \"user\" } }", aggregateValue2)
235+
testutil.Eq(t, `authenticated : true permission : { action : READ entity : "user" }`, aggregateValue2)
236+
237+
// message Another; first option
238+
aggregateValue3 := *fd.MessageType[4].Options.UninterpretedOption[0].AggregateValue
239+
testutil.Eq(t, `foo : "abc" s < name : "foo" , id : 123 > , array : [ 1 , 2 , 3 ] , r : [ < name : "f" > , { name : "s" } , { id : 456 } ] ,`, aggregateValue3)
240+
241+
// message Test.Nested._NestedNested; second option (rept)
242+
// (Test.Nested is at index 1 instead of 0 because of implicit nested message from map field m)
243+
aggregateValue4 := *fd.MessageType[1].NestedType[1].NestedType[0].Options.UninterpretedOption[1].AggregateValue
244+
testutil.Eq(t, `foo : "goo" [ foo . bar . Test . Nested . _NestedNested . _garblez ] : "boo"`, aggregateValue4)
234245
}
235246

236247
func TestParseFilesMessageComments(t *testing.T) {

0 commit comments

Comments
 (0)