|  | // 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/editionssupport" | 
|  | "google.golang.org/protobuf/internal/encoding/tag" | 
|  | "google.golang.org/protobuf/internal/filedesc" | 
|  | "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 | pluginpb.CodeGeneratorResponse_FEATURE_SUPPORTS_EDITIONS) | 
|  |  | 
|  | var SupportedEditionsMinimum = editionssupport.Minimum | 
|  | var SupportedEditionsMaximum = editionssupport.Maximum | 
|  |  | 
|  | // GenerateVersionMarkers specifies whether to generate version markers. | 
|  | var GenerateVersionMarkers = true | 
|  |  | 
|  | // Standard library dependencies. | 
|  | const ( | 
|  | base64Package  = protogen.GoImportPath("encoding/base64") | 
|  | jsonPackage    = protogen.GoImportPath("encoding/json") | 
|  | 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) | 
|  |  | 
|  | var packageDoc protogen.Comments | 
|  | if !gen.InternalStripForEditionsDiff() { | 
|  | 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) | 
|  |  | 
|  | // The descriptor contains a lot of information about the syntax which is | 
|  | // quite different between the proto2/3 version of a file and the equivalent | 
|  | // editions version. For example, when a proto3 file is translated from | 
|  | // proto3 to editions every field in that file that is marked optional in | 
|  | // proto3 will have a features.field_presence option set. | 
|  | // Another problem is that the descriptor contains implementation details | 
|  | // that are not relevant for the semantic. For example, proto3 optional | 
|  | // fields are implemented as oneof fields with one case. The descriptor | 
|  | // contains different information about oneofs. If the file is translated | 
|  | // to editions it no longer is treated as a oneof with one case and thus | 
|  | // none of the oneof specific information is generated. | 
|  | // To be able to compare the descriptor before and after translation of the | 
|  | // associated proto file, we would need to trim many parts. This would lead | 
|  | // to a brittle implementation in case the translation ever changes. | 
|  | if !g.InternalStripForEditionsDiff() { | 
|  | 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.AnnotateSymbol(e.GoIdent.GoName, protogen.Annotation{Location: 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.AnnotateSymbol(value.GoIdent.GoName, protogen.Annotation{Location: 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. | 
|  | needsUnmarshalJSONMethod := false | 
|  | if fde, ok := e.Desc.(*filedesc.Enum); ok { | 
|  | needsUnmarshalJSONMethod = fde.L1.EditionFeatures.GenerateLegacyUnmarshalJSON | 
|  | } | 
|  | if e.genJSONMethod && needsUnmarshalJSONMethod { | 
|  | 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.AnnotateSymbol(m.GoIdent.GoName, protogen.Annotation{Location: 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.AnnotateSymbol(m.GoIdent.GoName+"."+oneof.GoName, protogen.Annotation{Location: 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.AnnotateSymbol(m.GoIdent.GoName+"."+name, protogen.Annotation{Location: 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("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() | 
|  |  | 
|  | // 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.AnnotateSymbol(m.GoIdent.GoName+".Get"+oneof.GoName, protogen.Annotation{Location: 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.AnnotateSymbol(m.GoIdent.GoName+".Get"+field.GoName, protogen.Annotation{Location: 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.AnnotateSymbol(field.GoIdent.GoName, protogen.Annotation{Location: field.Location}) | 
|  | g.AnnotateSymbol(field.GoIdent.GoName+"."+field.GoName, protogen.Annotation{Location: 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 | 
|  | } |