in protoc-gen-go/generator/generator.go [2177:2426]
func (g *Generator) generateMessage(message *Descriptor) {
topLevelFields := []topLevelField{}
oFields := make(map[int32]*oneofField)
// The full type name
typeName := message.TypeName()
// The full type name, CamelCased.
goTypeName := CamelCaseSlice(typeName)
usedNames := make(map[string]bool)
for _, n := range methodNames {
usedNames[n] = true
}
// allocNames finds a conflict-free variation of the given strings,
// consistently mutating their suffixes.
// It returns the same number of strings.
allocNames := func(ns ...string) []string {
Loop:
for {
for _, n := range ns {
if usedNames[n] {
for i := range ns {
ns[i] += "_"
}
continue Loop
}
}
for _, n := range ns {
usedNames[n] = true
}
return ns
}
}
mapFieldTypes := make(map[*descriptor.FieldDescriptorProto]string) // keep track of the map fields to be added later
// Build a structure more suitable for generating the text in one pass
for i, field := range message.Field {
// Allocate the getter and the field at the same time so name
// collisions create field/method consistent names.
// TODO: This allocation occurs based on the order of the fields
// in the proto file, meaning that a change in the field
// ordering can change generated Method/Field names.
base := CamelCase(*field.Name)
ns := allocNames(base, "Get"+base)
fieldName, fieldGetterName := ns[0], ns[1]
typename, wiretype := g.GoType(message, field)
jsonName := *field.Name
tag := fmt.Sprintf("protobuf:%s json:%q", g.goTag(message, field, wiretype), jsonName+",omitempty")
oneof := field.OneofIndex != nil
if oneof && oFields[*field.OneofIndex] == nil {
odp := message.OneofDecl[int(*field.OneofIndex)]
base := CamelCase(odp.GetName())
names := allocNames(base, "Get"+base)
fname, gname := names[0], names[1]
// This is the first field of a oneof we haven't seen before.
// Generate the union field.
oneofFullPath := fmt.Sprintf("%s,%d,%d", message.path, messageOneofPath, *field.OneofIndex)
c, ok := g.makeComments(oneofFullPath)
if ok {
c += "\n//\n"
}
c += "// Types that are valid to be assigned to " + fname + ":\n"
// Generate the rest of this comment later,
// when we've computed any disambiguation.
dname := "is" + goTypeName + "_" + fname
tag := `protobuf_oneof:"` + odp.GetName() + `"`
of := oneofField{
fieldCommon: fieldCommon{
goName: fname,
getterName: gname,
goType: dname,
tags: tag,
protoName: odp.GetName(),
fullPath: oneofFullPath,
},
comment: c,
}
topLevelFields = append(topLevelFields, &of)
oFields[*field.OneofIndex] = &of
}
if *field.Type == descriptor.FieldDescriptorProto_TYPE_MESSAGE {
desc := g.ObjectNamed(field.GetTypeName())
if d, ok := desc.(*Descriptor); ok && d.GetOptions().GetMapEntry() {
// Figure out the Go types and tags for the key and value types.
keyField, valField := d.Field[0], d.Field[1]
keyType, keyWire := g.GoType(d, keyField)
valType, valWire := g.GoType(d, valField)
keyTag, valTag := g.goTag(d, keyField, keyWire), g.goTag(d, valField, valWire)
// We don't use stars, except for message-typed values.
// Message and enum types are the only two possibly foreign types used in maps,
// so record their use. They are not permitted as map keys.
keyType = strings.TrimPrefix(keyType, "*")
switch *valField.Type {
case descriptor.FieldDescriptorProto_TYPE_ENUM:
valType = strings.TrimPrefix(valType, "*")
g.RecordTypeUse(valField.GetTypeName())
case descriptor.FieldDescriptorProto_TYPE_MESSAGE:
g.RecordTypeUse(valField.GetTypeName())
default:
valType = strings.TrimPrefix(valType, "*")
}
typename = fmt.Sprintf("map[%s]%s", keyType, valType)
mapFieldTypes[field] = typename // record for the getter generation
tag += fmt.Sprintf(" protobuf_key:%s protobuf_val:%s", keyTag, valTag)
}
}
fieldDeprecated := ""
if field.GetOptions().GetDeprecated() {
fieldDeprecated = deprecationComment
}
dvalue := g.getterDefault(field, goTypeName)
if oneof {
tname := goTypeName + "_" + fieldName
// It is possible for this to collide with a message or enum
// nested in this message. Check for collisions.
for {
ok := true
for _, desc := range message.nested {
if CamelCaseSlice(desc.TypeName()) == tname {
ok = false
break
}
}
for _, enum := range message.enums {
if CamelCaseSlice(enum.TypeName()) == tname {
ok = false
break
}
}
if !ok {
tname += "_"
continue
}
break
}
oneofField := oFields[*field.OneofIndex]
tag := "protobuf:" + g.goTag(message, field, wiretype)
sf := oneofSubField{
fieldCommon: fieldCommon{
goName: fieldName,
getterName: fieldGetterName,
goType: typename,
tags: tag,
protoName: field.GetName(),
fullPath: fmt.Sprintf("%s,%d,%d", message.path, messageFieldPath, i),
},
protoTypeName: field.GetTypeName(),
fieldNumber: int(*field.Number),
protoType: *field.Type,
getterDef: dvalue,
protoDef: field.GetDefaultValue(),
oneofTypeName: tname,
deprecated: fieldDeprecated,
}
oneofField.subFields = append(oneofField.subFields, &sf)
g.RecordTypeUse(field.GetTypeName())
continue
}
fieldFullPath := fmt.Sprintf("%s,%d,%d", message.path, messageFieldPath, i)
c, ok := g.makeComments(fieldFullPath)
if ok {
c += "\n"
}
rf := simpleField{
fieldCommon: fieldCommon{
goName: fieldName,
getterName: fieldGetterName,
goType: typename,
tags: tag,
protoName: field.GetName(),
fullPath: fieldFullPath,
},
protoTypeName: field.GetTypeName(),
protoType: *field.Type,
deprecated: fieldDeprecated,
getterDef: dvalue,
protoDef: field.GetDefaultValue(),
comment: c,
}
var pf topLevelField = &rf
topLevelFields = append(topLevelFields, pf)
g.RecordTypeUse(field.GetTypeName())
}
mc := &msgCtx{
goName: goTypeName,
message: message,
}
g.generateMessageStruct(mc, topLevelFields)
g.P()
g.generateCommonMethods(mc)
g.P()
g.generateDefaultConstants(mc, topLevelFields)
g.P()
g.generateGetters(mc, topLevelFields)
g.P()
g.generateSetters(mc, topLevelFields)
g.P()
g.generateOneofFuncs(mc, topLevelFields)
g.P()
var oneofTypes []string
for _, f := range topLevelFields {
if of, ok := f.(*oneofField); ok {
for _, osf := range of.subFields {
oneofTypes = append(oneofTypes, osf.oneofTypeName)
}
}
}
opts := message.Options
ms := &messageSymbol{
sym: goTypeName,
hasExtensions: len(message.ExtensionRange) > 0,
isMessageSet: opts != nil && opts.GetMessageSetWireFormat(),
oneofTypes: oneofTypes,
}
g.file.addExport(message, ms)
for _, ext := range message.ext {
g.generateExtension(ext)
}
fullName := strings.Join(message.TypeName(), ".")
if g.file.Package != nil {
fullName = *g.file.Package + "." + fullName
}
g.addInitf("%s.RegisterType((*%s)(nil), %q)", g.Pkg["proto"], goTypeName, fullName)
// Register types for native map types.
for _, k := range mapFieldKeys(mapFieldTypes) {
fullName := strings.TrimPrefix(*k.TypeName, ".")
g.addInitf("%s.RegisterMapType((%s)(nil), %q)", g.Pkg["proto"], mapFieldTypes[k], fullName)
}
}