From 46abb575498ca9d34888b79e22165b7627dedc25 Mon Sep 17 00:00:00 2001 From: Damien Neil Date: Fri, 7 Sep 2018 12:45:37 -0700 Subject: [PATCH] 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 --- cmd/protoc-gen-go/main.go | 132 ++++++++- cmd/protoc-gen-go/testdata/proto2/enum.pb.go | 278 +++++++++++++++++++ cmd/protoc-gen-go/testdata/proto2/enum.proto | 47 ++++ cmd/protoc-gen-go/testdata/proto3/enum.pb.go | 53 ++++ cmd/protoc-gen-go/testdata/proto3/enum.proto | 15 + protogen/protogen.go | 98 ++++++- 6 files changed, 605 insertions(+), 18 deletions(-) create mode 100644 cmd/protoc-gen-go/testdata/proto2/enum.pb.go create mode 100644 cmd/protoc-gen-go/testdata/proto2/enum.proto create mode 100644 cmd/protoc-gen-go/testdata/proto3/enum.pb.go create mode 100644 cmd/protoc-gen-go/testdata/proto3/enum.proto diff --git a/cmd/protoc-gen-go/main.go b/cmd/protoc-gen-go/main.go index d3309416..d91e7a1c 100644 --- a/cmd/protoc-gen-go/main.go +++ b/cmd/protoc-gen-go/main.go @@ -18,8 +18,11 @@ import ( "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 @@ func main() { 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 @@ func genFile(gen *protogen.Plugin, file *protogen.File) { f.locationMap[key] = append(f.locationMap[key], loc) } + // Determine the name of the var holding the file descriptor: + // + // fileDescriptor_ + 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 @@ func genFile(gen *protogen.Plugin, file *protogen.File) { 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_ - 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 @@ func genFileDescriptor(gen *protogen.Plugin, g *protogen.GeneratedFile, f *File) 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 @@ func genFileDescriptor(gen *protogen.Plugin, g *protogen.GeneratedFile, f *File) 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, .. + // 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 @@ func pathKey(path []int32) string { } 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 00000000..85b2fadb --- /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 00000000..0faac3e6 --- /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 00000000..569298be --- /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 00000000..60e7b8ee --- /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 967f000b..ada17bad 100644 --- a/protogen/protogen.go +++ b/protogen/protogen.go @@ -305,6 +305,7 @@ type File struct { 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 @@ func newFile(gen *Plugin, p *descpb.FileDescriptorProto, packageName GoPackageNa 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 @@ type Message struct { 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 @@ func newMessage(gen *Plugin, f *File, parent *Message, desc protoreflect.Message } 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))) + } + 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())), } - return m } // A GeneratedFile is a generated file. @@ -432,11 +495,7 @@ func (g *GeneratedFile) P(v ...interface{}) { 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 @@ func (g *GeneratedFile) P(v ...interface{}) { 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 @@ const ( // 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