all: implement strip_enum_prefix editions feature

This change required updating editions_defaults.binpb with
--edition_defaults_maximum=2024 so that we can use edition 2024 in our
testdata/enumprefix.proto test file.

For end users, this feature will only be available once:
1. protoc declares support for edition 2024 (expected early 2025).
2. protoc-gen-go declares support for edition 2024.

related to golang/protobuf#513

Change-Id: Ib8daeecae39ef32eff942279707d256c312f2a53
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/618979
Reviewed-by: Lasse Folger <lassefolger@google.com>
Reviewed-by: Mike Kruskal <mkruskal@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Damien Neil <dneil@google.com>
This commit is contained in:
Michael Stapelberg 2024-10-10 15:15:19 +02:00
parent fb995f184a
commit d14ebce888
16 changed files with 626 additions and 28 deletions

View File

@ -0,0 +1,29 @@
// Copyright 2024 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"testing"
"google.golang.org/protobuf/cmd/protoc-gen-go/testdata/enumprefix"
)
func TestStripEnumPrefix(t *testing.T) {
// The file default for enumprefix.proto is to strip prefixes:
_ = enumprefix.Strip_ZERO
// The enum Both should generate both names:
_ = enumprefix.Both_ZERO
_ = enumprefix.Both_BOTH_ZERO
// The enum BothNoPrefix is configured to generate both names, but there is
// no prefix to be stripped, so only an unmodified name is generated.
_ = enumprefix.BothNoPrefix_ZERO
// The enum BothButOne is configured to generate both names, except for the
// ONE value, where only the prefixed name is generated.
_ = enumprefix.BothButOne_ZERO
_ = enumprefix.BothButOne_BOTH_BUT_ONE_ONE
}

View File

