github.com/Big-big-orange/protoreflect@v0.0.0-20240408141420-285cedfdf6a4/desc/internal/proto3_optional.go (about) 1 package internal 2 3 import ( 4 "strings" 5 6 "github.com/golang/protobuf/proto" 7 "google.golang.org/protobuf/types/descriptorpb" 8 ) 9 10 // ProcessProto3OptionalFields adds synthetic oneofs to the given message descriptor 11 // for each proto3 optional field. It also updates the fields to have the correct 12 // oneof index reference. The given callback, if not nil, is called for each synthetic 13 // oneof created. 14 func ProcessProto3OptionalFields(msgd *descriptorpb.DescriptorProto, callback func(*descriptorpb.FieldDescriptorProto, *descriptorpb.OneofDescriptorProto)) { 15 var allNames map[string]struct{} 16 for _, fd := range msgd.Field { 17 if fd.GetProto3Optional() { 18 // lazy init the set of all names 19 if allNames == nil { 20 allNames = map[string]struct{}{} 21 for _, fd := range msgd.Field { 22 allNames[fd.GetName()] = struct{}{} 23 } 24 for _, od := range msgd.OneofDecl { 25 allNames[od.GetName()] = struct{}{} 26 } 27 // NB: protoc only considers names of other fields and oneofs 28 // when computing the synthetic oneof name. But that feels like 29 // a bug, since it means it could generate a name that conflicts 30 // with some other symbol defined in the message. If it's decided 31 // that's NOT a bug and is desirable, then we should remove the 32 // following four loops to mimic protoc's behavior. 33 for _, xd := range msgd.Extension { 34 allNames[xd.GetName()] = struct{}{} 35 } 36 for _, ed := range msgd.EnumType { 37 allNames[ed.GetName()] = struct{}{} 38 for _, evd := range ed.Value { 39 allNames[evd.GetName()] = struct{}{} 40 } 41 } 42 for _, fd := range msgd.NestedType { 43 allNames[fd.GetName()] = struct{}{} 44 } 45 for _, n := range msgd.ReservedName { 46 allNames[n] = struct{}{} 47 } 48 } 49 50 // Compute a name for the synthetic oneof. This uses the same 51 // algorithm as used in protoc: 52 // https://github.com/protocolbuffers/protobuf/blob/74ad62759e0a9b5a21094f3fb9bb4ebfaa0d1ab8/src/google/protobuf/compiler/parser.cc#L785-L803 53 ooName := fd.GetName() 54 if !strings.HasPrefix(ooName, "_") { 55 ooName = "_" + ooName 56 } 57 for { 58 _, ok := allNames[ooName] 59 if !ok { 60 // found a unique name 61 allNames[ooName] = struct{}{} 62 break 63 } 64 ooName = "X" + ooName 65 } 66 67 fd.OneofIndex = proto.Int32(int32(len(msgd.OneofDecl))) 68 ood := &descriptorpb.OneofDescriptorProto{Name: proto.String(ooName)} 69 msgd.OneofDecl = append(msgd.OneofDecl, ood) 70 if callback != nil { 71 callback(fd, ood) 72 } 73 } 74 } 75 }