cmd/protoc-gen-go: generate deprecation comments
Generate deprecation comments on packages, enums, enum values, messages,
and fields.
Change-Id: I8a94aff535078d33d1cc6104cff17e062cbfe94f
Reviewed-on: https://go-review.googlesource.com/136355
Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
diff --git a/cmd/protoc-gen-go/main.go b/cmd/protoc-gen-go/main.go
index 2c94298..d5bef26 100644
--- a/cmd/protoc-gen-go/main.go
+++ b/cmd/protoc-gen-go/main.go
@@ -89,7 +89,11 @@
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())
+ if f.Proto.GetOptions().GetDeprecated() {
+ g.P("// ", f.Desc.Path(), " is a deprecated file.")
+ } else {
+ g.P("// source: ", f.Desc.Path())
+ }
g.P()
const filePackageField = 2 // FileDescriptorProto.package
genComment(g, f, []int32{filePackageField})
@@ -229,13 +233,13 @@
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("type ", enum.GoIdent, " int32",
+ deprecationComment(enumOptions(gen, enum).GetDeprecated()))
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(value.GoIdent, " ", enum.GoIdent, " = ", value.Desc.Number(),
+ deprecationComment(enumValueOptions(gen, value).GetDeprecated()))
}
g.P(")")
g.P()
@@ -321,8 +325,13 @@
return
}
- genComment(g, f, message.Path)
- // TODO: deprecation
+ hasComment := genComment(g, f, message.Path)
+ if messageOptions(gen, message).GetDeprecated() {
+ if hasComment {
+ g.P("//")
+ }
+ g.P(deprecationComment(true))
+ }
g.P("type ", message.GoIdent, " struct {")
for _, field := range message.Fields {
if field.OneofType != nil {
@@ -352,7 +361,8 @@
fmt.Sprintf("protobuf_val:%q", fieldProtobufTag(val)),
)
}
- g.P(field.GoName, " ", goType, " `", strings.Join(tags, " "), "`")
+ g.P(field.GoName, " ", goType, " `", strings.Join(tags, " "), "`",
+ deprecationComment(fieldOptions(gen, field).GetDeprecated()))
}
g.P("XXX_NoUnkeyedLiteral struct{} `json:\"-\"`")
@@ -524,6 +534,9 @@
}
goType, pointer := fieldGoType(g, field)
defaultValue := fieldDefaultValue(g, message, field)
+ if fieldOptions(gen, field).GetDeprecated() {
+ g.P(deprecationComment(true))
+ }
g.P("func (m *", message.GoIdent, ") Get", field.GoName, "() ", goType, " {")
if field.OneofType != nil {
g.P("if x, ok := m.Get", field.OneofType.GoName, "().(*", fieldOneofType(field), "); ok {")
@@ -830,6 +843,14 @@
return hasComment
}
+// deprecationComment returns a standard deprecation comment if deprecated is true.
+func deprecationComment(deprecated bool) string {
+ if !deprecated {
+ return ""
+ }
+ return "// Deprecated: Do not use."
+}
+
// pathKey converts a location path to a string suitable for use as a map key.
func pathKey(path []int32) string {
var buf []byte
diff --git a/cmd/protoc-gen-go/options.go b/cmd/protoc-gen-go/options.go
index a3828ea..53e3e78 100644
--- a/cmd/protoc-gen-go/options.go
+++ b/cmd/protoc-gen-go/options.go
@@ -9,6 +9,7 @@
package main
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"
@@ -16,26 +17,106 @@
// messageOptions returns the MessageOptions for a message.
func messageOptions(gen *protogen.Plugin, message *protogen.Message) *descpb.MessageOptions {
- file, ok := descriptorFile(gen, message.Desc)
- if !ok {
+ d := getDescriptorProto(gen, message.Desc, message.Path)
+ if d == nil {
return nil
}
- desc := file.Proto.MessageType[message.Path[1]]
- for i := 3; i < len(message.Path); i += 2 {
- desc = desc.NestedType[message.Path[1]]
- }
- return desc.GetOptions()
+ return d.(*descpb.DescriptorProto).GetOptions()
}
-func descriptorFile(gen *protogen.Plugin, desc protoreflect.Descriptor) (*protogen.File, bool) {
+// fieldOptions returns the FieldOptions for a message.
+func fieldOptions(gen *protogen.Plugin, field *protogen.Field) *descpb.FieldOptions {
+ d := getDescriptorProto(gen, field.Desc, field.Path)
+ if d == nil {
+ return nil
+ }
+ return d.(*descpb.FieldDescriptorProto).GetOptions()
+}
+
+// enumOptions returns the EnumOptions for an enum
+func enumOptions(gen *protogen.Plugin, enum *protogen.Enum) *descpb.EnumOptions {
+ d := getDescriptorProto(gen, enum.Desc, enum.Path)
+ if d == nil {
+ return nil
+ }
+ return d.(*descpb.EnumDescriptorProto).GetOptions()
+}
+
+// enumValueOptions returns the EnumValueOptions for an enum value
+func enumValueOptions(gen *protogen.Plugin, value *protogen.EnumValue) *descpb.EnumValueOptions {
+ d := getDescriptorProto(gen, value.Desc, value.Path)
+ if d == nil {
+ return nil
+ }
+ return d.(*descpb.EnumValueDescriptorProto).GetOptions()
+}
+
+func getDescriptorProto(gen *protogen.Plugin, desc protoreflect.Descriptor, path []int32) proto.Message {
+ var p proto.Message
+ // Look up the FileDescriptorProto.
for {
if fdesc, ok := desc.(protoreflect.FileDescriptor); ok {
- return gen.FileByName(fdesc.Path())
+ file, ok := gen.FileByName(fdesc.Path())
+ if !ok {
+ return nil
+ }
+ p = file.Proto
+ break
}
var ok bool
desc, ok = desc.Parent()
if !ok {
- return nil, false
+ return nil
}
}
+ const (
+ // field numbers in FileDescriptorProto
+ filePackageField = 2 // package
+ fileMessageField = 4 // message_type
+ fileEnumField = 5 // enum_type
+ fileExtensionField = 7 // extension
+ // field numbers in DescriptorProto
+ messageFieldField = 2 // field
+ messageMessageField = 3 // nested_type
+ messageEnumField = 4 // enum_type
+ messageExtensionField = 6 // extension
+ messageOneofField = 8 // oneof_decl
+ // field numbers in EnumDescriptorProto
+ enumValueField = 2 // value
+ )
+ for len(path) > 1 {
+ switch d := p.(type) {
+ case *descpb.FileDescriptorProto:
+ switch path[0] {
+ case fileMessageField:
+ p = d.MessageType[path[1]]
+ case fileEnumField:
+ p = d.EnumType[path[1]]
+ default:
+ return nil
+ }
+ case *descpb.DescriptorProto:
+ switch path[0] {
+ case messageFieldField:
+ p = d.Field[path[1]]
+ case messageMessageField:
+ p = d.NestedType[path[1]]
+ case messageEnumField:
+ p = d.EnumType[path[1]]
+ default:
+ return nil
+ }
+ case *descpb.EnumDescriptorProto:
+ switch path[0] {
+ case enumValueField:
+ p = d.Value[path[1]]
+ default:
+ return nil
+ }
+ default:
+ return nil
+ }
+ path = path[2:]
+ }
+ return p
}
diff --git a/cmd/protoc-gen-go/testdata/comments/comments.pb.go b/cmd/protoc-gen-go/testdata/comments/comments.pb.go
index 682a960..39fb376 100644
--- a/cmd/protoc-gen-go/testdata/comments/comments.pb.go
+++ b/cmd/protoc-gen-go/testdata/comments/comments.pb.go
@@ -1,9 +1,9 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: comments/comments.proto
-// COMMENT: package goproto.protoc.proto2
+// COMMENT: package goproto.protoc.comments;
-package proto2
+package comments
import (
fmt "fmt"
@@ -215,25 +215,25 @@
var xxx_messageInfo_Message2_Message2B proto.InternalMessageInfo
func init() {
- proto.RegisterType((*Message1)(nil), "goproto.protoc.proto2.Message1")
- proto.RegisterType((*Message1_Message1A)(nil), "goproto.protoc.proto2.Message1.Message1A")
- proto.RegisterType((*Message1_Message1B)(nil), "goproto.protoc.proto2.Message1.Message1B")
- proto.RegisterType((*Message2)(nil), "goproto.protoc.proto2.Message2")
- proto.RegisterType((*Message2_Message2A)(nil), "goproto.protoc.proto2.Message2.Message2A")
- proto.RegisterType((*Message2_Message2B)(nil), "goproto.protoc.proto2.Message2.Message2B")
+ proto.RegisterType((*Message1)(nil), "goproto.protoc.comments.Message1")
+ proto.RegisterType((*Message1_Message1A)(nil), "goproto.protoc.comments.Message1.Message1A")
+ proto.RegisterType((*Message1_Message1B)(nil), "goproto.protoc.comments.Message1.Message1B")
+ proto.RegisterType((*Message2)(nil), "goproto.protoc.comments.Message2")
+ proto.RegisterType((*Message2_Message2A)(nil), "goproto.protoc.comments.Message2.Message2A")
+ proto.RegisterType((*Message2_Message2B)(nil), "goproto.protoc.comments.Message2.Message2B")
}
func init() { proto.RegisterFile("comments/comments.proto", fileDescriptor_885e8293f1fab554) }
var fileDescriptor_885e8293f1fab554 = []byte{
- // 136 bytes of a gzipped FileDescriptorProto
+ // 135 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0x4f, 0xce, 0xcf, 0xcd,
- 0x4d, 0xcd, 0x2b, 0x29, 0xd6, 0x87, 0x31, 0xf4, 0x0a, 0x8a, 0xf2, 0x4b, 0xf2, 0x85, 0x44, 0xd3,
- 0xf3, 0xc1, 0x0c, 0x08, 0x37, 0x19, 0x42, 0x19, 0x29, 0xa9, 0x70, 0x71, 0xf8, 0xa6, 0x16, 0x17,
- 0x27, 0xa6, 0xa7, 0x1a, 0x4a, 0x71, 0x73, 0x71, 0xc2, 0xd8, 0x8e, 0xc8, 0x1c, 0x27, 0x24, 0x55,
- 0x46, 0x48, 0x12, 0x46, 0xc8, 0xaa, 0x8c, 0x9c, 0x9c, 0xac, 0xa3, 0x2c, 0xd3, 0xf3, 0xf3, 0xd3,
- 0x73, 0x52, 0xf5, 0xd2, 0xf3, 0x73, 0x12, 0xf3, 0xd2, 0xf5, 0xf2, 0x8b, 0xd2, 0xf5, 0xc1, 0xf6,
- 0xe8, 0x27, 0xe7, 0xa6, 0x40, 0x58, 0xc9, 0xba, 0xe9, 0xa9, 0x79, 0xba, 0xe9, 0xf9, 0xfa, 0x25,
- 0xa9, 0xc5, 0x25, 0x29, 0x89, 0x25, 0x89, 0x10, 0x61, 0x23, 0x40, 0x00, 0x00, 0x00, 0xff, 0xff,
- 0x32, 0x8f, 0xcd, 0x4f, 0xb9, 0x00, 0x00, 0x00,
+ 0x4d, 0xcd, 0x2b, 0x29, 0xd6, 0x87, 0x31, 0xf4, 0x0a, 0x8a, 0xf2, 0x4b, 0xf2, 0x85, 0xc4, 0xd3,
+ 0xf3, 0xc1, 0x0c, 0x08, 0x37, 0x59, 0x0f, 0x26, 0xad, 0xa4, 0xc2, 0xc5, 0xe1, 0x9b, 0x5a, 0x5c,
+ 0x9c, 0x98, 0x9e, 0x6a, 0x28, 0xc5, 0xcd, 0xc5, 0x09, 0x63, 0x3b, 0x22, 0x73, 0x9c, 0x90, 0x54,
+ 0x19, 0x21, 0x49, 0x18, 0x21, 0xab, 0x32, 0x72, 0x72, 0xb2, 0x8d, 0xb2, 0x4e, 0xcf, 0xcf, 0x4f,
+ 0xcf, 0x49, 0xd5, 0x4b, 0xcf, 0xcf, 0x49, 0xcc, 0x4b, 0xd7, 0xcb, 0x2f, 0x4a, 0xd7, 0x07, 0x5b,
+ 0xa8, 0x9f, 0x9c, 0x9b, 0x02, 0x61, 0x25, 0xeb, 0xa6, 0xa7, 0xe6, 0xe9, 0xa6, 0xe7, 0xeb, 0x97,
+ 0xa4, 0x16, 0x97, 0xa4, 0x24, 0x96, 0x24, 0xc2, 0x5d, 0x0a, 0x08, 0x00, 0x00, 0xff, 0xff, 0x2e,
+ 0x72, 0xe4, 0xcc, 0xbd, 0x00, 0x00, 0x00,
}
diff --git a/cmd/protoc-gen-go/testdata/comments/comments.proto b/cmd/protoc-gen-go/testdata/comments/comments.proto
index d97c8dc..7ee51ce 100644
--- a/cmd/protoc-gen-go/testdata/comments/comments.proto
+++ b/cmd/protoc-gen-go/testdata/comments/comments.proto
@@ -4,10 +4,10 @@
syntax = "proto2";
-// COMMENT: package goproto.protoc.proto2
-package goproto.protoc.proto2;
+// COMMENT: package goproto.protoc.comments;
+package goproto.protoc.comments;
-option go_package = "google.golang.org/proto/cmd/protoc-gen-go/testdata/proto2";
+option go_package = "google.golang.org/proto/cmd/protoc-gen-go/testdata/comments";
// COMMENT: Message1
message Message1 {
diff --git a/cmd/protoc-gen-go/testdata/comments/deprecated.pb.go b/cmd/protoc-gen-go/testdata/comments/deprecated.pb.go
new file mode 100644
index 0000000..e820c3b
--- /dev/null
+++ b/cmd/protoc-gen-go/testdata/comments/deprecated.pb.go
@@ -0,0 +1,107 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// comments/deprecated.proto is a deprecated file.
+
+package comments
+
+import (
+ fmt "fmt"
+ proto "github.com/golang/protobuf/proto"
+ math "math"
+)
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the proto package it is being compiled against.
+// A compilation error at this line likely means your copy of the
+// proto package needs to be updated.
+const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
+
+type DeprecatedEnum int32 // Deprecated: Do not use.
+const (
+ DeprecatedEnum_DEPRECATED DeprecatedEnum = 0 // Deprecated: Do not use.
+)
+
+var DeprecatedEnum_name = map[int32]string{
+ 0: "DEPRECATED",
+}
+
+var DeprecatedEnum_value = map[string]int32{
+ "DEPRECATED": 0,
+}
+
+func (x DeprecatedEnum) String() string {
+ return proto.EnumName(DeprecatedEnum_name, int32(x))
+}
+
+func (DeprecatedEnum) EnumDescriptor() ([]byte, []int) {
+ return fileDescriptor_0336e614ee2de5f7, []int{0}
+}
+
+// Deprecated: Do not use.
+type DeprecatedMessage struct {
+ DeprecatedField string `protobuf:"bytes,1,opt,name=deprecated_field,json=deprecatedField,proto3" json:"deprecated_field,omitempty"` // Deprecated: Do not use.
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *DeprecatedMessage) Reset() { *m = DeprecatedMessage{} }
+func (m *DeprecatedMessage) String() string { return proto.CompactTextString(m) }
+func (*DeprecatedMessage) ProtoMessage() {}
+func (*DeprecatedMessage) Descriptor() ([]byte, []int) {
+ return fileDescriptor_0336e614ee2de5f7, []int{0}
+}
+
+func (m *DeprecatedMessage) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_DeprecatedMessage.Unmarshal(m, b)
+}
+func (m *DeprecatedMessage) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_DeprecatedMessage.Marshal(b, m, deterministic)
+}
+func (m *DeprecatedMessage) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_DeprecatedMessage.Merge(m, src)
+}
+func (m *DeprecatedMessage) XXX_Size() int {
+ return xxx_messageInfo_DeprecatedMessage.Size(m)
+}
+func (m *DeprecatedMessage) XXX_DiscardUnknown() {
+ xxx_messageInfo_DeprecatedMessage.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_DeprecatedMessage proto.InternalMessageInfo
+
+// Deprecated: Do not use.
+func (m *DeprecatedMessage) GetDeprecatedField() string {
+ if m != nil {
+ return m.DeprecatedField
+ }
+ return ""
+}
+
+func init() {
+ proto.RegisterType((*DeprecatedMessage)(nil), "goproto.protoc.comments.DeprecatedMessage")
+ proto.RegisterEnum("goproto.protoc.comments.DeprecatedEnum", DeprecatedEnum_name, DeprecatedEnum_value)
+}
+
+func init() { proto.RegisterFile("comments/deprecated.proto", fileDescriptor_0336e614ee2de5f7) }
+
+var fileDescriptor_0336e614ee2de5f7 = []byte{
+ // 202 bytes of a gzipped FileDescriptorProto
+ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x44, 0x8e, 0x31, 0x4b, 0x04, 0x31,
+ 0x10, 0x85, 0x9d, 0x14, 0xa2, 0x29, 0xf4, 0x4c, 0x63, 0xbc, 0x4a, 0xac, 0x0e, 0x61, 0x93, 0xc2,
+ 0x4e, 0x1b, 0x3d, 0x37, 0xd7, 0x09, 0x72, 0x58, 0xd9, 0x48, 0x4c, 0xc6, 0x41, 0xd8, 0x64, 0x96,
+ 0x4d, 0xfc, 0x6f, 0xfe, 0x3c, 0x71, 0x97, 0xbd, 0xed, 0xde, 0xe3, 0x7d, 0x3c, 0x3e, 0x79, 0x15,
+ 0x38, 0x25, 0xcc, 0xb5, 0xd8, 0x88, 0xfd, 0x80, 0xc1, 0x57, 0x8c, 0xa6, 0x1f, 0xb8, 0xb2, 0xba,
+ 0x24, 0x1e, 0xc3, 0x54, 0x83, 0x99, 0xc9, 0x9b, 0x9d, 0xbc, 0x68, 0x0f, 0xf0, 0x0b, 0x96, 0xe2,
+ 0x09, 0x55, 0x23, 0x57, 0xcb, 0xc3, 0xc7, 0xd7, 0x37, 0x76, 0x51, 0xc3, 0x35, 0x6c, 0x4e, 0xb7,
+ 0x42, 0xc3, 0xfe, 0x7c, 0xd9, 0x76, 0xff, 0xd3, 0xbd, 0xd0, 0x70, 0xbb, 0x91, 0x67, 0xcb, 0x8f,
+ 0xcb, 0x3f, 0x49, 0x29, 0x29, 0x5b, 0xf7, 0xba, 0x77, 0xcf, 0x4f, 0x6f, 0xae, 0x5d, 0x1d, 0xad,
+ 0xc5, 0x09, 0xac, 0x85, 0x86, 0xed, 0xe3, 0xfb, 0x03, 0x31, 0x53, 0x87, 0x86, 0xb8, 0xf3, 0x99,
+ 0x0c, 0x0f, 0x64, 0x47, 0x2d, 0x1b, 0x52, 0x9c, 0x52, 0x68, 0x08, 0x73, 0x43, 0x6c, 0x2b, 0x96,
+ 0x1a, 0x7d, 0xf5, 0x76, 0x16, 0xfe, 0x05, 0xf8, 0x3c, 0x1e, 0x99, 0xbb, 0xbf, 0x00, 0x00, 0x00,
+ 0xff, 0xff, 0x58, 0x8a, 0xbc, 0xc2, 0xf0, 0x00, 0x00, 0x00,
+}
diff --git a/cmd/protoc-gen-go/testdata/comments/deprecated.proto b/cmd/protoc-gen-go/testdata/comments/deprecated.proto
new file mode 100644
index 0000000..a33cb33
--- /dev/null
+++ b/cmd/protoc-gen-go/testdata/comments/deprecated.proto
@@ -0,0 +1,20 @@
+// 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.comments;
+
+option deprecated = true;
+option go_package = "google.golang.org/proto/cmd/protoc-gen-go/testdata/comments";
+
+message DeprecatedMessage {
+ option deprecated = true;
+ string deprecated_field = 1 [deprecated=true];
+}
+
+enum DeprecatedEnum {
+ option deprecated = true;
+ DEPRECATED = 0 [deprecated=true];
+}