main.go 31 KB

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