Skip to content

Commit

Permalink
support editions in desc/builder package (#605)
Browse files Browse the repository at this point in the history
  • Loading branch information
jhump authored Apr 9, 2024
1 parent de71523 commit aee3749
Show file tree
Hide file tree
Showing 8 changed files with 110 additions and 25 deletions.
17 changes: 12 additions & 5 deletions desc/builder/builder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -342,10 +342,17 @@ func TestProto3Optional(t *testing.T) {
}

func TestBuildersFromDescriptors(t *testing.T) {
for _, s := range []string{"desc_test1.proto", "desc_test2.proto", "desc_test_defaults.proto", "desc_test_options.proto", "desc_test_proto3.proto", "desc_test_wellknowntypes.proto", "nopkg/desc_test_nopkg.proto", "nopkg/desc_test_nopkg_new.proto", "pkg/desc_test_pkg.proto"} {
fd, err := desc.LoadFileDescriptor(s)
testutil.Ok(t, err)
roundTripFile(t, fd)
for _, s := range []string{
"desc_test1.proto", "desc_test2.proto",
"desc_test_defaults.proto", "desc_test_editions.proto", "desc_test_options.proto",
"desc_test_proto3.proto", "desc_test_wellknowntypes.proto",
"nopkg/desc_test_nopkg.proto", "nopkg/desc_test_nopkg_new.proto", "pkg/desc_test_pkg.proto",
} {
t.Run(s, func(t *testing.T) {
fd, err := desc.LoadFileDescriptor(s)
testutil.Ok(t, err)
roundTripFile(t, fd)
})
}
}

Expand Down Expand Up @@ -1641,7 +1648,7 @@ func TestInvalid(t *testing.T) {
NewMessage("Foo").AddField(NewField("foo", FieldTypeBool()).SetRequired()),
)
},
expectedError: "proto3 does not allow required fields",
expectedError: "only proto2 allows required fields",
},
{
name: "extension range in proto3",
Expand Down
25 changes: 19 additions & 6 deletions desc/builder/field.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,23 @@ type FieldBuilder struct {
msgType *MessageBuilder
fieldType *FieldType

Options *descriptorpb.FieldOptions
Label descriptorpb.FieldDescriptorProto_Label
Options *descriptorpb.FieldOptions
Label descriptorpb.FieldDescriptorProto_Label

// Proto3Optional indicates if the field is a proto3 optional field. This
// only applies to fields in files with "proto3" syntax whose Cardinality
// is set to protoreflect.Optional.
//
// If the file's syntax is not "proto3", this may not be set to true.
//
// This allows setting a field in a proto3 file to have explicit field
// presence. To manage field presence for fields in files that use
// "editions", set the field_presence field of the features option in
// Options.
Proto3Optional bool
Default string
JsonName string

Default string
JsonName string

foreignExtendee *desc.MessageDescriptor
localExtendee *MessageBuilder
Expand Down Expand Up @@ -496,6 +508,7 @@ func (flb *FieldBuilder) buildProto(path []int32, sourceInfo *descriptorpb.Sourc
addCommentsTo(sourceInfo, path, &flb.comments)

isProto3 := flb.GetFile().IsProto3
isEditions := flb.GetFile().Edition > 0
if flb.Proto3Optional {
if !isProto3 {
return nil, fmt.Errorf("field %s is not in a proto3 syntax file but is marked as a proto3 optional field", GetFullyQualifiedName(flb))
Expand All @@ -510,8 +523,8 @@ func (flb *FieldBuilder) buildProto(path []int32, sourceInfo *descriptorpb.Sourc

var lbl *descriptorpb.FieldDescriptorProto_Label
if int32(flb.Label) != 0 {
if isProto3 && flb.Label == descriptorpb.FieldDescriptorProto_LABEL_REQUIRED {
return nil, fmt.Errorf("field %s: proto3 does not allow required fields", GetFullyQualifiedName(flb))
if (isProto3 || isEditions) && flb.Label == descriptorpb.FieldDescriptorProto_LABEL_REQUIRED {
return nil, fmt.Errorf("field %s: only proto2 allows required fields", GetFullyQualifiedName(flb))
}
lbl = flb.Label.Enum()
}
Expand Down
51 changes: 47 additions & 4 deletions desc/builder/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,17 @@ func makeUnique(name string, existingNames map[string]struct{}) string {
type FileBuilder struct {
name string

// IsProto3 indicates that the file's syntax is "proto3". If this is
// false the file either uses syntax "proto2" (when Edition is zero)
// or uses editions (when Edition is non-zero).
IsProto3 bool
Package string
Options *descriptorpb.FileOptions

// Edition indicates which edition this file uses. It may only be
// set when IsProto3 is false.
Edition descriptorpb.Edition

Package string
Options *descriptorpb.FileOptions

comments Comments
SyntaxComments Comments
Expand Down Expand Up @@ -82,14 +90,15 @@ func NewFile(name string) *FileBuilder {
func FromFile(fd *desc.FileDescriptor) (*FileBuilder, error) {
fb := NewFile(fd.GetName())
fb.IsProto3 = fd.IsProto3()
fb.Edition = fd.Edition()
fb.Package = fd.GetPackage()
fb.Options = fd.GetFileOptions()
setComments(&fb.comments, fd.GetSourceInfo())

// find syntax and package comments, too
for _, loc := range fd.AsFileDescriptorProto().GetSourceCodeInfo().GetLocation() {
if len(loc.Path) == 1 {
if loc.Path[0] == internal.File_syntaxTag {
if loc.Path[0] == internal.File_syntaxTag || loc.Path[0] == internal.File_editionTag {
setComments(&fb.SyntaxComments, loc)
} else if loc.Path[0] == internal.File_packageTag {
setComments(&fb.PackageComments, loc)
Expand Down Expand Up @@ -649,9 +658,31 @@ func (fb *FileBuilder) SetPackageName(pkg string) *FileBuilder {
}

// SetProto3 sets whether this file is declared to use "proto3" syntax or not
// and returns the file, for method chaining.
// and returns the file, for method chaining. If this is called with a value
// of false, then "proto2" syntax is assumed. To instead set the file to use
// editions, call SetEdition.
func (fb *FileBuilder) SetProto3(isProto3 bool) *FileBuilder {
fb.IsProto3 = isProto3
fb.Edition = 0
return fb
}

// SetEdition sets the edition that this file uses and returns the file, for
// method chaining. This supports the use of the EDITION_PROTO2 and EDITION_PROTO3
// values to actually set the value as "proto2" or "proto3" syntax respectively.
// If a value less than EDITION_PROTO2 is provided, the invalid value is ignored
// and the file is instead set to use "proto2" syntax.
func (fb *FileBuilder) SetEdition(edition descriptorpb.Edition) *FileBuilder {
if edition <= descriptorpb.Edition_EDITION_PROTO2 {
fb.IsProto3 = false
fb.Edition = 0
} else if edition == descriptorpb.Edition_EDITION_PROTO3 {
fb.IsProto3 = true
fb.Edition = 0
} else {
fb.IsProto3 = false
fb.Edition = edition
}
return fb
}

Expand All @@ -661,8 +692,19 @@ func (fb *FileBuilder) buildProto(deps []*desc.FileDescriptor) (*descriptorpb.Fi
name = uniqueFileName()
}
var syntax *string
var edition *descriptorpb.Edition
if fb.IsProto3 {
syntax = proto.String("proto3")
} else if fb.Edition > 0 {
if fb.Edition < descriptorpb.Edition_EDITION_2023 ||
fb.Edition >= descriptorpb.Edition_EDITION_MAX ||
descriptorpb.Edition_name[int32(fb.Edition)] == "" ||
strings.HasSuffix(fb.Edition.String(), "_TEST_ONLY") {
// Not a valid edition!
return nil, fmt.Errorf("builder contains unknown or invalid edition: %v", fb.Edition)
}
syntax = proto.String("editions")
edition = fb.Edition.Enum()
}
var pkg *string
if fb.Package != "" {
Expand Down Expand Up @@ -727,6 +769,7 @@ func (fb *FileBuilder) buildProto(deps []*desc.FileDescriptor) (*descriptorpb.Fi
Dependency: imports,
Options: fb.Options,
Syntax: syntax,
Edition: edition,
MessageType: messages,
EnumType: enums,
Extension: extensions,
Expand Down
12 changes: 12 additions & 0 deletions desc/descriptor.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,10 +155,22 @@ func (fd *FileDescriptor) String() string {
}

// IsProto3 returns true if the file declares a syntax of "proto3".
//
// When this returns false, the file is either syntax "proto2" (if
// Edition() returns zero) or the file uses editions.
func (fd *FileDescriptor) IsProto3() bool {
return fd.wrapped.Syntax() == protoreflect.Proto3
}

// Edition returns the edition of the file. If the file does not
// use editions syntax, zero is returned.
func (fd *FileDescriptor) Edition() descriptorpb.Edition {
if fd.wrapped.Syntax() == protoreflect.Editions {
return fd.proto.GetEdition()
}
return 0
}

// GetDependencies returns all of this file's dependencies. These correspond to
// import statements in the file.
func (fd *FileDescriptor) GetDependencies() []*FileDescriptor {
Expand Down
3 changes: 3 additions & 0 deletions desc/internal/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ const (
// File_syntaxTag is the tag number of the syntax element in a file
// descriptor proto.
File_syntaxTag = 12
// File_editionTag is the tag number of the edition element in a file
// descriptor proto.
File_editionTag = 14
// Message_nameTag is the tag number of the name element in a message
// descriptor proto.
Message_nameTag = 1
Expand Down
9 changes: 8 additions & 1 deletion desc/sourceinfo/wrappers.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package sourceinfo

import (
"fmt"

"google.golang.org/protobuf/reflect/protoreflect"
)

Expand All @@ -16,6 +15,14 @@ type fileDescriptor struct {
locs protoreflect.SourceLocations
}

func (f fileDescriptor) Edition() int32 {
ed, ok := f.FileDescriptor.(interface{ Edition() int32 })
if ok {
return ed.Edition()
}
return 0
}

func (f fileDescriptor) ParentFile() protoreflect.FileDescriptor {
return f
}
Expand Down
6 changes: 3 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ require (
github.com/jhump/gopoet v0.1.0
github.com/jhump/goprotoc v0.5.0
google.golang.org/grpc v1.61.0
google.golang.org/protobuf v1.33.1-0.20240319125436-3039476726e4
google.golang.org/protobuf v1.33.1-0.20240408130810-98873a205002
)

require (
golang.org/x/net v0.18.0 // indirect
golang.org/x/net v0.22.0 // indirect
golang.org/x/sync v0.6.0 // indirect
golang.org/x/sys v0.14.0 // indirect
golang.org/x/sys v0.18.0 // indirect
golang.org/x/text v0.14.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17 // indirect
)
12 changes: 6 additions & 6 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@ golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73r
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg=
golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ=
golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc=
golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
Expand All @@ -73,8 +73,8 @@ golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5h
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q=
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
Expand Down Expand Up @@ -110,8 +110,8 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.33.1-0.20240319125436-3039476726e4 h1:fea3X9JPnW4oM9z1ctAuAN7kAnM/YbdI7QHCZXKLVMk=
google.golang.org/protobuf v1.33.1-0.20240319125436-3039476726e4/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
google.golang.org/protobuf v1.33.1-0.20240408130810-98873a205002 h1:V7Da7qt0MkY3noVANIMVBk28nOnijADeOR3i5Hcvpj4=
google.golang.org/protobuf v1.33.1-0.20240408130810-98873a205002/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
Expand Down

0 comments on commit aee3749

Please sign in to comment.