1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000 |
- // 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.
- // Package internal_gengo is internal to the protobuf module.
- package internal_gengo
- import (
- "fmt"
- "go/ast"
- "go/parser"
- "go/token"
- "math"
- "strconv"
- "strings"
- "unicode"
- "unicode/utf8"
- "google.golang.org/protobuf/compiler/protogen"
- "google.golang.org/protobuf/internal/encoding/messageset"
- "google.golang.org/protobuf/internal/encoding/tag"
- "google.golang.org/protobuf/internal/fieldnum"
- "google.golang.org/protobuf/reflect/protoreflect"
- "google.golang.org/protobuf/runtime/protoimpl"
- "google.golang.org/protobuf/types/descriptorpb"
- )
- // GenerateVersionMarkers specifies whether to generate version markers.
- var GenerateVersionMarkers = true
- const (
- // generateEnumJSONMethods specifies whether to generate the UnmarshalJSON
- // method for proto2 enums.
- generateEnumJSONMethods = true
- // generateRawDescMethods specifies whether to generate EnumDescriptor and
- // Descriptor methods for enums and messages. These methods return the
- // GZIP'd contents of the raw file descriptor and the path from the root
- // to the given enum or message descriptor.
- generateRawDescMethods = true
- // generateExtensionRangeMethods specifies whether to generate the
- // ExtensionRangeArray method for messages that support extensions.
- generateExtensionRangeMethods = true
- // generateMessageStateFields specifies whether to generate an unexported
- // protoimpl.MessageState as the first field.
- generateMessageStateFields = true
- // generateExportedSizeCacheFields specifies whether to generate an exported
- // XXX_sizecache field instead of an unexported sizeCache field.
- generateExportedSizeCacheFields = false
- // generateExportedUnknownFields specifies whether to generate an exported
- // XXX_unrecognized field instead of an unexported unknownFields field.
- generateExportedUnknownFields = false
- // generateExportedExtensionFields specifies whether to generate an exported
- // XXX_InternalExtensions field instead of an unexported extensionFields field.
- generateExportedExtensionFields = false
- )
- // Standard library dependencies.
- const (
- mathPackage = protogen.GoImportPath("math")
- reflectPackage = protogen.GoImportPath("reflect")
- syncPackage = protogen.GoImportPath("sync")
- )
- // Protobuf library dependencies.
- //
- // These are declared as an interface type so that they can be more easily
- // patched to support unique build environments that impose restrictions
- // on the dependencies of generated source code.
- var (
- protoifacePackage goImportPath = protogen.GoImportPath("google.golang.org/protobuf/runtime/protoiface")
- protoimplPackage goImportPath = protogen.GoImportPath("google.golang.org/protobuf/runtime/protoimpl")
- protoreflectPackage goImportPath = protogen.GoImportPath("google.golang.org/protobuf/reflect/protoreflect")
- )
- type goImportPath interface {
- String() string
- Ident(string) protogen.GoIdent
- }
- type fileInfo struct {
- *protogen.File
- allEnums []*protogen.Enum
- allMessages []*protogen.Message
- allExtensions []*protogen.Extension
- allEnumsByPtr map[*protogen.Enum]int // value is index into allEnums
- allMessagesByPtr map[*protogen.Message]int // value is index into allMessages
- allMessageFieldsByPtr map[*protogen.Message]*structFields
- }
- type structFields struct {
- count int
- unexported map[int]string
- }
- func (sf *structFields) append(name string) {
- if r, _ := utf8.DecodeRuneInString(name); !unicode.IsUpper(r) {
- if sf.unexported == nil {
- sf.unexported = make(map[int]string)
- }
- sf.unexported[sf.count] = name
- }
- sf.count++
- }
- // GenerateFile generates the contents of a .pb.go file.
- func GenerateFile(gen *protogen.Plugin, file *protogen.File) *protogen.GeneratedFile {
- filename := file.GeneratedFilenamePrefix + ".pb.go"
- g := gen.NewGeneratedFile(filename, file.GoImportPath)
- f := &fileInfo{
- File: file,
- }
- // Collect all enums, messages, and extensions in "flattened ordering".
- // See filetype.TypeBuilder.
- f.allEnums = append(f.allEnums, f.Enums...)
- f.allMessages = append(f.allMessages, f.Messages...)
- f.allExtensions = append(f.allExtensions, f.Extensions...)
- walkMessages(f.Messages, func(m *protogen.Message) {
- f.allEnums = append(f.allEnums, m.Enums...)
- f.allMessages = append(f.allMessages, m.Messages...)
- f.allExtensions = append(f.allExtensions, m.Extensions...)
- })
- // Derive a reverse mapping of enum and message pointers to their index
- // in allEnums and allMessages.
- if len(f.allEnums) > 0 {
- f.allEnumsByPtr = make(map[*protogen.Enum]int)
- for i, e := range f.allEnums {
- f.allEnumsByPtr[e] = i
- }
- }
- if len(f.allMessages) > 0 {
- f.allMessagesByPtr = make(map[*protogen.Message]int)
- f.allMessageFieldsByPtr = make(map[*protogen.Message]*structFields)
- for i, m := range f.allMessages {
- f.allMessagesByPtr[m] = i
- f.allMessageFieldsByPtr[m] = new(structFields)
- }
- }
- genStandaloneComments(g, f, fieldnum.FileDescriptorProto_Syntax)
- genGeneratedHeader(gen, g, f)
- genStandaloneComments(g, f, fieldnum.FileDescriptorProto_Package)
- g.P("package ", f.GoPackageName)
- g.P()
- // Emit a static check that enforces a minimum version of the proto package.
- if GenerateVersionMarkers {
- g.P("const (")
- g.P("// Verify that this generated code is sufficiently up-to-date.")
- g.P("_ = ", protoimplPackage.Ident("EnforceVersion"), "(", protoimpl.GenVersion, " - ", protoimplPackage.Ident("MinVersion"), ")")
- g.P("// Verify that runtime/protoimpl is sufficiently up-to-date.")
- g.P("_ = ", protoimplPackage.Ident("EnforceVersion"), "(", protoimplPackage.Ident("MaxVersion"), " - ", protoimpl.GenVersion, ")")
- g.P(")")
- 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)
- }
- genExtensions(gen, g, f)
- genReflectFileDescriptor(gen, g, f)
- return g
- }
- // 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)
- }
- }
- // genStandaloneComments prints all leading comments for a FileDescriptorProto
- // location identified by the field number n.
- func genStandaloneComments(g *protogen.GeneratedFile, f *fileInfo, n int32) {
- for _, loc := range f.Proto.GetSourceCodeInfo().GetLocation() {
- if len(loc.Path) == 1 && loc.Path[0] == n {
- for _, s := range loc.GetLeadingDetachedComments() {
- g.P(protogen.Comments(s))
- g.P()
- }
- if s := loc.GetLeadingComments(); s != "" {
- g.P(protogen.Comments(s))
- g.P()
- }
- }
- }
- }
- func genGeneratedHeader(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo) {
- g.P("// Code generated by protoc-gen-go. DO NOT EDIT.")
- if GenerateVersionMarkers {
- g.P("// versions:")
- protocGenGoVersion := protoimpl.VersionString()
- protocVersion := "(unknown)"
- if v := gen.Request.GetCompilerVersion(); v != nil {
- protocVersion = fmt.Sprintf("v%v.%v.%v", v.GetMajor(), v.GetMinor(), v.GetPatch())
- }
- g.P("// \tprotoc-gen-go ", protocGenGoVersion)
- g.P("// \tprotoc ", protocVersion)
- }
- if f.Proto.GetOptions().GetDeprecated() {
- g.P("// ", f.Desc.Path(), " is a deprecated file.")
- } else {
- g.P("// source: ", f.Desc.Path())
- }
- g.P()
- }
- func genImport(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo, imp protoreflect.FileImport) {
- impFile, ok := gen.FilesByPath[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 non-weak 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.
- if !imp.IsWeak {
- g.Import(impFile.GoImportPath)
- }
- if !imp.IsPublic {
- return
- }
- // Generate public imports by generating the imported file, parsing it,
- // and extracting every symbol that should receive a forwarding declaration.
- impGen := GenerateFile(gen, impFile)
- impGen.Skip()
- b, err := impGen.Content()
- if err != nil {
- gen.Error(err)
- return
- }
- fset := token.NewFileSet()
- astFile, err := parser.ParseFile(fset, "", b, parser.ParseComments)
- if err != nil {
- gen.Error(err)
- return
- }
- genForward := func(tok token.Token, name string, expr ast.Expr) {
- // Don't import unexported symbols.
- r, _ := utf8.DecodeRuneInString(name)
- if !unicode.IsUpper(r) {
- return
- }
- // Don't import the FileDescriptor.
- if name == impFile.GoDescriptorIdent.GoName {
- return
- }
- // Don't import decls referencing a symbol defined in another package.
- // i.e., don't import decls which are themselves public imports:
- //
- // type T = somepackage.T
- if _, ok := expr.(*ast.SelectorExpr); ok {
- return
- }
- g.P(tok, " ", name, " = ", impFile.GoImportPath.Ident(name))
- }
- g.P("// Symbols defined in public import of ", imp.Path(), ".")
- g.P()
- for _, decl := range astFile.Decls {
- switch decl := decl.(type) {
- case *ast.GenDecl:
- for _, spec := range decl.Specs {
- switch spec := spec.(type) {
- case *ast.TypeSpec:
- genForward(decl.Tok, spec.Name.Name, spec.Type)
- case *ast.ValueSpec:
- for i, name := range spec.Names {
- var expr ast.Expr
- if i < len(spec.Values) {
- expr = spec.Values[i]
- }
- genForward(decl.Tok, name.Name, expr)
- }
- case *ast.ImportSpec:
- default:
- panic(fmt.Sprintf("can't generate forward for spec type %T", spec))
- }
- }
- }
- }
- g.P()
- }
- func genEnum(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo, enum *protogen.Enum) {
- // Enum type declaration.
- g.Annotate(enum.GoIdent.GoName, enum.Location)
- leadingComments := appendDeprecationSuffix(enum.Comments.Leading,
- enum.Desc.Options().(*descriptorpb.EnumOptions).GetDeprecated())
- g.P(leadingComments,
- "type ", enum.GoIdent, " int32")
- // Enum value constants.
- g.P("const (")
- for _, value := range enum.Values {
- g.Annotate(value.GoIdent.GoName, value.Location)
- leadingComments := appendDeprecationSuffix(value.Comments.Leading,
- value.Desc.Options().(*descriptorpb.EnumValueOptions).GetDeprecated())
- g.P(leadingComments,
- value.GoIdent, " ", enum.GoIdent, " = ", value.Desc.Number(),
- trailingComment(value.Comments.Trailing))
- }
- g.P(")")
- g.P()
- // Enum value maps.
- g.P("// Enum value maps for ", enum.GoIdent, ".")
- g.P("var (")
- g.P(enum.GoIdent.GoName+"_name", " = map[int32]string{")
- for _, value := range enum.Values {
- duplicate := ""
- if value.Desc != enum.Desc.Values().ByNumber(value.Desc.Number()) {
- duplicate = "// Duplicate value: "
- }
- g.P(duplicate, value.Desc.Number(), ": ", strconv.Quote(string(value.Desc.Name())), ",")
- }
- g.P("}")
- g.P(enum.GoIdent.GoName+"_value", " = map[string]int32{")
- for _, value := range enum.Values {
- g.P(strconv.Quote(string(value.Desc.Name())), ": ", value.Desc.Number(), ",")
- }
- g.P("}")
- g.P(")")
- g.P()
- // Enum method.
- //
- // NOTE: A pointer value is needed to represent presence in proto2.
- // Since a proto2 message can reference a proto3 enum, it is useful to
- // always generate this method (even on proto3 enums) to support that case.
- 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()
- // String method.
- g.P("func (x ", enum.GoIdent, ") String() string {")
- g.P("return ", protoimplPackage.Ident("X"), ".EnumStringOf(x.Descriptor(), ", protoreflectPackage.Ident("EnumNumber"), "(x))")
- g.P("}")
- g.P()
- genEnumReflectMethods(gen, g, f, enum)
- // UnmarshalJSON method.
- if generateEnumJSONMethods && enum.Desc.Syntax() == protoreflect.Proto2 {
- g.P("// Deprecated: Do not use.")
- g.P("func (x *", enum.GoIdent, ") UnmarshalJSON(b []byte) error {")
- g.P("num, err := ", protoimplPackage.Ident("X"), ".UnmarshalJSONEnum(x.Descriptor(), b)")
- g.P("if err != nil {")
- g.P("return err")
- g.P("}")
- g.P("*x = ", enum.GoIdent, "(num)")
- g.P("return nil")
- g.P("}")
- g.P()
- }
- // EnumDescriptor method.
- if generateRawDescMethods {
- var indexes []string
- for i := 1; i < len(enum.Location.Path); i += 2 {
- indexes = append(indexes, strconv.Itoa(int(enum.Location.Path[i])))
- }
- g.P("// Deprecated: Use ", enum.GoIdent, ".Descriptor instead.")
- g.P("func (", enum.GoIdent, ") EnumDescriptor() ([]byte, []int) {")
- g.P("return ", rawDescVarName(f), "GZIP(), []int{", strings.Join(indexes, ","), "}")
- g.P("}")
- g.P()
- }
- }
- type messageInfo struct {
- *protogen.Message
- messageFlags
- }
- func genMessage(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo, message *protogen.Message) {
- if message.Desc.IsMapEntry() {
- return
- }
- m := &messageInfo{message, loadMessageFlags(message)}
- // Message type declaration.
- g.Annotate(m.GoIdent.GoName, m.Location)
- leadingComments := appendDeprecationSuffix(m.Comments.Leading,
- m.Desc.Options().(*descriptorpb.MessageOptions).GetDeprecated())
- g.P(leadingComments,
- "type ", m.GoIdent, " struct {")
- genMessageFields(g, f, m)
- g.P("}")
- g.P()
- genMessageDefaultDecls(g, f, m)
- genMessageMethods(gen, g, f, m)
- genMessageOneofWrapperTypes(gen, g, f, m)
- }
- func genMessageFields(g *protogen.GeneratedFile, f *fileInfo, m *messageInfo) {
- sf := f.allMessageFieldsByPtr[m.Message]
- genMessageInternalFields(g, f, m, sf)
- for _, field := range m.Fields {
- genMessageField(g, f, m, field, sf)
- }
- }
- func genMessageInternalFields(g *protogen.GeneratedFile, f *fileInfo, m *messageInfo, sf *structFields) {
- if generateMessageStateFields {
- g.P("state ", protoimplPackage.Ident("MessageState"))
- sf.append("state")
- }
- if generateExportedSizeCacheFields {
- g.P("XXX_sizecache", " ", protoimplPackage.Ident("SizeCache"), jsonIgnoreTags)
- sf.append("XXX_sizecache")
- } else {
- g.P("sizeCache", " ", protoimplPackage.Ident("SizeCache"))
- sf.append("sizeCache")
- }
- if m.HasWeak {
- g.P("XXX_weak", " ", protoimplPackage.Ident("WeakFields"), jsonIgnoreTags)
- sf.append("XXX_weak")
- }
- if generateExportedUnknownFields {
- g.P("XXX_unrecognized", " ", protoimplPackage.Ident("UnknownFields"), jsonIgnoreTags)
- sf.append("XXX_unrecognized")
- } else {
- g.P("unknownFields", " ", protoimplPackage.Ident("UnknownFields"))
- sf.append("unknownFields")
- }
- if m.Desc.ExtensionRanges().Len() > 0 {
- if generateExportedExtensionFields {
- g.P("XXX_InternalExtensions", " ", protoimplPackage.Ident("ExtensionFields"), jsonIgnoreTags)
- sf.append("XXX_InternalExtensions")
- } else {
- g.P("extensionFields", " ", protoimplPackage.Ident("ExtensionFields"))
- sf.append("extensionFields")
- }
- }
- if sf.count > 0 {
- g.P()
- }
- }
- func genMessageField(g *protogen.GeneratedFile, f *fileInfo, m *messageInfo, field *protogen.Field, sf *structFields) {
- if oneof := field.Oneof; oneof != 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 oneof.Fields[0] != field {
- return // only generate for first appearance
- }
- tags := structTags{
- {"protobuf_oneof", string(oneof.Desc.Name())},
- }
- if m.IsTracked {
- tags = append(tags, gotrackTags...)
- }
- g.Annotate(m.GoIdent.GoName+"."+oneof.GoName, oneof.Location)
- leadingComments := oneof.Comments.Leading
- if leadingComments != "" {
- leadingComments += "\n"
- }
- ss := []string{fmt.Sprintf(" Types that are assignable to %s:\n", oneof.GoName)}
- for _, field := range oneof.Fields {
- ss = append(ss, "\t*"+field.GoIdent.GoName+"\n")
- }
- leadingComments += protogen.Comments(strings.Join(ss, ""))
- g.P(leadingComments,
- oneof.GoName, " ", oneofInterfaceName(oneof), tags)
- sf.append(oneof.GoName)
- return
- }
- goType, pointer := fieldGoType(g, f, field)
- if pointer {
- goType = "*" + goType
- }
- tags := structTags{
- {"protobuf", fieldProtobufTagValue(field)},
- {"json", fieldJSONTagValue(field)},
- }
- if field.Desc.IsMap() {
- key := field.Message.Fields[0]
- val := field.Message.Fields[1]
- tags = append(tags, structTags{
- {"protobuf_key", fieldProtobufTagValue(key)},
- {"protobuf_val", fieldProtobufTagValue(val)},
- }...)
- }
- if m.IsTracked {
- tags = append(tags, gotrackTags...)
- }
- name := field.GoName
- if field.Desc.IsWeak() {
- name = "XXX_weak_" + name
- }
- g.Annotate(m.GoIdent.GoName+"."+name, field.Location)
- leadingComments := appendDeprecationSuffix(field.Comments.Leading,
- field.Desc.Options().(*descriptorpb.FieldOptions).GetDeprecated())
- g.P(leadingComments,
- name, " ", goType, tags,
- trailingComment(field.Comments.Trailing))
- sf.append(field.GoName)
- }
- // genMessageDefaultDecls generates consts and vars holding the default
- // values of fields.
- func genMessageDefaultDecls(g *protogen.GeneratedFile, f *fileInfo, m *messageInfo) {
- var consts, vars []string
- for _, field := range m.Fields {
- if !field.Desc.HasDefault() {
- continue
- }
- name := "Default_" + m.GoIdent.GoName + "_" + field.GoName
- goType, _ := fieldGoType(g, f, field)
- defVal := field.Desc.Default()
- switch field.Desc.Kind() {
- case protoreflect.StringKind:
- consts = append(consts, fmt.Sprintf("%s = %s(%q)", name, goType, defVal.String()))
- case protoreflect.BytesKind:
- vars = append(vars, fmt.Sprintf("%s = %s(%q)", name, goType, defVal.Bytes()))
- case protoreflect.EnumKind:
- idx := field.Desc.DefaultEnumValue().Index()
- val := field.Enum.Values[idx]
- consts = append(consts, fmt.Sprintf("%s = %s", name, g.QualifiedGoIdent(val.GoIdent)))
- case protoreflect.FloatKind, protoreflect.DoubleKind:
- if f := defVal.Float(); math.IsNaN(f) || math.IsInf(f, 0) {
- var fn, arg string
- switch f := defVal.Float(); {
- case math.IsInf(f, -1):
- fn, arg = g.QualifiedGoIdent(mathPackage.Ident("Inf")), "-1"
- case math.IsInf(f, +1):
- fn, arg = g.QualifiedGoIdent(mathPackage.Ident("Inf")), "+1"
- case math.IsNaN(f):
- fn, arg = g.QualifiedGoIdent(mathPackage.Ident("NaN")), ""
- }
- vars = append(vars, fmt.Sprintf("%s = %s(%s(%s))", name, goType, fn, arg))
- } else {
- consts = append(consts, fmt.Sprintf("%s = %s(%v)", name, goType, f))
- }
- default:
- consts = append(consts, fmt.Sprintf("%s = %s(%v)", name, goType, defVal.Interface()))
- }
- }
- if len(consts) > 0 {
- g.P("// Default values for ", m.GoIdent, " fields.")
- g.P("const (")
- for _, s := range consts {
- g.P(s)
- }
- g.P(")")
- }
- if len(vars) > 0 {
- g.P("// Default values for ", m.GoIdent, " fields.")
- g.P("var (")
- for _, s := range vars {
- g.P(s)
- }
- g.P(")")
- }
- g.P()
- }
- func genMessageMethods(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo, m *messageInfo) {
- genMessageBaseMethods(gen, g, f, m)
- genMessageGetterMethods(gen, g, f, m)
- genMessageSetterMethods(gen, g, f, m)
- }
- func genMessageBaseMethods(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo, m *messageInfo) {
- // Reset method.
- g.P("func (x *", m.GoIdent, ") Reset() {")
- g.P("*x = ", m.GoIdent, "{}")
- g.P("}")
- g.P()
- // String method.
- g.P("func (x *", m.GoIdent, ") String() string {")
- g.P("return ", protoimplPackage.Ident("X"), ".MessageStringOf(x)")
- g.P("}")
- g.P()
- // ProtoMessage method.
- g.P("func (*", m.GoIdent, ") ProtoMessage() {}")
- g.P()
- // ProtoReflect method.
- genMessageReflectMethods(gen, g, f, m)
- // Descriptor method.
- if generateRawDescMethods {
- var indexes []string
- for i := 1; i < len(m.Location.Path); i += 2 {
- indexes = append(indexes, strconv.Itoa(int(m.Location.Path[i])))
- }
- g.P("// Deprecated: Use ", m.GoIdent, ".ProtoReflect.Descriptor instead.")
- g.P("func (*", m.GoIdent, ") Descriptor() ([]byte, []int) {")
- g.P("return ", rawDescVarName(f), "GZIP(), []int{", strings.Join(indexes, ","), "}")
- g.P("}")
- g.P()
- }
- // ExtensionRangeArray method.
- if generateExtensionRangeMethods {
- if extranges := m.Desc.ExtensionRanges(); extranges.Len() > 0 {
- protoExtRange := protoifacePackage.Ident("ExtensionRangeV1")
- extRangeVar := "extRange_" + m.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("// Deprecated: Use ", m.GoIdent, ".ProtoReflect.Descriptor.ExtensionRanges instead.")
- g.P("func (*", m.GoIdent, ") ExtensionRangeArray() []", protoExtRange, " {")
- g.P("return ", extRangeVar)
- g.P("}")
- g.P()
- }
- }
- }
- func genMessageGetterMethods(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo, m *messageInfo) {
- for _, field := range m.Fields {
- genNoInterfacePragma(g, m.IsTracked)
- // Getter for parent oneof.
- if oneof := field.Oneof; oneof != nil && oneof.Fields[0] == field {
- g.Annotate(m.GoIdent.GoName+".Get"+oneof.GoName, oneof.Location)
- g.P("func (m *", m.GoIdent.GoName, ") Get", oneof.GoName, "() ", oneofInterfaceName(oneof), " {")
- g.P("if m != nil {")
- g.P("return m.", oneof.GoName)
- g.P("}")
- g.P("return nil")
- g.P("}")
- g.P()
- }
- // Getter for message field.
- goType, pointer := fieldGoType(g, f, field)
- defaultValue := fieldDefaultValue(g, m, field)
- g.Annotate(m.GoIdent.GoName+".Get"+field.GoName, field.Location)
- leadingComments := appendDeprecationSuffix("",
- field.Desc.Options().(*descriptorpb.FieldOptions).GetDeprecated())
- switch {
- case field.Desc.IsWeak():
- g.P(leadingComments, "func (x *", m.GoIdent, ") Get", field.GoName, "() ", protoifacePackage.Ident("MessageV1"), "{")
- g.P("if x != nil {")
- g.P("v := x.XXX_weak[", field.Desc.Number(), "]")
- g.P("_ = x.XXX_weak_" + field.GoName) // for field-tracking
- g.P("if v != nil {")
- g.P("return v")
- g.P("}")
- g.P("}")
- g.P("return ", protoimplPackage.Ident("X"), ".WeakNil(", strconv.Quote(string(field.Message.Desc.FullName())), ")")
- g.P("}")
- case field.Oneof != nil:
- g.P(leadingComments, "func (x *", m.GoIdent, ") Get", field.GoName, "() ", goType, " {")
- g.P("if x, ok := x.Get", field.Oneof.GoName, "().(*", field.GoIdent, "); ok {")
- g.P("return x.", field.GoName)
- g.P("}")
- g.P("return ", defaultValue)
- g.P("}")
- default:
- g.P(leadingComments, "func (x *", m.GoIdent, ") Get", field.GoName, "() ", goType, " {")
- if field.Desc.Syntax() == protoreflect.Proto3 || defaultValue == "nil" {
- g.P("if x != nil {")
- } else {
- g.P("if x != nil && x.", field.GoName, " != nil {")
- }
- star := ""
- if pointer {
- star = "*"
- }
- g.P("return ", star, " x.", field.GoName)
- g.P("}")
- g.P("return ", defaultValue)
- g.P("}")
- }
- g.P()
- }
- }
- func genMessageSetterMethods(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo, m *messageInfo) {
- for _, field := range m.Fields {
- if !field.Desc.IsWeak() {
- continue
- }
- genNoInterfacePragma(g, m.IsTracked)
- g.Annotate(m.GoIdent.GoName+".Set"+field.GoName, field.Location)
- leadingComments := appendDeprecationSuffix("",
- field.Desc.Options().(*descriptorpb.FieldOptions).GetDeprecated())
- g.P(leadingComments, "func (x *", m.GoIdent, ") Set", field.GoName, "(v ", protoifacePackage.Ident("MessageV1"), ") {")
- g.P("if x.XXX_weak == nil {")
- g.P("x.XXX_weak = make(", protoimplPackage.Ident("WeakFields"), ")")
- g.P("}")
- g.P("if v == nil {")
- g.P("delete(x.XXX_weak, ", field.Desc.Number(), ")")
- g.P("} else {")
- g.P("x.XXX_weak[", field.Desc.Number(), "] = v")
- g.P("x.XXX_weak_"+field.GoName, " = struct{}{}") // for field-tracking
- g.P("}")
- g.P("}")
- g.P()
- }
- }
- // 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, f *fileInfo, field *protogen.Field) (goType string, pointer bool) {
- if field.Desc.IsWeak() {
- return "struct{}", false
- }
- pointer = true
- switch field.Desc.Kind() {
- case protoreflect.BoolKind:
- goType = "bool"
- case protoreflect.EnumKind:
- goType = g.QualifiedGoIdent(field.Enum.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:
- goType = "*" + g.QualifiedGoIdent(field.Message.GoIdent)
- pointer = false
- }
- switch {
- case field.Desc.IsList():
- goType = "[]" + goType
- pointer = false
- case field.Desc.IsMap():
- keyType, _ := fieldGoType(g, f, field.Message.Fields[0])
- valType, _ := fieldGoType(g, f, field.Message.Fields[1])
- return fmt.Sprintf("map[%v]%v", keyType, valType), false
- }
- // Extension fields always have pointer type, even when defined in a proto3 file.
- if field.Desc.Syntax() == protoreflect.Proto3 && !field.Desc.IsExtension() {
- pointer = false
- }
- return goType, pointer
- }
- func fieldProtobufTagValue(field *protogen.Field) string {
- var enumName string
- if field.Desc.Kind() == protoreflect.EnumKind {
- enumName = protoimpl.X.LegacyEnumName(field.Enum.Desc)
- }
- return tag.Marshal(field.Desc, enumName)
- }
- func fieldDefaultValue(g *protogen.GeneratedFile, m *messageInfo, field *protogen.Field) string {
- if field.Desc.IsList() {
- return "nil"
- }
- if field.Desc.HasDefault() {
- defVarName := "Default_" + m.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.Enum.Values[0].GoIdent)
- default:
- return "0"
- }
- }
- func fieldJSONTagValue(field *protogen.Field) string {
- return string(field.Desc.Name()) + ",omitempty"
- }
- func genExtensions(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo) {
- if len(f.allExtensions) == 0 {
- return
- }
- g.P("var ", extensionTypesVarName(f), " = []", protoimplPackage.Ident("ExtensionInfo"), "{")
- for _, extension := range f.allExtensions {
- // For MessageSet extensions, the name used is the parent message.
- name := extension.Desc.FullName()
- if messageset.IsMessageSetExtension(extension.Desc) {
- name = name.Parent()
- }
- g.P("{")
- g.P("ExtendedType: (*", extension.Extendee.GoIdent, ")(nil),")
- goType, pointer := fieldGoType(g, f, extension)
- if pointer {
- goType = "*" + goType
- }
- g.P("ExtensionType: (", goType, ")(nil),")
- g.P("Field: ", extension.Desc.Number(), ",")
- g.P("Name: ", strconv.Quote(string(name)), ",")
- g.P("Tag: ", strconv.Quote(fieldProtobufTagValue(extension)), ",")
- g.P("Filename: ", strconv.Quote(f.Desc.Path()), ",")
- g.P("},")
- }
- g.P("}")
- g.P()
- // Group extensions by the target message.
- var orderedTargets []protogen.GoIdent
- allExtensionsByTarget := make(map[protogen.GoIdent][]*protogen.Extension)
- allExtensionsByPtr := make(map[*protogen.Extension]int)
- for i, extension := range f.allExtensions {
- target := extension.Extendee.GoIdent
- if len(allExtensionsByTarget[target]) == 0 {
- orderedTargets = append(orderedTargets, target)
- }
- allExtensionsByTarget[target] = append(allExtensionsByTarget[target], extension)
- allExtensionsByPtr[extension] = i
- }
- for _, target := range orderedTargets {
- g.P("// Extension fields to ", target, ".")
- g.P("var (")
- for _, extension := range allExtensionsByTarget[target] {
- xd := extension.Desc
- typeName := xd.Kind().String()
- switch xd.Kind() {
- case protoreflect.EnumKind:
- typeName = string(xd.Enum().FullName())
- case protoreflect.MessageKind, protoreflect.GroupKind:
- typeName = string(xd.Message().FullName())
- }
- fieldName := string(xd.Name())
- leadingComments := extension.Comments.Leading
- if leadingComments != "" {
- leadingComments += "\n"
- }
- leadingComments += protogen.Comments(fmt.Sprintf(" %v %v %v = %v;\n",
- xd.Cardinality(), typeName, fieldName, xd.Number()))
- leadingComments = appendDeprecationSuffix(leadingComments,
- extension.Desc.Options().(*descriptorpb.FieldOptions).GetDeprecated())
- g.P(leadingComments,
- "E_", extension.GoIdent, " = &", extensionTypesVarName(f), "[", allExtensionsByPtr[extension], "]",
- trailingComment(extension.Comments.Trailing))
- }
- g.P(")")
- g.P()
- }
- }
- // genMessageOneofWrapperTypes generates the oneof wrapper types and
- // associates the types with the parent message type.
- func genMessageOneofWrapperTypes(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo, m *messageInfo) {
- for _, oneof := range m.Oneofs {
- ifName := oneofInterfaceName(oneof)
- g.P("type ", ifName, " interface {")
- g.P(ifName, "()")
- g.P("}")
- g.P()
- for _, field := range oneof.Fields {
- g.Annotate(field.GoIdent.GoName, field.Location)
- g.Annotate(field.GoIdent.GoName+"."+field.GoName, field.Location)
- g.P("type ", field.GoIdent, " struct {")
- goType, _ := fieldGoType(g, f, field)
- tags := structTags{
- {"protobuf", fieldProtobufTagValue(field)},
- }
- if m.IsTracked {
- tags = append(tags, gotrackTags...)
- }
- leadingComments := appendDeprecationSuffix(field.Comments.Leading,
- field.Desc.Options().(*descriptorpb.FieldOptions).GetDeprecated())
- g.P(leadingComments,
- field.GoName, " ", goType, tags,
- trailingComment(field.Comments.Trailing))
- g.P("}")
- g.P()
- }
- for _, field := range oneof.Fields {
- g.P("func (*", field.GoIdent, ") ", ifName, "() {}")
- g.P()
- }
- }
- }
- // oneofInterfaceName returns the name of the interface type implemented by
- // the oneof field value types.
- func oneofInterfaceName(oneof *protogen.Oneof) string {
- return "is" + oneof.GoIdent.GoName
- }
- // genNoInterfacePragma generates a standalone "nointerface" pragma to
- // decorate methods with field-tracking support.
- func genNoInterfacePragma(g *protogen.GeneratedFile, tracked bool) {
- if tracked {
- g.P("//go:nointerface")
- g.P()
- }
- }
- var (
- gotrackTags = structTags{{"go", "track"}}
- jsonIgnoreTags = structTags{{"json", "-"}}
- )
- // structTags is a data structure for build idiomatic Go struct tags.
- // Each [2]string is a key-value pair, where value is the unescaped string.
- //
- // Example: structTags{{"key", "value"}}.String() -> `key:"value"`
- type structTags [][2]string
- func (tags structTags) String() string {
- if len(tags) == 0 {
- return ""
- }
- var ss []string
- for _, tag := range tags {
- // NOTE: When quoting the value, we need to make sure the backtick
- // character does not appear. Convert all cases to the escaped hex form.
- key := tag[0]
- val := strings.Replace(strconv.Quote(tag[1]), "`", `\x60`, -1)
- ss = append(ss, fmt.Sprintf("%s:%s", key, val))
- }
- return "`" + strings.Join(ss, " ") + "`"
- }
- // appendDeprecationSuffix optionally appends a deprecation notice as a suffix.
- func appendDeprecationSuffix(prefix protogen.Comments, deprecated bool) protogen.Comments {
- if !deprecated {
- return prefix
- }
- if prefix != "" {
- prefix += "\n"
- }
- return prefix + " Deprecated: Do not use.\n"
- }
- // trailingComment is like protogen.Comments, but lacks a trailing newline.
- type trailingComment protogen.Comments
- func (c trailingComment) String() string {
- s := strings.TrimSuffix(protogen.Comments(c).String(), "\n")
- if strings.Contains(s, "\n") {
- // We don't support multi-lined trailing comments as it is unclear
- // how to best render them in the generated code.
- return ""
- }
- return s
- }
|