cmd/protoc-gen-go: generate oneofs

Generate everything related to oneofs: Message struct fields, wrapper
types, XXX_OneofFuncs.

Change-Id: I409040e0deb5716afabf59186eeaae21757d29f1
Reviewed-on: https://go-review.googlesource.com/135535
Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
diff --git a/protogen/protogen.go b/protogen/protogen.go
index 29a6289..ae31fd2 100644
--- a/protogen/protogen.go
+++ b/protogen/protogen.go
@@ -392,11 +392,7 @@
 	f.GeneratedFilenamePrefix = prefix
 
 	for i, mdescs := 0, desc.Messages(); i < mdescs.Len(); i++ {
-		message, err := newMessage(gen, f, nil, mdescs.Get(i))
-		if err != nil {
-			return nil, err
-		}
-		f.Messages = append(f.Messages, message)
+		f.Messages = append(f.Messages, newMessage(gen, f, nil, mdescs.Get(i)))
 	}
 	for i, edescs := 0, desc.Enums(); i < edescs.Len(); i++ {
 		f.Enums = append(f.Enums, newEnum(gen, f, nil, edescs.Get(i)))
@@ -433,12 +429,13 @@
 
 	GoIdent  GoIdent    // name of the generated Go type
 	Fields   []*Field   // message field declarations
+	Oneofs   []*Oneof   // oneof declarations
 	Messages []*Message // nested message declarations
 	Enums    []*Enum    // nested enum declarations
 	Path     []int32    // location path of this message
 }
 
-func newMessage(gen *Plugin, f *File, parent *Message, desc protoreflect.MessageDescriptor) (*Message, error) {
+func newMessage(gen *Plugin, f *File, parent *Message, desc protoreflect.MessageDescriptor) *Message {
 	var path []int32
 	if parent != nil {
 		path = pathAppend(parent.Path, messageMessageField, int32(desc.Index()))
@@ -452,21 +449,16 @@
 	}
 	gen.messagesByName[desc.FullName()] = message
 	for i, mdescs := 0, desc.Messages(); i < mdescs.Len(); i++ {
-		m, err := newMessage(gen, f, message, mdescs.Get(i))
-		if err != nil {
-			return nil, err
-		}
-		message.Messages = append(message.Messages, m)
+		message.Messages = append(message.Messages, newMessage(gen, f, message, mdescs.Get(i)))
 	}
 	for i, edescs := 0, desc.Enums(); i < edescs.Len(); i++ {
 		message.Enums = append(message.Enums, newEnum(gen, f, message, edescs.Get(i)))
 	}
+	for i, odescs := 0, desc.Oneofs(); i < odescs.Len(); i++ {
+		message.Oneofs = append(message.Oneofs, newOneof(gen, f, message, odescs.Get(i)))
+	}
 	for i, fdescs := 0, desc.Fields(); i < fdescs.Len(); i++ {
-		field, err := newField(gen, f, message, fdescs.Get(i))
-		if err != nil {
-			return nil, err
-		}
-		message.Fields = append(message.Fields, field)
+		message.Fields = append(message.Fields, newField(gen, f, message, fdescs.Get(i)))
 	}
 
 	// Field name conflict resolution.
@@ -502,13 +494,20 @@
 		usedNames["Get"+name] = true
 		return name
 	}
+	seenOneofs := make(map[int]bool)
 	for _, field := range message.Fields {
-		field.GoIdent.GoName = makeNameUnique(field.GoIdent.GoName)
-		// TODO: If this is the first field of a oneof that we haven't seen
-		// before, generate the name for the oneof.
+		field.GoName = makeNameUnique(field.GoName)
+		if field.OneofType != nil {
+			if !seenOneofs[field.OneofType.Desc.Index()] {
+				// If this is a field in a oneof that we haven't seen before,
+				// make the name for that oneof unique as well.
+				field.OneofType.GoName = makeNameUnique(field.OneofType.GoName)
+				seenOneofs[field.OneofType.Desc.Index()] = true
+			}
+		}
 	}
 
-	return message, nil
+	return message
 }
 
 func (message *Message) init(gen *Plugin) error {
@@ -522,6 +521,9 @@
 			return err
 		}
 	}
+	for _, oneof := range message.Oneofs {
+		oneof.init(gen, message)
+	}
 	return nil
 }
 
@@ -529,26 +531,29 @@
 type Field struct {
 	Desc protoreflect.FieldDescriptor
 
-	// GoIdent is the base name of this field's Go fields and methods.
+	// GoName is the base name of this field's Go field and methods.
 	// For code generated by protoc-gen-go, this means a field named
-	// '{{GoIdent}}' and a getter method named 'Get{{GoIdent}}'.
-	GoIdent GoIdent
+	// '{{GoName}}' and a getter method named 'Get{{GoName}}'.
+	GoName string
 
-	MessageType *Message // type for message or group fields; nil otherwise
-	EnumType    *Enum    // type for enum fields; nil otherwise
-	Path        []int32  // location path of this field
+	ContainingType *Message // message in which this field occurs
+	MessageType    *Message // type for message or group fields; nil otherwise
+	EnumType       *Enum    // type for enum fields; nil otherwise
+	OneofType      *Oneof   // containing oneof; nil if not part of a oneof
+	Path           []int32  // location path of this field
 }
 
-func newField(gen *Plugin, f *File, message *Message, desc protoreflect.FieldDescriptor) (*Field, error) {
+func newField(gen *Plugin, f *File, message *Message, desc protoreflect.FieldDescriptor) *Field {
 	field := &Field{
-		Desc: desc,
-		GoIdent: GoIdent{
-			GoName:       camelCase(string(desc.Name())),
-			GoImportPath: f.GoImportPath,
-		},
-		Path: pathAppend(message.Path, messageFieldField, int32(desc.Index())),
+		Desc:           desc,
+		GoName:         camelCase(string(desc.Name())),
+		ContainingType: message,
+		Path:           pathAppend(message.Path, messageFieldField, int32(desc.Index())),
 	}
-	return field, nil
+	if desc.OneofType() != nil {
+		field.OneofType = message.Oneofs[desc.OneofType().Index()]
+	}
+	return field
 }
 
 func (field *Field) init(gen *Plugin) error {
@@ -572,6 +577,31 @@
 	return nil
 }
 
+// A Oneof describes a oneof field.
+type Oneof struct {
+	Desc protoreflect.OneofDescriptor
+
+	GoName         string   // Go field name of this oneof
+	ContainingType *Message // message in which this oneof occurs
+	Fields         []*Field // fields that are part of this oneof
+	Path           []int32  // location path of this oneof
+}
+
+func newOneof(gen *Plugin, f *File, message *Message, desc protoreflect.OneofDescriptor) *Oneof {
+	return &Oneof{
+		Desc:           desc,
+		ContainingType: message,
+		GoName:         camelCase(string(desc.Name())),
+		Path:           pathAppend(message.Path, messageOneofField, int32(desc.Index())),
+	}
+}
+
+func (oneof *Oneof) init(gen *Plugin, parent *Message) {
+	for i, fdescs := 0, oneof.Desc.Fields(); i < fdescs.Len(); i++ {
+		oneof.Fields = append(oneof.Fields, parent.Fields[fdescs.Get(i).Index()])
+	}
+}
+
 // An Enum describes an enum.
 type Enum struct {
 	Desc protoreflect.EnumDescriptor