github.com/whiteCcinn/protobuf-go@v1.0.9/cmd/protoc-gen-go/internal_gengo/main.go (about)

     1  // Copyright 2018 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // Package internal_gengo is internal to the protobuf module.
     6  package internal_gengo
     7  
     8  import (
     9  	"fmt"
    10  	"go/ast"
    11  	"go/parser"
    12  	"go/token"
    13  	"math"
    14  	"os"
    15  	"strconv"
    16  	"strings"
    17  	"unicode"
    18  	"unicode/utf8"
    19  
    20  	"github.com/whiteCcinn/protobuf-go/compiler/protogen"
    21  	"github.com/whiteCcinn/protobuf-go/internal/encoding/tag"
    22  	"github.com/whiteCcinn/protobuf-go/internal/genid"
    23  	"github.com/whiteCcinn/protobuf-go/internal/version"
    24  	"github.com/whiteCcinn/protobuf-go/reflect/protoreflect"
    25  	"github.com/whiteCcinn/protobuf-go/runtime/protoimpl"
    26  
    27  	"github.com/whiteCcinn/protobuf-go/types/descriptorpb"
    28  	"github.com/whiteCcinn/protobuf-go/types/pluginpb"
    29  )
    30  
    31  // SupportedFeatures reports the set of supported protobuf language features.
    32  var SupportedFeatures = uint64(pluginpb.CodeGeneratorResponse_FEATURE_PROTO3_OPTIONAL)
    33  
    34  // GenerateVersionMarkers specifies whether to generate version markers.
    35  var GenerateVersionMarkers = true
    36  
    37  // Standard library dependencies.
    38  const (
    39  	base64Package  = protogen.GoImportPath("encoding/base64")
    40  	mathPackage    = protogen.GoImportPath("math")
    41  	reflectPackage = protogen.GoImportPath("reflect")
    42  	sortPackage    = protogen.GoImportPath("sort")
    43  	stringsPackage = protogen.GoImportPath("strings")
    44  	syncPackage    = protogen.GoImportPath("sync")
    45  	timePackage    = protogen.GoImportPath("time")
    46  	utf8Package    = protogen.GoImportPath("unicode/utf8")
    47  )
    48  
    49  // Protobuf library dependencies.
    50  //
    51  // These are declared as an interface type so that they can be more easily
    52  // patched to support unique build environments that impose restrictions
    53  // on the dependencies of generated source code.
    54  var (
    55  	protoPackage         goImportPath = protogen.GoImportPath("github.com/whiteCcinn/protobuf-go/proto")
    56  	protoifacePackage    goImportPath = protogen.GoImportPath("github.com/whiteCcinn/protobuf-go/runtime/protoiface")
    57  	protoimplPackage     goImportPath = protogen.GoImportPath("github.com/whiteCcinn/protobuf-go/runtime/protoimpl")
    58  	protojsonPackage     goImportPath = protogen.GoImportPath("github.com/whiteCcinn/protobuf-go/encoding/protojson")
    59  	protoreflectPackage  goImportPath = protogen.GoImportPath("github.com/whiteCcinn/protobuf-go/reflect/protoreflect")
    60  	protoregistryPackage goImportPath = protogen.GoImportPath("github.com/whiteCcinn/protobuf-go/reflect/protoregistry")
    61  )
    62  
    63  type goImportPath interface {
    64  	String() string
    65  	Ident(string) protogen.GoIdent
    66  }
    67  
    68  // GenerateFile generates the contents of a .pb.go file.
    69  func GenerateFile(gen *protogen.Plugin, file *protogen.File) *protogen.GeneratedFile {
    70  	fmt.Fprint(os.Stderr, "1")
    71  	filename := file.GeneratedFilenamePrefix + ".pb.go"
    72  	g := gen.NewGeneratedFile(filename, file.GoImportPath)
    73  	//f := newFileInfo(file)
    74  	fmt.Fprint(os.Stderr, "2")
    75  
    76  	//genStandaloneComments(g, f, int32(genid.FileDescriptorProto_Syntax_field_number))
    77  	//genGeneratedHeader(gen, g, f)
    78  	//genStandaloneComments(g, f, int32(genid.FileDescriptorProto_Package_field_number))
    79  	//
    80  	//packageDoc := genPackageKnownComment(f)
    81  	//g.P(packageDoc, "package ", f.GoPackageName)
    82  	//g.P()
    83  	//
    84  	//// Emit a static check that enforces a minimum version of the proto package.
    85  	//if GenerateVersionMarkers {
    86  	//	g.P("const (")
    87  	//	g.P("// Verify that this generated code is sufficiently up-to-date.")
    88  	//	g.P("_ = ", protoimplPackage.Ident("EnforceVersion"), "(", protoimpl.GenVersion, " - ", protoimplPackage.Ident("MinVersion"), ")")
    89  	//	g.P("// Verify that runtime/protoimpl is sufficiently up-to-date.")
    90  	//	g.P("_ = ", protoimplPackage.Ident("EnforceVersion"), "(", protoimplPackage.Ident("MaxVersion"), " - ", protoimpl.GenVersion, ")")
    91  	//	g.P(")")
    92  	//	g.P()
    93  	//}
    94  	//
    95  	//for i, imps := 0, f.Desc.Imports(); i < imps.Len(); i++ {
    96  	//	genImport(gen, g, f, imps.Get(i))
    97  	//}
    98  	//for _, enum := range f.allEnums {
    99  	//	genEnum(g, f, enum)
   100  	//}
   101  	//for _, message := range f.allMessages {
   102  	//	genMessage(g, f, message)
   103  	//}
   104  	//c, _ := g.Content()
   105  	//fmt.Fprintln(os.Stderr, string(c))
   106  	//genExtensions(g, f)
   107  	//
   108  	//genReflectFileDescriptor(gen, g, f)
   109  
   110  	return g
   111  }
   112  
   113  // genStandaloneComments prints all leading comments for a FileDescriptorProto
   114  // location identified by the field number n.
   115  func genStandaloneComments(g *protogen.GeneratedFile, f *fileInfo, n int32) {
   116  	loc := f.Desc.SourceLocations().ByPath(protoreflect.SourcePath{n})
   117  	for _, s := range loc.LeadingDetachedComments {
   118  		g.P(protogen.Comments(s))
   119  		g.P()
   120  	}
   121  	if s := loc.LeadingComments; s != "" {
   122  		g.P(protogen.Comments(s))
   123  		g.P()
   124  	}
   125  }
   126  
   127  func genGeneratedHeader(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo) {
   128  	g.P("// Code generated by protoc-gen-go. DO NOT EDIT.")
   129  
   130  	if GenerateVersionMarkers {
   131  		g.P("// versions:")
   132  		protocGenGoVersion := version.String()
   133  		protocVersion := "(unknown)"
   134  		if v := gen.Request.GetCompilerVersion(); v != nil {
   135  			protocVersion = fmt.Sprintf("v%v.%v.%v", v.GetMajor(), v.GetMinor(), v.GetPatch())
   136  			if s := v.GetSuffix(); s != "" {
   137  				protocVersion += "-" + s
   138  			}
   139  		}
   140  		g.P("// \tprotoc-gen-go ", protocGenGoVersion)
   141  		g.P("// \tprotoc        ", protocVersion)
   142  	}
   143  
   144  	if f.Proto.GetOptions().GetDeprecated() {
   145  		g.P("// ", f.Desc.Path(), " is a deprecated file.")
   146  	} else {
   147  		g.P("// source: ", f.Desc.Path())
   148  	}
   149  	g.P()
   150  }
   151  
   152  func genImport(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo, imp protoreflect.FileImport) {
   153  	impFile, ok := gen.FilesByPath[imp.Path()]
   154  	if !ok {
   155  		return
   156  	}
   157  	if impFile.GoImportPath == f.GoImportPath {
   158  		// Don't generate imports or aliases for types in the same Go package.
   159  		return
   160  	}
   161  	// Generate imports for all non-weak dependencies, even if they are not
   162  	// referenced, because other code and tools depend on having the
   163  	// full transitive closure of protocol buffer types in the binary.
   164  	if !imp.IsWeak {
   165  		g.Import(impFile.GoImportPath)
   166  	}
   167  	if !imp.IsPublic {
   168  		return
   169  	}
   170  
   171  	// Generate public imports by generating the imported file, parsing it,
   172  	// and extracting every symbol that should receive a forwarding declaration.
   173  	impGen := GenerateFile(gen, impFile)
   174  	impGen.Skip()
   175  	b, err := impGen.Content()
   176  	if err != nil {
   177  		gen.Error(err)
   178  		return
   179  	}
   180  	fset := token.NewFileSet()
   181  	astFile, err := parser.ParseFile(fset, "", b, parser.ParseComments)
   182  	if err != nil {
   183  		gen.Error(err)
   184  		return
   185  	}
   186  	genForward := func(tok token.Token, name string, expr ast.Expr) {
   187  		// Don't import unexported symbols.
   188  		r, _ := utf8.DecodeRuneInString(name)
   189  		if !unicode.IsUpper(r) {
   190  			return
   191  		}
   192  		// Don't import the FileDescriptor.
   193  		if name == impFile.GoDescriptorIdent.GoName {
   194  			return
   195  		}
   196  		// Don't import decls referencing a symbol defined in another package.
   197  		// i.e., don't import decls which are themselves public imports:
   198  		//
   199  		//	type T = somepackage.T
   200  		if _, ok := expr.(*ast.SelectorExpr); ok {
   201  			return
   202  		}
   203  		g.P(tok, " ", name, " = ", impFile.GoImportPath.Ident(name))
   204  	}
   205  	g.P("// Symbols defined in public import of ", imp.Path(), ".")
   206  	g.P()
   207  	for _, decl := range astFile.Decls {
   208  		switch decl := decl.(type) {
   209  		case *ast.GenDecl:
   210  			for _, spec := range decl.Specs {
   211  				switch spec := spec.(type) {
   212  				case *ast.TypeSpec:
   213  					genForward(decl.Tok, spec.Name.Name, spec.Type)
   214  				case *ast.ValueSpec:
   215  					for i, name := range spec.Names {
   216  						var expr ast.Expr
   217  						if i < len(spec.Values) {
   218  							expr = spec.Values[i]
   219  						}
   220  						genForward(decl.Tok, name.Name, expr)
   221  					}
   222  				case *ast.ImportSpec:
   223  				default:
   224  					panic(fmt.Sprintf("can't generate forward for spec type %T", spec))
   225  				}
   226  			}
   227  		}
   228  	}
   229  	g.P()
   230  }
   231  
   232  func genEnum(g *protogen.GeneratedFile, f *fileInfo, e *enumInfo) {
   233  	// Enum type declaration.
   234  	g.Annotate(e.GoIdent.GoName, e.Location)
   235  	leadingComments := appendDeprecationSuffix(e.Comments.Leading,
   236  		e.Desc.Options().(*descriptorpb.EnumOptions).GetDeprecated())
   237  	g.P(leadingComments,
   238  		"type ", e.GoIdent, " int32")
   239  
   240  	// Enum value constants.
   241  	g.P("const (")
   242  	for _, value := range e.Values {
   243  		g.Annotate(value.GoIdent.GoName, value.Location)
   244  		leadingComments := appendDeprecationSuffix(value.Comments.Leading,
   245  			value.Desc.Options().(*descriptorpb.EnumValueOptions).GetDeprecated())
   246  		g.P(leadingComments,
   247  			value.GoIdent, " ", e.GoIdent, " = ", value.Desc.Number(),
   248  			trailingComment(value.Comments.Trailing))
   249  	}
   250  	g.P(")")
   251  	g.P()
   252  
   253  	// Enum value maps.
   254  	g.P("// Enum value maps for ", e.GoIdent, ".")
   255  	g.P("var (")
   256  	g.P(e.GoIdent.GoName+"_name", " = map[int32]string{")
   257  	for _, value := range e.Values {
   258  		duplicate := ""
   259  		if value.Desc != e.Desc.Values().ByNumber(value.Desc.Number()) {
   260  			duplicate = "// Duplicate value: "
   261  		}
   262  		g.P(duplicate, value.Desc.Number(), ": ", strconv.Quote(string(value.Desc.Name())), ",")
   263  	}
   264  	g.P("}")
   265  	g.P(e.GoIdent.GoName+"_value", " = map[string]int32{")
   266  	for _, value := range e.Values {
   267  		g.P(strconv.Quote(string(value.Desc.Name())), ": ", value.Desc.Number(), ",")
   268  	}
   269  	g.P("}")
   270  	g.P(")")
   271  	g.P()
   272  
   273  	// Enum method.
   274  	//
   275  	// NOTE: A pointer value is needed to represent presence in proto2.
   276  	// Since a proto2 message can reference a proto3 enum, it is useful to
   277  	// always generate this method (even on proto3 enums) to support that case.
   278  	g.P("func (x ", e.GoIdent, ") Enum() *", e.GoIdent, " {")
   279  	g.P("p := new(", e.GoIdent, ")")
   280  	g.P("*p = x")
   281  	g.P("return p")
   282  	g.P("}")
   283  	g.P()
   284  
   285  	// String method.
   286  	g.P("func (x ", e.GoIdent, ") String() string {")
   287  	g.P("return ", protoimplPackage.Ident("X"), ".EnumStringOf(x.Descriptor(), ", protoreflectPackage.Ident("EnumNumber"), "(x))")
   288  	g.P("}")
   289  	g.P()
   290  
   291  	genEnumReflectMethods(g, f, e)
   292  
   293  	// UnmarshalJSON method.
   294  	if e.genJSONMethod && e.Desc.Syntax() == protoreflect.Proto2 {
   295  		g.P("// Deprecated: Do not use.")
   296  		g.P("func (x *", e.GoIdent, ") UnmarshalJSON(b []byte) error {")
   297  		g.P("num, err := ", protoimplPackage.Ident("X"), ".UnmarshalJSONEnum(x.Descriptor(), b)")
   298  		g.P("if err != nil {")
   299  		g.P("return err")
   300  		g.P("}")
   301  		g.P("*x = ", e.GoIdent, "(num)")
   302  		g.P("return nil")
   303  		g.P("}")
   304  		g.P()
   305  	}
   306  
   307  	// EnumDescriptor method.
   308  	if e.genRawDescMethod {
   309  		var indexes []string
   310  		for i := 1; i < len(e.Location.Path); i += 2 {
   311  			indexes = append(indexes, strconv.Itoa(int(e.Location.Path[i])))
   312  		}
   313  		g.P("// Deprecated: Use ", e.GoIdent, ".Descriptor instead.")
   314  		g.P("func (", e.GoIdent, ") EnumDescriptor() ([]byte, []int) {")
   315  		g.P("return ", rawDescVarName(f), "GZIP(), []int{", strings.Join(indexes, ","), "}")
   316  		g.P("}")
   317  		g.P()
   318  		f.needRawDesc = true
   319  	}
   320  }
   321  
   322  func genMessage(g *protogen.GeneratedFile, f *fileInfo, m *messageInfo) {
   323  	if m.Desc.IsMapEntry() {
   324  		return
   325  	}
   326  
   327  	// Message type declaration.
   328  	g.Annotate(m.GoIdent.GoName, m.Location)
   329  	leadingComments := appendDeprecationSuffix(m.Comments.Leading,
   330  		m.Desc.Options().(*descriptorpb.MessageOptions).GetDeprecated())
   331  	g.P(leadingComments,
   332  		"type ", m.GoIdent, " struct {")
   333  	genMessageFields(g, f, m)
   334  	g.P("}")
   335  	g.P()
   336  
   337  	genMessageKnownFunctions(g, f, m)
   338  	genMessageDefaultDecls(g, f, m)
   339  	genMessageMethods(g, f, m)
   340  	genMessageOneofWrapperTypes(g, f, m)
   341  }
   342  
   343  func genMessageFields(g *protogen.GeneratedFile, f *fileInfo, m *messageInfo) {
   344  	sf := f.allMessageFieldsByPtr[m]
   345  	genMessageInternalFields(g, f, m, sf)
   346  	for _, field := range m.Fields {
   347  		genMessageField(g, f, m, field, sf)
   348  	}
   349  }
   350  
   351  func genMessageInternalFields(g *protogen.GeneratedFile, f *fileInfo, m *messageInfo, sf *structFields) {
   352  	g.P(genid.State_goname, " ", protoimplPackage.Ident("MessageState"))
   353  	sf.append(genid.State_goname)
   354  	g.P(genid.SizeCache_goname, " ", protoimplPackage.Ident("SizeCache"))
   355  	sf.append(genid.SizeCache_goname)
   356  	if m.hasWeak {
   357  		g.P(genid.WeakFields_goname, " ", protoimplPackage.Ident("WeakFields"))
   358  		sf.append(genid.WeakFields_goname)
   359  	}
   360  	g.P(genid.UnknownFields_goname, " ", protoimplPackage.Ident("UnknownFields"))
   361  	sf.append(genid.UnknownFields_goname)
   362  	if m.Desc.ExtensionRanges().Len() > 0 {
   363  		g.P(genid.ExtensionFields_goname, " ", protoimplPackage.Ident("ExtensionFields"))
   364  		sf.append(genid.ExtensionFields_goname)
   365  	}
   366  	if sf.count > 0 {
   367  		g.P()
   368  	}
   369  }
   370  
   371  func genMessageField(g *protogen.GeneratedFile, f *fileInfo, m *messageInfo, field *protogen.Field, sf *structFields) {
   372  	if oneof := field.Oneof; oneof != nil && !oneof.Desc.IsSynthetic() {
   373  		// It would be a bit simpler to iterate over the oneofs below,
   374  		// but generating the field here keeps the contents of the Go
   375  		// struct in the same order as the contents of the source
   376  		// .proto file.
   377  		if oneof.Fields[0] != field {
   378  			return // only generate for first appearance
   379  		}
   380  
   381  		tags := structTags{
   382  			{"protobuf_oneof", string(oneof.Desc.Name())},
   383  		}
   384  		if m.isTracked {
   385  			tags = append(tags, gotrackTags...)
   386  		}
   387  
   388  		g.Annotate(m.GoIdent.GoName+"."+oneof.GoName, oneof.Location)
   389  		leadingComments := oneof.Comments.Leading
   390  		if leadingComments != "" {
   391  			leadingComments += "\n"
   392  		}
   393  		ss := []string{fmt.Sprintf(" Types that are assignable to %s:\n", oneof.GoName)}
   394  		for _, field := range oneof.Fields {
   395  			ss = append(ss, "\t*"+field.GoIdent.GoName+"\n")
   396  		}
   397  		leadingComments += protogen.Comments(strings.Join(ss, ""))
   398  		g.P(leadingComments,
   399  			oneof.GoName, " ", oneofInterfaceName(oneof), tags)
   400  		sf.append(oneof.GoName)
   401  		return
   402  	}
   403  	goType, pointer := fieldGoType(g, f, field)
   404  	if pointer {
   405  		goType = "*" + goType
   406  	}
   407  	tags := structTags{
   408  		{"protobuf", fieldProtobufTagValue(field)},
   409  		{"json", fieldJSONTagValue(field)},
   410  	}
   411  	if field.Desc.IsMap() {
   412  		key := field.Message.Fields[0]
   413  		val := field.Message.Fields[1]
   414  		tags = append(tags, structTags{
   415  			{"protobuf_key", fieldProtobufTagValue(key)},
   416  			{"protobuf_val", fieldProtobufTagValue(val)},
   417  		}...)
   418  	}
   419  	if m.isTracked {
   420  		tags = append(tags, gotrackTags...)
   421  	}
   422  
   423  	name := field.GoName
   424  	if field.Desc.IsWeak() {
   425  		name = genid.WeakFieldPrefix_goname + name
   426  	}
   427  	g.Annotate(m.GoIdent.GoName+"."+name, field.Location)
   428  	leadingComments := appendDeprecationSuffix(field.Comments.Leading,
   429  		field.Desc.Options().(*descriptorpb.FieldOptions).GetDeprecated())
   430  	g.P(leadingComments,
   431  		name, " ", goType, tags,
   432  		trailingComment(field.Comments.Trailing))
   433  	sf.append(field.GoName)
   434  }
   435  
   436  // genMessageDefaultDecls generates consts and vars holding the default
   437  // values of fields.
   438  func genMessageDefaultDecls(g *protogen.GeneratedFile, f *fileInfo, m *messageInfo) {
   439  	var consts, vars []string
   440  	for _, field := range m.Fields {
   441  		if !field.Desc.HasDefault() {
   442  			continue
   443  		}
   444  		name := "Default_" + m.GoIdent.GoName + "_" + field.GoName
   445  		goType, _ := fieldGoType(g, f, field)
   446  		defVal := field.Desc.Default()
   447  		switch field.Desc.Kind() {
   448  		case protoreflect.StringKind:
   449  			consts = append(consts, fmt.Sprintf("%s = %s(%q)", name, goType, defVal.String()))
   450  		case protoreflect.BytesKind:
   451  			vars = append(vars, fmt.Sprintf("%s = %s(%q)", name, goType, defVal.Bytes()))
   452  		case protoreflect.EnumKind:
   453  			idx := field.Desc.DefaultEnumValue().Index()
   454  			val := field.Enum.Values[idx]
   455  			if val.GoIdent.GoImportPath == f.GoImportPath {
   456  				consts = append(consts, fmt.Sprintf("%s = %s", name, g.QualifiedGoIdent(val.GoIdent)))
   457  			} else {
   458  				// If the enum value is declared in a different Go package,
   459  				// reference it by number since the name may not be correct.
   460  				// See https://github.com/golang/protobuf/issues/513.
   461  				consts = append(consts, fmt.Sprintf("%s = %s(%d) // %s",
   462  					name, g.QualifiedGoIdent(field.Enum.GoIdent), val.Desc.Number(), g.QualifiedGoIdent(val.GoIdent)))
   463  			}
   464  		case protoreflect.FloatKind, protoreflect.DoubleKind:
   465  			if f := defVal.Float(); math.IsNaN(f) || math.IsInf(f, 0) {
   466  				var fn, arg string
   467  				switch f := defVal.Float(); {
   468  				case math.IsInf(f, -1):
   469  					fn, arg = g.QualifiedGoIdent(mathPackage.Ident("Inf")), "-1"
   470  				case math.IsInf(f, +1):
   471  					fn, arg = g.QualifiedGoIdent(mathPackage.Ident("Inf")), "+1"
   472  				case math.IsNaN(f):
   473  					fn, arg = g.QualifiedGoIdent(mathPackage.Ident("NaN")), ""
   474  				}
   475  				vars = append(vars, fmt.Sprintf("%s = %s(%s(%s))", name, goType, fn, arg))
   476  			} else {
   477  				consts = append(consts, fmt.Sprintf("%s = %s(%v)", name, goType, f))
   478  			}
   479  		default:
   480  			consts = append(consts, fmt.Sprintf("%s = %s(%v)", name, goType, defVal.Interface()))
   481  		}
   482  	}
   483  	if len(consts) > 0 {
   484  		g.P("// Default values for ", m.GoIdent, " fields.")
   485  		g.P("const (")
   486  		for _, s := range consts {
   487  			g.P(s)
   488  		}
   489  		g.P(")")
   490  	}
   491  	if len(vars) > 0 {
   492  		g.P("// Default values for ", m.GoIdent, " fields.")
   493  		g.P("var (")
   494  		for _, s := range vars {
   495  			g.P(s)
   496  		}
   497  		g.P(")")
   498  	}
   499  	g.P()
   500  }
   501  
   502  func genMessageMethods(g *protogen.GeneratedFile, f *fileInfo, m *messageInfo) {
   503  	genMessageBaseMethods(g, f, m)
   504  	genMessageGetterMethods(g, f, m)
   505  	genMessageSetterMethods(g, f, m)
   506  }
   507  
   508  func genMessageBaseMethods(g *protogen.GeneratedFile, f *fileInfo, m *messageInfo) {
   509  	// Reset method.
   510  	g.P("func (x *", m.GoIdent, ") Reset() {")
   511  	g.P("*x = ", m.GoIdent, "{}")
   512  	g.P("if ", protoimplPackage.Ident("UnsafeEnabled"), " {")
   513  	g.P("mi := &", messageTypesVarName(f), "[", f.allMessagesByPtr[m], "]")
   514  	g.P("ms := ", protoimplPackage.Ident("X"), ".MessageStateOf(", protoimplPackage.Ident("Pointer"), "(x))")
   515  	g.P("ms.StoreMessageInfo(mi)")
   516  	g.P("}")
   517  	g.P("}")
   518  	g.P()
   519  
   520  	// String method.
   521  	g.P("func (x *", m.GoIdent, ") String() string {")
   522  	g.P("return ", protoimplPackage.Ident("X"), ".MessageStringOf(x)")
   523  	g.P("}")
   524  	g.P()
   525  
   526  	// ProtoMessage method.
   527  	g.P("func (*", m.GoIdent, ") ProtoMessage() {}")
   528  	g.P()
   529  
   530  	// ProtoReflect method.
   531  	genMessageReflectMethods(g, f, m)
   532  
   533  	// Descriptor method.
   534  	if m.genRawDescMethod {
   535  		var indexes []string
   536  		for i := 1; i < len(m.Location.Path); i += 2 {
   537  			indexes = append(indexes, strconv.Itoa(int(m.Location.Path[i])))
   538  		}
   539  		g.P("// Deprecated: Use ", m.GoIdent, ".ProtoReflect.Descriptor instead.")
   540  		g.P("func (*", m.GoIdent, ") Descriptor() ([]byte, []int) {")
   541  		g.P("return ", rawDescVarName(f), "GZIP(), []int{", strings.Join(indexes, ","), "}")
   542  		g.P("}")
   543  		g.P()
   544  		f.needRawDesc = true
   545  	}
   546  }
   547  
   548  func genMessageGetterMethods(g *protogen.GeneratedFile, f *fileInfo, m *messageInfo) {
   549  	for _, field := range m.Fields {
   550  		genNoInterfacePragma(g, m.isTracked)
   551  
   552  		// Getter for parent oneof.
   553  		if oneof := field.Oneof; oneof != nil && oneof.Fields[0] == field && !oneof.Desc.IsSynthetic() {
   554  			g.Annotate(m.GoIdent.GoName+".Get"+oneof.GoName, oneof.Location)
   555  			g.P("func (m *", m.GoIdent.GoName, ") Get", oneof.GoName, "() ", oneofInterfaceName(oneof), " {")
   556  			g.P("if m != nil {")
   557  			g.P("return m.", oneof.GoName)
   558  			g.P("}")
   559  			g.P("return nil")
   560  			g.P("}")
   561  			g.P()
   562  		}
   563  
   564  		// Getter for message field.
   565  		goType, pointer := fieldGoType(g, f, field)
   566  		defaultValue := fieldDefaultValue(g, f, m, field)
   567  		g.Annotate(m.GoIdent.GoName+".Get"+field.GoName, field.Location)
   568  		leadingComments := appendDeprecationSuffix("",
   569  			field.Desc.Options().(*descriptorpb.FieldOptions).GetDeprecated())
   570  		switch {
   571  		case field.Desc.IsWeak():
   572  			g.P(leadingComments, "func (x *", m.GoIdent, ") Get", field.GoName, "() ", protoPackage.Ident("Message"), "{")
   573  			g.P("var w ", protoimplPackage.Ident("WeakFields"))
   574  			g.P("if x != nil {")
   575  			g.P("w = x.", genid.WeakFields_goname)
   576  			if m.isTracked {
   577  				g.P("_ = x.", genid.WeakFieldPrefix_goname+field.GoName)
   578  			}
   579  			g.P("}")
   580  			g.P("return ", protoimplPackage.Ident("X"), ".GetWeak(w, ", field.Desc.Number(), ", ", strconv.Quote(string(field.Message.Desc.FullName())), ")")
   581  			g.P("}")
   582  		case field.Oneof != nil && !field.Oneof.Desc.IsSynthetic():
   583  			g.P(leadingComments, "func (x *", m.GoIdent, ") Get", field.GoName, "() ", goType, " {")
   584  			g.P("if x, ok := x.Get", field.Oneof.GoName, "().(*", field.GoIdent, "); ok {")
   585  			g.P("return x.", field.GoName)
   586  			g.P("}")
   587  			g.P("return ", defaultValue)
   588  			g.P("}")
   589  		default:
   590  			g.P(leadingComments, "func (x *", m.GoIdent, ") Get", field.GoName, "() ", goType, " {")
   591  			if !field.Desc.HasPresence() || defaultValue == "nil" {
   592  				g.P("if x != nil {")
   593  			} else {
   594  				g.P("if x != nil && x.", field.GoName, " != nil {")
   595  			}
   596  			star := ""
   597  			if pointer {
   598  				star = "*"
   599  			}
   600  			g.P("return ", star, " x.", field.GoName)
   601  			g.P("}")
   602  			g.P("return ", defaultValue)
   603  			g.P("}")
   604  		}
   605  		g.P()
   606  	}
   607  }
   608  
   609  func genMessageSetterMethods(g *protogen.GeneratedFile, f *fileInfo, m *messageInfo) {
   610  	for _, field := range m.Fields {
   611  		if !field.Desc.IsWeak() {
   612  			continue
   613  		}
   614  
   615  		genNoInterfacePragma(g, m.isTracked)
   616  
   617  		g.Annotate(m.GoIdent.GoName+".Set"+field.GoName, field.Location)
   618  		leadingComments := appendDeprecationSuffix("",
   619  			field.Desc.Options().(*descriptorpb.FieldOptions).GetDeprecated())
   620  		g.P(leadingComments, "func (x *", m.GoIdent, ") Set", field.GoName, "(v ", protoPackage.Ident("Message"), ") {")
   621  		g.P("var w *", protoimplPackage.Ident("WeakFields"))
   622  		g.P("if x != nil {")
   623  		g.P("w = &x.", genid.WeakFields_goname)
   624  		if m.isTracked {
   625  			g.P("_ = x.", genid.WeakFieldPrefix_goname+field.GoName)
   626  		}
   627  		g.P("}")
   628  		g.P(protoimplPackage.Ident("X"), ".SetWeak(w, ", field.Desc.Number(), ", ", strconv.Quote(string(field.Message.Desc.FullName())), ", v)")
   629  		g.P("}")
   630  		g.P()
   631  	}
   632  }
   633  
   634  // fieldGoType returns the Go type used for a field.
   635  //
   636  // If it returns pointer=true, the struct field is a pointer to the type.
   637  func fieldGoType(g *protogen.GeneratedFile, f *fileInfo, field *protogen.Field) (goType string, pointer bool) {
   638  	if field.Desc.IsWeak() {
   639  		return "struct{}", false
   640  	}
   641  
   642  	pointer = field.Desc.HasPresence()
   643  	switch field.Desc.Kind() {
   644  	case protoreflect.BoolKind:
   645  		goType = "bool"
   646  	case protoreflect.EnumKind:
   647  		goType = g.QualifiedGoIdent(field.Enum.GoIdent)
   648  	case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind:
   649  		goType = "int32"
   650  	case protoreflect.Uint32Kind, protoreflect.Fixed32Kind:
   651  		goType = "uint32"
   652  	case protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Sfixed64Kind:
   653  		goType = "int64"
   654  	case protoreflect.Uint64Kind, protoreflect.Fixed64Kind:
   655  		goType = "uint64"
   656  	case protoreflect.FloatKind:
   657  		goType = "float32"
   658  	case protoreflect.DoubleKind:
   659  		goType = "float64"
   660  	case protoreflect.StringKind:
   661  		goType = "string"
   662  	case protoreflect.BytesKind:
   663  		goType = "[]byte"
   664  		pointer = false // rely on nullability of slices for presence
   665  	case protoreflect.MessageKind, protoreflect.GroupKind:
   666  		goType = "*" + g.QualifiedGoIdent(field.Message.GoIdent)
   667  		pointer = false // pointer captured as part of the type
   668  	}
   669  	switch {
   670  	case field.Desc.IsList():
   671  		return "[]" + goType, false
   672  	case field.Desc.IsMap():
   673  		keyType, _ := fieldGoType(g, f, field.Message.Fields[0])
   674  		valType, _ := fieldGoType(g, f, field.Message.Fields[1])
   675  		return fmt.Sprintf("map[%v]%v", keyType, valType), false
   676  	}
   677  	return goType, pointer
   678  }
   679  
   680  func fieldProtobufTagValue(field *protogen.Field) string {
   681  	var enumName string
   682  	if field.Desc.Kind() == protoreflect.EnumKind {
   683  		enumName = protoimpl.X.LegacyEnumName(field.Enum.Desc)
   684  	}
   685  	return tag.Marshal(field.Desc, enumName)
   686  }
   687  
   688  func fieldDefaultValue(g *protogen.GeneratedFile, f *fileInfo, m *messageInfo, field *protogen.Field) string {
   689  	if field.Desc.IsList() {
   690  		return "nil"
   691  	}
   692  	if field.Desc.HasDefault() {
   693  		defVarName := "Default_" + m.GoIdent.GoName + "_" + field.GoName
   694  		if field.Desc.Kind() == protoreflect.BytesKind {
   695  			return "append([]byte(nil), " + defVarName + "...)"
   696  		}
   697  		return defVarName
   698  	}
   699  	switch field.Desc.Kind() {
   700  	case protoreflect.BoolKind:
   701  		return "false"
   702  	case protoreflect.StringKind:
   703  		return `""`
   704  	case protoreflect.MessageKind, protoreflect.GroupKind, protoreflect.BytesKind:
   705  		return "nil"
   706  	case protoreflect.EnumKind:
   707  		val := field.Enum.Values[0]
   708  		if val.GoIdent.GoImportPath == f.GoImportPath {
   709  			return g.QualifiedGoIdent(val.GoIdent)
   710  		} else {
   711  			// If the enum value is declared in a different Go package,
   712  			// reference it by number since the name may not be correct.
   713  			// See https://github.com/golang/protobuf/issues/513.
   714  			return g.QualifiedGoIdent(field.Enum.GoIdent) + "(" + strconv.FormatInt(int64(val.Desc.Number()), 10) + ")"
   715  		}
   716  	default:
   717  		return "0"
   718  	}
   719  }
   720  
   721  func fieldJSONTagValue(field *protogen.Field) string {
   722  	return string(field.Desc.Name()) + ",omitempty"
   723  }
   724  
   725  func genExtensions(g *protogen.GeneratedFile, f *fileInfo) {
   726  	if len(f.allExtensions) == 0 {
   727  		return
   728  	}
   729  
   730  	g.P("var ", extensionTypesVarName(f), " = []", protoimplPackage.Ident("ExtensionInfo"), "{")
   731  	for _, x := range f.allExtensions {
   732  		g.P("{")
   733  		g.P("ExtendedType: (*", x.Extendee.GoIdent, ")(nil),")
   734  		goType, pointer := fieldGoType(g, f, x.Extension)
   735  		if pointer {
   736  			goType = "*" + goType
   737  		}
   738  		g.P("ExtensionType: (", goType, ")(nil),")
   739  		g.P("Field: ", x.Desc.Number(), ",")
   740  		g.P("Name: ", strconv.Quote(string(x.Desc.FullName())), ",")
   741  		g.P("Tag: ", strconv.Quote(fieldProtobufTagValue(x.Extension)), ",")
   742  		g.P("Filename: ", strconv.Quote(f.Desc.Path()), ",")
   743  		g.P("},")
   744  	}
   745  	g.P("}")
   746  	g.P()
   747  
   748  	// Group extensions by the target message.
   749  	var orderedTargets []protogen.GoIdent
   750  	allExtensionsByTarget := make(map[protogen.GoIdent][]*extensionInfo)
   751  	allExtensionsByPtr := make(map[*extensionInfo]int)
   752  	for i, x := range f.allExtensions {
   753  		target := x.Extendee.GoIdent
   754  		if len(allExtensionsByTarget[target]) == 0 {
   755  			orderedTargets = append(orderedTargets, target)
   756  		}
   757  		allExtensionsByTarget[target] = append(allExtensionsByTarget[target], x)
   758  		allExtensionsByPtr[x] = i
   759  	}
   760  	for _, target := range orderedTargets {
   761  		g.P("// Extension fields to ", target, ".")
   762  		g.P("var (")
   763  		for _, x := range allExtensionsByTarget[target] {
   764  			xd := x.Desc
   765  			typeName := xd.Kind().String()
   766  			switch xd.Kind() {
   767  			case protoreflect.EnumKind:
   768  				typeName = string(xd.Enum().FullName())
   769  			case protoreflect.MessageKind, protoreflect.GroupKind:
   770  				typeName = string(xd.Message().FullName())
   771  			}
   772  			fieldName := string(xd.Name())
   773  
   774  			leadingComments := x.Comments.Leading
   775  			if leadingComments != "" {
   776  				leadingComments += "\n"
   777  			}
   778  			leadingComments += protogen.Comments(fmt.Sprintf(" %v %v %v = %v;\n",
   779  				xd.Cardinality(), typeName, fieldName, xd.Number()))
   780  			leadingComments = appendDeprecationSuffix(leadingComments,
   781  				x.Desc.Options().(*descriptorpb.FieldOptions).GetDeprecated())
   782  			g.P(leadingComments,
   783  				"E_", x.GoIdent, " = &", extensionTypesVarName(f), "[", allExtensionsByPtr[x], "]",
   784  				trailingComment(x.Comments.Trailing))
   785  		}
   786  		g.P(")")
   787  		g.P()
   788  	}
   789  }
   790  
   791  // genMessageOneofWrapperTypes generates the oneof wrapper types and
   792  // associates the types with the parent message type.
   793  func genMessageOneofWrapperTypes(g *protogen.GeneratedFile, f *fileInfo, m *messageInfo) {
   794  	for _, oneof := range m.Oneofs {
   795  		if oneof.Desc.IsSynthetic() {
   796  			continue
   797  		}
   798  		ifName := oneofInterfaceName(oneof)
   799  		g.P("type ", ifName, " interface {")
   800  		g.P(ifName, "()")
   801  		g.P("}")
   802  		g.P()
   803  		for _, field := range oneof.Fields {
   804  			g.Annotate(field.GoIdent.GoName, field.Location)
   805  			g.Annotate(field.GoIdent.GoName+"."+field.GoName, field.Location)
   806  			g.P("type ", field.GoIdent, " struct {")
   807  			goType, _ := fieldGoType(g, f, field)
   808  			tags := structTags{
   809  				{"protobuf", fieldProtobufTagValue(field)},
   810  			}
   811  			if m.isTracked {
   812  				tags = append(tags, gotrackTags...)
   813  			}
   814  			leadingComments := appendDeprecationSuffix(field.Comments.Leading,
   815  				field.Desc.Options().(*descriptorpb.FieldOptions).GetDeprecated())
   816  			g.P(leadingComments,
   817  				field.GoName, " ", goType, tags,
   818  				trailingComment(field.Comments.Trailing))
   819  			g.P("}")
   820  			g.P()
   821  		}
   822  		for _, field := range oneof.Fields {
   823  			g.P("func (*", field.GoIdent, ") ", ifName, "() {}")
   824  			g.P()
   825  		}
   826  	}
   827  }
   828  
   829  // oneofInterfaceName returns the name of the interface type implemented by
   830  // the oneof field value types.
   831  func oneofInterfaceName(oneof *protogen.Oneof) string {
   832  	return "is" + oneof.GoIdent.GoName
   833  }
   834  
   835  // genNoInterfacePragma generates a standalone "nointerface" pragma to
   836  // decorate methods with field-tracking support.
   837  func genNoInterfacePragma(g *protogen.GeneratedFile, tracked bool) {
   838  	if tracked {
   839  		g.P("//go:nointerface")
   840  		g.P()
   841  	}
   842  }
   843  
   844  var gotrackTags = structTags{{"go", "track"}}
   845  
   846  // structTags is a data structure for build idiomatic Go struct tags.
   847  // Each [2]string is a key-value pair, where value is the unescaped string.
   848  //
   849  // Example: structTags{{"key", "value"}}.String() -> `key:"value"`
   850  type structTags [][2]string
   851  
   852  func (tags structTags) String() string {
   853  	if len(tags) == 0 {
   854  		return ""
   855  	}
   856  	var ss []string
   857  	for _, tag := range tags {
   858  		// NOTE: When quoting the value, we need to make sure the backtick
   859  		// character does not appear. Convert all cases to the escaped hex form.
   860  		key := tag[0]
   861  		val := strings.Replace(strconv.Quote(tag[1]), "`", `\x60`, -1)
   862  		ss = append(ss, fmt.Sprintf("%s:%s", key, val))
   863  	}
   864  	return "`" + strings.Join(ss, " ") + "`"
   865  }
   866  
   867  // appendDeprecationSuffix optionally appends a deprecation notice as a suffix.
   868  func appendDeprecationSuffix(prefix protogen.Comments, deprecated bool) protogen.Comments {
   869  	if !deprecated {
   870  		return prefix
   871  	}
   872  	if prefix != "" {
   873  		prefix += "\n"
   874  	}
   875  	return prefix + " Deprecated: Do not use.\n"
   876  }
   877  
   878  // trailingComment is like protogen.Comments, but lacks a trailing newline.
   879  type trailingComment protogen.Comments
   880  
   881  func (c trailingComment) String() string {
   882  	s := strings.TrimSuffix(protogen.Comments(c).String(), "\n")
   883  	if strings.Contains(s, "\n") {
   884  		// We don't support multi-lined trailing comments as it is unclear
   885  		// how to best render them in the generated code.
   886  		return ""
   887  	}
   888  	return s
   889  }