// Copyright 2018 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // The protoc-gen-go binary is a protoc plugin to generate a Go protocol // buffer package. package main import ( "bytes" "compress/gzip" "crypto/sha256" "encoding/hex" "flag" "fmt" "math" "sort" "strconv" "strings" "github.com/golang/protobuf/proto" descpb "github.com/golang/protobuf/protoc-gen-go/descriptor" "google.golang.org/proto/protogen" "google.golang.org/proto/reflect/protoreflect" ) // generatedCodeVersion indicates a version of the generated code. // It is incremented whenever an incompatibility between the generated code and // proto package is introduced; the generated code references // a constant, proto.ProtoPackageIsVersionN (where N is generatedCodeVersion). const generatedCodeVersion = 2 const protoPackage = "github.com/golang/protobuf/proto" func main() { var flags flag.FlagSet // TODO: Decide what to do for backwards compatibility with plugins=grpc. flags.String("plugins", "", "") opts := &protogen.Options{ ParamFunc: flags.Set, } protogen.Run(opts, func(gen *protogen.Plugin) error { for _, f := range gen.Files { if !f.Generate { continue } genFile(gen, f) } return nil }) } type File struct { *protogen.File locationMap map[string][]*descpb.SourceCodeInfo_Location descriptorVar string // var containing the gzipped FileDescriptorProto allEnums []*protogen.Enum allMessages []*protogen.Message allExtensions []*protogen.Extension } func genFile(gen *protogen.Plugin, file *protogen.File) { f := &File{ File: file, locationMap: make(map[string][]*descpb.SourceCodeInfo_Location), } for _, loc := range file.Proto.GetSourceCodeInfo().GetLocation() { key := pathKey(loc.Path) f.locationMap[key] = append(f.locationMap[key], loc) } // The different order for enums and extensions is to match the output // of the previous implementation. // // TODO: Eventually make this consistent. f.allEnums = append(f.allEnums, f.File.Enums...) walkMessages(f.Messages, func(message *protogen.Message) { f.allMessages = append(f.allMessages, message) f.allEnums = append(f.allEnums, message.Enums...) f.allExtensions = append(f.allExtensions, message.Extensions...) }) f.allExtensions = append(f.allExtensions, f.File.Extensions...) // Determine the name of the var holding the file descriptor: // // fileDescriptor_ filenameHash := sha256.Sum256([]byte(f.Desc.Path())) f.descriptorVar = fmt.Sprintf("fileDescriptor_%s", hex.EncodeToString(filenameHash[:8])) g := gen.NewGeneratedFile(f.GeneratedFilenamePrefix+".pb.go", f.GoImportPath) g.P("// Code generated by protoc-gen-go. DO NOT EDIT.") if f.Proto.GetOptions().GetDeprecated() { g.P("// ", f.Desc.Path(), " is a deprecated file.") } else { g.P("// source: ", f.Desc.Path()) } g.P() const filePackageField = 2 // FileDescriptorProto.package genComment(g, f, []int32{filePackageField}) g.P() g.P("package ", f.GoPackageName) g.P() // These references are not necessary, since we automatically add // all necessary imports before formatting the generated file. // // This section exists to generate output more consistent with // the previous version of protoc-gen-go, to make it easier to // detect unintended variations. // // TODO: Eventually remove this. g.P("// Reference imports to suppress errors if they are not otherwise used.") g.P("var _ = ", protogen.GoIdent{GoImportPath: protoPackage, GoName: "Marshal"}) g.P("var _ = ", protogen.GoIdent{GoImportPath: "fmt", GoName: "Errorf"}) g.P("var _ = ", protogen.GoIdent{GoImportPath: "math", GoName: "Inf"}) g.P() g.P("// This is a compile-time assertion to ensure that this generated file") g.P("// is compatible with the proto package it is being compiled against.") g.P("// A compilation error at this line likely means your copy of the") g.P("// proto package needs to be updated.") g.P("const _ = ", protogen.GoIdent{ GoImportPath: protoPackage, GoName: fmt.Sprintf("ProtoPackageIsVersion%d", generatedCodeVersion), }, "// please upgrade the proto package") g.P() for i, imps := 0, f.Desc.Imports(); i < imps.Len(); i++ { genImport(gen, g, f, imps.Get(i)) } for _, enum := range f.allEnums { genEnum(gen, g, f, enum) } for _, message := range f.allMessages { genMessage(gen, g, f, message) } for _, extension := range f.Extensions { genExtension(gen, g, f, extension) } genInitFunction(gen, g, f) genFileDescriptor(gen, g, f) } // walkMessages calls f on each message and all of its descendants. func walkMessages(messages []*protogen.Message, f func(*protogen.Message)) { for _, m := range messages { f(m) walkMessages(m.Messages, f) } } func genImport(gen *protogen.Plugin, g *protogen.GeneratedFile, f *File, imp protoreflect.FileImport) { impFile, ok := gen.FileByName(imp.Path()) if !ok { return } if impFile.GoImportPath == f.GoImportPath { // Don't generate imports or aliases for types in the same Go package. return } // Generate imports for all dependencies, even if they are not // referenced, because other code and tools depend on having the // full transitive closure of protocol buffer types in the binary. g.Import(impFile.GoImportPath) if !imp.IsPublic { return } var enums []*protogen.Enum enums = append(enums, impFile.Enums...) walkMessages(impFile.Messages, func(message *protogen.Message) { enums = append(enums, message.Enums...) g.P("// ", message.GoIdent.GoName, " from public import ", imp.Path()) g.P("type ", message.GoIdent.GoName, " = ", message.GoIdent) for _, oneof := range message.Oneofs { for _, field := range oneof.Fields { typ := fieldOneofType(field) g.P("type ", typ.GoName, " = ", typ) } } g.P() }) for _, enum := range enums { g.P("// ", enum.GoIdent.GoName, " from public import ", imp.Path()) g.P("type ", enum.GoIdent.GoName, " = ", enum.GoIdent) g.P("var ", enum.GoIdent.GoName, "_name = ", enum.GoIdent, "_name") g.P("var ", enum.GoIdent.GoName, "_value = ", enum.GoIdent, "_value") g.P() for _, value := range enum.Values { g.P("const ", value.GoIdent.GoName, " = ", enum.GoIdent.GoName, "(", value.GoIdent, ")") } } } func genFileDescriptor(gen *protogen.Plugin, g *protogen.GeneratedFile, f *File) { // Trim the source_code_info from the descriptor. // Marshal and gzip it. descProto := proto.Clone(f.Proto).(*descpb.FileDescriptorProto) descProto.SourceCodeInfo = nil b, err := proto.Marshal(descProto) if err != nil { gen.Error(err) return } var buf bytes.Buffer w, _ := gzip.NewWriterLevel(&buf, gzip.BestCompression) w.Write(b) w.Close() b = buf.Bytes() g.P("func init() { proto.RegisterFile(", strconv.Quote(f.Desc.Path()), ", ", f.descriptorVar, ") }") g.P() g.P("var ", f.descriptorVar, " = []byte{") g.P("// ", len(b), " bytes of a gzipped FileDescriptorProto") for len(b) > 0 { n := 16 if n > len(b) { n = len(b) } s := "" for _, c := range b[:n] { s += fmt.Sprintf("0x%02x,", c) } g.P(s) b = b[n:] } g.P("}") g.P() } func genEnum(gen *protogen.Plugin, g *protogen.GeneratedFile, f *File, enum *protogen.Enum) { genComment(g, f, enum.Path) g.P("type ", enum.GoIdent, " int32", deprecationComment(enumOptions(gen, enum).GetDeprecated())) g.P("const (") for _, value := range enum.Values { genComment(g, f, value.Path) g.P(value.GoIdent, " ", enum.GoIdent, " = ", value.Desc.Number(), deprecationComment(enumValueOptions(gen, value).GetDeprecated())) } g.P(")") g.P() nameMap := enum.GoIdent.GoName + "_name" g.P("var ", nameMap, " = map[int32]string{") generated := make(map[protoreflect.EnumNumber]bool) for _, value := range enum.Values { duplicate := "" if _, present := generated[value.Desc.Number()]; present { duplicate = "// Duplicate value: " } g.P(duplicate, value.Desc.Number(), ": ", strconv.Quote(string(value.Desc.Name())), ",") generated[value.Desc.Number()] = true } g.P("}") g.P() valueMap := enum.GoIdent.GoName + "_value" g.P("var ", valueMap, " = map[string]int32{") for _, value := range enum.Values { g.P(strconv.Quote(string(value.Desc.Name())), ": ", value.Desc.Number(), ",") } g.P("}") g.P() if enum.Desc.Syntax() != protoreflect.Proto3 { g.P("func (x ", enum.GoIdent, ") Enum() *", enum.GoIdent, " {") g.P("p := new(", enum.GoIdent, ")") g.P("*p = x") g.P("return p") g.P("}") g.P() } g.P("func (x ", enum.GoIdent, ") String() string {") g.P("return ", protogen.GoIdent{GoImportPath: protoPackage, GoName: "EnumName"}, "(", enum.GoIdent, "_name, int32(x))") g.P("}") g.P() if enum.Desc.Syntax() != protoreflect.Proto3 { g.P("func (x *", enum.GoIdent, ") UnmarshalJSON(data []byte) error {") g.P("value, err := ", protogen.GoIdent{GoImportPath: protoPackage, GoName: "UnmarshalJSONEnum"}, "(", enum.GoIdent, `_value, data, "`, enum.GoIdent, `")`) g.P("if err != nil {") g.P("return err") g.P("}") g.P("*x = ", enum.GoIdent, "(value)") g.P("return nil") g.P("}") g.P() } var indexes []string for i := 1; i < len(enum.Path); i += 2 { indexes = append(indexes, strconv.Itoa(int(enum.Path[i]))) } g.P("func (", enum.GoIdent, ") EnumDescriptor() ([]byte, []int) {") g.P("return ", f.descriptorVar, ", []int{", strings.Join(indexes, ","), "}") g.P("}") g.P() genWellKnownType(g, enum.GoIdent, enum.Desc) } // enumRegistryName returns the name used to register an enum with the proto // package registry. // // Confusingly, this is .. This probably should have // been the full name of the proto enum type instead, but changing it at this // point would require thought. func enumRegistryName(enum *protogen.Enum) string { // Find the FileDescriptor for this enum. var desc protoreflect.Descriptor = enum.Desc for { p, ok := desc.Parent() if !ok { break } desc = p } fdesc := desc.(protoreflect.FileDescriptor) return string(fdesc.Package()) + "." + enum.GoIdent.GoName } func genMessage(gen *protogen.Plugin, g *protogen.GeneratedFile, f *File, message *protogen.Message) { if message.Desc.IsMapEntry() { return } hasComment := genComment(g, f, message.Path) if messageOptions(gen, message).GetDeprecated() { if hasComment { g.P("//") } g.P(deprecationComment(true)) } g.P("type ", message.GoIdent, " struct {") for _, field := range message.Fields { if field.OneofType != nil { // It would be a bit simpler to iterate over the oneofs below, // but generating the field here keeps the contents of the Go // struct in the same order as the contents of the source // .proto file. if field == field.OneofType.Fields[0] { genOneofField(gen, g, f, message, field.OneofType) } continue } genComment(g, f, field.Path) goType, pointer := fieldGoType(g, field) if pointer { goType = "*" + goType } tags := []string{ fmt.Sprintf("protobuf:%q", fieldProtobufTag(field)), fmt.Sprintf("json:%q", fieldJSONTag(field)), } if field.Desc.IsMap() { key := field.MessageType.Fields[0] val := field.MessageType.Fields[1] tags = append(tags, fmt.Sprintf("protobuf_key:%q", fieldProtobufTag(key)), fmt.Sprintf("protobuf_val:%q", fieldProtobufTag(val)), ) } g.P(field.GoName, " ", goType, " `", strings.Join(tags, " "), "`", deprecationComment(fieldOptions(gen, field).GetDeprecated())) } g.P("XXX_NoUnkeyedLiteral struct{} `json:\"-\"`") if message.Desc.ExtensionRanges().Len() > 0 { var tags []string if messageOptions(gen, message).GetMessageSetWireFormat() { tags = append(tags, `protobuf_messageset:"1"`) } tags = append(tags, `json:"-"`) g.P(protogen.GoIdent{ GoImportPath: protoPackage, GoName: "XXX_InternalExtensions", }, " `", strings.Join(tags, " "), "`") } // TODO XXX_InternalExtensions g.P("XXX_unrecognized []byte `json:\"-\"`") g.P("XXX_sizecache int32 `json:\"-\"`") g.P("}") g.P() // Reset g.P("func (m *", message.GoIdent, ") Reset() { *m = ", message.GoIdent, "{} }") // String g.P("func (m *", message.GoIdent, ") String() string { return ", protogen.GoIdent{ GoImportPath: protoPackage, GoName: "CompactTextString", }, "(m) }") // ProtoMessage g.P("func (*", message.GoIdent, ") ProtoMessage() {}") // Descriptor var indexes []string for i := 1; i < len(message.Path); i += 2 { indexes = append(indexes, strconv.Itoa(int(message.Path[i]))) } g.P("func (*", message.GoIdent, ") Descriptor() ([]byte, []int) {") g.P("return ", f.descriptorVar, ", []int{", strings.Join(indexes, ","), "}") g.P("}") g.P() // ExtensionRangeArray if extranges := message.Desc.ExtensionRanges(); extranges.Len() > 0 { if messageOptions(gen, message).GetMessageSetWireFormat() { g.P("func (m *", message.GoIdent, ") MarshalJSON() ([]byte, error) {") g.P("return ", protogen.GoIdent{ GoImportPath: protoPackage, GoName: "MarshalMessageSetJSON", }, "(&m.XXX_InternalExtensions)") g.P("}") g.P("func (m *", message.GoIdent, ") UnmarshalJSON(buf []byte) error {") g.P("return ", protogen.GoIdent{ GoImportPath: protoPackage, GoName: "UnmarshalMessageSetJSON", }, "(buf, &m.XXX_InternalExtensions)") g.P("}") g.P() } protoExtRange := protogen.GoIdent{ GoImportPath: protoPackage, GoName: "ExtensionRange", } extRangeVar := "extRange_" + message.GoIdent.GoName g.P("var ", extRangeVar, " = []", protoExtRange, " {") for i := 0; i < extranges.Len(); i++ { r := extranges.Get(i) g.P("{Start:", r[0], ", End:", r[1]-1 /* inclusive */, "},") } g.P("}") g.P() g.P("func (*", message.GoIdent, ") ExtensionRangeArray() []", protoExtRange, " {") g.P("return ", extRangeVar) g.P("}") g.P() } // Table-driven proto support. // // TODO: It does not scale to keep adding another method for every // operation on protos that we want to switch over to using the // table-driven approach. Instead, we should only add a single method // that allows getting access to the *InternalMessageInfo struct and then // calling Unmarshal, Marshal, Merge, Size, and Discard directly on that. messageInfoVar := "xxx_messageInfo_" + message.GoIdent.GoName // XXX_Unmarshal g.P("func (m *", message.GoIdent, ") XXX_Unmarshal(b []byte) error {") g.P("return ", messageInfoVar, ".Unmarshal(m, b)") g.P("}") // XXX_Marshal g.P("func (m *", message.GoIdent, ") XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {") g.P("return ", messageInfoVar, ".Marshal(b, m, deterministic)") g.P("}") // XXX_Merge g.P("func (m *", message.GoIdent, ") XXX_Merge(src proto.Message) {") g.P(messageInfoVar, ".Merge(m, src)") g.P("}") // XXX_Size g.P("func (m *", message.GoIdent, ") XXX_Size() int {") g.P("return ", messageInfoVar, ".Size(m)") g.P("}") // XXX_DiscardUnknown g.P("func (m *", message.GoIdent, ") XXX_DiscardUnknown() {") g.P(messageInfoVar, ".DiscardUnknown(m)") g.P("}") g.P() g.P("var ", messageInfoVar, " ", protogen.GoIdent{ GoImportPath: protoPackage, GoName: "InternalMessageInfo", }) g.P() // Constants and vars holding the default values of fields. for _, field := range message.Fields { if !field.Desc.HasDefault() { continue } defVarName := "Default_" + message.GoIdent.GoName + "_" + field.GoName def := field.Desc.Default() switch field.Desc.Kind() { case protoreflect.StringKind: g.P("const ", defVarName, " string = ", strconv.Quote(def.String())) case protoreflect.BytesKind: g.P("var ", defVarName, " []byte = []byte(", strconv.Quote(string(def.Bytes())), ")") case protoreflect.EnumKind: enum := field.EnumType evalue := enum.Values[enum.Desc.Values().ByNumber(def.Enum()).Index()] g.P("const ", defVarName, " ", field.EnumType.GoIdent, " = ", evalue.GoIdent) case protoreflect.FloatKind, protoreflect.DoubleKind: // Floating point numbers need extra handling for -Inf/Inf/NaN. f := field.Desc.Default().Float() goType := "float64" if field.Desc.Kind() == protoreflect.FloatKind { goType = "float32" } // funcCall returns a call to a function in the math package, // possibly converting the result to float32. funcCall := func(fn, param string) string { s := g.QualifiedGoIdent(protogen.GoIdent{ GoImportPath: "math", GoName: fn, }) + param if goType != "float64" { s = goType + "(" + s + ")" } return s } switch { case math.IsInf(f, -1): g.P("var ", defVarName, " ", goType, " = ", funcCall("Inf", "(-1)")) case math.IsInf(f, 1): g.P("var ", defVarName, " ", goType, " = ", funcCall("Inf", "(1)")) case math.IsNaN(f): g.P("var ", defVarName, " ", goType, " = ", funcCall("NaN", "()")) default: g.P("const ", defVarName, " ", goType, " = ", f) } default: goType, _ := fieldGoType(g, field) g.P("const ", defVarName, " ", goType, " = ", def.Interface()) } } g.P() // Getters. for _, field := range message.Fields { if field.OneofType != nil { if field == field.OneofType.Fields[0] { genOneofTypes(gen, g, f, message, field.OneofType) } } goType, pointer := fieldGoType(g, field) defaultValue := fieldDefaultValue(g, message, field) if fieldOptions(gen, field).GetDeprecated() { g.P(deprecationComment(true)) } g.P("func (m *", message.GoIdent, ") Get", field.GoName, "() ", goType, " {") if field.OneofType != nil { g.P("if x, ok := m.Get", field.OneofType.GoName, "().(*", fieldOneofType(field), "); ok {") g.P("return x.", field.GoName) g.P("}") } else { if field.Desc.Syntax() == protoreflect.Proto3 || defaultValue == "nil" { g.P("if m != nil {") } else { g.P("if m != nil && m.", field.GoName, " != nil {") } star := "" if pointer { star = "*" } g.P("return ", star, " m.", field.GoName) g.P("}") } g.P("return ", defaultValue) g.P("}") g.P() } genWellKnownType(g, message.GoIdent, message.Desc) if len(message.Oneofs) > 0 { genOneofFuncs(gen, g, f, message) } for _, extension := range message.Extensions { genExtension(gen, g, f, extension) } } // fieldGoType returns the Go type used for a field. // // If it returns pointer=true, the struct field is a pointer to the type. func fieldGoType(g *protogen.GeneratedFile, field *protogen.Field) (goType string, pointer bool) { pointer = true switch field.Desc.Kind() { case protoreflect.BoolKind: goType = "bool" case protoreflect.EnumKind: goType = g.QualifiedGoIdent(field.EnumType.GoIdent) case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind: goType = "int32" case protoreflect.Uint32Kind, protoreflect.Fixed32Kind: goType = "uint32" case protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Sfixed64Kind: goType = "int64" case protoreflect.Uint64Kind, protoreflect.Fixed64Kind: goType = "uint64" case protoreflect.FloatKind: goType = "float32" case protoreflect.DoubleKind: goType = "float64" case protoreflect.StringKind: goType = "string" case protoreflect.BytesKind: goType = "[]byte" pointer = false case protoreflect.MessageKind, protoreflect.GroupKind: if field.Desc.IsMap() { keyType, _ := fieldGoType(g, field.MessageType.Fields[0]) valType, _ := fieldGoType(g, field.MessageType.Fields[1]) return fmt.Sprintf("map[%v]%v", keyType, valType), false } goType = "*" + g.QualifiedGoIdent(field.MessageType.GoIdent) pointer = false } if field.Desc.Cardinality() == protoreflect.Repeated { goType = "[]" + goType pointer = false } if field.Desc.Syntax() == protoreflect.Proto3 { pointer = false } return goType, pointer } func fieldProtobufTag(field *protogen.Field) string { var tag []string // wire type tag = append(tag, wireTypes[field.Desc.Kind()]) // field number tag = append(tag, strconv.Itoa(int(field.Desc.Number()))) // cardinality switch field.Desc.Cardinality() { case protoreflect.Optional: tag = append(tag, "opt") case protoreflect.Required: tag = append(tag, "req") case protoreflect.Repeated: tag = append(tag, "rep") } if field.Desc.IsPacked() { tag = append(tag, "packed") } // TODO: packed // name name := string(field.Desc.Name()) if field.Desc.Kind() == protoreflect.GroupKind { // The name of the FieldDescriptor for a group field is // lowercased. To find the original capitalization, we // look in the field's MessageType. name = string(field.MessageType.Desc.Name()) } tag = append(tag, "name="+name) // JSON name if jsonName := field.Desc.JSONName(); jsonName != "" && jsonName != name { tag = append(tag, "json="+jsonName) } // proto3 if field.Desc.Syntax() == protoreflect.Proto3 { tag = append(tag, "proto3") } // enum if field.Desc.Kind() == protoreflect.EnumKind { tag = append(tag, "enum="+enumRegistryName(field.EnumType)) } // oneof if field.Desc.OneofType() != nil { tag = append(tag, "oneof") } // default value // This must appear last in the tag, since commas in strings aren't escaped. if field.Desc.HasDefault() { var def string switch field.Desc.Kind() { case protoreflect.BoolKind: if field.Desc.Default().Bool() { def = "1" } else { def = "0" } case protoreflect.BytesKind: def = string(field.Desc.Default().Bytes()) case protoreflect.FloatKind, protoreflect.DoubleKind: f := field.Desc.Default().Float() switch { case math.IsInf(f, -1): def = "-inf" case math.IsInf(f, 1): def = "inf" case math.IsNaN(f): def = "nan" default: def = fmt.Sprint(f) } default: def = fmt.Sprint(field.Desc.Default().Interface()) } tag = append(tag, "def="+def) } return strings.Join(tag, ",") } func fieldDefaultValue(g *protogen.GeneratedFile, message *protogen.Message, field *protogen.Field) string { if field.Desc.Cardinality() == protoreflect.Repeated { return "nil" } if field.Desc.HasDefault() { defVarName := "Default_" + message.GoIdent.GoName + "_" + field.GoName if field.Desc.Kind() == protoreflect.BytesKind { return "append([]byte(nil), " + defVarName + "...)" } return defVarName } switch field.Desc.Kind() { case protoreflect.BoolKind: return "false" case protoreflect.StringKind: return `""` case protoreflect.MessageKind, protoreflect.GroupKind, protoreflect.BytesKind: return "nil" case protoreflect.EnumKind: return g.QualifiedGoIdent(field.EnumType.Values[0].GoIdent) default: return "0" } } var wireTypes = map[protoreflect.Kind]string{ protoreflect.BoolKind: "varint", protoreflect.EnumKind: "varint", protoreflect.Int32Kind: "varint", protoreflect.Sint32Kind: "zigzag32", protoreflect.Uint32Kind: "varint", protoreflect.Int64Kind: "varint", protoreflect.Sint64Kind: "zigzag64", protoreflect.Uint64Kind: "varint", protoreflect.Sfixed32Kind: "fixed32", protoreflect.Fixed32Kind: "fixed32", protoreflect.FloatKind: "fixed32", protoreflect.Sfixed64Kind: "fixed64", protoreflect.Fixed64Kind: "fixed64", protoreflect.DoubleKind: "fixed64", protoreflect.StringKind: "bytes", protoreflect.BytesKind: "bytes", protoreflect.MessageKind: "bytes", protoreflect.GroupKind: "group", } func fieldJSONTag(field *protogen.Field) string { return string(field.Desc.Name()) + ",omitempty" } func genExtension(gen *protogen.Plugin, g *protogen.GeneratedFile, f *File, extension *protogen.Extension) { g.P("var ", extensionVar(f, extension), " = &", protogen.GoIdent{ GoImportPath: protoPackage, GoName: "ExtensionDesc", }, "{") g.P("ExtendedType: (*", extension.ExtendedType.GoIdent, ")(nil),") goType, pointer := fieldGoType(g, extension) if pointer { goType = "*" + goType } g.P("ExtensionType: (", goType, ")(nil),") g.P("Field: ", extension.Desc.Number(), ",") g.P("Name: ", strconv.Quote(string(extension.Desc.FullName())), ",") g.P("Tag: ", strconv.Quote(fieldProtobufTag(extension)), ",") g.P("Filename: ", strconv.Quote(f.Desc.Path()), ",") g.P("}") g.P() } // extensionVar returns the var holding the ExtensionDesc for an extension. func extensionVar(f *File, extension *protogen.Extension) protogen.GoIdent { name := "E_" if extension.ParentMessage != nil { name += extension.ParentMessage.GoIdent.GoName + "_" } name += extension.GoName return protogen.GoIdent{ GoImportPath: f.GoImportPath, GoName: name, } } // genInitFunction generates an init function that registers the types in the // generated file with the proto package. func genInitFunction(gen *protogen.Plugin, g *protogen.GeneratedFile, f *File) { if len(f.allMessages) == 0 && len(f.allEnums) == 0 && len(f.allExtensions) == 0 { return } g.P("func init() {") for _, message := range f.allMessages { if message.Desc.IsMapEntry() { continue } name := message.GoIdent.GoName g.P(protogen.GoIdent{ GoImportPath: protoPackage, GoName: "RegisterType", }, fmt.Sprintf("((*%s)(nil), %q)", name, message.Desc.FullName())) // Types of map fields, sorted by the name of the field message type. var mapFields []*protogen.Field for _, field := range message.Fields { if field.Desc.IsMap() { mapFields = append(mapFields, field) } } sort.Slice(mapFields, func(i, j int) bool { ni := mapFields[i].MessageType.Desc.FullName() nj := mapFields[j].MessageType.Desc.FullName() return ni < nj }) for _, field := range mapFields { typeName := string(field.MessageType.Desc.FullName()) goType, _ := fieldGoType(g, field) g.P(protogen.GoIdent{ GoImportPath: protoPackage, GoName: "RegisterMapType", }, fmt.Sprintf("((%v)(nil), %q)", goType, typeName)) } } for _, enum := range f.allEnums { name := enum.GoIdent.GoName g.P(protogen.GoIdent{ GoImportPath: protoPackage, GoName: "RegisterEnum", }, fmt.Sprintf("(%q, %s_name, %s_value)", enumRegistryName(enum), name, name)) } for _, extension := range f.allExtensions { g.P(protogen.GoIdent{ GoImportPath: protoPackage, GoName: "RegisterExtension", }, "(", extensionVar(f, extension), ")") } g.P("}") g.P() } func genComment(g *protogen.GeneratedFile, f *File, path []int32) (hasComment bool) { for _, loc := range f.locationMap[pathKey(path)] { if loc.LeadingComments == nil { continue } for _, line := range strings.Split(strings.TrimSuffix(loc.GetLeadingComments(), "\n"), "\n") { hasComment = true g.P("//", line) } break } return hasComment } // deprecationComment returns a standard deprecation comment if deprecated is true. func deprecationComment(deprecated bool) string { if !deprecated { return "" } return "// Deprecated: Do not use." } // pathKey converts a location path to a string suitable for use as a map key. func pathKey(path []int32) string { var buf []byte for i, x := range path { if i != 0 { buf = append(buf, ',') } buf = strconv.AppendInt(buf, int64(x), 10) } return string(buf) } func genWellKnownType(g *protogen.GeneratedFile, ident protogen.GoIdent, desc protoreflect.Descriptor) { if wellKnownTypes[desc.FullName()] { g.P("func (", ident, `) XXX_WellKnownType() string { return "`, desc.Name(), `" }`) g.P() } } // Names of messages and enums for which we will generate XXX_WellKnownType methods. var wellKnownTypes = map[protoreflect.FullName]bool{ "google.protobuf.NullValue": true, }