func()

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)
	}

}