@ -260,6 +260,7 @@ func genEnum(g *protogen.GeneratedFile, f *fileInfo, e *enumInfo) {
// Enum value constants.
g.P("const (")
anyOldName := false
for _, value := range e.Values {
g.AnnotateSymbol(value.GoIdent.GoName, protogen.Annotation{Location: value.Location})
leadingComments := appendDeprecationSuffix(value.Comments.Leading,
@ -268,9 +269,26 @@ func genEnum(g *protogen.GeneratedFile, f *fileInfo, e *enumInfo) {
g.P(leadingComments,
value.GoIdent, " ", e.GoIdent, " = ", value.Desc.Number(),
trailingComment(value.Comments.Trailing))
if value.PrefixedAlias.GoName != "" &&
value.PrefixedAlias.GoName != value.GoIdent.GoName {
anyOldName = true
}
}
g.P(")")
g.P()
if anyOldName {
g.P("// Old (prefixed) names for ", e.GoIdent, " enum values.")
g.P("const (")
for _, value := range e.Values {
if value.PrefixedAlias.GoName != "" &&
value.PrefixedAlias.GoName != value.GoIdent.GoName {
g.P(value.PrefixedAlias, " ", e.GoIdent, " = ", value.GoIdent)
}
}
g.P(")")
g.P()
}
// Enum value maps.
g.P("// Enum value maps for ", e.GoIdent, ".")

View File

@ -0,0 +1,294 @@
// Copyright 2024 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: cmd/protoc-gen-go/testdata/enumprefix/enumprefix.proto
package enumprefix
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
_ "google.golang.org/protobuf/types/gofeaturespb"
reflect "reflect"
sync "sync"
)
type Strip int32
const (
Strip_ZERO Strip = 0
Strip_ONE Strip = 1
)
// Enum value maps for Strip.
var (
Strip_name = map[int32]string{
0: "STRIP_ZERO",
1: "STRIP_ONE",
}
Strip_value = map[string]int32{
"STRIP_ZERO": 0,
"STRIP_ONE": 1,
}
)
func (x Strip) Enum() *Strip {
p := new(Strip)
*p = x
return p
}
func (x Strip) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
func (Strip) Descriptor() protoreflect.EnumDescriptor {
return file_cmd_protoc_gen_go_testdata_enumprefix_enumprefix_proto_enumTypes[0].Descriptor()
}
func (Strip) Type() protoreflect.EnumType {
return &file_cmd_protoc_gen_go_testdata_enumprefix_enumprefix_proto_enumTypes[0]
}
func (x Strip) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x)
}
// Deprecated: Use Strip.Descriptor instead.
func (Strip) EnumDescriptor() ([]byte, []int) {
return file_cmd_protoc_gen_go_testdata_enumprefix_enumprefix_proto_rawDescGZIP(), []int{0}
}
type Both int32
const (
Both_ZERO Both = 0
Both_ONE Both = 1
)
// Old (prefixed) names for Both enum values.
const (
Both_BOTH_ZERO Both = Both_ZERO
Both_BOTH_ONE Both = Both_ONE
)
// Enum value maps for Both.
var (
Both_name = map[int32]string{
0: "BOTH_ZERO",
1: "BOTH_ONE",
}
Both_value = map[string]int32{
"BOTH_ZERO": 0,
"BOTH_ONE": 1,
}
)
func (x Both) Enum() *Both {
p := new(Both)
*p = x
return p
}
func (x Both) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
func (Both) Descriptor() protoreflect.EnumDescriptor {
return file_cmd_protoc_gen_go_testdata_enumprefix_enumprefix_proto_enumTypes[1].Descriptor()
}
func (Both) Type() protoreflect.EnumType {
return &file_cmd_protoc_gen_go_testdata_enumprefix_enumprefix_proto_enumTypes[1]
}
func (x Both) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x)
}
// Deprecated: Use Both.Descriptor instead.
func (Both) EnumDescriptor() ([]byte, []int) {
return file_cmd_protoc_gen_go_testdata_enumprefix_enumprefix_proto_rawDescGZIP(), []int{1}
}
type BothNoPrefix int32
const (
BothNoPrefix_ZERO BothNoPrefix = 0
BothNoPrefix_ONE BothNoPrefix = 1
)
// Enum value maps for BothNoPrefix.
var (
BothNoPrefix_name = map[int32]string{
0: "ZERO",
1: "ONE",
}
BothNoPrefix_value = map[string]int32{
"ZERO": 0,
"ONE": 1,
}
)
func (x BothNoPrefix) Enum() *BothNoPrefix {
p := new(BothNoPrefix)
*p = x
return p
}
func (x BothNoPrefix) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
func (BothNoPrefix) Descriptor() protoreflect.EnumDescriptor {
return file_cmd_protoc_gen_go_testdata_enumprefix_enumprefix_proto_enumTypes[2].Descriptor()
}
func (BothNoPrefix) Type() protoreflect.EnumType {
return &file_cmd_protoc_gen_go_testdata_enumprefix_enumprefix_proto_enumTypes[2]
}
func (x BothNoPrefix) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x)
}
// Deprecated: Use BothNoPrefix.Descriptor instead.
func (BothNoPrefix) EnumDescriptor() ([]byte, []int) {
return file_cmd_protoc_gen_go_testdata_enumprefix_enumprefix_proto_rawDescGZIP(), []int{2}
}
type BothButOne int32
const (
BothButOne_ZERO BothButOne = 0
BothButOne_BOTH_BUT_ONE_ONE BothButOne = 1
)
// Old (prefixed) names for BothButOne enum values.
const (
BothButOne_BOTH_BUT_ONE_ZERO BothButOne = BothButOne_ZERO
)
// Enum value maps for BothButOne.
var (
BothButOne_name = map[int32]string{
0: "BOTH_BUT_ONE_ZERO",
1: "BOTH_BUT_ONE_ONE",
}
BothButOne_value = map[string]int32{
"BOTH_BUT_ONE_ZERO": 0,
"BOTH_BUT_ONE_ONE": 1,
}
)
func (x BothButOne) Enum() *BothButOne {
p := new(BothButOne)
*p = x
return p
}
func (x BothButOne) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
func (BothButOne) Descriptor() protoreflect.EnumDescriptor {
return file_cmd_protoc_gen_go_testdata_enumprefix_enumprefix_proto_enumTypes[3].Descriptor()
}
func (BothButOne) Type() protoreflect.EnumType {
return &file_cmd_protoc_gen_go_testdata_enumprefix_enumprefix_proto_enumTypes[3]
}
func (x BothButOne) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x)
}
// Deprecated: Use BothButOne.Descriptor instead.
func (BothButOne) EnumDescriptor() ([]byte, []int) {
return file_cmd_protoc_gen_go_testdata_enumprefix_enumprefix_proto_rawDescGZIP(), []int{3}
}
var File_cmd_protoc_gen_go_testdata_enumprefix_enumprefix_proto protoreflect.FileDescriptor
var file_cmd_protoc_gen_go_testdata_enumprefix_enumprefix_proto_rawDesc = []byte{
0x0a, 0x36, 0x63, 0x6d, 0x64, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x2d, 0x67, 0x65, 0x6e,
0x2d, 0x67, 0x6f, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x64, 0x61, 0x74, 0x61, 0x2f, 0x65, 0x6e, 0x75,
0x6d, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x2f, 0x65, 0x6e, 0x75, 0x6d, 0x70, 0x72, 0x65, 0x66,
0x69, 0x78, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x19, 0x67, 0x6f, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x2e, 0x65, 0x6e, 0x75, 0x6d, 0x70, 0x72, 0x65,
0x66, 0x69, 0x78, 0x1a, 0x21, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x62, 0x75, 0x66, 0x2f, 0x67, 0x6f, 0x5f, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73,
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2a, 0x26, 0x0a, 0x05, 0x53, 0x74, 0x72, 0x69, 0x70, 0x12,
0x0e, 0x0a, 0x0a, 0x53, 0x54, 0x52, 0x49, 0x50, 0x5f, 0x5a, 0x45, 0x52, 0x4f, 0x10, 0x00, 0x12,
0x0d, 0x0a, 0x09, 0x53, 0x54, 0x52, 0x49, 0x50, 0x5f, 0x4f, 0x4e, 0x45, 0x10, 0x01, 0x2a, 0x2c,
0x0a, 0x04, 0x42, 0x6f, 0x74, 0x68, 0x12, 0x0d, 0x0a, 0x09, 0x42, 0x4f, 0x54, 0x48, 0x5f, 0x5a,
0x45, 0x52, 0x4f, 0x10, 0x00, 0x12, 0x0c, 0x0a, 0x08, 0x42, 0x4f, 0x54, 0x48, 0x5f, 0x4f, 0x4e,
0x45, 0x10, 0x01, 0x1a, 0x07, 0x3a, 0x05, 0xd2, 0x3e, 0x02, 0x18, 0x02, 0x2a, 0x2a, 0x0a, 0x0c,
0x42, 0x6f, 0x74, 0x68, 0x4e, 0x6f, 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, 0x12, 0x08, 0x0a, 0x04,
0x5a, 0x45, 0x52, 0x4f, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x4f, 0x4e, 0x45, 0x10, 0x01, 0x1a,
0x07, 0x3a, 0x05, 0xd2, 0x3e, 0x02, 0x18, 0x02, 0x2a, 0x4b, 0x0a, 0x0a, 0x42, 0x6f, 0x74, 0x68,
0x42, 0x75, 0x74, 0x4f, 0x6e, 0x65, 0x12, 0x15, 0x0a, 0x11, 0x42, 0x4f, 0x54, 0x48, 0x5f, 0x42,
0x55, 0x54, 0x5f, 0x4f, 0x4e, 0x45, 0x5f, 0x5a, 0x45, 0x52, 0x4f, 0x10, 0x00, 0x12, 0x1d, 0x0a,
0x10, 0x42, 0x4f, 0x54, 0x48, 0x5f, 0x42, 0x55, 0x54, 0x5f, 0x4f, 0x4e, 0x45, 0x5f, 0x4f, 0x4e,
0x45, 0x10, 0x01, 0x1a, 0x07, 0x12, 0x05, 0xd2, 0x3e, 0x02, 0x18, 0x01, 0x1a, 0x07, 0x3a, 0x05,
0xd2, 0x3e, 0x02, 0x18, 0x02, 0x42, 0x4a, 0x5a, 0x40, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,
0x67, 0x6f, 0x6c, 0x61, 0x6e, 0x67, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x62, 0x75, 0x66, 0x2f, 0x63, 0x6d, 0x64, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x2d, 0x67,
0x65, 0x6e, 0x2d, 0x67, 0x6f, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x64, 0x61, 0x74, 0x61, 0x2f, 0x65,
0x6e, 0x75, 0x6d, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x92, 0x03, 0x05, 0xd2, 0x3e, 0x02, 0x18,
0x03, 0x62, 0x08, 0x65, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x70, 0xe9, 0x07,
}
var (
file_cmd_protoc_gen_go_testdata_enumprefix_enumprefix_proto_rawDescOnce sync.Once
file_cmd_protoc_gen_go_testdata_enumprefix_enumprefix_proto_rawDescData = file_cmd_protoc_gen_go_testdata_enumprefix_enumprefix_proto_rawDesc
)
func file_cmd_protoc_gen_go_testdata_enumprefix_enumprefix_proto_rawDescGZIP() []byte {
file_cmd_protoc_gen_go_testdata_enumprefix_enumprefix_proto_rawDescOnce.Do(func() {
file_cmd_protoc_gen_go_testdata_enumprefix_enumprefix_proto_rawDescData = protoimpl.X.CompressGZIP(file_cmd_protoc_gen_go_testdata_enumprefix_enumprefix_proto_rawDescData)
})
return file_cmd_protoc_gen_go_testdata_enumprefix_enumprefix_proto_rawDescData
}
var file_cmd_protoc_gen_go_testdata_enumprefix_enumprefix_proto_enumTypes = make([]protoimpl.EnumInfo, 4)
var file_cmd_protoc_gen_go_testdata_enumprefix_enumprefix_proto_goTypes = []any{
(Strip)(0), // 0: goproto.protoc.enumprefix.Strip
(Both)(0), // 1: goproto.protoc.enumprefix.Both
(BothNoPrefix)(0), // 2: goproto.protoc.enumprefix.BothNoPrefix
(BothButOne)(0), // 3: goproto.protoc.enumprefix.BothButOne
}
var file_cmd_protoc_gen_go_testdata_enumprefix_enumprefix_proto_depIdxs = []int32{
0, // [0:0] is the sub-list for method output_type
0, // [0:0] is the sub-list for method input_type
0, // [0:0] is the sub-list for extension type_name
0, // [0:0] is the sub-list for extension extendee
0, // [0:0] is the sub-list for field type_name
}
func init() { file_cmd_protoc_gen_go_testdata_enumprefix_enumprefix_proto_init() }
func file_cmd_protoc_gen_go_testdata_enumprefix_enumprefix_proto_init() {
if File_cmd_protoc_gen_go_testdata_enumprefix_enumprefix_proto != nil {
return
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_cmd_protoc_gen_go_testdata_enumprefix_enumprefix_proto_rawDesc,
NumEnums: 4,
NumMessages: 0,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_cmd_protoc_gen_go_testdata_enumprefix_enumprefix_proto_goTypes,
DependencyIndexes: file_cmd_protoc_gen_go_testdata_enumprefix_enumprefix_proto_depIdxs,
EnumInfos: file_cmd_protoc_gen_go_testdata_enumprefix_enumprefix_proto_enumTypes,
}.Build()
File_cmd_protoc_gen_go_testdata_enumprefix_enumprefix_proto = out.File
file_cmd_protoc_gen_go_testdata_enumprefix_enumprefix_proto_rawDesc = nil
file_cmd_protoc_gen_go_testdata_enumprefix_enumprefix_proto_goTypes = nil
file_cmd_protoc_gen_go_testdata_enumprefix_enumprefix_proto_depIdxs = nil
}

