main.go 29 KB

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