cmd/protoc-gen-go: generate enums
This produces exactly the same output (to the best of my ability to
determine) as github.com/golang/protobuf.
Change-Id: Ib60e7a836efb1eb0e5167b30458049ec239e7903
Reviewed-on: https://go-review.googlesource.com/134695
Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
diff --git a/cmd/protoc-gen-go/main.go b/cmd/protoc-gen-go/main.go
index d330941..d91e7a1 100644
--- a/cmd/protoc-gen-go/main.go
+++ b/cmd/protoc-gen-go/main.go
@@ -18,8 +18,11 @@
"github.com/golang/protobuf/proto"
descpb "github.com/golang/protobuf/protoc-gen-go/descriptor"
"google.golang.org/proto/protogen"
+ "google.golang.org/proto/reflect/protoreflect"
)
+const protoPackage = "github.com/golang/protobuf/proto"
+
func main() {
protogen.Run(func(gen *protogen.Plugin) error {
for _, f := range gen.Files {
@@ -34,7 +37,9 @@
type File struct {
*protogen.File
- locationMap map[string][]*descpb.SourceCodeInfo_Location
+ locationMap map[string][]*descpb.SourceCodeInfo_Location
+ descriptorVar string // var containing the gzipped FileDescriptorProto
+ init []string
}
func genFile(gen *protogen.Plugin, file *protogen.File) {
@@ -47,6 +52,12 @@
f.locationMap[key] = append(f.locationMap[key], loc)
}
+ // Determine the name of the var holding the file descriptor:
+ //
+ // fileDescriptor_<hash of filename>
+ filenameHash := sha256.Sum256([]byte(f.Desc.Path()))
+ f.descriptorVar = fmt.Sprintf("fileDescriptor_%s", hex.EncodeToString(filenameHash[:8]))
+
g := gen.NewGeneratedFile(f.GeneratedFilenamePrefix+".pb.go", f.GoImportPath)
g.P("// Code generated by protoc-gen-go. DO NOT EDIT.")
g.P("// source: ", f.Desc.Path())
@@ -57,20 +68,26 @@
g.P("package ", f.GoPackageName)
g.P()
+ for _, enum := range f.Enums {
+ genEnum(gen, g, f, enum)
+ }
for _, message := range f.Messages {
genMessage(gen, g, f, message)
}
+ if len(f.init) != 0 {
+ g.P("func init() {")
+ for _, s := range f.init {
+ g.P(s)
+ }
+ g.P("}")
+ g.P()
+ }
+
genFileDescriptor(gen, g, f)
}
func genFileDescriptor(gen *protogen.Plugin, g *protogen.GeneratedFile, f *File) {
- // Determine the name of the var holding the file descriptor:
- //
- // fileDescriptor_<hash of filename>
- filenameHash := sha256.Sum256([]byte(f.Desc.Path()))
- varName := fmt.Sprintf("fileDescriptor_%s", hex.EncodeToString(filenameHash[:8]))
-
// Trim the source_code_info from the descriptor.
// Marshal and gzip it.
descProto := proto.Clone(f.Proto).(*descpb.FileDescriptorProto)
@@ -86,9 +103,9 @@
w.Close()
b = buf.Bytes()
- g.P("func init() { proto.RegisterFile(", strconv.Quote(f.Desc.Path()), ", ", varName, ") }")
+ g.P("func init() { proto.RegisterFile(", strconv.Quote(f.Desc.Path()), ", ", f.descriptorVar, ") }")
g.P()
- g.P("var ", varName, " = []byte{")
+ g.P("var ", f.descriptorVar, " = []byte{")
g.P("// ", len(b), " bytes of a gzipped FileDescriptorProto")
for len(b) > 0 {
n := 16
@@ -108,7 +125,92 @@
g.P()
}
+func genEnum(gen *protogen.Plugin, g *protogen.GeneratedFile, f *File, enum *protogen.Enum) {
+ genComment(g, f, enum.Path)
+ // TODO: deprecation
+ g.P("type ", enum.GoIdent, " int32")
+ g.P("const (")
+ for _, value := range enum.Values {
+ genComment(g, f, value.Path)
+ // TODO: deprecation
+ g.P(value.GoIdent, " ", enum.GoIdent, " = ", value.Desc.Number())
+ }
+ g.P(")")
+ g.P()
+ nameMap := enum.GoIdent.GoName + "_name"
+ g.P("var ", nameMap, " = map[int32]string{")
+ generated := make(map[protoreflect.EnumNumber]bool)
+ for _, value := range enum.Values {
+ duplicate := ""
+ if _, present := generated[value.Desc.Number()]; present {
+ duplicate = "// Duplicate value: "
+ }
+ g.P(duplicate, value.Desc.Number(), ": ", strconv.Quote(string(value.Desc.Name())), ",")
+ generated[value.Desc.Number()] = true
+ }
+ g.P("}")
+ g.P()
+ valueMap := enum.GoIdent.GoName + "_value"
+ g.P("var ", valueMap, " = map[string]int32{")
+ for _, value := range enum.Values {
+ g.P(strconv.Quote(string(value.Desc.Name())), ": ", value.Desc.Number(), ",")
+ }
+ g.P("}")
+ g.P()
+ if enum.Desc.Syntax() != protoreflect.Proto3 {
+ g.P("func (x ", enum.GoIdent, ") Enum() *", enum.GoIdent, " {")
+ g.P("p := new(", enum.GoIdent, ")")
+ g.P("*p = x")
+ g.P("return p")
+ g.P("}")
+ g.P()
+ }
+ g.P("func (x ", enum.GoIdent, ") String() string {")
+ g.P("return ", protogen.GoIdent{GoImportPath: protoPackage, GoName: "EnumName"}, "(", enum.GoIdent, "_name, int32(x))")
+ g.P("}")
+ g.P()
+
+ if enum.Desc.Syntax() != protoreflect.Proto3 {
+ g.P("func (x *", enum.GoIdent, ") UnmarshalJSON(data []byte) error {")
+ g.P("value, err := ", protogen.GoIdent{GoImportPath: protoPackage, GoName: "UnmarshalJSONEnum"}, "(", enum.GoIdent, `_value, data, "`, enum.GoIdent, `")`)
+ g.P("if err != nil {")
+ g.P("return err")
+ g.P("}")
+ g.P("*x = ", enum.GoIdent, "(value)")
+ g.P("return nil")
+ g.P("}")
+ g.P()
+ }
+
+ var indexes []string
+ for i := 1; i < len(enum.Path); i += 2 {
+ indexes = append(indexes, strconv.Itoa(int(enum.Path[i])))
+ }
+ g.P("func (", enum.GoIdent, ") EnumDescriptor() ([]byte, []int) {")
+ g.P("return ", f.descriptorVar, ", []int{", strings.Join(indexes, ","), "}")
+ g.P("}")
+ g.P()
+
+ genWellKnownType(g, enum.GoIdent, enum.Desc)
+
+ // The name registered is, confusingly, <proto_package>.<go_ident>.
+ // This probably should have been the full name of the proto enum
+ // type instead, but changing it at this point would require thought.
+ regName := string(f.Desc.Package()) + "." + enum.GoIdent.GoName
+ f.init = append(f.init, fmt.Sprintf("%s(%q, %s, %s)",
+ g.QualifiedGoIdent(protogen.GoIdent{
+ GoImportPath: protoPackage,
+ GoName: "RegisterEnum",
+ }),
+ regName, nameMap, valueMap,
+ ))
+}
+
func genMessage(gen *protogen.Plugin, g *protogen.GeneratedFile, f *File, message *protogen.Message) {
+ for _, enum := range message.Enums {
+ genEnum(gen, g, f, enum)
+ }
+
genComment(g, f, message.Path)
g.P("type ", message.GoIdent, " struct {")
g.P("}")
@@ -142,3 +244,15 @@
}
return string(buf)
}
+
+func genWellKnownType(g *protogen.GeneratedFile, ident protogen.GoIdent, desc protoreflect.Descriptor) {
+ if wellKnownTypes[desc.FullName()] {
+ g.P("func (", ident, `) XXX_WellKnownType() string { return "`, desc.Name(), `" }`)
+ g.P()
+ }
+}
+
+// Names of messages and enums for which we will generate XXX_WellKnownType methods.
+var wellKnownTypes = map[protoreflect.FullName]bool{
+ "google.protobuf.NullValue": true,
+}
diff --git a/cmd/protoc-gen-go/testdata/proto2/enum.pb.go b/cmd/protoc-gen-go/testdata/proto2/enum.pb.go
new file mode 100644
index 0000000..85b2fad
--- /dev/null
+++ b/cmd/protoc-gen-go/testdata/proto2/enum.pb.go
@@ -0,0 +1,278 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// source: proto2/enum.proto
+
+package proto2
+
+import proto "github.com/golang/protobuf/proto"
+
+// EnumType1 comment.
+type EnumType1 int32
+
+const (
+ // EnumType1_ONE comment.
+ EnumType1_ONE EnumType1 = 1
+ // EnumType1_TWO comment.
+ EnumType1_TWO EnumType1 = 2
+)
+
+var EnumType1_name = map[int32]string{
+ 1: "ONE",
+ 2: "TWO",
+}
+
+var EnumType1_value = map[string]int32{
+ "ONE": 1,
+ "TWO": 2,
+}
+
+func (x EnumType1) Enum() *EnumType1 {
+ p := new(EnumType1)
+ *p = x
+ return p
+}
+
+func (x EnumType1) String() string {
+ return proto.EnumName(EnumType1_name, int32(x))
+}
+
+func (x *EnumType1) UnmarshalJSON(data []byte) error {
+ value, err := proto.UnmarshalJSONEnum(EnumType1_value, data, "EnumType1")
+ if err != nil {
+ return err
+ }
+ *x = EnumType1(value)
+ return nil
+}
+
+func (EnumType1) EnumDescriptor() ([]byte, []int) {
+ return fileDescriptor_de9f68860d540858, []int{0}
+}
+
+type EnumType2 int32
+
+const (
+ EnumType2_duplicate1 EnumType2 = 1
+ EnumType2_duplicate2 EnumType2 = 1
+)
+
+var EnumType2_name = map[int32]string{
+ 1: "duplicate1",
+ // Duplicate value: 1: "duplicate2",
+}
+
+var EnumType2_value = map[string]int32{
+ "duplicate1": 1,
+ "duplicate2": 1,
+}
+
+func (x EnumType2) Enum() *EnumType2 {
+ p := new(EnumType2)
+ *p = x
+ return p
+}
+
+func (x EnumType2) String() string {
+ return proto.EnumName(EnumType2_name, int32(x))
+}
+
+func (x *EnumType2) UnmarshalJSON(data []byte) error {
+ value, err := proto.UnmarshalJSONEnum(EnumType2_value, data, "EnumType2")
+ if err != nil {
+ return err
+ }
+ *x = EnumType2(value)
+ return nil
+}
+
+func (EnumType2) EnumDescriptor() ([]byte, []int) {
+ return fileDescriptor_de9f68860d540858, []int{1}
+}
+
+// NestedEnumType1A comment.
+type EnumContainerMessage1_NestedEnumType1A int32
+
+const (
+ // NestedEnumType1A_VALUE comment.
+ EnumContainerMessage1_NESTED_1A_VALUE EnumContainerMessage1_NestedEnumType1A = 0
+)
+
+var EnumContainerMessage1_NestedEnumType1A_name = map[int32]string{
+ 0: "NESTED_1A_VALUE",
+}
+
+var EnumContainerMessage1_NestedEnumType1A_value = map[string]int32{
+ "NESTED_1A_VALUE": 0,
+}
+
+func (x EnumContainerMessage1_NestedEnumType1A) Enum() *EnumContainerMessage1_NestedEnumType1A {
+ p := new(EnumContainerMessage1_NestedEnumType1A)
+ *p = x
+ return p
+}
+
+func (x EnumContainerMessage1_NestedEnumType1A) String() string {
+ return proto.EnumName(EnumContainerMessage1_NestedEnumType1A_name, int32(x))
+}
+
+func (x *EnumContainerMessage1_NestedEnumType1A) UnmarshalJSON(data []byte) error {
+ value, err := proto.UnmarshalJSONEnum(EnumContainerMessage1_NestedEnumType1A_value, data, "EnumContainerMessage1_NestedEnumType1A")
+ if err != nil {
+ return err
+ }
+ *x = EnumContainerMessage1_NestedEnumType1A(value)
+ return nil
+}
+
+func (EnumContainerMessage1_NestedEnumType1A) EnumDescriptor() ([]byte, []int) {
+ return fileDescriptor_de9f68860d540858, []int{0, 0}
+}
+
+type EnumContainerMessage1_NestedEnumType1B int32
+
+const (
+ EnumContainerMessage1_NESTED_1B_VALUE EnumContainerMessage1_NestedEnumType1B = 0
+)
+
+var EnumContainerMessage1_NestedEnumType1B_name = map[int32]string{
+ 0: "NESTED_1B_VALUE",
+}
+
+var EnumContainerMessage1_NestedEnumType1B_value = map[string]int32{
+ "NESTED_1B_VALUE": 0,
+}
+
+func (x EnumContainerMessage1_NestedEnumType1B) Enum() *EnumContainerMessage1_NestedEnumType1B {
+ p := new(EnumContainerMessage1_NestedEnumType1B)
+ *p = x
+ return p
+}
+
+func (x EnumContainerMessage1_NestedEnumType1B) String() string {
+ return proto.EnumName(EnumContainerMessage1_NestedEnumType1B_name, int32(x))
+}
+
+func (x *EnumContainerMessage1_NestedEnumType1B) UnmarshalJSON(data []byte) error {
+ value, err := proto.UnmarshalJSONEnum(EnumContainerMessage1_NestedEnumType1B_value, data, "EnumContainerMessage1_NestedEnumType1B")
+ if err != nil {
+ return err
+ }
+ *x = EnumContainerMessage1_NestedEnumType1B(value)
+ return nil
+}
+
+func (EnumContainerMessage1_NestedEnumType1B) EnumDescriptor() ([]byte, []int) {
+ return fileDescriptor_de9f68860d540858, []int{0, 1}
+}
+
+type EnumContainerMessage1 struct {
+}
+
+// NestedEnumType2A comment.
+type EnumContainerMessage1_EnumContainerMessage2_NestedEnumType2A int32
+
+const (
+ // NestedEnumType2A_VALUE comment.
+ EnumContainerMessage1_EnumContainerMessage2_NESTED_2A_VALUE EnumContainerMessage1_EnumContainerMessage2_NestedEnumType2A = 0
+)
+
+var EnumContainerMessage1_EnumContainerMessage2_NestedEnumType2A_name = map[int32]string{
+ 0: "NESTED_2A_VALUE",
+}
+
+var EnumContainerMessage1_EnumContainerMessage2_NestedEnumType2A_value = map[string]int32{
+ "NESTED_2A_VALUE": 0,
+}
+
+func (x EnumContainerMessage1_EnumContainerMessage2_NestedEnumType2A) Enum() *EnumContainerMessage1_EnumContainerMessage2_NestedEnumType2A {
+ p := new(EnumContainerMessage1_EnumContainerMessage2_NestedEnumType2A)
+ *p = x
+ return p
+}
+
+func (x EnumContainerMessage1_EnumContainerMessage2_NestedEnumType2A) String() string {
+ return proto.EnumName(EnumContainerMessage1_EnumContainerMessage2_NestedEnumType2A_name, int32(x))
+}
+
+func (x *EnumContainerMessage1_EnumContainerMessage2_NestedEnumType2A) UnmarshalJSON(data []byte) error {
+ value, err := proto.UnmarshalJSONEnum(EnumContainerMessage1_EnumContainerMessage2_NestedEnumType2A_value, data, "EnumContainerMessage1_EnumContainerMessage2_NestedEnumType2A")
+ if err != nil {
+ return err
+ }
+ *x = EnumContainerMessage1_EnumContainerMessage2_NestedEnumType2A(value)
+ return nil
+}
+
+func (EnumContainerMessage1_EnumContainerMessage2_NestedEnumType2A) EnumDescriptor() ([]byte, []int) {
+ return fileDescriptor_de9f68860d540858, []int{0, 0, 0}
+}
+
+type EnumContainerMessage1_EnumContainerMessage2_NestedEnumType2B int32
+
+const (
+ EnumContainerMessage1_EnumContainerMessage2_NESTED_2B_VALUE EnumContainerMessage1_EnumContainerMessage2_NestedEnumType2B = 0
+)
+
+var EnumContainerMessage1_EnumContainerMessage2_NestedEnumType2B_name = map[int32]string{
+ 0: "NESTED_2B_VALUE",
+}
+
+var EnumContainerMessage1_EnumContainerMessage2_NestedEnumType2B_value = map[string]int32{
+ "NESTED_2B_VALUE": 0,
+}
+
+func (x EnumContainerMessage1_EnumContainerMessage2_NestedEnumType2B) Enum() *EnumContainerMessage1_EnumContainerMessage2_NestedEnumType2B {
+ p := new(EnumContainerMessage1_EnumContainerMessage2_NestedEnumType2B)
+ *p = x
+ return p
+}
+
+func (x EnumContainerMessage1_EnumContainerMessage2_NestedEnumType2B) String() string {
+ return proto.EnumName(EnumContainerMessage1_EnumContainerMessage2_NestedEnumType2B_name, int32(x))
+}
+
+func (x *EnumContainerMessage1_EnumContainerMessage2_NestedEnumType2B) UnmarshalJSON(data []byte) error {
+ value, err := proto.UnmarshalJSONEnum(EnumContainerMessage1_EnumContainerMessage2_NestedEnumType2B_value, data, "EnumContainerMessage1_EnumContainerMessage2_NestedEnumType2B")
+ if err != nil {
+ return err
+ }
+ *x = EnumContainerMessage1_EnumContainerMessage2_NestedEnumType2B(value)
+ return nil
+}
+
+func (EnumContainerMessage1_EnumContainerMessage2_NestedEnumType2B) EnumDescriptor() ([]byte, []int) {
+ return fileDescriptor_de9f68860d540858, []int{0, 0, 1}
+}
+
+type EnumContainerMessage1_EnumContainerMessage2 struct {
+}
+
+func init() {
+ proto.RegisterEnum("goproto.protoc.proto2.EnumType1", EnumType1_name, EnumType1_value)
+ proto.RegisterEnum("goproto.protoc.proto2.EnumType2", EnumType2_name, EnumType2_value)
+ proto.RegisterEnum("goproto.protoc.proto2.EnumContainerMessage1_NestedEnumType1A", EnumContainerMessage1_NestedEnumType1A_name, EnumContainerMessage1_NestedEnumType1A_value)
+ proto.RegisterEnum("goproto.protoc.proto2.EnumContainerMessage1_NestedEnumType1B", EnumContainerMessage1_NestedEnumType1B_name, EnumContainerMessage1_NestedEnumType1B_value)
+ proto.RegisterEnum("goproto.protoc.proto2.EnumContainerMessage1_EnumContainerMessage2_NestedEnumType2A", EnumContainerMessage1_EnumContainerMessage2_NestedEnumType2A_name, EnumContainerMessage1_EnumContainerMessage2_NestedEnumType2A_value)
+ proto.RegisterEnum("goproto.protoc.proto2.EnumContainerMessage1_EnumContainerMessage2_NestedEnumType2B", EnumContainerMessage1_EnumContainerMessage2_NestedEnumType2B_name, EnumContainerMessage1_EnumContainerMessage2_NestedEnumType2B_value)
+}
+
+func init() { proto.RegisterFile("proto2/enum.proto", fileDescriptor_de9f68860d540858) }
+
+var fileDescriptor_de9f68860d540858 = []byte{
+ // 242 bytes of a gzipped FileDescriptorProto
+ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x90, 0xb1, 0x4b, 0xc4, 0x30,
+ 0x14, 0xc6, 0xcd, 0x39, 0x88, 0x19, 0x34, 0x56, 0x6e, 0x39, 0x70, 0xb9, 0x45, 0x38, 0xb8, 0x86,
+ 0x64, 0x13, 0xa7, 0x56, 0xb3, 0x69, 0x6f, 0xb0, 0x2a, 0xb8, 0x1c, 0xa1, 0x7d, 0x3c, 0x0a, 0x6d,
+ 0x5e, 0x69, 0xd3, 0xc1, 0xff, 0xd3, 0x3f, 0x48, 0xae, 0x81, 0xb3, 0x42, 0x75, 0xca, 0xf7, 0xe5,
+ 0xfb, 0xf1, 0x1b, 0x1e, 0xbf, 0x6a, 0x3b, 0xf2, 0xa4, 0x25, 0xb8, 0xa1, 0x89, 0xc7, 0x1c, 0x2d,
+ 0x91, 0xc6, 0x10, 0x6a, 0x11, 0x1e, 0xbd, 0xfe, 0x62, 0x7c, 0x69, 0xdc, 0xd0, 0x3c, 0x90, 0xf3,
+ 0xb6, 0x72, 0xd0, 0x3d, 0x43, 0xdf, 0x5b, 0x04, 0xb5, 0xaa, 0xe6, 0x07, 0xbd, 0xbe, 0xe5, 0x22,
+ 0x83, 0xde, 0x43, 0x79, 0x98, 0xf3, 0xcf, 0x16, 0x74, 0x12, 0x5d, 0xf3, 0xcb, 0xcc, 0xbc, 0xe4,
+ 0xe6, 0x71, 0xaf, 0x93, 0xfd, 0x5b, 0xf2, 0xf4, 0x6a, 0xc4, 0xc9, 0x0c, 0x98, 0x4e, 0xc1, 0xf4,
+ 0x6f, 0x50, 0x4d, 0x8d, 0xea, 0x1f, 0xa3, 0x9a, 0x1a, 0xd5, 0xd1, 0xb8, 0xb9, 0xe1, 0xe7, 0x47,
+ 0x24, 0x3a, 0xe3, 0xa7, 0xbb, 0xcc, 0x08, 0x76, 0x08, 0xf9, 0xfb, 0x4e, 0x2c, 0x36, 0xf2, 0x67,
+ 0xd6, 0xd1, 0x05, 0xe7, 0xe5, 0xd0, 0xd6, 0x55, 0x61, 0x3d, 0x28, 0xc1, 0x7e, 0x75, 0x2d, 0xd8,
+ 0x6a, 0x21, 0x58, 0x7a, 0xff, 0x71, 0x87, 0x44, 0x58, 0x43, 0x8c, 0x54, 0x5b, 0x87, 0x31, 0x75,
+ 0x28, 0xc7, 0x13, 0xca, 0xa2, 0x29, 0x43, 0x2a, 0xb6, 0x08, 0x6e, 0x8b, 0x24, 0x3d, 0xf4, 0xbe,
+ 0xb4, 0xde, 0x86, 0x6f, 0xfd, 0x1d, 0x00, 0x00, 0xff, 0xff, 0xf6, 0x53, 0xf7, 0xde, 0x8e, 0x01,
+ 0x00, 0x00,
+}
diff --git a/cmd/protoc-gen-go/testdata/proto2/enum.proto b/cmd/protoc-gen-go/testdata/proto2/enum.proto
new file mode 100644
index 0000000..0faac3e
--- /dev/null
+++ b/cmd/protoc-gen-go/testdata/proto2/enum.proto
@@ -0,0 +1,47 @@
+// Copyright 2018 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.
+
+syntax = "proto2";
+
+package goproto.protoc.proto2;
+
+option go_package = "google.golang.org/proto/cmd/protoc-gen-go/testdata/proto2";
+
+// EnumType1 comment.
+enum EnumType1 {
+ // EnumType1_ONE comment.
+ ONE = 1;
+ // EnumType1_TWO comment.
+ TWO = 2;
+}
+
+enum EnumType2 {
+ option allow_alias = true;
+ duplicate1 = 1;
+ duplicate2 = 1;
+}
+
+message EnumContainerMessage1 {
+ // NestedEnumType1A comment.
+ enum NestedEnumType1A {
+ // NestedEnumType1A_VALUE comment.
+ NESTED_1A_VALUE = 0;
+ }
+
+ enum NestedEnumType1B {
+ NESTED_1B_VALUE = 0;
+ }
+
+ message EnumContainerMessage2 {
+ // NestedEnumType2A comment.
+ enum NestedEnumType2A {
+ // NestedEnumType2A_VALUE comment.
+ NESTED_2A_VALUE = 0;
+ }
+
+ enum NestedEnumType2B {
+ NESTED_2B_VALUE = 0;
+ }
+ }
+}
diff --git a/cmd/protoc-gen-go/testdata/proto3/enum.pb.go b/cmd/protoc-gen-go/testdata/proto3/enum.pb.go
new file mode 100644
index 0000000..569298b
--- /dev/null
+++ b/cmd/protoc-gen-go/testdata/proto3/enum.pb.go
@@ -0,0 +1,53 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// source: proto3/enum.proto
+
+package proto3
+
+import proto "github.com/golang/protobuf/proto"
+
+type Enum int32
+
+const (
+ Enum_ZERO Enum = 0
+ Enum_ONE Enum = 1
+ Enum_TWO Enum = 2
+)
+
+var Enum_name = map[int32]string{
+ 0: "ZERO",
+ 1: "ONE",
+ 2: "TWO",
+}
+
+var Enum_value = map[string]int32{
+ "ZERO": 0,
+ "ONE": 1,
+ "TWO": 2,
+}
+
+func (x Enum) String() string {
+ return proto.EnumName(Enum_name, int32(x))
+}
+
+func (Enum) EnumDescriptor() ([]byte, []int) {
+ return fileDescriptor_b4b9b1e8d161a9a6, []int{0}
+}
+
+func init() {
+ proto.RegisterEnum("goproto.protoc.proto3.Enum", Enum_name, Enum_value)
+}
+
+func init() { proto.RegisterFile("proto3/enum.proto", fileDescriptor_b4b9b1e8d161a9a6) }
+
+var fileDescriptor_b4b9b1e8d161a9a6 = []byte{
+ // 138 bytes of a gzipped FileDescriptorProto
+ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0x2c, 0x28, 0xca, 0x2f,
+ 0xc9, 0x37, 0xd6, 0x4f, 0xcd, 0x2b, 0xcd, 0xd5, 0x03, 0xb3, 0x85, 0x44, 0xd3, 0xf3, 0xc1, 0x0c,
+ 0x08, 0x37, 0x19, 0x42, 0x19, 0x6b, 0x29, 0x71, 0xb1, 0xb8, 0xe6, 0x95, 0xe6, 0x0a, 0x71, 0x70,
+ 0xb1, 0x44, 0xb9, 0x06, 0xf9, 0x0b, 0x30, 0x08, 0xb1, 0x73, 0x31, 0xfb, 0xfb, 0xb9, 0x0a, 0x30,
+ 0x82, 0x18, 0x21, 0xe1, 0xfe, 0x02, 0x4c, 0x4e, 0xd6, 0x51, 0x96, 0xe9, 0xf9, 0xf9, 0xe9, 0x39,
+ 0xa9, 0x7a, 0xe9, 0xf9, 0x39, 0x89, 0x79, 0xe9, 0x7a, 0xf9, 0x45, 0xe9, 0xfa, 0x60, 0xfd, 0xfa,
+ 0xc9, 0xb9, 0x29, 0x10, 0x56, 0xb2, 0x6e, 0x7a, 0x6a, 0x9e, 0x6e, 0x7a, 0xbe, 0x7e, 0x49, 0x6a,
+ 0x71, 0x49, 0x4a, 0x62, 0x49, 0x22, 0x44, 0xd8, 0x38, 0x89, 0x0d, 0x42, 0x03, 0x02, 0x00, 0x00,
+ 0xff, 0xff, 0x02, 0x01, 0x6a, 0x95, 0x93, 0x00, 0x00, 0x00,
+}
diff --git a/cmd/protoc-gen-go/testdata/proto3/enum.proto b/cmd/protoc-gen-go/testdata/proto3/enum.proto
new file mode 100644
index 0000000..60e7b8e
--- /dev/null
+++ b/cmd/protoc-gen-go/testdata/proto3/enum.proto
@@ -0,0 +1,15 @@
+// Copyright 2018 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.
+
+syntax = "proto3";
+
+package goproto.protoc.proto3;
+
+option go_package = "google.golang.org/proto/cmd/protoc-gen-go/testdata/proto3";
+
+enum Enum {
+ ZERO = 0;
+ ONE = 1;
+ TWO = 2;
+}
diff --git a/protogen/protogen.go b/protogen/protogen.go
index 967f000..ada17ba 100644
--- a/protogen/protogen.go
+++ b/protogen/protogen.go
@@ -305,6 +305,7 @@
GoPackageName GoPackageName // name of this file's Go package
GoImportPath GoImportPath // import path of this file's Go package
Messages []*Message // top-level message declarations
+ Enums []*Enum // top-level enum declarations
Generate bool // true if we should generate code for this file
// GeneratedFilenamePrefix is used to construct filenames for generated
@@ -351,6 +352,9 @@
for i, mdescs := 0, desc.Messages(); i < mdescs.Len(); i++ {
f.Messages = append(f.Messages, newMessage(gen, f, nil, mdescs.Get(i)))
}
+ for i, edescs := 0, desc.Enums(); i < edescs.Len(); i++ {
+ f.Enums = append(f.Enums, newEnum(gen, f, nil, edescs.Get(i)))
+ }
return f, nil
}
@@ -380,6 +384,7 @@
GoIdent GoIdent // name of the generated Go type
Messages []*Message // nested message declarations
+ Enums []*Enum // nested enum declarations
Path []int32 // location path of this message
}
@@ -390,15 +395,73 @@
} else {
path = []int32{fileMessageField, int32(desc.Index())}
}
- m := &Message{
+ message := &Message{
Desc: desc,
GoIdent: newGoIdent(f, desc),
Path: path,
}
for i, mdescs := 0, desc.Messages(); i < mdescs.Len(); i++ {
- m.Messages = append(m.Messages, newMessage(gen, f, m, mdescs.Get(i)))
+ message.Messages = append(message.Messages, newMessage(gen, f, message, mdescs.Get(i)))
}
- return m
+ for i, edescs := 0, desc.Enums(); i < edescs.Len(); i++ {
+ message.Enums = append(message.Enums, newEnum(gen, f, message, edescs.Get(i)))
+ }
+ return message
+}
+
+// An Enum describes an enum.
+type Enum struct {
+ Desc protoreflect.EnumDescriptor
+
+ GoIdent GoIdent // name of the generated Go type
+ Values []*EnumValue // enum values
+ Path []int32 // location path of this enum
+}
+
+func newEnum(gen *Plugin, f *File, parent *Message, desc protoreflect.EnumDescriptor) *Enum {
+ var path []int32
+ if parent != nil {
+ path = pathAppend(parent.Path, messageEnumField, int32(desc.Index()))
+ } else {
+ path = []int32{fileEnumField, int32(desc.Index())}
+ }
+ enum := &Enum{
+ Desc: desc,
+ GoIdent: newGoIdent(f, desc),
+ Path: path,
+ }
+ for i, evdescs := 0, enum.Desc.Values(); i < evdescs.Len(); i++ {
+ enum.Values = append(enum.Values, newEnumValue(gen, f, parent, enum, evdescs.Get(i)))
+ }
+ return enum
+}
+
+// An EnumValue describes an enum value.
+type EnumValue struct {
+ Desc protoreflect.EnumValueDescriptor
+
+ GoIdent GoIdent // name of the generated Go type
+ Path []int32 // location path of this enum value
+}
+
+func newEnumValue(gen *Plugin, f *File, message *Message, enum *Enum, desc protoreflect.EnumValueDescriptor) *EnumValue {
+ // A top-level enum value's name is: EnumName_ValueName
+ // An enum value contained in a message is: MessageName_ValueName
+ //
+ // Enum value names are not camelcased.
+ parentIdent := enum.GoIdent
+ if message != nil {
+ parentIdent = message.GoIdent
+ }
+ name := parentIdent.GoName + "_" + string(desc.Name())
+ return &EnumValue{
+ Desc: desc,
+ GoIdent: GoIdent{
+ GoName: name,
+ GoImportPath: f.GoImportPath,
+ },
+ Path: pathAppend(enum.Path, enumValueField, int32(desc.Index())),
+ }
}
// A GeneratedFile is a generated file.
@@ -432,11 +495,7 @@
for _, x := range v {
switch x := x.(type) {
case GoIdent:
- if x.GoImportPath != g.goImportPath {
- fmt.Fprint(&g.buf, g.goPackageName(x.GoImportPath))
- fmt.Fprint(&g.buf, ".")
- }
- fmt.Fprint(&g.buf, x.GoName)
+ fmt.Fprint(&g.buf, g.QualifiedGoIdent(x))
default:
fmt.Fprint(&g.buf, x)
}
@@ -444,6 +503,27 @@
fmt.Fprintln(&g.buf)
}
+// QualifiedGoIdent returns the string to use for a Go identifier.
+//
+// If the identifier is from a different Go package than the generated file,
+// the returned name will be qualified (package.name) and an import statement
+// for the identifier's package will be included in the file.
+func (g *GeneratedFile) QualifiedGoIdent(ident GoIdent) string {
+ if ident.GoImportPath == g.goImportPath {
+ return ident.GoName
+ }
+ if packageName, ok := g.packageNames[ident.GoImportPath]; ok {
+ return string(packageName) + "." + ident.GoName
+ }
+ packageName := cleanPackageName(baseName(string(ident.GoImportPath)))
+ for i, orig := 1, packageName; g.usedPackageNames[packageName]; i++ {
+ packageName = orig + GoPackageName(strconv.Itoa(i))
+ }
+ g.packageNames[ident.GoImportPath] = packageName
+ g.usedPackageNames[packageName] = true
+ return string(packageName) + "." + ident.GoName
+}
+
func (g *GeneratedFile) goPackageName(importPath GoImportPath) GoPackageName {
if name, ok := g.packageNames[importPath]; ok {
return name
@@ -522,7 +602,7 @@
// field numbers in FileDescriptorProto
filePackageField = 2 // package
fileMessageField = 4 // message_type
- fileenumField = 5 // enum_type
+ fileEnumField = 5 // enum_type
// field numbers in DescriptorProto
messageFieldField = 2 // field
messageMessageField = 3 // nested_type