View File

@ -0,0 +1,39 @@
// Copyright 2024 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
edition = "2024";
package goproto.protoc.enumprefix;
import "google/protobuf/go_features.proto";
option go_package = "google.golang.org/protobuf/cmd/protoc-gen-go/testdata/enumprefix";
option features.(pb.go).strip_enum_prefix = STRIP_ENUM_PREFIX_STRIP;
enum Strip {
STRIP_ZERO = 0;
STRIP_ONE = 1;
}
enum Both {
option features.(pb.go).strip_enum_prefix = STRIP_ENUM_PREFIX_GENERATE_BOTH;
BOTH_ZERO = 0;
BOTH_ONE = 1;
}
enum BothNoPrefix {
option features.(pb.go).strip_enum_prefix = STRIP_ENUM_PREFIX_GENERATE_BOTH;
ZERO = 0;
ONE = 1;
}
enum BothButOne {
option features.(pb.go).strip_enum_prefix = STRIP_ENUM_PREFIX_GENERATE_BOTH;
BOTH_BUT_ONE_ZERO = 0;
BOTH_BUT_ONE_ONE = 1
[features.(pb.go).strip_enum_prefix = STRIP_ENUM_PREFIX_KEEP];
}

View File

@ -9,6 +9,7 @@ package main
import (
_ "google.golang.org/protobuf/cmd/protoc-gen-go/testdata/annotations"
_ "google.golang.org/protobuf/cmd/protoc-gen-go/testdata/comments"
_ "google.golang.org/protobuf/cmd/protoc-gen-go/testdata/enumprefix"
_ "google.golang.org/protobuf/cmd/protoc-gen-go/testdata/extensions/base"
_ "google.golang.org/protobuf/cmd/protoc-gen-go/testdata/extensions/ext"
_ "google.golang.org/protobuf/cmd/protoc-gen-go/testdata/extensions/extra"

View File

@ -28,6 +28,7 @@ import (
"strings"
"google.golang.org/protobuf/encoding/prototext"
"google.golang.org/protobuf/internal/filedesc"
"google.golang.org/protobuf/internal/genid"
"google.golang.org/protobuf/internal/strs"
"google.golang.org/protobuf/proto"
@ -37,6 +38,7 @@ import (
"google.golang.org/protobuf/types/descriptorpb"
"google.golang.org/protobuf/types/dynamicpb"
"google.golang.org/protobuf/types/gofeaturespb"
"google.golang.org/protobuf/types/pluginpb"
)
@ -574,6 +576,12 @@ type EnumValue struct {
GoIdent GoIdent // name of the generated Go declaration
// PrefixedAlias is usually empty, except when the strip_enum_prefix feature
// for this enum was set to GENERATE_BOTH, in which case PrefixedAlias holds
// the old name which should be generated as an alias for the new name for
// compatibility.
PrefixedAlias GoIdent
Parent *Enum // enum in which this value is declared
Location Location // location of this enum value
@ -590,14 +598,46 @@ func newEnumValue(gen *Plugin, f *File, message *Message, enum *Enum, desc proto
parentIdent = message.GoIdent
}
name := parentIdent.GoName + "_" + string(desc.Name())
var prefixedName string
loc := enum.Location.appendPath(genid.EnumDescriptorProto_Value_field_number, desc.Index())
return &EnumValue{
if ed, ok := enum.Desc.(*filedesc.Enum); ok {
prefix := strings.Replace(strings.ToLower(string(enum.Desc.Name())), "_", "", -1)
// Start with the StripEnumPrefix of the enum descriptor,
// then override it with the StripEnumPrefix of the enum value descriptor,
// if any.
sep := ed.L1.EditionFeatures.StripEnumPrefix
evof := desc.Options().(*descriptorpb.EnumValueOptions).GetFeatures()
if proto.HasExtension(evof, gofeaturespb.E_Go) {
gf := proto.GetExtension(evof, gofeaturespb.E_Go).(*gofeaturespb.GoFeatures)
if gf.StripEnumPrefix != nil {
sep = int(*gf.StripEnumPrefix)
}
}
switch sep {
case genid.GoFeatures_STRIP_ENUM_PREFIX_KEEP_enum_value:
// keep long name
case genid.GoFeatures_STRIP_ENUM_PREFIX_STRIP_enum_value:
name = parentIdent.GoName + "_" + strs.TrimEnumPrefix(string(desc.Name()), prefix)
case genid.GoFeatures_STRIP_ENUM_PREFIX_GENERATE_BOTH_enum_value:
prefixedName = name
name = parentIdent.GoName + "_" + strs.TrimEnumPrefix(string(desc.Name()), prefix)
}
}
ev := &EnumValue{
Desc: desc,
GoIdent: f.GoImportPath.Ident(name),
Parent: enum,
Location: loc,
Comments: makeCommentSet(gen, f.Desc.SourceLocations().ByDescriptor(desc)),
}
if prefixedName != "" {
ev.PrefixedAlias = f.GoImportPath.Ident(prefixedName)
}
return ev
}
// A Message describes a message.

View File

@ -16,6 +16,7 @@ import (
"path"
"path/filepath"
"regexp"
"slices"
"sort"
"strconv"
"strings"
@ -95,9 +96,13 @@ func main() {
panic("protobuf source root is not set")
}
// Generate editions_defaults.binpb first before generating any code for
// protos: the .proto files might specify a very recent edition for which
// editions_default.binpb was not yet updated.
generateEditionsDefaults()
generateLocalProtos()
generateRemoteProtos()
generateEditionsDefaults()
}
func generateEditionsDefaults() {
@ -108,7 +113,7 @@ func generateEditionsDefaults() {
// the flag in the form "${EDITION}". To work around this, we trim the
// "EDITION_" prefix.
minEdition := strings.TrimPrefix(fmt.Sprint(editionssupport.Minimum), "EDITION_")
maxEdition := strings.TrimPrefix(fmt.Sprint(editionssupport.Maximum), "EDITION_")
maxEdition := strings.TrimPrefix(fmt.Sprint(editionssupport.MaximumKnown), "EDITION_")
cmd := exec.Command(
"protoc",
"--edition_defaults_out", dest,
@ -279,8 +284,15 @@ func protoc(args ...string) {
cmd := exec.Command(
"protoc",
"--plugin=protoc-gen-go="+os.Args[0],
"--experimental_allow_proto3_optional",
"--experimental_editions")
"--experimental_allow_proto3_optional")
if slices.ContainsFunc(args, func(s string) bool {
return strings.Contains(s, "cmd/protoc-gen-go/testdata/") ||
strings.Contains(s, "internal/testprotos/")
}) {
// Our testprotos use edition features of upcoming editions that protoc
// has not yet declared support for:
cmd.Args = append(cmd.Args, "--experimental_editions")
}
cmd.Args = append(cmd.Args, args...)
cmd.Env = append(os.Environ(), "RUN_AS_PROTOC_PLUGIN=1")
out, err := cmd.CombinedOutput()

View File

@ -10,4 +10,9 @@ import "google.golang.org/protobuf/types/descriptorpb"
const (
Minimum = descriptorpb.Edition_EDITION_PROTO2
Maximum = descriptorpb.Edition_EDITION_2023
// MaximumKnown is the maximum edition that is known to Go Protobuf, but not
// declared as supported. In other words: end users cannot use it, but
// testprotos inside Go Protobuf can.
MaximumKnown = descriptorpb.Edition_EDITION_2024
)

View File

@ -32,6 +32,7 @@ const (
EditionProto2 Edition = 998
EditionProto3 Edition = 999
Edition2023 Edition = 1000
Edition2024 Edition = 1001
EditionUnsupported Edition = 100000
)
@ -77,28 +78,42 @@ type (
Locations SourceLocations
}
// EditionFeatures is a frequently-instantiated struct, so please take care
// to minimize padding when adding new fields to this struct (add them in
// the right place/order).
EditionFeatures struct {
// StripEnumPrefix determines if the plugin generates enum value
// constants as-is, with their prefix stripped, or both variants.
StripEnumPrefix int
// IsFieldPresence is true if field_presence is EXPLICIT
// https://protobuf.dev/editions/features/#field_presence
IsFieldPresence bool
// IsFieldPresence is true if field_presence is LEGACY_REQUIRED
// https://protobuf.dev/editions/features/#field_presence
IsLegacyRequired bool
// IsOpenEnum is true if enum_type is OPEN
// https://protobuf.dev/editions/features/#enum_type
IsOpenEnum bool
// IsPacked is true if repeated_field_encoding is PACKED
// https://protobuf.dev/editions/features/#repeated_field_encoding
IsPacked bool
// IsUTF8Validated is true if utf_validation is VERIFY
// https://protobuf.dev/editions/features/#utf8_validation
IsUTF8Validated bool
// IsDelimitedEncoded is true if message_encoding is DELIMITED
// https://protobuf.dev/editions/features/#message_encoding
IsDelimitedEncoded bool
// IsJSONCompliant is true if json_format is ALLOW
// https://protobuf.dev/editions/features/#json_format
IsJSONCompliant bool
// GenerateLegacyUnmarshalJSON determines if the plugin generates the
// UnmarshalJSON([]byte) error method for enums.
GenerateLegacyUnmarshalJSON bool

View File

@ -32,6 +32,10 @@ func unmarshalGoFeature(b []byte, parent EditionFeatures) EditionFeatures {
v, m := protowire.ConsumeVarint(b)
b = b[m:]
parent.GenerateLegacyUnmarshalJSON = protowire.DecodeBool(v)
case genid.GoFeatures_StripEnumPrefix_field_number:
v, m := protowire.ConsumeVarint(b)
b = b[m:]
parent.StripEnumPrefix = int(v)
default:
panic(fmt.Sprintf("unkown field number %d while unmarshalling GoFeatures", num))
}

View File

@ -21,13 +21,30 @@ const (
// Field names for pb.GoFeatures.
const (
GoFeatures_LegacyUnmarshalJsonEnum_field_name protoreflect.Name = "legacy_unmarshal_json_enum"
GoFeatures_StripEnumPrefix_field_name protoreflect.Name = "strip_enum_prefix"
GoFeatures_LegacyUnmarshalJsonEnum_field_fullname protoreflect.FullName = "pb.GoFeatures.legacy_unmarshal_json_enum"
GoFeatures_StripEnumPrefix_field_fullname protoreflect.FullName = "pb.GoFeatures.strip_enum_prefix"
)
// Field numbers for pb.GoFeatures.
const (
GoFeatures_LegacyUnmarshalJsonEnum_field_number protoreflect.FieldNumber = 1
GoFeatures_StripEnumPrefix_field_number protoreflect.FieldNumber = 3
)
// Full and short names for pb.GoFeatures.StripEnumPrefix.
const (
GoFeatures_StripEnumPrefix_enum_fullname = "pb.GoFeatures.StripEnumPrefix"
GoFeatures_StripEnumPrefix_enum_name = "StripEnumPrefix"
)
// Enum values for pb.GoFeatures.StripEnumPrefix.
const (
GoFeatures_STRIP_ENUM_PREFIX_UNSPECIFIED_enum_value = 0
GoFeatures_STRIP_ENUM_PREFIX_KEEP_enum_value = 1
GoFeatures_STRIP_ENUM_PREFIX_GENERATE_BOTH_enum_value = 2
GoFeatures_STRIP_ENUM_PREFIX_STRIP_enum_value = 3
)
// Extension numbers

View File

@ -13,6 +13,8 @@
package protodesc
import (
"strings"
"google.golang.org/protobuf/internal/editionssupport"
"google.golang.org/protobuf/internal/errors"
"google.golang.org/protobuf/internal/filedesc"
@ -102,13 +104,17 @@ func (o FileOptions) New(fd *descriptorpb.FileDescriptorProto, r Resolver) (prot
default:
return nil, errors.New("invalid syntax: %q", fd.GetSyntax())
}
if f.L1.Syntax == protoreflect.Editions && (fd.GetEdition() < editionssupport.Minimum || fd.GetEdition() > editionssupport.Maximum) {
return nil, errors.New("use of edition %v not yet supported by the Go Protobuf runtime", fd.GetEdition())
}
f.L1.Path = fd.GetName()
if f.L1.Path == "" {
return nil, errors.New("file path must be populated")
}
if f.L1.Syntax == protoreflect.Editions && (fd.GetEdition() < editionssupport.Minimum || fd.GetEdition() > editionssupport.Maximum) {
// Allow cmd/protoc-gen-go/testdata to use any edition for easier
// testing of upcoming edition features.
if !strings.HasPrefix(fd.GetName(), "cmd/protoc-gen-go/testdata/") {
return nil, errors.New("use of edition %v not yet supported by the Go Protobuf runtime", fd.GetEdition())
}
}
f.L1.Package = protoreflect.FullName(fd.GetPackage())
if !f.L1.Package.IsValid() && f.L1.Package != "" {
return nil, errors.New("invalid package: %q", f.L1.Package)

View File

@ -43,6 +43,8 @@ func toEditionProto(ed filedesc.Edition) descriptorpb.Edition {
return descriptorpb.Edition_EDITION_PROTO3
case filedesc.Edition2023:
return descriptorpb.Edition_EDITION_2023
case filedesc.Edition2024:
return descriptorpb.Edition_EDITION_2024
default:
panic(fmt.Sprintf("unknown value for edition: %v", ed))
}
@ -127,6 +129,9 @@ func mergeEditionFeatures(parentDesc protoreflect.Descriptor, child *descriptorp
if luje := goFeatures.LegacyUnmarshalJsonEnum; luje != nil {
parentFS.GenerateLegacyUnmarshalJSON = *luje
}
if sep := goFeatures.StripEnumPrefix; sep != nil {
parentFS.StripEnumPrefix = int(*sep)
}
}
return parentFS

View File

@ -32,4 +32,26 @@ message GoFeatures {
edition_defaults = { edition: EDITION_LEGACY, value: "true" },
edition_defaults = { edition: EDITION_PROTO3, value: "false" }
];
enum StripEnumPrefix {
STRIP_ENUM_PREFIX_UNSPECIFIED = 0;
STRIP_ENUM_PREFIX_KEEP = 1;
STRIP_ENUM_PREFIX_GENERATE_BOTH = 2;
STRIP_ENUM_PREFIX_STRIP = 3;
}
optional StripEnumPrefix strip_enum_prefix = 3 [
retention = RETENTION_RUNTIME,
targets = TARGET_TYPE_ENUM,
targets = TARGET_TYPE_ENUM_ENTRY,
targets = TARGET_TYPE_FILE,
feature_support = {
edition_introduced: EDITION_2024,
},
// TODO: change the default to STRIP_ENUM_PREFIX_STRIP for edition 2025.
edition_defaults = {
edition: EDITION_LEGACY,
value: "STRIP_ENUM_PREFIX_KEEP"
}
];
}

View File

@ -18,13 +18,76 @@ import (
sync "sync"
)
type GoFeatures_StripEnumPrefix int32
const (
GoFeatures_STRIP_ENUM_PREFIX_UNSPECIFIED GoFeatures_StripEnumPrefix = 0
GoFeatures_STRIP_ENUM_PREFIX_KEEP GoFeatures_StripEnumPrefix = 1
GoFeatures_STRIP_ENUM_PREFIX_GENERATE_BOTH GoFeatures_StripEnumPrefix = 2
GoFeatures_STRIP_ENUM_PREFIX_STRIP GoFeatures_StripEnumPrefix = 3
)
// Enum value maps for GoFeatures_StripEnumPrefix.
var (
GoFeatures_StripEnumPrefix_name = map[int32]string{
0: "STRIP_ENUM_PREFIX_UNSPECIFIED",
1: "STRIP_ENUM_PREFIX_KEEP",
2: "STRIP_ENUM_PREFIX_GENERATE_BOTH",
3: "STRIP_ENUM_PREFIX_STRIP",
}
GoFeatures_StripEnumPrefix_value = map[string]int32{
"STRIP_ENUM_PREFIX_UNSPECIFIED": 0,
"STRIP_ENUM_PREFIX_KEEP": 1,
"STRIP_ENUM_PREFIX_GENERATE_BOTH": 2,
"STRIP_ENUM_PREFIX_STRIP": 3,
}
)
func (x GoFeatures_StripEnumPrefix) Enum() *GoFeatures_StripEnumPrefix {
p := new(GoFeatures_StripEnumPrefix)
*p = x
return p
}
func (x GoFeatures_StripEnumPrefix) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
func (GoFeatures_StripEnumPrefix) Descriptor() protoreflect.EnumDescriptor {
return file_google_protobuf_go_features_proto_enumTypes[0].Descriptor()
}
func (GoFeatures_StripEnumPrefix) Type() protoreflect.EnumType {
return &file_google_protobuf_go_features_proto_enumTypes[0]
}
func (x GoFeatures_StripEnumPrefix) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x)
}
// Deprecated: Do not use.
func (x *GoFeatures_StripEnumPrefix) UnmarshalJSON(b []byte) error {
num, err := protoimpl.X.UnmarshalJSONEnum(x.Descriptor(), b)
if err != nil {
return err
}
*x = GoFeatures_StripEnumPrefix(num)
return nil
}
// Deprecated: Use GoFeatures_StripEnumPrefix.Descriptor instead.
func (GoFeatures_StripEnumPrefix) EnumDescriptor() ([]byte, []int) {
return file_google_protobuf_go_features_proto_rawDescGZIP(), []int{0, 0}
}
type GoFeatures struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// Whether or not to generate the deprecated UnmarshalJSON method for enums.
LegacyUnmarshalJsonEnum *bool `protobuf:"varint,1,opt,name=legacy_unmarshal_json_enum,json=legacyUnmarshalJsonEnum" json:"legacy_unmarshal_json_enum,omitempty"`
LegacyUnmarshalJsonEnum *bool `protobuf:"varint,1,opt,name=legacy_unmarshal_json_enum,json=legacyUnmarshalJsonEnum" json:"legacy_unmarshal_json_enum,omitempty"`
StripEnumPrefix *GoFeatures_StripEnumPrefix `protobuf:"varint,3,opt,name=strip_enum_prefix,json=stripEnumPrefix,enum=pb.GoFeatures_StripEnumPrefix" json:"strip_enum_prefix,omitempty"`
}
func (x *GoFeatures) Reset() {
@ -64,6 +127,13 @@ func (x *GoFeatures) GetLegacyUnmarshalJsonEnum() bool {
return false
}
func (x *GoFeatures) GetStripEnumPrefix() GoFeatures_StripEnumPrefix {
if x != nil && x.StripEnumPrefix != nil {
return *x.StripEnumPrefix
}
return GoFeatures_STRIP_ENUM_PREFIX_UNSPECIFIED
}
var file_google_protobuf_go_features_proto_extTypes = []protoimpl.ExtensionInfo{
{
ExtendedType: (*descriptorpb.FeatureSet)(nil),
@ -88,7 +158,7 @@ var file_google_protobuf_go_features_proto_rawDesc = []byte{
0x66, 0x2f, 0x67, 0x6f, 0x5f, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x2e, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x12, 0x02, 0x70, 0x62, 0x1a, 0x20, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70,
0x74, 0x6f, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xcd, 0x01, 0x0a, 0x0a, 0x47, 0x6f,
0x74, 0x6f, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xe0, 0x03, 0x0a, 0x0a, 0x47, 0x6f,
0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x12, 0xbe, 0x01, 0x0a, 0x1a, 0x6c, 0x65, 0x67,
0x61, 0x63, 0x79, 0x5f, 0x75, 0x6e, 0x6d, 0x61, 0x72, 0x73, 0x68, 0x61, 0x6c, 0x5f, 0x6a, 0x73,
0x6f, 0x6e, 0x5f, 0x65, 0x6e, 0x75, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x42, 0x80, 0x01,
@ -101,14 +171,31 @@ var file_google_protobuf_go_features_proto_rawDesc = []byte{
0x20, 0x62, 0x65, 0x20, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x20, 0x61,
0x20, 0x66, 0x75, 0x74, 0x75, 0x72, 0x65, 0x20, 0x65, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x2e,
0x52, 0x17, 0x6c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x55, 0x6e, 0x6d, 0x61, 0x72, 0x73, 0x68, 0x61,
0x6c, 0x4a, 0x73, 0x6f, 0x6e, 0x45, 0x6e, 0x75, 0x6d, 0x3a, 0x3c, 0x0a, 0x02, 0x67, 0x6f, 0x12,
0x1b, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,
0x66, 0x2e, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x53, 0x65, 0x74, 0x18, 0xea, 0x07, 0x20,
0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x62, 0x2e, 0x47, 0x6f, 0x46, 0x65, 0x61, 0x74, 0x75,
0x72, 0x65, 0x73, 0x52, 0x02, 0x67, 0x6f, 0x42, 0x2f, 0x5a, 0x2d, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
0x65, 0x2e, 0x67, 0x6f, 0x6c, 0x61, 0x6e, 0x67, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x67, 0x6f, 0x66, 0x65,
0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x70, 0x62,
0x6c, 0x4a, 0x73, 0x6f, 0x6e, 0x45, 0x6e, 0x75, 0x6d, 0x12, 0x7c, 0x0a, 0x11, 0x73, 0x74, 0x72,
0x69, 0x70, 0x5f, 0x65, 0x6e, 0x75, 0x6d, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x18, 0x03,
0x20, 0x01, 0x28, 0x0e, 0x32, 0x1e, 0x2e, 0x70, 0x62, 0x2e, 0x47, 0x6f, 0x46, 0x65, 0x61, 0x74,
0x75, 0x72, 0x65, 0x73, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x70, 0x45, 0x6e, 0x75, 0x6d, 0x50, 0x72,
0x65, 0x66, 0x69, 0x78, 0x42, 0x30, 0x88, 0x01, 0x01, 0x98, 0x01, 0x06, 0x98, 0x01, 0x07, 0x98,
0x01, 0x01, 0xa2, 0x01, 0x1b, 0x12, 0x16, 0x53, 0x54, 0x52, 0x49, 0x50, 0x5f, 0x45, 0x4e, 0x55,
0x4d, 0x5f, 0x50, 0x52, 0x45, 0x46, 0x49, 0x58, 0x5f, 0x4b, 0x45, 0x45, 0x50, 0x18, 0x84, 0x07,
0xb2, 0x01, 0x03, 0x08, 0xe9, 0x07, 0x52, 0x0f, 0x73, 0x74, 0x72, 0x69, 0x70, 0x45, 0x6e, 0x75,
0x6d, 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, 0x22, 0x92, 0x01, 0x0a, 0x0f, 0x53, 0x74, 0x72, 0x69,
0x70, 0x45, 0x6e, 0x75, 0x6d, 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, 0x12, 0x21, 0x0a, 0x1d, 0x53,
0x54, 0x52, 0x49, 0x50, 0x5f, 0x45, 0x4e, 0x55, 0x4d, 0x5f, 0x50, 0x52, 0x45, 0x46, 0x49, 0x58,
0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1a,
0x0a, 0x16, 0x53, 0x54, 0x52, 0x49, 0x50, 0x5f, 0x45, 0x4e, 0x55, 0x4d, 0x5f, 0x50, 0x52, 0x45,
0x46, 0x49, 0x58, 0x5f, 0x4b, 0x45, 0x45, 0x50, 0x10, 0x01, 0x12, 0x23, 0x0a, 0x1f, 0x53, 0x54,
0x52, 0x49, 0x50, 0x5f, 0x45, 0x4e, 0x55, 0x4d, 0x5f, 0x50, 0x52, 0x45, 0x46, 0x49, 0x58, 0x5f,
0x47, 0x45, 0x4e, 0x45, 0x52, 0x41, 0x54, 0x45, 0x5f, 0x42, 0x4f, 0x54, 0x48, 0x10, 0x02, 0x12,
0x1b, 0x0a, 0x17, 0x53, 0x54, 0x52, 0x49, 0x50, 0x5f, 0x45, 0x4e, 0x55, 0x4d, 0x5f, 0x50, 0x52,
0x45, 0x46, 0x49, 0x58, 0x5f, 0x53, 0x54, 0x52, 0x49, 0x50, 0x10, 0x03, 0x3a, 0x3c, 0x0a, 0x02,
0x67, 0x6f, 0x12, 0x1b, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x53, 0x65, 0x74, 0x18,
0xea, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x62, 0x2e, 0x47, 0x6f, 0x46, 0x65,
0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x52, 0x02, 0x67, 0x6f, 0x42, 0x2f, 0x5a, 0x2d, 0x67, 0x6f,
0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x67, 0x6f, 0x6c, 0x61, 0x6e, 0x67, 0x2e, 0x6f, 0x72, 0x67, 0x2f,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x67,
0x6f, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x70, 0x62,
}
var (
@ -123,19 +210,22 @@ func file_google_protobuf_go_features_proto_rawDescGZIP() []byte {
return file_google_protobuf_go_features_proto_rawDescData
}
var file_google_protobuf_go_features_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
var file_google_protobuf_go_features_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
var file_google_protobuf_go_features_proto_goTypes = []any{
(*GoFeatures)(nil), // 0: pb.GoFeatures
(*descriptorpb.FeatureSet)(nil), // 1: google.protobuf.FeatureSet
(GoFeatures_StripEnumPrefix)(0), // 0: pb.GoFeatures.StripEnumPrefix
(*GoFeatures)(nil), // 1: pb.GoFeatures
(*descriptorpb.FeatureSet)(nil), // 2: google.protobuf.FeatureSet
}
var file_google_protobuf_go_features_proto_depIdxs = []int32{
1, // 0: pb.go:extendee -> google.protobuf.FeatureSet
0, // 1: pb.go:type_name -> pb.GoFeatures
2, // [2:2] is the sub-list for method output_type
2, // [2:2] is the sub-list for method input_type
1, // [1:2] is the sub-list for extension type_name
0, // [0:1] is the sub-list for extension extendee
0, // [0:0] is the sub-list for field type_name
0, // 0: pb.GoFeatures.strip_enum_prefix:type_name -> pb.GoFeatures.StripEnumPrefix
2, // 1: pb.go:extendee -> google.protobuf.FeatureSet
1, // 2: pb.go:type_name -> pb.GoFeatures
3, // [3:3] is the sub-list for method output_type
3, // [3:3] is the sub-list for method input_type
2, // [2:3] is the sub-list for extension type_name
1, // [1:2] is the sub-list for extension extendee
0, // [0:1] is the sub-list for field type_name
}
func init() { file_google_protobuf_go_features_proto_init() }
@ -148,13 +238,14 @@ func file_google_protobuf_go_features_proto_init() {
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_google_protobuf_go_features_proto_rawDesc,
NumEnums: 0,
NumEnums: 1,
NumMessages: 1,
NumExtensions: 1,
NumServices: 0,
},
GoTypes: file_google_protobuf_go_features_proto_goTypes,
DependencyIndexes: file_google_protobuf_go_features_proto_depIdxs,
EnumInfos: file_google_protobuf_go_features_proto_enumTypes,
MessageInfos: file_google_protobuf_go_features_proto_msgTypes,
ExtensionInfos: file_google_protobuf_go_features_proto_extTypes,
}.Build()