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