main.go 30 KB

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