compiler/protogen: move name mangling logic to protogen

The name mangling logic should be unified in a single place
rather than being split between compiler/protogen and cmd/protoc-gen-go.
Move it over compiler/protogen.

Change-Id: Iea65e0b3fba45e0c95c76e3fc1f061e0fa8f84d7
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/191117
Reviewed-by: Damien Neil <dneil@google.com>
diff --git a/compiler/protogen/protogen.go b/compiler/protogen/protogen.go
index 0620d3a..ec03704 100644
--- a/compiler/protogen/protogen.go
+++ b/compiler/protogen/protogen.go
@@ -556,7 +556,7 @@
 	// A top-level enum value's name is: EnumName_ValueName
 	// An enum value contained in a message is: MessageName_ValueName
 	//
-	// Enum value names are not camelcased.
+	// For historical reasons, enum value names are not camel-cased.
 	parentIdent := enum.GoIdent
 	if message != nil {
 		parentIdent = message.GoIdent
@@ -661,15 +661,39 @@
 		usedNames["Get"+name] = hasGetter
 		return name
 	}
-	seenOneofs := make(map[int]bool)
 	for _, field := range message.Fields {
 		field.GoName = makeNameUnique(field.GoName, true)
+		field.GoIdent.GoName = message.GoIdent.GoName + "_" + field.GoName
+		if field.Oneof != nil && field.Oneof.Fields[0] == field {
+			// Make the name for a oneof unique as well. For historical reasons,
+			// this assumes that a getter method is not generated for oneofs.
+			// This is incorrect, but fixing it breaks existing code.
+			field.Oneof.GoName = makeNameUnique(field.Oneof.GoName, false)
+			field.Oneof.GoIdent.GoName = message.GoIdent.GoName + "_" + field.Oneof.GoName
+		}
+	}
+
+	// Oneof field name conflict resolution.
+	//
+	// This conflict resolution is incomplete as it does not consider collisions
+	// with other oneof field types, but fixing it breaks existing code.
+	for _, field := range message.Fields {
 		if field.Oneof != nil {
-			if !seenOneofs[field.Oneof.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.Oneof.GoName = makeNameUnique(field.Oneof.GoName, false)
-				seenOneofs[field.Oneof.Desc.Index()] = true
+		Loop:
+			for {
+				for _, nestedMessage := range message.Messages {
+					if nestedMessage.GoIdent == field.GoIdent {
+						field.GoIdent.GoName += "_"
+						continue Loop
+					}
+				}
+				for _, nestedEnum := range message.Enums {
+					if nestedEnum.GoIdent == field.GoIdent {
+						field.GoIdent.GoName += "_"
+						continue Loop
+					}
+				}
+				break Loop
 			}
 		}
 	}
@@ -703,7 +727,13 @@
 	// 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
 	// '{{GoName}}' and a getter method named 'Get{{GoName}}'.
-	GoName string
+	GoName string // e.g., "FieldName"
+
+	// GoIdent is the base name of a top-level declaration for this field.
+	// For code generated by protoc-gen-go, this means a wrapper type named
+	// '{{GoIdent}}' for members fields of a oneof, and a variable named
+	// 'E_{{GoIdent}}' for extension fields.
+	GoIdent GoIdent // e.g., "MessageName_FieldName"
 
 	Parent   *Message // message in which this field is declared; nil if top-level extension
 	Oneof    *Oneof   // containing oneof; nil if not part of a oneof
@@ -726,9 +756,18 @@
 	default:
 		loc = message.Location.appendPath(fieldnum.DescriptorProto_Field, int32(desc.Index()))
 	}
+	camelCased := camelCase(string(desc.Name()))
+	var parentPrefix string
+	if message != nil {
+		parentPrefix = message.GoIdent.GoName + "_"
+	}
 	field := &Field{
-		Desc:     desc,
-		GoName:   camelCase(string(desc.Name())),
+		Desc:   desc,
+		GoName: camelCased,
+		GoIdent: GoIdent{
+			GoImportPath: f.GoImportPath,
+			GoName:       parentPrefix + camelCased,
+		},
 		Parent:   message,
 		Location: loc,
 		Comments: f.comments[newPathKey(loc.Path)],
@@ -769,7 +808,13 @@
 type Oneof struct {
 	Desc protoreflect.OneofDescriptor
 
-	GoName string // Go field name of this oneof
+	// GoName is the base name of this oneof's Go field and methods.
+	// For code generated by protoc-gen-go, this means a field named
+	// '{{GoName}}' and a getter method named 'Get{{GoName}}'.
+	GoName string // e.g., "OneofName"
+
+	// GoIdent is the base name of a top-level declaration for this oneof.
+	GoIdent GoIdent // e.g., "MessageName_OneofName"
 
 	Parent *Message // message in which this oneof is declared
 
@@ -781,10 +826,16 @@
 
 func newOneof(gen *Plugin, f *File, message *Message, desc protoreflect.OneofDescriptor) *Oneof {
 	loc := message.Location.appendPath(fieldnum.DescriptorProto_OneofDecl, int32(desc.Index()))
+	camelCased := camelCase(string(desc.Name()))
+	parentPrefix := message.GoIdent.GoName + "_"
 	return &Oneof{
-		Desc:     desc,
-		Parent:   message,
-		GoName:   camelCase(string(desc.Name())),
+		Desc:   desc,
+		Parent: message,
+		GoName: camelCased,
+		GoIdent: GoIdent{
+			GoImportPath: f.GoImportPath,
+			GoName:       parentPrefix + camelCased,
+		},
 		Location: loc,
 		Comments: f.comments[newPathKey(loc.Path)],
 	}