protogen.go 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772
  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 protogen provides support for writing protoc plugins.
  5. //
  6. // Plugins for protoc, the Protocol Buffers Compiler, are programs which read
  7. // a CodeGeneratorRequest protocol buffer from standard input and write a
  8. // CodeGeneratorResponse protocol buffer to standard output. This package
  9. // provides support for writing plugins which generate Go code.
  10. package protogen
  11. import (
  12. "bufio"
  13. "bytes"
  14. "fmt"
  15. "go/parser"
  16. "go/printer"
  17. "go/token"
  18. "io/ioutil"
  19. "os"
  20. "path"
  21. "path/filepath"
  22. "sort"
  23. "strconv"
  24. "strings"
  25. "github.com/golang/protobuf/proto"
  26. descpb "github.com/golang/protobuf/protoc-gen-go/descriptor"
  27. pluginpb "github.com/golang/protobuf/protoc-gen-go/plugin"
  28. "golang.org/x/tools/go/ast/astutil"
  29. "google.golang.org/proto/reflect/protoreflect"
  30. "google.golang.org/proto/reflect/protoregistry"
  31. "google.golang.org/proto/reflect/prototype"
  32. )
  33. // Run executes a function as a protoc plugin.
  34. //
  35. // It reads a CodeGeneratorRequest message from os.Stdin, invokes the plugin
  36. // function, and writes a CodeGeneratorResponse message to os.Stdout.
  37. //
  38. // If a failure occurs while reading or writing, Run prints an error to
  39. // os.Stderr and calls os.Exit(1).
  40. //
  41. // Passing a nil options is equivalent to passing a zero-valued one.
  42. func Run(opts *Options, f func(*Plugin) error) {
  43. if err := run(opts, f); err != nil {
  44. fmt.Fprintf(os.Stderr, "%s: %v\n", filepath.Base(os.Args[0]), err)
  45. os.Exit(1)
  46. }
  47. }
  48. func run(opts *Options, f func(*Plugin) error) error {
  49. in, err := ioutil.ReadAll(os.Stdin)
  50. if err != nil {
  51. return err
  52. }
  53. req := &pluginpb.CodeGeneratorRequest{}
  54. if err := proto.Unmarshal(in, req); err != nil {
  55. return err
  56. }
  57. gen, err := New(req, opts)
  58. if err != nil {
  59. return err
  60. }
  61. if err := f(gen); err != nil {
  62. // Errors from the plugin function are reported by setting the
  63. // error field in the CodeGeneratorResponse.
  64. //
  65. // In contrast, errors that indicate a problem in protoc
  66. // itself (unparsable input, I/O errors, etc.) are reported
  67. // to stderr.
  68. gen.Error(err)
  69. }
  70. resp := gen.Response()
  71. out, err := proto.Marshal(resp)
  72. if err != nil {
  73. return err
  74. }
  75. if _, err := os.Stdout.Write(out); err != nil {
  76. return err
  77. }
  78. return nil
  79. }
  80. // A Plugin is a protoc plugin invocation.
  81. type Plugin struct {
  82. // Request is the CodeGeneratorRequest provided by protoc.
  83. Request *pluginpb.CodeGeneratorRequest
  84. // Files is the set of files to generate and everything they import.
  85. // Files appear in topological order, so each file appears before any
  86. // file that imports it.
  87. Files []*File
  88. filesByName map[string]*File
  89. fileReg *protoregistry.Files
  90. messagesByName map[protoreflect.FullName]*Message
  91. enumsByName map[protoreflect.FullName]*Enum
  92. pathType pathType
  93. genFiles []*GeneratedFile
  94. err error
  95. }
  96. // Options are optional parameters to New.
  97. type Options struct {
  98. // If ParamFunc is non-nil, it will be called with each unknown
  99. // generator parameter.
  100. //
  101. // Plugins for protoc can accept parameters from the command line,
  102. // passed in the --<lang>_out protoc, separated from the output
  103. // directory with a colon; e.g.,
  104. //
  105. // --go_out=<param1>=<value1>,<param2>=<value2>:<output_directory>
  106. //
  107. // Parameters passed in this fashion as a comma-separated list of
  108. // key=value pairs will be passed to the ParamFunc.
  109. //
  110. // The (flag.FlagSet).Set method matches this function signature,
  111. // so parameters can be converted into flags as in the following:
  112. //
  113. // var flags flag.FlagSet
  114. // value := flags.Bool("param", false, "")
  115. // opts := &protogen.Options{
  116. // ParamFunc: flags.Set,
  117. // }
  118. // protogen.Run(opts, func(p *protogen.Plugin) error {
  119. // if *value { ... }
  120. // })
  121. ParamFunc func(name, value string) error
  122. }
  123. // New returns a new Plugin.
  124. //
  125. // Passing a nil Options is equivalent to passing a zero-valued one.
  126. func New(req *pluginpb.CodeGeneratorRequest, opts *Options) (*Plugin, error) {
  127. if opts == nil {
  128. opts = &Options{}
  129. }
  130. gen := &Plugin{
  131. Request: req,
  132. filesByName: make(map[string]*File),
  133. fileReg: protoregistry.NewFiles(),
  134. messagesByName: make(map[protoreflect.FullName]*Message),
  135. enumsByName: make(map[protoreflect.FullName]*Enum),
  136. }
  137. packageNames := make(map[string]GoPackageName) // filename -> package name
  138. importPaths := make(map[string]GoImportPath) // filename -> import path
  139. var packageImportPath GoImportPath
  140. for _, param := range strings.Split(req.GetParameter(), ",") {
  141. var value string
  142. if i := strings.Index(param, "="); i >= 0 {
  143. value = param[i+1:]
  144. param = param[0:i]
  145. }
  146. switch param {
  147. case "":
  148. // Ignore.
  149. case "import_prefix":
  150. // TODO
  151. case "import_path":
  152. packageImportPath = GoImportPath(value)
  153. case "paths":
  154. switch value {
  155. case "import":
  156. gen.pathType = pathTypeImport
  157. case "source_relative":
  158. gen.pathType = pathTypeSourceRelative
  159. default:
  160. return nil, fmt.Errorf(`unknown path type %q: want "import" or "source_relative"`, value)
  161. }
  162. case "annotate_code":
  163. // TODO
  164. default:
  165. if param[0] == 'M' {
  166. importPaths[param[1:]] = GoImportPath(value)
  167. continue
  168. }
  169. if opts.ParamFunc != nil {
  170. if err := opts.ParamFunc(param, value); err != nil {
  171. return nil, err
  172. }
  173. }
  174. }
  175. }
  176. // Figure out the import path and package name for each file.
  177. //
  178. // The rules here are complicated and have grown organically over time.
  179. // Interactions between different ways of specifying package information
  180. // may be surprising.
  181. //
  182. // The recommended approach is to include a go_package option in every
  183. // .proto source file specifying the full import path of the Go package
  184. // associated with this file.
  185. //
  186. // option go_package = "github.com/golang/protobuf/ptypes/any";
  187. //
  188. // Build systems which want to exert full control over import paths may
  189. // specify M<filename>=<import_path> flags.
  190. //
  191. // Other approaches are not recommend.
  192. generatedFileNames := make(map[string]bool)
  193. for _, name := range gen.Request.FileToGenerate {
  194. generatedFileNames[name] = true
  195. }
  196. // We need to determine the import paths before the package names,
  197. // because the Go package name for a file is sometimes derived from
  198. // different file in the same package.
  199. packageNameForImportPath := make(map[GoImportPath]GoPackageName)
  200. for _, fdesc := range gen.Request.ProtoFile {
  201. filename := fdesc.GetName()
  202. packageName, importPath := goPackageOption(fdesc)
  203. switch {
  204. case importPaths[filename] != "":
  205. // Command line: M=foo.proto=quux/bar
  206. //
  207. // Explicit mapping of source file to import path.
  208. case generatedFileNames[filename] && packageImportPath != "":
  209. // Command line: import_path=quux/bar
  210. //
  211. // The import_path flag sets the import path for every file that
  212. // we generate code for.
  213. importPaths[filename] = packageImportPath
  214. case importPath != "":
  215. // Source file: option go_package = "quux/bar";
  216. //
  217. // The go_package option sets the import path. Most users should use this.
  218. importPaths[filename] = importPath
  219. default:
  220. // Source filename.
  221. //
  222. // Last resort when nothing else is available.
  223. importPaths[filename] = GoImportPath(path.Dir(filename))
  224. }
  225. if packageName != "" {
  226. packageNameForImportPath[importPaths[filename]] = packageName
  227. }
  228. }
  229. for _, fdesc := range gen.Request.ProtoFile {
  230. filename := fdesc.GetName()
  231. packageName, _ := goPackageOption(fdesc)
  232. defaultPackageName := packageNameForImportPath[importPaths[filename]]
  233. switch {
  234. case packageName != "":
  235. // Source file: option go_package = "quux/bar";
  236. packageNames[filename] = packageName
  237. case defaultPackageName != "":
  238. // A go_package option in another file in the same package.
  239. //
  240. // This is a poor choice in general, since every source file should
  241. // contain a go_package option. Supported mainly for historical
  242. // compatibility.
  243. packageNames[filename] = defaultPackageName
  244. case generatedFileNames[filename] && packageImportPath != "":
  245. // Command line: import_path=quux/bar
  246. packageNames[filename] = cleanPackageName(path.Base(string(packageImportPath)))
  247. case fdesc.GetPackage() != "":
  248. // Source file: package quux.bar;
  249. packageNames[filename] = cleanPackageName(fdesc.GetPackage())
  250. default:
  251. // Source filename.
  252. packageNames[filename] = cleanPackageName(baseName(filename))
  253. }
  254. }
  255. // Consistency check: Every file with the same Go import path should have
  256. // the same Go package name.
  257. packageFiles := make(map[GoImportPath][]string)
  258. for filename, importPath := range importPaths {
  259. packageFiles[importPath] = append(packageFiles[importPath], filename)
  260. }
  261. for importPath, filenames := range packageFiles {
  262. for i := 1; i < len(filenames); i++ {
  263. if a, b := packageNames[filenames[0]], packageNames[filenames[i]]; a != b {
  264. return nil, fmt.Errorf("Go package %v has inconsistent names %v (%v) and %v (%v)",
  265. importPath, a, filenames[0], b, filenames[i])
  266. }
  267. }
  268. }
  269. for _, fdesc := range gen.Request.ProtoFile {
  270. filename := fdesc.GetName()
  271. if gen.filesByName[filename] != nil {
  272. return nil, fmt.Errorf("duplicate file name: %q", filename)
  273. }
  274. f, err := newFile(gen, fdesc, packageNames[filename], importPaths[filename])
  275. if err != nil {
  276. return nil, err
  277. }
  278. gen.Files = append(gen.Files, f)
  279. gen.filesByName[filename] = f
  280. }
  281. for _, filename := range gen.Request.FileToGenerate {
  282. f, ok := gen.FileByName(filename)
  283. if !ok {
  284. return nil, fmt.Errorf("no descriptor for generated file: %v", filename)
  285. }
  286. f.Generate = true
  287. }
  288. return gen, nil
  289. }
  290. // Error records an error in code generation. The generator will report the
  291. // error back to protoc and will not produce output.
  292. func (gen *Plugin) Error(err error) {
  293. if gen.err == nil {
  294. gen.err = err
  295. }
  296. }
  297. // Response returns the generator output.
  298. func (gen *Plugin) Response() *pluginpb.CodeGeneratorResponse {
  299. resp := &pluginpb.CodeGeneratorResponse{}
  300. if gen.err != nil {
  301. resp.Error = proto.String(gen.err.Error())
  302. return resp
  303. }
  304. for _, gf := range gen.genFiles {
  305. content, err := gf.Content()
  306. if err != nil {
  307. return &pluginpb.CodeGeneratorResponse{
  308. Error: proto.String(err.Error()),
  309. }
  310. }
  311. resp.File = append(resp.File, &pluginpb.CodeGeneratorResponse_File{
  312. Name: proto.String(gf.filename),
  313. Content: proto.String(string(content)),
  314. })
  315. }
  316. return resp
  317. }
  318. // FileByName returns the file with the given name.
  319. func (gen *Plugin) FileByName(name string) (f *File, ok bool) {
  320. f, ok = gen.filesByName[name]
  321. return f, ok
  322. }
  323. // A File describes a .proto source file.
  324. type File struct {
  325. Desc protoreflect.FileDescriptor
  326. Proto *descpb.FileDescriptorProto
  327. GoPackageName GoPackageName // name of this file's Go package
  328. GoImportPath GoImportPath // import path of this file's Go package
  329. Messages []*Message // top-level message declarations
  330. Enums []*Enum // top-level enum declarations
  331. Generate bool // true if we should generate code for this file
  332. // GeneratedFilenamePrefix is used to construct filenames for generated
  333. // files associated with this source file.
  334. //
  335. // For example, the source file "dir/foo.proto" might have a filename prefix
  336. // of "dir/foo". Appending ".pb.go" produces an output file of "dir/foo.pb.go".
  337. GeneratedFilenamePrefix string
  338. }
  339. func newFile(gen *Plugin, p *descpb.FileDescriptorProto, packageName GoPackageName, importPath GoImportPath) (*File, error) {
  340. desc, err := prototype.NewFileFromDescriptorProto(p, gen.fileReg)
  341. if err != nil {
  342. return nil, fmt.Errorf("invalid FileDescriptorProto %q: %v", p.GetName(), err)
  343. }
  344. if err := gen.fileReg.Register(desc); err != nil {
  345. return nil, fmt.Errorf("cannot register descriptor %q: %v", p.GetName(), err)
  346. }
  347. f := &File{
  348. Desc: desc,
  349. Proto: p,
  350. GoPackageName: packageName,
  351. GoImportPath: importPath,
  352. }
  353. // Determine the prefix for generated Go files.
  354. prefix := p.GetName()
  355. if ext := path.Ext(prefix); ext == ".proto" || ext == ".protodevel" {
  356. prefix = prefix[:len(prefix)-len(ext)]
  357. }
  358. if gen.pathType == pathTypeImport {
  359. // If paths=import (the default) and the file contains a go_package option
  360. // with a full import path, the output filename is derived from the Go import
  361. // path.
  362. //
  363. // Pass the paths=source_relative flag to always derive the output filename
  364. // from the input filename instead.
  365. if _, importPath := goPackageOption(p); importPath != "" {
  366. prefix = path.Join(string(importPath), path.Base(prefix))
  367. }
  368. }
  369. f.GeneratedFilenamePrefix = prefix
  370. for i, mdescs := 0, desc.Messages(); i < mdescs.Len(); i++ {
  371. message, err := newMessage(gen, f, nil, mdescs.Get(i))
  372. if err != nil {
  373. return nil, err
  374. }
  375. f.Messages = append(f.Messages, message)
  376. }
  377. for i, edescs := 0, desc.Enums(); i < edescs.Len(); i++ {
  378. f.Enums = append(f.Enums, newEnum(gen, f, nil, edescs.Get(i)))
  379. }
  380. for _, message := range f.Messages {
  381. message.init(gen)
  382. }
  383. return f, nil
  384. }
  385. // goPackageOption interprets a file's go_package option.
  386. // If there is no go_package, it returns ("", "").
  387. // If there's a simple name, it returns (pkg, "").
  388. // If the option implies an import path, it returns (pkg, impPath).
  389. func goPackageOption(d *descpb.FileDescriptorProto) (pkg GoPackageName, impPath GoImportPath) {
  390. opt := d.GetOptions().GetGoPackage()
  391. if opt == "" {
  392. return "", ""
  393. }
  394. // A semicolon-delimited suffix delimits the import path and package name.
  395. if i := strings.Index(opt, ";"); i >= 0 {
  396. return cleanPackageName(opt[i+1:]), GoImportPath(opt[:i])
  397. }
  398. // The presence of a slash implies there's an import path.
  399. if i := strings.LastIndex(opt, "/"); i >= 0 {
  400. return cleanPackageName(opt[i+1:]), GoImportPath(opt)
  401. }
  402. return cleanPackageName(opt), ""
  403. }
  404. // A Message describes a message.
  405. type Message struct {
  406. Desc protoreflect.MessageDescriptor
  407. GoIdent GoIdent // name of the generated Go type
  408. Fields []*Field // message field declarations
  409. Messages []*Message // nested message declarations
  410. Enums []*Enum // nested enum declarations
  411. Path []int32 // location path of this message
  412. }
  413. func newMessage(gen *Plugin, f *File, parent *Message, desc protoreflect.MessageDescriptor) (*Message, error) {
  414. var path []int32
  415. if parent != nil {
  416. path = pathAppend(parent.Path, messageMessageField, int32(desc.Index()))
  417. } else {
  418. path = []int32{fileMessageField, int32(desc.Index())}
  419. }
  420. message := &Message{
  421. Desc: desc,
  422. GoIdent: newGoIdent(f, desc),
  423. Path: path,
  424. }
  425. gen.messagesByName[desc.FullName()] = message
  426. for i, mdescs := 0, desc.Messages(); i < mdescs.Len(); i++ {
  427. m, err := newMessage(gen, f, message, mdescs.Get(i))
  428. if err != nil {
  429. return nil, err
  430. }
  431. message.Messages = append(message.Messages, m)
  432. }
  433. for i, edescs := 0, desc.Enums(); i < edescs.Len(); i++ {
  434. message.Enums = append(message.Enums, newEnum(gen, f, message, edescs.Get(i)))
  435. }
  436. for i, fdescs := 0, desc.Fields(); i < fdescs.Len(); i++ {
  437. field, err := newField(gen, f, message, fdescs.Get(i))
  438. if err != nil {
  439. return nil, err
  440. }
  441. message.Fields = append(message.Fields, field)
  442. }
  443. // Field name conflict resolution.
  444. //
  445. // We assume well-known method names that may be attached to a generated
  446. // message type, as well as a 'Get*' method for each field. For each
  447. // field in turn, we add _s to its name until there are no conflicts.
  448. //
  449. // Any change to the following set of method names is a potential
  450. // incompatible API change because it may change generated field names.
  451. //
  452. // TODO: If we ever support a 'go_name' option to set the Go name of a
  453. // field, we should consider dropping this entirely. The conflict
  454. // resolution algorithm is subtle and surprising (changing the order
  455. // in which fields appear in the .proto source file can change the
  456. // names of fields in generated code), and does not adapt well to
  457. // adding new per-field methods such as setters.
  458. usedNames := map[string]bool{
  459. "Reset": true,
  460. "String": true,
  461. "ProtoMessage": true,
  462. "Marshal": true,
  463. "Unmarshal": true,
  464. "ExtensionRangeArray": true,
  465. "ExtensionMap": true,
  466. "Descriptor": true,
  467. }
  468. makeNameUnique := func(name string) string {
  469. for usedNames[name] || usedNames["Get"+name] {
  470. name += "_"
  471. }
  472. usedNames[name] = true
  473. usedNames["Get"+name] = true
  474. return name
  475. }
  476. for _, field := range message.Fields {
  477. field.GoIdent.GoName = makeNameUnique(field.GoIdent.GoName)
  478. // TODO: If this is the first field of a oneof that we haven't seen
  479. // before, generate the name for the oneof.
  480. }
  481. return message, nil
  482. }
  483. func (message *Message) init(gen *Plugin) error {
  484. for _, child := range message.Messages {
  485. if err := child.init(gen); err != nil {
  486. return err
  487. }
  488. }
  489. for _, field := range message.Fields {
  490. if err := field.init(gen); err != nil {
  491. return err
  492. }
  493. }
  494. return nil
  495. }
  496. // A Field describes a message field.
  497. type Field struct {
  498. Desc protoreflect.FieldDescriptor
  499. // GoIdent is the base name of this field's Go fields and methods.
  500. // For code generated by protoc-gen-go, this means a field named
  501. // '{{GoIdent}}' and a getter method named 'Get{{GoIdent}}'.
  502. GoIdent GoIdent
  503. MessageType *Message // type for message or group fields; nil otherwise
  504. EnumType *Enum // type for enum fields; nil otherwise
  505. Path []int32 // location path of this field
  506. }
  507. func newField(gen *Plugin, f *File, message *Message, desc protoreflect.FieldDescriptor) (*Field, error) {
  508. field := &Field{
  509. Desc: desc,
  510. GoIdent: GoIdent{
  511. GoName: camelCase(string(desc.Name())),
  512. GoImportPath: f.GoImportPath,
  513. },
  514. Path: pathAppend(message.Path, messageFieldField, int32(desc.Index())),
  515. }
  516. return field, nil
  517. }
  518. func (field *Field) init(gen *Plugin) error {
  519. desc := field.Desc
  520. switch desc.Kind() {
  521. case protoreflect.MessageKind, protoreflect.GroupKind:
  522. mname := desc.MessageType().FullName()
  523. message, ok := gen.messagesByName[mname]
  524. if !ok {
  525. return fmt.Errorf("field %v: no descriptor for type %v", desc.FullName(), mname)
  526. }
  527. field.MessageType = message
  528. case protoreflect.EnumKind:
  529. ename := field.Desc.EnumType().FullName()
  530. enum, ok := gen.enumsByName[ename]
  531. if !ok {
  532. return fmt.Errorf("field %v: no descriptor for enum %v", desc.FullName(), ename)
  533. }
  534. field.EnumType = enum
  535. }
  536. return nil
  537. }
  538. // An Enum describes an enum.
  539. type Enum struct {
  540. Desc protoreflect.EnumDescriptor
  541. GoIdent GoIdent // name of the generated Go type
  542. Values []*EnumValue // enum values
  543. Path []int32 // location path of this enum
  544. }
  545. func newEnum(gen *Plugin, f *File, parent *Message, desc protoreflect.EnumDescriptor) *Enum {
  546. var path []int32
  547. if parent != nil {
  548. path = pathAppend(parent.Path, messageEnumField, int32(desc.Index()))
  549. } else {
  550. path = []int32{fileEnumField, int32(desc.Index())}
  551. }
  552. enum := &Enum{
  553. Desc: desc,
  554. GoIdent: newGoIdent(f, desc),
  555. Path: path,
  556. }
  557. gen.enumsByName[desc.FullName()] = enum
  558. for i, evdescs := 0, enum.Desc.Values(); i < evdescs.Len(); i++ {
  559. enum.Values = append(enum.Values, newEnumValue(gen, f, parent, enum, evdescs.Get(i)))
  560. }
  561. return enum
  562. }
  563. // An EnumValue describes an enum value.
  564. type EnumValue struct {
  565. Desc protoreflect.EnumValueDescriptor
  566. GoIdent GoIdent // name of the generated Go type
  567. Path []int32 // location path of this enum value
  568. }
  569. func newEnumValue(gen *Plugin, f *File, message *Message, enum *Enum, desc protoreflect.EnumValueDescriptor) *EnumValue {
  570. // A top-level enum value's name is: EnumName_ValueName
  571. // An enum value contained in a message is: MessageName_ValueName
  572. //
  573. // Enum value names are not camelcased.
  574. parentIdent := enum.GoIdent
  575. if message != nil {
  576. parentIdent = message.GoIdent
  577. }
  578. name := parentIdent.GoName + "_" + string(desc.Name())
  579. return &EnumValue{
  580. Desc: desc,
  581. GoIdent: GoIdent{
  582. GoName: name,
  583. GoImportPath: f.GoImportPath,
  584. },
  585. Path: pathAppend(enum.Path, enumValueField, int32(desc.Index())),
  586. }
  587. }
  588. // A GeneratedFile is a generated file.
  589. type GeneratedFile struct {
  590. filename string
  591. goImportPath GoImportPath
  592. buf bytes.Buffer
  593. packageNames map[GoImportPath]GoPackageName
  594. usedPackageNames map[GoPackageName]bool
  595. }
  596. // NewGeneratedFile creates a new generated file with the given filename
  597. // and import path.
  598. func (gen *Plugin) NewGeneratedFile(filename string, goImportPath GoImportPath) *GeneratedFile {
  599. g := &GeneratedFile{
  600. filename: filename,
  601. goImportPath: goImportPath,
  602. packageNames: make(map[GoImportPath]GoPackageName),
  603. usedPackageNames: make(map[GoPackageName]bool),
  604. }
  605. gen.genFiles = append(gen.genFiles, g)
  606. return g
  607. }
  608. // P prints a line to the generated output. It converts each parameter to a
  609. // string following the same rules as fmt.Print. It never inserts spaces
  610. // between parameters.
  611. //
  612. // TODO: .meta file annotations.
  613. func (g *GeneratedFile) P(v ...interface{}) {
  614. for _, x := range v {
  615. switch x := x.(type) {
  616. case GoIdent:
  617. fmt.Fprint(&g.buf, g.QualifiedGoIdent(x))
  618. default:
  619. fmt.Fprint(&g.buf, x)
  620. }
  621. }
  622. fmt.Fprintln(&g.buf)
  623. }
  624. // QualifiedGoIdent returns the string to use for a Go identifier.
  625. //
  626. // If the identifier is from a different Go package than the generated file,
  627. // the returned name will be qualified (package.name) and an import statement
  628. // for the identifier's package will be included in the file.
  629. func (g *GeneratedFile) QualifiedGoIdent(ident GoIdent) string {
  630. if ident.GoImportPath == g.goImportPath {
  631. return ident.GoName
  632. }
  633. if packageName, ok := g.packageNames[ident.GoImportPath]; ok {
  634. return string(packageName) + "." + ident.GoName
  635. }
  636. packageName := cleanPackageName(baseName(string(ident.GoImportPath)))
  637. for i, orig := 1, packageName; g.usedPackageNames[packageName]; i++ {
  638. packageName = orig + GoPackageName(strconv.Itoa(i))
  639. }
  640. g.packageNames[ident.GoImportPath] = packageName
  641. g.usedPackageNames[packageName] = true
  642. return string(packageName) + "." + ident.GoName
  643. }
  644. // Write implements io.Writer.
  645. func (g *GeneratedFile) Write(p []byte) (n int, err error) {
  646. return g.buf.Write(p)
  647. }
  648. // Content returns the contents of the generated file.
  649. func (g *GeneratedFile) Content() ([]byte, error) {
  650. if !strings.HasSuffix(g.filename, ".go") {
  651. return g.buf.Bytes(), nil
  652. }
  653. // Reformat generated code.
  654. original := g.buf.Bytes()
  655. fset := token.NewFileSet()
  656. ast, err := parser.ParseFile(fset, "", original, parser.ParseComments)
  657. if err != nil {
  658. // Print out the bad code with line numbers.
  659. // This should never happen in practice, but it can while changing generated code
  660. // so consider this a debugging aid.
  661. var src bytes.Buffer
  662. s := bufio.NewScanner(bytes.NewReader(original))
  663. for line := 1; s.Scan(); line++ {
  664. fmt.Fprintf(&src, "%5d\t%s\n", line, s.Bytes())
  665. }
  666. return nil, fmt.Errorf("%v: unparsable Go source: %v\n%v", g.filename, err, src.String())
  667. }
  668. // Add imports.
  669. var importPaths []string
  670. for importPath := range g.packageNames {
  671. importPaths = append(importPaths, string(importPath))
  672. }
  673. sort.Strings(importPaths)
  674. for _, importPath := range importPaths {
  675. astutil.AddNamedImport(fset, ast, string(g.packageNames[GoImportPath(importPath)]), importPath)
  676. }
  677. var out bytes.Buffer
  678. if err = (&printer.Config{Mode: printer.TabIndent | printer.UseSpaces, Tabwidth: 8}).Fprint(&out, fset, ast); err != nil {
  679. return nil, fmt.Errorf("%v: can not reformat Go source: %v", g.filename, err)
  680. }
  681. // TODO: Annotations.
  682. return out.Bytes(), nil
  683. }
  684. type pathType int
  685. const (
  686. pathTypeImport pathType = iota
  687. pathTypeSourceRelative
  688. )
  689. // The SourceCodeInfo message describes the location of elements of a parsed
  690. // .proto file by way of a "path", which is a sequence of integers that
  691. // describe the route from a FileDescriptorProto to the relevant submessage.
  692. // The path alternates between a field number of a repeated field, and an index
  693. // into that repeated field. The constants below define the field numbers that
  694. // are used.
  695. //
  696. // See descriptor.proto for more information about this.
  697. const (
  698. // field numbers in FileDescriptorProto
  699. filePackageField = 2 // package
  700. fileMessageField = 4 // message_type
  701. fileEnumField = 5 // enum_type
  702. // field numbers in DescriptorProto
  703. messageFieldField = 2 // field
  704. messageMessageField = 3 // nested_type
  705. messageEnumField = 4 // enum_type
  706. messageOneofField = 8 // oneof_decl
  707. // field numbers in EnumDescriptorProto
  708. enumValueField = 2 // value
  709. )
  710. // pathAppend appends elements to a location path.
  711. // It does not alias the original path.
  712. func pathAppend(path []int32, a ...int32) []int32 {
  713. var n []int32
  714. n = append(n, path...)
  715. n = append(n, a...)
  716. return n
  717. }