encoding/textpb: add support for MessageSet

This only handles compliant MessageSet extension fields where the field
name has to be message_set_extension.

Current C++ lib allows for different message field names, which is a
possible bug as it makes marshal output possibly contain duplicate names
when more than one field extends the same MessageSet, and makes
unmarshaling confusing as to which field to populate.

Change-Id: Ifda828ba794fe7e058ee6004f03001b1031f6d1e
Reviewed-on: https://go-review.googlesource.com/c/156758
Reviewed-by: Damien Neil <dneil@google.com>
diff --git a/encoding/textpb/decode_test.go b/encoding/textpb/decode_test.go
index 90a9e78..fa9c5a3 100644
--- a/encoding/textpb/decode_test.go
+++ b/encoding/textpb/decode_test.go
@@ -26,6 +26,7 @@
 )
 
 func init() {
+	// TODO: remove this when generated code registers to V2 global registry.
 	registerExtension(pb2.E_OptExtBool)
 	registerExtension(pb2.E_OptExtString)
 	registerExtension(pb2.E_OptExtEnum)
@@ -40,6 +41,11 @@
 	registerExtension(pb2.E_ExtensionsContainer_RptExtString)
 	registerExtension(pb2.E_ExtensionsContainer_RptExtEnum)
 	registerExtension(pb2.E_ExtensionsContainer_RptExtNested)
+	registerExtension(pb2.E_MessageSetExtension)
+	registerExtension(pb2.E_MessageSetExtension_MessageSetExtension)
+	registerExtension(pb2.E_MessageSetExtension_NotMessageSetExtension)
+	registerExtension(pb2.E_MessageSetExtension_ExtNested)
+	registerExtension(pb2.E_FakeMessageSetExtension_MessageSetExtension)
 }
 
 func registerExtension(xd *protoapi.ExtensionDesc) {
@@ -511,6 +517,20 @@
 			},
 		},
 	}, {
+		desc:         "oneof field set to last value",
+		inputMessage: &pb2.Oneofs{},
+		inputText: `
+msg: {
+  opt_string: "nested message"
+}
+str: "wins"
+`,
+		wantMessage: &pb2.Oneofs{
+			Union: &pb2.Oneofs_Str{
+				Str: "wins",
+			},
+		},
+	}, {
 		desc:         "repeated scalar using same field name",
 		inputMessage: &pb2.Repeats{},
 		inputText: `
@@ -1125,6 +1145,72 @@
 		inputText:    "[pb2.invalid_message_field]: true",
 		wantErr:      true,
 	}, {
+		desc:         "MessageSet",
+		inputMessage: &pb2.MessageSet{},
+		inputText: `
+[pb2.MessageSetExtension]: {
+  opt_string: "a messageset extension"
+}
+[pb2.MessageSetExtension.ext_nested]: {
+  opt_string: "just a regular extension"
+}
+[pb2.MessageSetExtension.not_message_set_extension]: {
+  opt_string: "not a messageset extension"
+}
+`,
+		wantMessage: func() proto.Message {
+			m := &pb2.MessageSet{}
+			setExtension(m, pb2.E_MessageSetExtension_MessageSetExtension, &pb2.MessageSetExtension{
+				OptString: scalar.String("a messageset extension"),
+			})
+			setExtension(m, pb2.E_MessageSetExtension_NotMessageSetExtension, &pb2.MessageSetExtension{
+				OptString: scalar.String("not a messageset extension"),
+			})
+			setExtension(m, pb2.E_MessageSetExtension_ExtNested, &pb2.Nested{
+				OptString: scalar.String("just a regular extension"),
+			})
+			return m
+		}(),
+	}, {
+		desc:         "not real MessageSet 1",
+		inputMessage: &pb2.FakeMessageSet{},
+		inputText: `
+[pb2.FakeMessageSetExtension.message_set_extension]: {
+  opt_string: "not a messageset extension"
+}
+`,
+		wantMessage: func() proto.Message {
+			m := &pb2.FakeMessageSet{}
+			setExtension(m, pb2.E_FakeMessageSetExtension_MessageSetExtension, &pb2.FakeMessageSetExtension{
+				OptString: scalar.String("not a messageset extension"),
+			})
+			return m
+		}(),
+	}, {
+		desc:         "not real MessageSet 2",
+		inputMessage: &pb2.FakeMessageSet{},
+		inputText: `
+[pb2.FakeMessageSetExtension]: {
+  opt_string: "not a messageset extension"
+}
+`,
+		wantErr: true,
+	}, {
+		desc:         "not real MessageSet 3",
+		inputMessage: &pb2.MessageSet{},
+		inputText: `
+[pb2.message_set_extension]: {
+  opt_string: "another not a messageset extension"
+}
+`,
+		wantMessage: func() proto.Message {
+			m := &pb2.MessageSet{}
+			setExtension(m, pb2.E_MessageSetExtension, &pb2.FakeMessageSetExtension{
+				OptString: scalar.String("another not a messageset extension"),
+			})
+			return m
+		}(),
+	}, {
 		desc:         "Any not expanded",
 		inputMessage: &pb2.KnownTypes{},
 		inputText: `opt_any: {