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