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