123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899 |
- // 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/tag"
- "google.golang.org/protobuf/internal/genid"
- "google.golang.org/protobuf/internal/version"
- "google.golang.org/protobuf/reflect/protoreflect"
- "google.golang.org/protobuf/runtime/protoimpl"
- "google.golang.org/protobuf/types/descriptorpb"
- "google.golang.org/protobuf/types/pluginpb"
- )
- // SupportedFeatures reports the set of supported protobuf language features.
- var SupportedFeatures = uint64(pluginpb.CodeGeneratorResponse_FEATURE_PROTO3_OPTIONAL)
- // GenerateVersionMarkers specifies whether to generate version markers.
- var GenerateVersionMarkers = true
- // Standard library dependencies.
- const (
- base64Package = protogen.GoImportPath("encoding/base64")
- mathPackage = protogen.GoImportPath("math")
- reflectPackage = protogen.GoImportPath("reflect")
- sortPackage = protogen.GoImportPath("sort")
- stringsPackage = protogen.GoImportPath("strings")
- syncPackage = protogen.GoImportPath("sync")
- timePackage = protogen.GoImportPath("time")
- utf8Package = protogen.GoImportPath("unicode/utf8")
- )
- // 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 (
- protoPackage goImportPath = protogen.GoImportPath("google.golang.org/protobuf/proto")
- protoifacePackage goImportPath = protogen.GoImportPath("google.golang.org/protobuf/runtime/protoiface")
- protoimplPackage goImportPath = protogen.GoImportPath("google.golang.org/protobuf/runtime/protoimpl")
- protojsonPackage goImportPath = protogen.GoImportPath("google.golang.org/protobuf/encoding/protojson")
- protoreflectPackage goImportPath = protogen.GoImportPath("google.golang.org/protobuf/reflect/protoreflect")
- protoregistryPackage goImportPath = protogen.GoImportPath("google.golang.org/protobuf/reflect/protoregistry")
- )
- type goImportPath interface {
- String() string
- Ident(string) protogen.GoIdent
- }
- // 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 := newFileInfo(file)
- genStandaloneComments(g, f, int32(genid.FileDescriptorProto_Syntax_field_number))
- genGeneratedHeader(gen, g, f)
- genStandaloneComments(g, f, int32(genid.FileDescriptorProto_Package_field_number))
- packageDoc := genPackageKnownComment(f)
- g.P(packageDoc, "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(g, f, enum)
- }
- for _, message := range f.allMessages {
- genMessage(g, f, message)
- }
- genExtensions(g, f)
- genReflectFileDescriptor(gen, g, f)
- return g
- }
- // genStandaloneComments prints all leading comments for a FileDescriptorProto
- // location identified by the field number n.
- func genStandaloneComments(g *protogen.GeneratedFile, f *fileInfo, n int32) {
- loc := f.Desc.SourceLocations().ByPath(protoreflect.SourcePath{n})
- for _, s := range loc.LeadingDetachedComments {
- g.P(protogen.Comments(s))
- g.P()
- }
- if s := loc.LeadingComments; 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 := version.String()
- protocVersion := "(unknown)"
- if v := gen.Request.GetCompilerVersion(); v != nil {
- protocVersion = fmt.Sprintf("v%v.%v.%v", v.GetMajor(), v.GetMinor(), v.GetPatch())
- if s := v.GetSuffix(); s != "" {
- protocVersion += "-" + s
- }
- }
- 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(g *protogen.GeneratedFile, f *fileInfo, e *enumInfo) {
- // Enum type declaration.
- g.Annotate(e.GoIdent.GoName, e.Location)
- leadingComments := appendDeprecationSuffix(e.Comments.Leading,
- e.Desc.ParentFile(),
- e.Desc.Options().(*descriptorpb.EnumOptions).GetDeprecated())
- g.P(leadingComments,
- "type ", e.GoIdent, " int32")
- // Enum value constants.
- g.P("const (")
- for _, value := range e.Values {
- g.Annotate(value.GoIdent.GoName, value.Location)
- leadingComments := appendDeprecationSuffix(value.Comments.Leading,
- value.Desc.ParentFile(),
- value.Desc.Options().(*descriptorpb.EnumValueOptions).GetDeprecated())
- g.P(leadingComments,
- value.GoIdent, " ", e.GoIdent, " = ", value.Desc.Number(),
- trailingComment(value.Comments.Trailing))
- }
- g.P(")")
- g.P()
- // Enum value maps.
- g.P("// Enum value maps for ", e.GoIdent, ".")
- g.P("var (")
- g.P(e.GoIdent.GoName+"_name", " = map[int32]string{")
- for _, value := range e.Values {
- duplicate := ""
- if value.Desc != e.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(e.GoIdent.GoName+"_value", " = map[string]int32{")
- for _, value := range e.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 ", e.GoIdent, ") Enum() *", e.GoIdent, " {")
- g.P("p := new(", e.GoIdent, ")")
- g.P("*p = x")
- g.P("return p")
- g.P("}")
- g.P()
- // String method.
- g.P("func (x ", e.GoIdent, ") String() string {")
- g.P("return ", protoimplPackage.Ident("X"), ".EnumStringOf(x.Descriptor(), ", protoreflectPackage.Ident("EnumNumber"), "(x))")
- g.P("}")
- g.P()
- genEnumReflectMethods(g, f, e)
- // UnmarshalJSON method.
- if e.genJSONMethod && e.Desc.Syntax() == protoreflect.Proto2 {
- g.P("// Deprecated: Do not use.")
- g.P("func (x *", e.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 = ", e.GoIdent, "(num)")
- g.P("return nil")
- g.P("}")
- g.P()
- }
- // EnumDescriptor method.
- if e.genRawDescMethod {
- var indexes []string
- for i := 1; i < len(e.Location.Path); i += 2 {
- indexes = append(indexes, strconv.Itoa(int(e.Location.Path[i])))
- }
- g.P("// Deprecated: Use ", e.GoIdent, ".Descriptor instead.")
- g.P("func (", e.GoIdent, ") EnumDescriptor() ([]byte, []int) {")
- g.P("return ", rawDescVarName(f), "GZIP(), []int{", strings.Join(indexes, ","), "}")
- g.P("}")
- g.P()
- f.needRawDesc = true
- }
- }
- func genMessage(g *protogen.GeneratedFile, f *fileInfo, m *messageInfo) {
- if m.Desc.IsMapEntry() {
- return
- }
- // Message type declaration.
- g.Annotate(m.GoIdent.GoName, m.Location)
- leadingComments := appendDeprecationSuffix(m.Comments.Leading,
- m.Desc.ParentFile(),
- m.Desc.Options().(*descriptorpb.MessageOptions).GetDeprecated())
- g.P(leadingComments,
- "type ", m.GoIdent, " struct {")
- genMessageFields(g, f, m)
- g.P("}")
- g.P()
- genMessageKnownFunctions(g, f, m)
- genMessageDefaultDecls(g, f, m)
- genMessageMethods(g, f, m)
- genMessageOneofWrapperTypes(g, f, m)
- }
- func genMessageFields(g *protogen.GeneratedFile, f *fileInfo, m *messageInfo) {
- sf := f.allMessageFieldsByPtr[m]
- 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) {
- g.P(genid.State_goname, " ", protoimplPackage.Ident("MessageState"))
- sf.append(genid.State_goname)
- g.P(genid.SizeCache_goname, " ", protoimplPackage.Ident("SizeCache"))
- sf.append(genid.SizeCache_goname)
- if m.hasWeak {
- g.P(genid.WeakFields_goname, " ", protoimplPackage.Ident("WeakFields"))
- sf.append(genid.WeakFields_goname)
- }
- g.P(genid.UnknownFields_goname, " ", protoimplPackage.Ident("UnknownFields"))
- sf.append(genid.UnknownFields_goname)
- if m.Desc.ExtensionRanges().Len() > 0 {
- g.P(genid.ExtensionFields_goname, " ", protoimplPackage.Ident("ExtensionFields"))
- sf.append(genid.ExtensionFields_goname)
- }
- 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 && !oneof.Desc.IsSynthetic() {
- // 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 = genid.WeakFieldPrefix_goname + name
- }
- g.Annotate(m.GoIdent.GoName+"."+name, field.Location)
- leadingComments := appendDeprecationSuffix(field.Comments.Leading,
- field.Desc.ParentFile(),
- 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]
- if val.GoIdent.GoImportPath == f.GoImportPath {
- consts = append(consts, fmt.Sprintf("%s = %s", name, g.QualifiedGoIdent(val.GoIdent)))
- } else {
- // If the enum value is declared in a different Go package,
- // reference it by number since the name may not be correct.
- // See https://github.com/golang/protobuf/issues/513.
- consts = append(consts, fmt.Sprintf("%s = %s(%d) // %s",
- name, g.QualifiedGoIdent(field.Enum.GoIdent), val.Desc.Number(), 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(g *protogen.GeneratedFile, f *fileInfo, m *messageInfo) {
- genMessageBaseMethods(g, f, m)
- genMessageGetterMethods(g, f, m)
- genMessageSetterMethods(g, f, m)
- }
- func genMessageBaseMethods(g *protogen.GeneratedFile, f *fileInfo, m *messageInfo) {
- // Reset method.
- g.P("func (x *", m.GoIdent, ") Reset() {")
- g.P("*x = ", m.GoIdent, "{}")
- g.P("if ", protoimplPackage.Ident("UnsafeEnabled"), " {")
- g.P("mi := &", messageTypesVarName(f), "[", f.allMessagesByPtr[m], "]")
- g.P("ms := ", protoimplPackage.Ident("X"), ".MessageStateOf(", protoimplPackage.Ident("Pointer"), "(x))")
- g.P("ms.StoreMessageInfo(mi)")
- g.P("}")
- 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(g, f, m)
- // Descriptor method.
- if m.genRawDescMethod {
- 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()
- f.needRawDesc = true
- }
- }
- func genMessageGetterMethods(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 && !oneof.Desc.IsSynthetic() {
- 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, f, m, field)
- g.Annotate(m.GoIdent.GoName+".Get"+field.GoName, field.Location)
- leadingComments := appendDeprecationSuffix("",
- field.Desc.ParentFile(),
- field.Desc.Options().(*descriptorpb.FieldOptions).GetDeprecated())
- switch {
- case field.Desc.IsWeak():
- g.P(leadingComments, "func (x *", m.GoIdent, ") Get", field.GoName, "() ", protoPackage.Ident("Message"), "{")
- g.P("var w ", protoimplPackage.Ident("WeakFields"))
- g.P("if x != nil {")
- g.P("w = x.", genid.WeakFields_goname)
- if m.isTracked {
- g.P("_ = x.", genid.WeakFieldPrefix_goname+field.GoName)
- }
- g.P("}")
- g.P("return ", protoimplPackage.Ident("X"), ".GetWeak(w, ", field.Desc.Number(), ", ", strconv.Quote(string(field.Message.Desc.FullName())), ")")
- g.P("}")
- case field.Oneof != nil && !field.Oneof.Desc.IsSynthetic():
- 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.HasPresence() || 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(g *protogen.GeneratedFile, f *fileInfo, m *messageInfo) {
- for _, field := range m.Fields {
- if !field.Desc.IsWeak() {
- continue
- }
- genNoInterfacePragma(g, m.isTracked)
- g.AnnotateSymbol(m.GoIdent.GoName+".Set"+field.GoName, protogen.Annotation{
- Location: field.Location,
- Semantic: descriptorpb.GeneratedCodeInfo_Annotation_SET.Enum(),
- })
- leadingComments := appendDeprecationSuffix("",
- field.Desc.ParentFile(),
- field.Desc.Options().(*descriptorpb.FieldOptions).GetDeprecated())
- g.P(leadingComments, "func (x *", m.GoIdent, ") Set", field.GoName, "(v ", protoPackage.Ident("Message"), ") {")
- g.P("var w *", protoimplPackage.Ident("WeakFields"))
- g.P("if x != nil {")
- g.P("w = &x.", genid.WeakFields_goname)
- if m.isTracked {
- g.P("_ = x.", genid.WeakFieldPrefix_goname+field.GoName)
- }
- g.P("}")
- g.P(protoimplPackage.Ident("X"), ".SetWeak(w, ", field.Desc.Number(), ", ", strconv.Quote(string(field.Message.Desc.FullName())), ", v)")
- 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 = field.Desc.HasPresence()
- 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 // rely on nullability of slices for presence
- case protoreflect.MessageKind, protoreflect.GroupKind:
- goType = "*" + g.QualifiedGoIdent(field.Message.GoIdent)
- pointer = false // pointer captured as part of the type
- }
- switch {
- case field.Desc.IsList():
- return "[]" + goType, 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
- }
- 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, f *fileInfo, 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:
- val := field.Enum.Values[0]
- if val.GoIdent.GoImportPath == f.GoImportPath {
- return g.QualifiedGoIdent(val.GoIdent)
- } else {
- // If the enum value is declared in a different Go package,
- // reference it by number since the name may not be correct.
- // See https://github.com/golang/protobuf/issues/513.
- return g.QualifiedGoIdent(field.Enum.GoIdent) + "(" + strconv.FormatInt(int64(val.Desc.Number()), 10) + ")"
- }
- default:
- return "0"
- }
- }
- func fieldJSONTagValue(field *protogen.Field) string {
- return string(field.Desc.Name()) + ",omitempty"
- }
- func genExtensions(g *protogen.GeneratedFile, f *fileInfo) {
- if len(f.allExtensions) == 0 {
- return
- }
- g.P("var ", extensionTypesVarName(f), " = []", protoimplPackage.Ident("ExtensionInfo"), "{")
- for _, x := range f.allExtensions {
- g.P("{")
- g.P("ExtendedType: (*", x.Extendee.GoIdent, ")(nil),")
- goType, pointer := fieldGoType(g, f, x.Extension)
- if pointer {
- goType = "*" + goType
- }
- g.P("ExtensionType: (", goType, ")(nil),")
- g.P("Field: ", x.Desc.Number(), ",")
- g.P("Name: ", strconv.Quote(string(x.Desc.FullName())), ",")
- g.P("Tag: ", strconv.Quote(fieldProtobufTagValue(x.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][]*extensionInfo)
- allExtensionsByPtr := make(map[*extensionInfo]int)
- for i, x := range f.allExtensions {
- target := x.Extendee.GoIdent
- if len(allExtensionsByTarget[target]) == 0 {
- orderedTargets = append(orderedTargets, target)
- }
- allExtensionsByTarget[target] = append(allExtensionsByTarget[target], x)
- allExtensionsByPtr[x] = i
- }
- for _, target := range orderedTargets {
- g.P("// Extension fields to ", target, ".")
- g.P("var (")
- for _, x := range allExtensionsByTarget[target] {
- xd := x.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 := x.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,
- x.Desc.ParentFile(),
- x.Desc.Options().(*descriptorpb.FieldOptions).GetDeprecated())
- g.P(leadingComments,
- "E_", x.GoIdent, " = &", extensionTypesVarName(f), "[", allExtensionsByPtr[x], "]",
- trailingComment(x.Comments.Trailing))
- }
- g.P(")")
- g.P()
- }
- }
- // genMessageOneofWrapperTypes generates the oneof wrapper types and
- // associates the types with the parent message type.
- func genMessageOneofWrapperTypes(g *protogen.GeneratedFile, f *fileInfo, m *messageInfo) {
- for _, oneof := range m.Oneofs {
- if oneof.Desc.IsSynthetic() {
- continue
- }
- 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.ParentFile(),
- 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"}}
- // 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, parentFile protoreflect.FileDescriptor, deprecated bool) protogen.Comments {
- fileDeprecated := parentFile.Options().(*descriptorpb.FileOptions).GetDeprecated()
- if !deprecated && !fileDeprecated {
- return prefix
- }
- if prefix != "" {
- prefix += "\n"
- }
- if fileDeprecated {
- return prefix + " Deprecated: The entire proto file " + protogen.Comments(parentFile.Path()) + " is marked as deprecated.\n"
- }
- return prefix + " Deprecated: Marked as deprecated in " + protogen.Comments(parentFile.Path()) + ".\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
- }
|