blob: 67f307181478c9240e51009163f077ec61c4cb5b [file] [log] [blame]
Damien Neil220c2022018-08-15 11:24:18 -07001// 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
Damien Neil1adaec92018-09-24 13:43:03 -07005// Package internal_gengo is internal to the protobuf module.
6package internal_gengo
Damien Neil220c2022018-08-15 11:24:18 -07007
8import (
Damien Neil7779e052018-09-07 14:14:06 -07009 "fmt"
Damien Neil7bf3ce22018-12-21 15:54:06 -080010 "go/ast"
11 "go/parser"
12 "go/token"
Damien Neilebc699d2018-09-13 08:50:13 -070013 "math"
Damien Neil7779e052018-09-07 14:14:06 -070014 "strconv"
Damien Neilcab8dfe2018-09-06 14:51:28 -070015 "strings"
Damien Neil7bf3ce22018-12-21 15:54:06 -080016 "unicode"
17 "unicode/utf8"
Damien Neil7779e052018-09-07 14:14:06 -070018
Damien Neil5c5b5312019-05-14 12:44:37 -070019 "google.golang.org/protobuf/compiler/protogen"
Damien Neile89e6242019-05-13 23:55:40 -070020 "google.golang.org/protobuf/internal/encoding/tag"
21 "google.golang.org/protobuf/internal/fieldnum"
Damien Neile89e6242019-05-13 23:55:40 -070022 "google.golang.org/protobuf/reflect/protoreflect"
Joe Tsai58b42d82019-05-22 16:27:51 -040023 "google.golang.org/protobuf/runtime/protoimpl"
Joe Tsaie1f8d502018-11-26 18:55:29 -080024
Joe Tsaia95b29f2019-05-16 12:47:20 -070025 "google.golang.org/protobuf/types/descriptorpb"
Damien Neil220c2022018-08-15 11:24:18 -070026)
27
Joe Tsaic1c17aa2018-11-16 11:14:14 -080028const (
Joe Tsaiab61d412019-04-16 15:23:29 -070029 // generateEnumMapVars specifies whether to generate enum maps,
30 // which provide a bi-directional mapping between enum numbers and names.
31 generateEnumMapVars = true
32
Joe Tsaib3d57df2019-04-17 14:07:32 -070033 // generateEnumJSONMethods specifies whether to generate the UnmarshalJSON
34 // method for proto2 enums.
35 generateEnumJSONMethods = true
36
Joe Tsaiab61d412019-04-16 15:23:29 -070037 // generateRawDescMethods specifies whether to generate EnumDescriptor and
38 // Descriptor methods for enums and messages. These methods return the
39 // GZIP'd contents of the raw file descriptor and the path from the root
40 // to the given enum or message descriptor.
41 generateRawDescMethods = true
Joe Tsai09912272019-07-08 10:38:11 -070042
43 // generateOneofWrapperMethods specifies whether to generate
44 // XXX_OneofWrappers methods on messages with oneofs.
45 generateOneofWrapperMethods = false
Joe Tsaic0e4bb22019-07-06 13:05:11 -070046
Joe Tsaib3d57df2019-04-17 14:07:32 -070047 // generateExtensionRangeMethods specifies whether to generate the
48 // ExtensionRangeArray method for messages that support extensions.
49 generateExtensionRangeMethods = true
50
Joe Tsai82760ce2019-06-20 03:09:57 -070051 // generateWKTMarkerMethods specifies whether to generate
Joe Tsai5455ef52019-07-08 13:49:48 -070052 // XXX_WellKnownType methods on well-known types.
53 generateWKTMarkerMethods = false
54
Joe Tsai82760ce2019-06-20 03:09:57 -070055 // generateMessateStateFields specifies whether to generate an unexported
56 // protoimpl.MessageState as the first field.
57 generateMessateStateFields = true
58
Joe Tsaic0e4bb22019-07-06 13:05:11 -070059 // generateNoUnkeyedLiteralFields specifies whether to generate
60 // the XXX_NoUnkeyedLiteral field.
61 generateNoUnkeyedLiteralFields = false
62
63 // generateExportedSizeCacheFields specifies whether to generate an exported
64 // XXX_sizecache field instead of an unexported sizeCache field.
65 generateExportedSizeCacheFields = false
66
67 // generateExportedUnknownFields specifies whether to generate an exported
68 // XXX_unrecognized field instead of an unexported unknownFields field.
69 generateExportedUnknownFields = false
70
71 // generateExportedExtensionFields specifies whether to generate an exported
72 // XXX_InternalExtensions field instead of an unexported extensionFields field.
73 generateExportedExtensionFields = false
Joe Tsaiab61d412019-04-16 15:23:29 -070074)
75
76const (
Joe Tsai5d72cc22019-03-28 01:13:26 -070077 syncPackage = protogen.GoImportPath("sync")
Joe Tsai4fddeba2019-03-20 18:29:32 -070078 mathPackage = protogen.GoImportPath("math")
Damien Neile89e6242019-05-13 23:55:40 -070079 protoifacePackage = protogen.GoImportPath("google.golang.org/protobuf/runtime/protoiface")
80 protoimplPackage = protogen.GoImportPath("google.golang.org/protobuf/runtime/protoimpl")
81 protoreflectPackage = protogen.GoImportPath("google.golang.org/protobuf/reflect/protoreflect")
82 protoregistryPackage = protogen.GoImportPath("google.golang.org/protobuf/reflect/protoregistry")
Joe Tsaid8881392019-06-06 13:01:53 -070083 prototypePackage = protogen.GoImportPath("google.golang.org/protobuf/reflect/prototype")
Joe Tsaic1c17aa2018-11-16 11:14:14 -080084)
Damien Neil46abb572018-09-07 12:45:37 -070085
Damien Neild39efc82018-09-24 12:38:10 -070086type fileInfo struct {
Damien Neilcab8dfe2018-09-06 14:51:28 -070087 *protogen.File
Damien Neil8012b442019-01-18 09:32:24 -080088
Joe Tsaic0e4bb22019-07-06 13:05:11 -070089 allEnums []*protogen.Enum
90 allMessages []*protogen.Message
91 allExtensions []*protogen.Extension
92
93 allEnumsByPtr map[*protogen.Enum]int // value is index into allEnums
94 allMessagesByPtr map[*protogen.Message]int // value is index into allMessages
95 allMessageFieldsByPtr map[*protogen.Message]*structFields
96}
97
98type structFields struct {
99 count int
100 unexported map[int]string
101}
102
103func (sf *structFields) append(name string) {
104 if r, _ := utf8.DecodeRuneInString(name); !unicode.IsUpper(r) {
105 if sf.unexported == nil {
106 sf.unexported = make(map[int]string)
107 }
108 sf.unexported[sf.count] = name
109 }
110 sf.count++
Damien Neilcab8dfe2018-09-06 14:51:28 -0700111}
112
Damien Neil9c420a62018-09-27 15:26:33 -0700113// GenerateFile generates the contents of a .pb.go file.
Joe Tsai19058432019-02-27 21:46:29 -0800114func GenerateFile(gen *protogen.Plugin, file *protogen.File) *protogen.GeneratedFile {
115 filename := file.GeneratedFilenamePrefix + ".pb.go"
116 g := gen.NewGeneratedFile(filename, file.GoImportPath)
Damien Neild39efc82018-09-24 12:38:10 -0700117 f := &fileInfo{
Damien Neilba1159f2018-10-17 12:53:18 -0700118 File: file,
Damien Neilcab8dfe2018-09-06 14:51:28 -0700119 }
120
Damien Neil8012b442019-01-18 09:32:24 -0800121 // Collect all enums, messages, and extensions in "flattened ordering".
Joe Tsaid8881392019-06-06 13:01:53 -0700122 // See filetype.TypeBuilder.
Joe Tsai9667c482018-12-05 15:42:52 -0800123 f.allEnums = append(f.allEnums, f.Enums...)
124 f.allMessages = append(f.allMessages, f.Messages...)
125 f.allExtensions = append(f.allExtensions, f.Extensions...)
126 walkMessages(f.Messages, func(m *protogen.Message) {
127 f.allEnums = append(f.allEnums, m.Enums...)
128 f.allMessages = append(f.allMessages, m.Messages...)
129 f.allExtensions = append(f.allExtensions, m.Extensions...)
Damien Neil73ac8852018-09-17 15:11:24 -0700130 })
Damien Neilce36f8d2018-09-13 15:19:08 -0700131
Joe Tsai9667c482018-12-05 15:42:52 -0800132 // Derive a reverse mapping of enum and message pointers to their index
133 // in allEnums and allMessages.
134 if len(f.allEnums) > 0 {
135 f.allEnumsByPtr = make(map[*protogen.Enum]int)
136 for i, e := range f.allEnums {
137 f.allEnumsByPtr[e] = i
138 }
139 }
140 if len(f.allMessages) > 0 {
141 f.allMessagesByPtr = make(map[*protogen.Message]int)
Joe Tsaic0e4bb22019-07-06 13:05:11 -0700142 f.allMessageFieldsByPtr = make(map[*protogen.Message]*structFields)
Joe Tsai9667c482018-12-05 15:42:52 -0800143 for i, m := range f.allMessages {
144 f.allMessagesByPtr[m] = i
Joe Tsaic0e4bb22019-07-06 13:05:11 -0700145 f.allMessageFieldsByPtr[m] = new(structFields)
Joe Tsai9667c482018-12-05 15:42:52 -0800146 }
147 }
Joe Tsaib6405bd2018-11-15 14:44:37 -0800148
Damien Neil220c2022018-08-15 11:24:18 -0700149 g.P("// Code generated by protoc-gen-go. DO NOT EDIT.")
Damien Neil55fe1c02018-09-17 15:11:24 -0700150 if f.Proto.GetOptions().GetDeprecated() {
151 g.P("// ", f.Desc.Path(), " is a deprecated file.")
152 } else {
153 g.P("// source: ", f.Desc.Path())
154 }
Damien Neil220c2022018-08-15 11:24:18 -0700155 g.P()
Damien Neilba1159f2018-10-17 12:53:18 -0700156 g.PrintLeadingComments(protogen.Location{
157 SourceFile: f.Proto.GetName(),
Joe Tsaica46d8c2019-03-20 16:51:09 -0700158 Path: []int32{fieldnum.FileDescriptorProto_Package},
Damien Neilba1159f2018-10-17 12:53:18 -0700159 })
Damien Neilcab8dfe2018-09-06 14:51:28 -0700160 g.P()
Damien Neil082ce922018-09-06 10:23:53 -0700161 g.P("package ", f.GoPackageName)
Damien Neilc7d07d92018-08-22 13:46:02 -0700162 g.P()
Damien Neil1ec33152018-09-13 13:12:36 -0700163
Joe Tsai5d72cc22019-03-28 01:13:26 -0700164 // Emit a static check that enforces a minimum version of the proto package.
Joe Tsai58b42d82019-05-22 16:27:51 -0400165 g.P("const (")
166 g.P("// Verify that runtime/protoimpl is sufficiently up-to-date.")
167 g.P("_ = ", protoimplPackage.Ident("EnforceVersion"), "(", protoimplPackage.Ident("MaxVersion"), " - ", protoimpl.Version, ")")
168 g.P("// Verify that this generated code is sufficiently up-to-date.")
169 g.P("_ = ", protoimplPackage.Ident("EnforceVersion"), "(", protoimpl.Version, " - ", protoimplPackage.Ident("MinVersion"), ")")
170 g.P(")")
Joe Tsai5d72cc22019-03-28 01:13:26 -0700171 g.P()
172
Damien Neil73ac8852018-09-17 15:11:24 -0700173 for i, imps := 0, f.Desc.Imports(); i < imps.Len(); i++ {
174 genImport(gen, g, f, imps.Get(i))
175 }
Damien Neilce36f8d2018-09-13 15:19:08 -0700176 for _, enum := range f.allEnums {
Damien Neil46abb572018-09-07 12:45:37 -0700177 genEnum(gen, g, f, enum)
178 }
Damien Neilce36f8d2018-09-13 15:19:08 -0700179 for _, message := range f.allMessages {
Damien Neilcab8dfe2018-09-06 14:51:28 -0700180 genMessage(gen, g, f, message)
Damien Neilc7d07d92018-08-22 13:46:02 -0700181 }
Joe Tsaiafb455e2019-03-14 16:08:22 -0700182 genExtensions(gen, g, f)
Damien Neil220c2022018-08-15 11:24:18 -0700183
Joe Tsaib6405bd2018-11-15 14:44:37 -0800184 genReflectFileDescriptor(gen, g, f)
Joe Tsai19058432019-02-27 21:46:29 -0800185
186 return g
Damien Neil7779e052018-09-07 14:14:06 -0700187}
188
Damien Neil73ac8852018-09-17 15:11:24 -0700189// walkMessages calls f on each message and all of its descendants.
190func walkMessages(messages []*protogen.Message, f func(*protogen.Message)) {
191 for _, m := range messages {
192 f(m)
193 walkMessages(m.Messages, f)
194 }
195}
196
Damien Neild39efc82018-09-24 12:38:10 -0700197func genImport(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo, imp protoreflect.FileImport) {
Damien Neil73ac8852018-09-17 15:11:24 -0700198 impFile, ok := gen.FileByName(imp.Path())
199 if !ok {
200 return
201 }
202 if impFile.GoImportPath == f.GoImportPath {
Damien Neil2e0c3da2018-09-19 12:51:36 -0700203 // Don't generate imports or aliases for types in the same Go package.
204 return
205 }
Damien Neil40a08052018-10-29 09:07:41 -0700206 // Generate imports for all non-weak dependencies, even if they are not
Damien Neil2e0c3da2018-09-19 12:51:36 -0700207 // referenced, because other code and tools depend on having the
208 // full transitive closure of protocol buffer types in the binary.
Damien Neil40a08052018-10-29 09:07:41 -0700209 if !imp.IsWeak {
210 g.Import(impFile.GoImportPath)
211 }
Damien Neil2e0c3da2018-09-19 12:51:36 -0700212 if !imp.IsPublic {
Damien Neil73ac8852018-09-17 15:11:24 -0700213 return
214 }
Damien Neil7bf3ce22018-12-21 15:54:06 -0800215
216 // Generate public imports by generating the imported file, parsing it,
217 // and extracting every symbol that should receive a forwarding declaration.
Joe Tsai19058432019-02-27 21:46:29 -0800218 impGen := GenerateFile(gen, impFile)
Damien Neil7bf3ce22018-12-21 15:54:06 -0800219 impGen.Skip()
Damien Neil7bf3ce22018-12-21 15:54:06 -0800220 b, err := impGen.Content()
221 if err != nil {
222 gen.Error(err)
223 return
224 }
225 fset := token.NewFileSet()
226 astFile, err := parser.ParseFile(fset, "", b, parser.ParseComments)
227 if err != nil {
228 gen.Error(err)
229 return
230 }
Damien Neila7cbd062019-01-06 16:29:14 -0800231 genForward := func(tok token.Token, name string, expr ast.Expr) {
Damien Neil7bf3ce22018-12-21 15:54:06 -0800232 // Don't import unexported symbols.
233 r, _ := utf8.DecodeRuneInString(name)
234 if !unicode.IsUpper(r) {
Damien Neil2193e8d2018-10-09 12:49:13 -0700235 return
236 }
Damien Neil7bf3ce22018-12-21 15:54:06 -0800237 // Don't import the FileDescriptor.
238 if name == impFile.GoDescriptorIdent.GoName {
239 return
240 }
Damien Neila7cbd062019-01-06 16:29:14 -0800241 // Don't import decls referencing a symbol defined in another package.
242 // i.e., don't import decls which are themselves public imports:
243 //
244 // type T = somepackage.T
245 if _, ok := expr.(*ast.SelectorExpr); ok {
246 return
247 }
Damien Neil7bf3ce22018-12-21 15:54:06 -0800248 g.P(tok, " ", name, " = ", impFile.GoImportPath.Ident(name))
249 }
250 g.P("// Symbols defined in public import of ", imp.Path())
251 g.P()
252 for _, decl := range astFile.Decls {
253 switch decl := decl.(type) {
254 case *ast.GenDecl:
255 for _, spec := range decl.Specs {
256 switch spec := spec.(type) {
257 case *ast.TypeSpec:
Damien Neila7cbd062019-01-06 16:29:14 -0800258 genForward(decl.Tok, spec.Name.Name, spec.Type)
Damien Neil7bf3ce22018-12-21 15:54:06 -0800259 case *ast.ValueSpec:
Damien Neila7cbd062019-01-06 16:29:14 -0800260 for i, name := range spec.Names {
261 var expr ast.Expr
262 if i < len(spec.Values) {
263 expr = spec.Values[i]
264 }
265 genForward(decl.Tok, name.Name, expr)
Damien Neil7bf3ce22018-12-21 15:54:06 -0800266 }
267 case *ast.ImportSpec:
268 default:
269 panic(fmt.Sprintf("can't generate forward for spec type %T", spec))
Damien Neil7e5c6472018-11-29 08:57:07 -0800270 }
Damien Neil2193e8d2018-10-09 12:49:13 -0700271 }
Damien Neil2193e8d2018-10-09 12:49:13 -0700272 }
Damien Neil6b541312018-10-29 09:14:14 -0700273 }
Damien Neil2193e8d2018-10-09 12:49:13 -0700274 g.P()
Damien Neilce36f8d2018-09-13 15:19:08 -0700275}
276
Damien Neild39efc82018-09-24 12:38:10 -0700277func genEnum(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo, enum *protogen.Enum) {
Joe Tsai61968ce2019-04-01 12:59:24 -0700278 // Enum type declaration.
Damien Neilba1159f2018-10-17 12:53:18 -0700279 g.PrintLeadingComments(enum.Location)
Damien Neil162c1272018-10-04 12:42:37 -0700280 g.Annotate(enum.GoIdent.GoName, enum.Location)
Damien Neil55fe1c02018-09-17 15:11:24 -0700281 g.P("type ", enum.GoIdent, " int32",
Joe Tsaie1f8d502018-11-26 18:55:29 -0800282 deprecationComment(enum.Desc.Options().(*descriptorpb.EnumOptions).GetDeprecated()))
Joe Tsai61968ce2019-04-01 12:59:24 -0700283
284 // Enum value constants.
Damien Neil46abb572018-09-07 12:45:37 -0700285 g.P("const (")
286 for _, value := range enum.Values {
Damien Neilba1159f2018-10-17 12:53:18 -0700287 g.PrintLeadingComments(value.Location)
Damien Neil162c1272018-10-04 12:42:37 -0700288 g.Annotate(value.GoIdent.GoName, value.Location)
Damien Neil55fe1c02018-09-17 15:11:24 -0700289 g.P(value.GoIdent, " ", enum.GoIdent, " = ", value.Desc.Number(),
Joe Tsaie1f8d502018-11-26 18:55:29 -0800290 deprecationComment(value.Desc.Options().(*descriptorpb.EnumValueOptions).GetDeprecated()))
Damien Neil46abb572018-09-07 12:45:37 -0700291 }
292 g.P(")")
293 g.P()
Joe Tsaib6405bd2018-11-15 14:44:37 -0800294
Joe Tsai61968ce2019-04-01 12:59:24 -0700295 // Enum value mapping (number -> name).
Joe Tsaiab61d412019-04-16 15:23:29 -0700296 if generateEnumMapVars {
297 nameMap := enum.GoIdent.GoName + "_name"
Joe Tsaiab61d412019-04-16 15:23:29 -0700298 g.P("var ", nameMap, " = map[int32]string{")
299 generated := make(map[protoreflect.EnumNumber]bool)
300 for _, value := range enum.Values {
301 duplicate := ""
302 if _, present := generated[value.Desc.Number()]; present {
303 duplicate = "// Duplicate value: "
304 }
305 g.P(duplicate, value.Desc.Number(), ": ", strconv.Quote(string(value.Desc.Name())), ",")
306 generated[value.Desc.Number()] = true
Damien Neil46abb572018-09-07 12:45:37 -0700307 }
Joe Tsaiab61d412019-04-16 15:23:29 -0700308 g.P("}")
309 g.P()
Damien Neil46abb572018-09-07 12:45:37 -0700310 }
Joe Tsai8e506a82019-03-16 00:05:34 -0700311
Joe Tsai61968ce2019-04-01 12:59:24 -0700312 // Enum value mapping (name -> number).
Joe Tsaiab61d412019-04-16 15:23:29 -0700313 if generateEnumMapVars {
314 valueMap := enum.GoIdent.GoName + "_value"
Joe Tsaiab61d412019-04-16 15:23:29 -0700315 g.P("var ", valueMap, " = map[string]int32{")
316 for _, value := range enum.Values {
317 g.P(strconv.Quote(string(value.Desc.Name())), ": ", value.Desc.Number(), ",")
318 }
319 g.P("}")
320 g.P()
Damien Neil46abb572018-09-07 12:45:37 -0700321 }
Joe Tsai8e506a82019-03-16 00:05:34 -0700322
Joe Tsai61968ce2019-04-01 12:59:24 -0700323 // Enum method.
Joe Tsaidbab6c02019-05-14 15:06:03 -0700324 //
325 // NOTE: A pointer value is needed to represent presence in proto2.
326 // Since a proto2 message can reference a proto3 enum, it is useful to
327 // always generate this method (even on proto3 enums) to support that case.
328 g.P("func (x ", enum.GoIdent, ") Enum() *", enum.GoIdent, " {")
329 g.P("p := new(", enum.GoIdent, ")")
330 g.P("*p = x")
331 g.P("return p")
332 g.P("}")
333 g.P()
334
Joe Tsai61968ce2019-04-01 12:59:24 -0700335 // String method.
Damien Neil46abb572018-09-07 12:45:37 -0700336 g.P("func (x ", enum.GoIdent, ") String() string {")
Joe Tsai0fc49f82019-05-01 12:29:25 -0700337 g.P("return ", protoimplPackage.Ident("X"), ".EnumStringOf(x.Descriptor(), ", protoreflectPackage.Ident("EnumNumber"), "(x))")
Damien Neil46abb572018-09-07 12:45:37 -0700338 g.P("}")
339 g.P()
340
Joe Tsai61968ce2019-04-01 12:59:24 -0700341 genReflectEnum(gen, g, f, enum)
342
343 // UnmarshalJSON method.
Joe Tsaib3d57df2019-04-17 14:07:32 -0700344 if generateEnumJSONMethods && enum.Desc.Syntax() == protoreflect.Proto2 {
Joe Tsai8e506a82019-03-16 00:05:34 -0700345 g.P("// Deprecated: Do not use.")
346 g.P("func (x *", enum.GoIdent, ") UnmarshalJSON(b []byte) error {")
Joe Tsai0fc49f82019-05-01 12:29:25 -0700347 g.P("num, err := ", protoimplPackage.Ident("X"), ".UnmarshalJSONEnum(x.Descriptor(), b)")
Damien Neil46abb572018-09-07 12:45:37 -0700348 g.P("if err != nil {")
349 g.P("return err")
350 g.P("}")
Joe Tsai8e506a82019-03-16 00:05:34 -0700351 g.P("*x = ", enum.GoIdent, "(num)")
Damien Neil46abb572018-09-07 12:45:37 -0700352 g.P("return nil")
353 g.P("}")
354 g.P()
355 }
356
Joe Tsai61968ce2019-04-01 12:59:24 -0700357 // EnumDescriptor method.
Joe Tsaiab61d412019-04-16 15:23:29 -0700358 if generateRawDescMethods {
359 var indexes []string
360 for i := 1; i < len(enum.Location.Path); i += 2 {
361 indexes = append(indexes, strconv.Itoa(int(enum.Location.Path[i])))
362 }
363 g.P("// Deprecated: Use ", enum.GoIdent, ".Type instead.")
364 g.P("func (", enum.GoIdent, ") EnumDescriptor() ([]byte, []int) {")
365 g.P("return ", rawDescVarName(f), "GZIP(), []int{", strings.Join(indexes, ","), "}")
366 g.P("}")
367 g.P()
Damien Neil46abb572018-09-07 12:45:37 -0700368 }
Damien Neil46abb572018-09-07 12:45:37 -0700369
Damien Neilea7baf42018-09-28 14:23:44 -0700370 genWellKnownType(g, "", enum.GoIdent, enum.Desc)
Damien Neil46abb572018-09-07 12:45:37 -0700371}
372
Joe Tsai61968ce2019-04-01 12:59:24 -0700373// enumLegacyName returns the name used by the v1 proto package.
Damien Neil658051b2018-09-10 12:26:21 -0700374//
375// Confusingly, this is <proto_package>.<go_ident>. This probably should have
376// been the full name of the proto enum type instead, but changing it at this
377// point would require thought.
Joe Tsai61968ce2019-04-01 12:59:24 -0700378func enumLegacyName(enum *protogen.Enum) string {
Joe Tsai67c1d9b2019-05-12 02:27:46 -0700379 fdesc := enum.Desc.ParentFile()
Damien Neildaa4fad2018-10-08 14:08:27 -0700380 if fdesc.Package() == "" {
381 return enum.GoIdent.GoName
382 }
Damien Neil658051b2018-09-10 12:26:21 -0700383 return string(fdesc.Package()) + "." + enum.GoIdent.GoName
384}
385
Damien Neild39efc82018-09-24 12:38:10 -0700386func genMessage(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo, message *protogen.Message) {
Damien Neil0bd5a382018-09-13 15:07:10 -0700387 if message.Desc.IsMapEntry() {
388 return
389 }
390
Joe Tsai61968ce2019-04-01 12:59:24 -0700391 // Message type declaration.
Damien Neilba1159f2018-10-17 12:53:18 -0700392 hasComment := g.PrintLeadingComments(message.Location)
Joe Tsaie1f8d502018-11-26 18:55:29 -0800393 if message.Desc.Options().(*descriptorpb.MessageOptions).GetDeprecated() {
Damien Neil55fe1c02018-09-17 15:11:24 -0700394 if hasComment {
395 g.P("//")
396 }
397 g.P(deprecationComment(true))
398 }
Damien Neil162c1272018-10-04 12:42:37 -0700399 g.Annotate(message.GoIdent.GoName, message.Location)
Damien Neilcab8dfe2018-09-06 14:51:28 -0700400 g.P("type ", message.GoIdent, " struct {")
Joe Tsaic0e4bb22019-07-06 13:05:11 -0700401 sf := f.allMessageFieldsByPtr[message]
Joe Tsai82760ce2019-06-20 03:09:57 -0700402 if generateMessateStateFields {
403 g.P("state ", protoimplPackage.Ident("MessageState"))
404 sf.append("state")
405 }
Damien Neil658051b2018-09-10 12:26:21 -0700406 for _, field := range message.Fields {
Joe Tsaid24bc722019-04-15 23:39:09 -0700407 if field.Oneof != nil {
Damien Neil1fa78d82018-09-13 13:12:36 -0700408 // It would be a bit simpler to iterate over the oneofs below,
409 // but generating the field here keeps the contents of the Go
410 // struct in the same order as the contents of the source
411 // .proto file.
Joe Tsaic0e4bb22019-07-06 13:05:11 -0700412 oneof := field.Oneof
413 if field != oneof.Fields[0] {
414 continue // already generated oneof field for first entry
Damien Neil1fa78d82018-09-13 13:12:36 -0700415 }
Joe Tsaic0e4bb22019-07-06 13:05:11 -0700416 if g.PrintLeadingComments(oneof.Location) {
417 g.P("//")
418 }
419 g.P("// Types that are valid to be assigned to ", oneofFieldName(oneof), ":")
420 for _, field := range oneof.Fields {
421 g.PrintLeadingComments(field.Location)
422 g.P("//\t*", fieldOneofType(field))
423 }
424 g.Annotate(message.GoIdent.GoName+"."+oneofFieldName(oneof), oneof.Location)
425 g.P(oneofFieldName(oneof), " ", oneofInterfaceName(oneof), " `protobuf_oneof:\"", oneof.Desc.Name(), "\"`")
426 sf.append(oneofFieldName(oneof))
Damien Neil658051b2018-09-10 12:26:21 -0700427 continue
428 }
Damien Neilba1159f2018-10-17 12:53:18 -0700429 g.PrintLeadingComments(field.Location)
Damien Neil77f82fe2018-09-13 10:59:17 -0700430 goType, pointer := fieldGoType(g, field)
431 if pointer {
432 goType = "*" + goType
433 }
Damien Neil0bd5a382018-09-13 15:07:10 -0700434 tags := []string{
435 fmt.Sprintf("protobuf:%q", fieldProtobufTag(field)),
436 fmt.Sprintf("json:%q", fieldJSONTag(field)),
437 }
438 if field.Desc.IsMap() {
Joe Tsaid24bc722019-04-15 23:39:09 -0700439 key := field.Message.Fields[0]
440 val := field.Message.Fields[1]
Damien Neil0bd5a382018-09-13 15:07:10 -0700441 tags = append(tags,
442 fmt.Sprintf("protobuf_key:%q", fieldProtobufTag(key)),
443 fmt.Sprintf("protobuf_val:%q", fieldProtobufTag(val)),
444 )
445 }
Damien Neil162c1272018-10-04 12:42:37 -0700446 g.Annotate(message.GoIdent.GoName+"."+field.GoName, field.Location)
Damien Neil55fe1c02018-09-17 15:11:24 -0700447 g.P(field.GoName, " ", goType, " `", strings.Join(tags, " "), "`",
Joe Tsaie1f8d502018-11-26 18:55:29 -0800448 deprecationComment(field.Desc.Options().(*descriptorpb.FieldOptions).GetDeprecated()))
Joe Tsaic0e4bb22019-07-06 13:05:11 -0700449 sf.append(field.GoName)
Damien Neil658051b2018-09-10 12:26:21 -0700450 }
Damien Neil993c04d2018-09-14 15:41:11 -0700451
Joe Tsaic0e4bb22019-07-06 13:05:11 -0700452 if generateNoUnkeyedLiteralFields {
453 g.P("XXX_NoUnkeyedLiteral", " struct{} `json:\"-\"`")
454 sf.append("XXX_NoUnkeyedLiteral")
455 }
456 if generateExportedSizeCacheFields {
457 g.P("XXX_sizecache", " ", protoimplPackage.Ident("SizeCache"), " `json:\"-\"`")
458 sf.append("XXX_sizecache")
459 } else {
460 g.P("sizeCache", " ", protoimplPackage.Ident("SizeCache"))
461 sf.append("sizeCache")
462 }
463 if generateExportedUnknownFields {
464 g.P("XXX_unrecognized", " ", protoimplPackage.Ident("UnknownFields"), " `json:\"-\"`")
465 sf.append("XXX_unrecognized")
466 } else {
467 g.P("unknownFields", " ", protoimplPackage.Ident("UnknownFields"))
468 sf.append("unknownFields")
469 }
Damien Neil993c04d2018-09-14 15:41:11 -0700470 if message.Desc.ExtensionRanges().Len() > 0 {
Joe Tsaic0e4bb22019-07-06 13:05:11 -0700471 if generateExportedExtensionFields {
Joe Tsai6ceeaab2019-07-08 12:31:21 -0700472 g.P("XXX_InternalExtensions", " ", protoimplPackage.Ident("ExtensionFields"), " `json:\"-\"`")
Joe Tsaic0e4bb22019-07-06 13:05:11 -0700473 sf.append("XXX_InternalExtensions")
474 } else {
Joe Tsai6ceeaab2019-07-08 12:31:21 -0700475 g.P("extensionFields", " ", protoimplPackage.Ident("ExtensionFields"))
Joe Tsaic0e4bb22019-07-06 13:05:11 -0700476 sf.append("extensionFields")
477 }
Damien Neil993c04d2018-09-14 15:41:11 -0700478 }
Damien Neilc7d07d92018-08-22 13:46:02 -0700479 g.P("}")
480 g.P()
481
Joe Tsai61968ce2019-04-01 12:59:24 -0700482 // Reset method.
483 g.P("func (x *", message.GoIdent, ") Reset() {")
484 g.P("*x = ", message.GoIdent, "{}")
485 g.P("}")
486 g.P()
487 // String method.
488 g.P("func (x *", message.GoIdent, ") String() string {")
489 g.P("return ", protoimplPackage.Ident("X"), ".MessageStringOf(x)")
490 g.P("}")
491 g.P()
492 // ProtoMessage method.
493 g.P("func (*", message.GoIdent, ") ProtoMessage() {}")
494 g.P()
495
Joe Tsaib6405bd2018-11-15 14:44:37 -0800496 genReflectMessage(gen, g, f, message)
497
Joe Tsai61968ce2019-04-01 12:59:24 -0700498 // Descriptor method.
Joe Tsaiab61d412019-04-16 15:23:29 -0700499 if generateRawDescMethods {
500 var indexes []string
501 for i := 1; i < len(message.Location.Path); i += 2 {
502 indexes = append(indexes, strconv.Itoa(int(message.Location.Path[i])))
503 }
504 g.P("// Deprecated: Use ", message.GoIdent, ".ProtoReflect.Type instead.")
505 g.P("func (*", message.GoIdent, ") Descriptor() ([]byte, []int) {")
506 g.P("return ", rawDescVarName(f), "GZIP(), []int{", strings.Join(indexes, ","), "}")
507 g.P("}")
508 g.P()
Damien Neila1c6abc2018-09-12 13:36:34 -0700509 }
Damien Neil993c04d2018-09-14 15:41:11 -0700510
Joe Tsai61968ce2019-04-01 12:59:24 -0700511 // ExtensionRangeArray method.
Joe Tsaib3d57df2019-04-17 14:07:32 -0700512 if generateExtensionRangeMethods {
513 if extranges := message.Desc.ExtensionRanges(); extranges.Len() > 0 {
514 protoExtRange := protoifacePackage.Ident("ExtensionRangeV1")
515 extRangeVar := "extRange_" + message.GoIdent.GoName
516 g.P("var ", extRangeVar, " = []", protoExtRange, " {")
517 for i := 0; i < extranges.Len(); i++ {
518 r := extranges.Get(i)
519 g.P("{Start:", r[0], ", End:", r[1]-1 /* inclusive */, "},")
520 }
521 g.P("}")
522 g.P()
523 g.P("// Deprecated: Use ", message.GoIdent, ".ProtoReflect.Type.ExtensionRanges instead.")
524 g.P("func (*", message.GoIdent, ") ExtensionRangeArray() []", protoExtRange, " {")
525 g.P("return ", extRangeVar)
526 g.P("}")
527 g.P()
Damien Neil993c04d2018-09-14 15:41:11 -0700528 }
Damien Neil993c04d2018-09-14 15:41:11 -0700529 }
Damien Neila1c6abc2018-09-12 13:36:34 -0700530
Damien Neilea7baf42018-09-28 14:23:44 -0700531 genWellKnownType(g, "*", message.GoIdent, message.Desc)
532
Damien Neilebc699d2018-09-13 08:50:13 -0700533 // Constants and vars holding the default values of fields.
534 for _, field := range message.Fields {
Joe Tsai9667c482018-12-05 15:42:52 -0800535 if !field.Desc.HasDefault() {
Damien Neilebc699d2018-09-13 08:50:13 -0700536 continue
537 }
Damien Neil1fa78d82018-09-13 13:12:36 -0700538 defVarName := "Default_" + message.GoIdent.GoName + "_" + field.GoName
Damien Neilebc699d2018-09-13 08:50:13 -0700539 def := field.Desc.Default()
540 switch field.Desc.Kind() {
541 case protoreflect.StringKind:
542 g.P("const ", defVarName, " string = ", strconv.Quote(def.String()))
543 case protoreflect.BytesKind:
544 g.P("var ", defVarName, " []byte = []byte(", strconv.Quote(string(def.Bytes())), ")")
545 case protoreflect.EnumKind:
Damien Neila485fbd2018-10-26 13:28:37 -0700546 evalueDesc := field.Desc.DefaultEnumValue()
Joe Tsaid24bc722019-04-15 23:39:09 -0700547 enum := field.Enum
Damien Neila485fbd2018-10-26 13:28:37 -0700548 evalue := enum.Values[evalueDesc.Index()]
Joe Tsaid24bc722019-04-15 23:39:09 -0700549 g.P("const ", defVarName, " ", field.Enum.GoIdent, " = ", evalue.GoIdent)
Damien Neilebc699d2018-09-13 08:50:13 -0700550 case protoreflect.FloatKind, protoreflect.DoubleKind:
551 // Floating point numbers need extra handling for -Inf/Inf/NaN.
552 f := field.Desc.Default().Float()
553 goType := "float64"
554 if field.Desc.Kind() == protoreflect.FloatKind {
555 goType = "float32"
556 }
557 // funcCall returns a call to a function in the math package,
558 // possibly converting the result to float32.
559 funcCall := func(fn, param string) string {
Joe Tsaic1c17aa2018-11-16 11:14:14 -0800560 s := g.QualifiedGoIdent(mathPackage.Ident(fn)) + param
Damien Neilebc699d2018-09-13 08:50:13 -0700561 if goType != "float64" {
562 s = goType + "(" + s + ")"
563 }
564 return s
565 }
566 switch {
567 case math.IsInf(f, -1):
568 g.P("var ", defVarName, " ", goType, " = ", funcCall("Inf", "(-1)"))
569 case math.IsInf(f, 1):
570 g.P("var ", defVarName, " ", goType, " = ", funcCall("Inf", "(1)"))
571 case math.IsNaN(f):
572 g.P("var ", defVarName, " ", goType, " = ", funcCall("NaN", "()"))
573 default:
Damien Neil982684b2018-09-28 14:12:41 -0700574 g.P("const ", defVarName, " ", goType, " = ", field.Desc.Default().Interface())
Damien Neilebc699d2018-09-13 08:50:13 -0700575 }
576 default:
Damien Neil77f82fe2018-09-13 10:59:17 -0700577 goType, _ := fieldGoType(g, field)
Damien Neilebc699d2018-09-13 08:50:13 -0700578 g.P("const ", defVarName, " ", goType, " = ", def.Interface())
579 }
580 }
581 g.P()
582
Joe Tsai61968ce2019-04-01 12:59:24 -0700583 // Getter methods.
Damien Neil77f82fe2018-09-13 10:59:17 -0700584 for _, field := range message.Fields {
Joe Tsai872b5002019-04-08 14:03:15 -0700585 if isFirstOneofField(field) {
Joe Tsaid24bc722019-04-15 23:39:09 -0700586 genOneofGetter(gen, g, f, message, field.Oneof)
Damien Neil1fa78d82018-09-13 13:12:36 -0700587 }
Damien Neil77f82fe2018-09-13 10:59:17 -0700588 goType, pointer := fieldGoType(g, field)
589 defaultValue := fieldDefaultValue(g, message, field)
Joe Tsaie1f8d502018-11-26 18:55:29 -0800590 if field.Desc.Options().(*descriptorpb.FieldOptions).GetDeprecated() {
Damien Neil55fe1c02018-09-17 15:11:24 -0700591 g.P(deprecationComment(true))
592 }
Damien Neil162c1272018-10-04 12:42:37 -0700593 g.Annotate(message.GoIdent.GoName+".Get"+field.GoName, field.Location)
Joe Tsai61968ce2019-04-01 12:59:24 -0700594 g.P("func (x *", message.GoIdent, ") Get", field.GoName, "() ", goType, " {")
Joe Tsaid24bc722019-04-15 23:39:09 -0700595 if field.Oneof != nil {
596 g.P("if x, ok := x.Get", field.Oneof.GoName, "().(*", fieldOneofType(field), "); ok {")
Damien Neil1fa78d82018-09-13 13:12:36 -0700597 g.P("return x.", field.GoName)
598 g.P("}")
Damien Neil77f82fe2018-09-13 10:59:17 -0700599 } else {
Damien Neil1fa78d82018-09-13 13:12:36 -0700600 if field.Desc.Syntax() == protoreflect.Proto3 || defaultValue == "nil" {
Joe Tsai61968ce2019-04-01 12:59:24 -0700601 g.P("if x != nil {")
Damien Neil1fa78d82018-09-13 13:12:36 -0700602 } else {
Joe Tsai61968ce2019-04-01 12:59:24 -0700603 g.P("if x != nil && x.", field.GoName, " != nil {")
Damien Neil1fa78d82018-09-13 13:12:36 -0700604 }
605 star := ""
606 if pointer {
607 star = "*"
608 }
Joe Tsai61968ce2019-04-01 12:59:24 -0700609 g.P("return ", star, " x.", field.GoName)
Damien Neil1fa78d82018-09-13 13:12:36 -0700610 g.P("}")
Damien Neil77f82fe2018-09-13 10:59:17 -0700611 }
Damien Neil77f82fe2018-09-13 10:59:17 -0700612 g.P("return ", defaultValue)
613 g.P("}")
614 g.P()
615 }
Damien Neila1c6abc2018-09-12 13:36:34 -0700616
Joe Tsai09912272019-07-08 10:38:11 -0700617 // Oneof wrapper types.
Damien Neil1fa78d82018-09-13 13:12:36 -0700618 if len(message.Oneofs) > 0 {
Joe Tsaid7e97bc2018-11-26 12:57:27 -0800619 genOneofWrappers(gen, g, f, message)
Damien Neil1fa78d82018-09-13 13:12:36 -0700620 }
Damien Neilc7d07d92018-08-22 13:46:02 -0700621}
Damien Neilcab8dfe2018-09-06 14:51:28 -0700622
Damien Neil77f82fe2018-09-13 10:59:17 -0700623// fieldGoType returns the Go type used for a field.
624//
625// If it returns pointer=true, the struct field is a pointer to the type.
626func fieldGoType(g *protogen.GeneratedFile, field *protogen.Field) (goType string, pointer bool) {
Damien Neil77f82fe2018-09-13 10:59:17 -0700627 pointer = true
Damien Neil658051b2018-09-10 12:26:21 -0700628 switch field.Desc.Kind() {
629 case protoreflect.BoolKind:
Damien Neil77f82fe2018-09-13 10:59:17 -0700630 goType = "bool"
Damien Neil658051b2018-09-10 12:26:21 -0700631 case protoreflect.EnumKind:
Joe Tsaid24bc722019-04-15 23:39:09 -0700632 goType = g.QualifiedGoIdent(field.Enum.GoIdent)
Damien Neil658051b2018-09-10 12:26:21 -0700633 case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind:
Damien Neil77f82fe2018-09-13 10:59:17 -0700634 goType = "int32"
Damien Neil658051b2018-09-10 12:26:21 -0700635 case protoreflect.Uint32Kind, protoreflect.Fixed32Kind:
Damien Neil77f82fe2018-09-13 10:59:17 -0700636 goType = "uint32"
Damien Neil658051b2018-09-10 12:26:21 -0700637 case protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Sfixed64Kind:
Damien Neil77f82fe2018-09-13 10:59:17 -0700638 goType = "int64"
Damien Neil658051b2018-09-10 12:26:21 -0700639 case protoreflect.Uint64Kind, protoreflect.Fixed64Kind:
Damien Neil77f82fe2018-09-13 10:59:17 -0700640 goType = "uint64"
Damien Neil658051b2018-09-10 12:26:21 -0700641 case protoreflect.FloatKind:
Damien Neil77f82fe2018-09-13 10:59:17 -0700642 goType = "float32"
Damien Neil658051b2018-09-10 12:26:21 -0700643 case protoreflect.DoubleKind:
Damien Neil77f82fe2018-09-13 10:59:17 -0700644 goType = "float64"
Damien Neil658051b2018-09-10 12:26:21 -0700645 case protoreflect.StringKind:
Damien Neil77f82fe2018-09-13 10:59:17 -0700646 goType = "string"
Damien Neil658051b2018-09-10 12:26:21 -0700647 case protoreflect.BytesKind:
Damien Neil77f82fe2018-09-13 10:59:17 -0700648 goType = "[]byte"
649 pointer = false
Damien Neil658051b2018-09-10 12:26:21 -0700650 case protoreflect.MessageKind, protoreflect.GroupKind:
Joe Tsaid24bc722019-04-15 23:39:09 -0700651 goType = "*" + g.QualifiedGoIdent(field.Message.GoIdent)
Damien Neil77f82fe2018-09-13 10:59:17 -0700652 pointer = false
Damien Neil658051b2018-09-10 12:26:21 -0700653 }
Joe Tsaiac31a352019-05-13 14:32:56 -0700654 switch {
655 case field.Desc.IsList():
Damien Neil77f82fe2018-09-13 10:59:17 -0700656 goType = "[]" + goType
657 pointer = false
Joe Tsaiac31a352019-05-13 14:32:56 -0700658 case field.Desc.IsMap():
659 keyType, _ := fieldGoType(g, field.Message.Fields[0])
660 valType, _ := fieldGoType(g, field.Message.Fields[1])
661 return fmt.Sprintf("map[%v]%v", keyType, valType), false
Damien Neil658051b2018-09-10 12:26:21 -0700662 }
Joe Tsaiac31a352019-05-13 14:32:56 -0700663
Damien Neil44000a12018-10-24 12:31:16 -0700664 // Extension fields always have pointer type, even when defined in a proto3 file.
Joe Tsaiac31a352019-05-13 14:32:56 -0700665 if field.Desc.Syntax() == protoreflect.Proto3 && !field.Desc.IsExtension() {
Damien Neil77f82fe2018-09-13 10:59:17 -0700666 pointer = false
Damien Neil658051b2018-09-10 12:26:21 -0700667 }
Damien Neil77f82fe2018-09-13 10:59:17 -0700668 return goType, pointer
Damien Neil658051b2018-09-10 12:26:21 -0700669}
670
671func fieldProtobufTag(field *protogen.Field) string {
Joe Tsai05828db2018-11-01 13:52:16 -0700672 var enumName string
Damien Neil658051b2018-09-10 12:26:21 -0700673 if field.Desc.Kind() == protoreflect.EnumKind {
Joe Tsaid24bc722019-04-15 23:39:09 -0700674 enumName = enumLegacyName(field.Enum)
Damien Neil658051b2018-09-10 12:26:21 -0700675 }
Joe Tsai05828db2018-11-01 13:52:16 -0700676 return tag.Marshal(field.Desc, enumName)
Damien Neil658051b2018-09-10 12:26:21 -0700677}
678
Damien Neil77f82fe2018-09-13 10:59:17 -0700679func fieldDefaultValue(g *protogen.GeneratedFile, message *protogen.Message, field *protogen.Field) string {
Joe Tsaiac31a352019-05-13 14:32:56 -0700680 if field.Desc.IsList() {
Damien Neil77f82fe2018-09-13 10:59:17 -0700681 return "nil"
682 }
Joe Tsai9667c482018-12-05 15:42:52 -0800683 if field.Desc.HasDefault() {
Damien Neil1fa78d82018-09-13 13:12:36 -0700684 defVarName := "Default_" + message.GoIdent.GoName + "_" + field.GoName
Damien Neil77f82fe2018-09-13 10:59:17 -0700685 if field.Desc.Kind() == protoreflect.BytesKind {
686 return "append([]byte(nil), " + defVarName + "...)"
687 }
688 return defVarName
689 }
690 switch field.Desc.Kind() {
691 case protoreflect.BoolKind:
692 return "false"
693 case protoreflect.StringKind:
694 return `""`
695 case protoreflect.MessageKind, protoreflect.GroupKind, protoreflect.BytesKind:
696 return "nil"
697 case protoreflect.EnumKind:
Joe Tsaid24bc722019-04-15 23:39:09 -0700698 return g.QualifiedGoIdent(field.Enum.Values[0].GoIdent)
Damien Neil77f82fe2018-09-13 10:59:17 -0700699 default:
700 return "0"
701 }
702}
703
Damien Neil658051b2018-09-10 12:26:21 -0700704func fieldJSONTag(field *protogen.Field) string {
705 return string(field.Desc.Name()) + ",omitempty"
706}
707
Joe Tsaiafb455e2019-03-14 16:08:22 -0700708func genExtensions(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo) {
709 if len(f.allExtensions) == 0 {
710 return
Damien Neil154da982018-09-19 13:21:58 -0700711 }
712
Joe Tsaid8881392019-06-06 13:01:53 -0700713 g.P("var ", extDescsVarName(f), " = []", protoifacePackage.Ident("ExtensionDescV1"), "{")
Joe Tsaiafb455e2019-03-14 16:08:22 -0700714 for _, extension := range f.allExtensions {
Joe Tsaiafb455e2019-03-14 16:08:22 -0700715 g.P("{")
Joe Tsaid24bc722019-04-15 23:39:09 -0700716 g.P("ExtendedType: (*", extension.Extendee.GoIdent, ")(nil),")
Joe Tsaiafb455e2019-03-14 16:08:22 -0700717 goType, pointer := fieldGoType(g, extension)
718 if pointer {
719 goType = "*" + goType
720 }
721 g.P("ExtensionType: (", goType, ")(nil),")
722 g.P("Field: ", extension.Desc.Number(), ",")
Joe Tsai6ceeaab2019-07-08 12:31:21 -0700723 g.P("Name: ", strconv.Quote(string(extension.Desc.FullName())), ",")
Joe Tsaiafb455e2019-03-14 16:08:22 -0700724 g.P("Tag: ", strconv.Quote(fieldProtobufTag(extension)), ",")
725 g.P("Filename: ", strconv.Quote(f.Desc.Path()), ",")
726 g.P("},")
Damien Neil993c04d2018-09-14 15:41:11 -0700727 }
Damien Neil993c04d2018-09-14 15:41:11 -0700728 g.P("}")
Joe Tsaiafb455e2019-03-14 16:08:22 -0700729
730 g.P("var (")
731 for i, extension := range f.allExtensions {
732 ed := extension.Desc
Joe Tsaiac31a352019-05-13 14:32:56 -0700733 targetName := string(ed.ContainingMessage().FullName())
Joe Tsaiafb455e2019-03-14 16:08:22 -0700734 typeName := ed.Kind().String()
735 switch ed.Kind() {
736 case protoreflect.EnumKind:
Joe Tsaid24bc722019-04-15 23:39:09 -0700737 typeName = string(ed.Enum().FullName())
Joe Tsaiafb455e2019-03-14 16:08:22 -0700738 case protoreflect.MessageKind, protoreflect.GroupKind:
Joe Tsaid24bc722019-04-15 23:39:09 -0700739 typeName = string(ed.Message().FullName())
Joe Tsaiafb455e2019-03-14 16:08:22 -0700740 }
741 fieldName := string(ed.Name())
742 g.P("// extend ", targetName, " { ", ed.Cardinality().String(), " ", typeName, " ", fieldName, " = ", ed.Number(), "; }")
Joe Tsaid8881392019-06-06 13:01:53 -0700743 g.P(extensionVar(f.File, extension), " = &", extDescsVarName(f), "[", i, "]")
Joe Tsaiafb455e2019-03-14 16:08:22 -0700744 g.P()
745 }
746 g.P(")")
Damien Neil993c04d2018-09-14 15:41:11 -0700747}
748
749// extensionVar returns the var holding the ExtensionDesc for an extension.
Damien Neil6b541312018-10-29 09:14:14 -0700750func extensionVar(f *protogen.File, extension *protogen.Extension) protogen.GoIdent {
Damien Neil993c04d2018-09-14 15:41:11 -0700751 name := "E_"
Joe Tsaid24bc722019-04-15 23:39:09 -0700752 if extension.Parent != nil {
753 name += extension.Parent.GoIdent.GoName + "_"
Damien Neil993c04d2018-09-14 15:41:11 -0700754 }
755 name += extension.GoName
Joe Tsaic1c17aa2018-11-16 11:14:14 -0800756 return f.GoImportPath.Ident(name)
Damien Neil993c04d2018-09-14 15:41:11 -0700757}
758
Damien Neil55fe1c02018-09-17 15:11:24 -0700759// deprecationComment returns a standard deprecation comment if deprecated is true.
760func deprecationComment(deprecated bool) string {
761 if !deprecated {
762 return ""
763 }
764 return "// Deprecated: Do not use."
765}
766
Damien Neilea7baf42018-09-28 14:23:44 -0700767func genWellKnownType(g *protogen.GeneratedFile, ptr string, ident protogen.GoIdent, desc protoreflect.Descriptor) {
Joe Tsai5455ef52019-07-08 13:49:48 -0700768 if generateWKTMarkerMethods && wellKnownTypes[desc.FullName()] {
Damien Neilea7baf42018-09-28 14:23:44 -0700769 g.P("func (", ptr, ident, `) XXX_WellKnownType() string { return "`, desc.Name(), `" }`)
Damien Neil46abb572018-09-07 12:45:37 -0700770 g.P()
771 }
772}
773
774// Names of messages and enums for which we will generate XXX_WellKnownType methods.
775var wellKnownTypes = map[protoreflect.FullName]bool{
Damien Neilea7baf42018-09-28 14:23:44 -0700776 "google.protobuf.Any": true,
777 "google.protobuf.Duration": true,
778 "google.protobuf.Empty": true,
779 "google.protobuf.Struct": true,
780 "google.protobuf.Timestamp": true,
781
782 "google.protobuf.BoolValue": true,
783 "google.protobuf.BytesValue": true,
784 "google.protobuf.DoubleValue": true,
785 "google.protobuf.FloatValue": true,
786 "google.protobuf.Int32Value": true,
787 "google.protobuf.Int64Value": true,
788 "google.protobuf.ListValue": true,
789 "google.protobuf.NullValue": true,
790 "google.protobuf.StringValue": true,
791 "google.protobuf.UInt32Value": true,
792 "google.protobuf.UInt64Value": true,
793 "google.protobuf.Value": true,
Damien Neil46abb572018-09-07 12:45:37 -0700794}
Joe Tsaid7e97bc2018-11-26 12:57:27 -0800795
Joe Tsai872b5002019-04-08 14:03:15 -0700796// genOneofGetter generate a Get method for a oneof.
797func genOneofGetter(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo, message *protogen.Message, oneof *protogen.Oneof) {
798 g.Annotate(message.GoIdent.GoName+".Get"+oneof.GoName, oneof.Location)
799 g.P("func (m *", message.GoIdent.GoName, ") Get", oneof.GoName, "() ", oneofInterfaceName(oneof), " {")
800 g.P("if m != nil {")
801 g.P("return m.", oneofFieldName(oneof))
802 g.P("}")
803 g.P("return nil")
804 g.P("}")
805 g.P()
806}
807
Joe Tsai09912272019-07-08 10:38:11 -0700808// genOneofWrappers generates the oneof wrapper types and associates the types
809// with the parent message type.
Joe Tsai872b5002019-04-08 14:03:15 -0700810func genOneofWrappers(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo, message *protogen.Message) {
Joe Tsai09912272019-07-08 10:38:11 -0700811 idx := f.allMessagesByPtr[message]
812 typesVar := messageTypesVarName(f)
813
814 // Associate the wrapper types through a XXX_OneofWrappers method.
815 if generateOneofWrapperMethods {
816 g.P("// XXX_OneofWrappers is for the internal use of the proto package.")
817 g.P("func (*", message.GoIdent.GoName, ") XXX_OneofWrappers() []interface{} {")
818 g.P("return ", typesVar, "[", idx, "].OneofWrappers")
819 g.P("}")
820 g.P()
Joe Tsai872b5002019-04-08 14:03:15 -0700821 }
Joe Tsai09912272019-07-08 10:38:11 -0700822
823 // Generate the oneof wrapper types.
824 for _, oneof := range message.Oneofs {
825 genOneofTypes(gen, g, f, message, oneof)
826 }
Joe Tsai872b5002019-04-08 14:03:15 -0700827}
828
Joe Tsaid7e97bc2018-11-26 12:57:27 -0800829// genOneofTypes generates the interface type used for a oneof field,
830// and the wrapper types that satisfy that interface.
Joe Tsaid7e97bc2018-11-26 12:57:27 -0800831func genOneofTypes(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo, message *protogen.Message, oneof *protogen.Oneof) {
832 ifName := oneofInterfaceName(oneof)
833 g.P("type ", ifName, " interface {")
834 g.P(ifName, "()")
835 g.P("}")
836 g.P()
837 for _, field := range oneof.Fields {
838 name := fieldOneofType(field)
839 g.Annotate(name.GoName, field.Location)
840 g.Annotate(name.GoName+"."+field.GoName, field.Location)
841 g.P("type ", name, " struct {")
842 goType, _ := fieldGoType(g, field)
843 tags := []string{
844 fmt.Sprintf("protobuf:%q", fieldProtobufTag(field)),
845 }
846 g.P(field.GoName, " ", goType, " `", strings.Join(tags, " "), "`")
847 g.P("}")
848 g.P()
849 }
850 for _, field := range oneof.Fields {
851 g.P("func (*", fieldOneofType(field), ") ", ifName, "() {}")
852 g.P()
853 }
Joe Tsai872b5002019-04-08 14:03:15 -0700854}
855
856// isFirstOneofField reports whether this is the first field in a oneof.
857func isFirstOneofField(field *protogen.Field) bool {
Joe Tsaid24bc722019-04-15 23:39:09 -0700858 return field.Oneof != nil && field.Oneof.Fields[0] == field
Joe Tsaid7e97bc2018-11-26 12:57:27 -0800859}
860
861// oneofFieldName returns the name of the struct field holding the oneof value.
862//
863// This function is trivial, but pulling out the name like this makes it easier
864// to experiment with alternative oneof implementations.
865func oneofFieldName(oneof *protogen.Oneof) string {
866 return oneof.GoName
867}
868
869// oneofInterfaceName returns the name of the interface type implemented by
870// the oneof field value types.
871func oneofInterfaceName(oneof *protogen.Oneof) string {
Joe Tsaid24bc722019-04-15 23:39:09 -0700872 return fmt.Sprintf("is%s_%s", oneof.Parent.GoIdent.GoName, oneof.GoName)
Joe Tsaid7e97bc2018-11-26 12:57:27 -0800873}
874
Joe Tsaid7e97bc2018-11-26 12:57:27 -0800875// fieldOneofType returns the wrapper type used to represent a field in a oneof.
876func fieldOneofType(field *protogen.Field) protogen.GoIdent {
877 ident := protogen.GoIdent{
Joe Tsaid24bc722019-04-15 23:39:09 -0700878 GoImportPath: field.Parent.GoIdent.GoImportPath,
879 GoName: field.Parent.GoIdent.GoName + "_" + field.GoName,
Joe Tsaid7e97bc2018-11-26 12:57:27 -0800880 }
881 // Check for collisions with nested messages or enums.
882 //
883 // This conflict resolution is incomplete: Among other things, it
884 // does not consider collisions with other oneof field types.
885 //
886 // TODO: Consider dropping this entirely. Detecting conflicts and
887 // producing an error is almost certainly better than permuting
888 // field and type names in mostly unpredictable ways.
889Loop:
890 for {
Joe Tsaid24bc722019-04-15 23:39:09 -0700891 for _, message := range field.Parent.Messages {
Joe Tsaid7e97bc2018-11-26 12:57:27 -0800892 if message.GoIdent == ident {
893 ident.GoName += "_"
894 continue Loop
895 }
896 }
Joe Tsaid24bc722019-04-15 23:39:09 -0700897 for _, enum := range field.Parent.Enums {
Joe Tsaid7e97bc2018-11-26 12:57:27 -0800898 if enum.GoIdent == ident {
899 ident.GoName += "_"
900 continue Loop
901 }
902 }
903 return ident
904 }
905}