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  }