reflect/protoreflect: add Descriptor.ParentFile
Querying for the parent file that contains a descriptor declaration
is a common enough operation to warrant its own first-class method.
Change-Id: I2f41e5126a5b465df23897904a6513dd3ed8dd92
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/176777
Reviewed-by: Damien Neil <dneil@google.com>
diff --git a/cmd/protoc-gen-go/internal_gengo/main.go b/cmd/protoc-gen-go/internal_gengo/main.go
index 312e1ac..9e11fb0 100644
--- a/cmd/protoc-gen-go/internal_gengo/main.go
+++ b/cmd/protoc-gen-go/internal_gengo/main.go
@@ -318,16 +318,7 @@
// been the full name of the proto enum type instead, but changing it at this
// point would require thought.
func enumLegacyName(enum *protogen.Enum) string {
- // Find the FileDescriptor for this enum.
- var desc protoreflect.Descriptor = enum.Desc
- for {
- p, ok := desc.Parent()
- if !ok {
- break
- }
- desc = p
- }
- fdesc := desc.(protoreflect.FileDescriptor)
+ fdesc := enum.Desc.ParentFile()
if fdesc.Package() == "" {
return enum.GoIdent.GoName
}
diff --git a/internal/fileinit/desc.go b/internal/fileinit/desc.go
index 0e9ef53..2729da6 100644
--- a/internal/fileinit/desc.go
+++ b/internal/fileinit/desc.go
@@ -250,6 +250,7 @@
}
)
+func (fd *fileDesc) ParentFile() pref.FileDescriptor { return fd }
func (fd *fileDesc) Parent() (pref.Descriptor, bool) { return nil, false }
func (fd *fileDesc) Index() int { return 0 }
func (fd *fileDesc) Syntax() pref.Syntax { return fd.lazyInit().syntax }
@@ -581,6 +582,7 @@
fullName
}
+func (d *baseDesc) ParentFile() pref.FileDescriptor { return d.parentFile }
func (d *baseDesc) Parent() (pref.Descriptor, bool) { return d.parent, true }
func (d *baseDesc) Index() int { return d.index }
func (d *baseDesc) Syntax() pref.Syntax { return d.parentFile.Syntax() }
diff --git a/internal/legacy/extension.go b/internal/legacy/extension.go
index e9c8bb7..9174555 100644
--- a/internal/legacy/extension.go
+++ b/internal/legacy/extension.go
@@ -106,7 +106,7 @@
// Derive the proto package name.
// For legacy enums, obtain the proto package from the raw descriptor.
var protoPkg string
- if fd := parentFileDescriptor(xt.Descriptor().Enum()); fd != nil {
+ if fd := xt.Descriptor().Enum().ParentFile(); fd != nil {
protoPkg = string(fd.Package())
}
if ed, ok := reflect.Zero(t).Interface().(enumV1); ok && protoPkg == "" {
@@ -121,7 +121,7 @@
// Derive the proto file that the extension was declared within.
var filename string
- if fd := parentFileDescriptor(xt.Descriptor()); fd != nil {
+ if fd := xt.Descriptor().ParentFile(); fd != nil {
filename = fd.Path()
}
diff --git a/internal/legacy/file.go b/internal/legacy/file.go
index 0f871a0..e98508b 100644
--- a/internal/legacy/file.go
+++ b/internal/legacy/file.go
@@ -9,8 +9,6 @@
"compress/gzip"
"io/ioutil"
"sync"
-
- pref "github.com/golang/protobuf/v2/reflect/protoreflect"
)
// Every enum and message type generated by protoc-gen-go since commit 2fc053c5
@@ -59,14 +57,3 @@
}
return fd
}
-
-// parentFileDescriptor returns the parent protoreflect.FileDescriptor for the
-// provide descriptor. It returns nil if there is no parent.
-func parentFileDescriptor(d pref.Descriptor) pref.FileDescriptor {
- for ok := true; ok; d, ok = d.Parent() {
- if fd, _ := d.(pref.FileDescriptor); fd != nil {
- return fd
- }
- }
- return nil
-}
diff --git a/internal/legacy/file_test.go b/internal/legacy/file_test.go
index 395a2ef..9a9a3fc 100644
--- a/internal/legacy/file_test.go
+++ b/internal/legacy/file_test.go
@@ -421,6 +421,8 @@
name := v.Type().Method(i).Name
if m := v.Method(i); m.Type().NumIn() == 0 && m.Type().NumOut() == 1 {
switch name {
+ case "ParentFile":
+ // Ignore parent file to avoid recursive cycle.
case "Index":
// Ignore index since legacy descriptors have no parent.
case "Options":
diff --git a/internal/prototype/placeholder_type.go b/internal/prototype/placeholder_type.go
index be28bd3..d4c0107 100644
--- a/internal/prototype/placeholder_type.go
+++ b/internal/prototype/placeholder_type.go
@@ -30,6 +30,7 @@
type placeholderName pref.FullName
+func (t placeholderName) ParentFile() pref.FileDescriptor { return nil }
func (t placeholderName) Parent() (pref.Descriptor, bool) { return nil, false }
func (t placeholderName) Index() int { return 0 }
func (t placeholderName) Syntax() pref.Syntax { return 0 }
@@ -43,6 +44,7 @@
placeholderName
}
+func (t placeholderFile) ParentFile() pref.FileDescriptor { return t }
func (t placeholderFile) Options() pref.ProtoMessage { return descopts.File }
func (t placeholderFile) Path() string { return t.path }
func (t placeholderFile) Package() pref.FullName { return t.FullName() }
diff --git a/internal/prototype/protofile_type.go b/internal/prototype/protofile_type.go
index 49d0db1..4e3416f 100644
--- a/internal/prototype/protofile_type.go
+++ b/internal/prototype/protofile_type.go
@@ -13,6 +13,7 @@
descopts "github.com/golang/protobuf/v2/internal/descopts"
pragma "github.com/golang/protobuf/v2/internal/pragma"
pfmt "github.com/golang/protobuf/v2/internal/typefmt"
+ "github.com/golang/protobuf/v2/reflect/protoreflect"
pref "github.com/golang/protobuf/v2/reflect/protoreflect"
)
@@ -62,6 +63,7 @@
f.fileMeta = new(fileMeta)
return fileDesc{f}
}
+func (t fileDesc) ParentFile() pref.FileDescriptor { return t }
func (t fileDesc) Parent() (pref.Descriptor, bool) { return nil, false }
func (t fileDesc) Index() int { return 0 }
func (t fileDesc) Syntax() pref.Syntax { return t.f.Syntax }
@@ -80,6 +82,15 @@
func (t fileDesc) ProtoType(pref.FileDescriptor) {}
func (t fileDesc) ProtoInternal(pragma.DoNotImplement) {}
+func parentFile(d protoreflect.Descriptor) protoreflect.FileDescriptor {
+ for ; d != nil; d, _ = d.Parent() {
+ if fd, ok := d.(protoreflect.FileDescriptor); ok {
+ return fd
+ }
+ }
+ return nil
+}
+
type messageMeta struct {
inheritedMeta
@@ -92,6 +103,7 @@
}
type messageDesc struct{ m *Message }
+func (t messageDesc) ParentFile() pref.FileDescriptor { return parentFile(t) }
func (t messageDesc) Parent() (pref.Descriptor, bool) { return t.m.parent, true }
func (t messageDesc) Index() int { return t.m.index }
func (t messageDesc) Syntax() pref.Syntax { return t.m.syntax }
@@ -143,6 +155,7 @@
}
type fieldDesc struct{ f *Field }
+func (t fieldDesc) ParentFile() pref.FileDescriptor { return parentFile(t) }
func (t fieldDesc) Parent() (pref.Descriptor, bool) { return t.f.parent, true }
func (t fieldDesc) Index() int { return t.f.index }
func (t fieldDesc) Syntax() pref.Syntax { return t.f.syntax }
@@ -244,6 +257,7 @@
}
type oneofDesc struct{ o *Oneof }
+func (t oneofDesc) ParentFile() pref.FileDescriptor { return parentFile(t) }
func (t oneofDesc) Parent() (pref.Descriptor, bool) { return t.o.parent, true }
func (t oneofDesc) Index() int { return t.o.index }
func (t oneofDesc) Syntax() pref.Syntax { return t.o.syntax }
@@ -266,6 +280,7 @@
}
type extensionDesc struct{ x *Extension }
+func (t extensionDesc) ParentFile() pref.FileDescriptor { return parentFile(t) }
func (t extensionDesc) Parent() (pref.Descriptor, bool) { return t.x.parent, true }
func (t extensionDesc) Syntax() pref.Syntax { return t.x.syntax }
func (t extensionDesc) Index() int { return t.x.index }
@@ -304,6 +319,7 @@
}
type enumDesc struct{ e *Enum }
+func (t enumDesc) ParentFile() pref.FileDescriptor { return parentFile(t) }
func (t enumDesc) Parent() (pref.Descriptor, bool) { return t.e.parent, true }
func (t enumDesc) Index() int { return t.e.index }
func (t enumDesc) Syntax() pref.Syntax { return t.e.syntax }
@@ -323,6 +339,7 @@
}
type enumValueDesc struct{ v *EnumValue }
+func (t enumValueDesc) ParentFile() pref.FileDescriptor { return parentFile(t) }
func (t enumValueDesc) Parent() (pref.Descriptor, bool) { return t.v.parent, true }
func (t enumValueDesc) Index() int { return t.v.index }
func (t enumValueDesc) Syntax() pref.Syntax { return t.v.syntax }
@@ -344,6 +361,7 @@
}
type serviceDesc struct{ s *Service }
+func (t serviceDesc) ParentFile() pref.FileDescriptor { return parentFile(t) }
func (t serviceDesc) Parent() (pref.Descriptor, bool) { return t.s.parent, true }
func (t serviceDesc) Index() int { return t.s.index }
func (t serviceDesc) Syntax() pref.Syntax { return t.s.syntax }
@@ -366,6 +384,7 @@
}
type methodDesc struct{ m *Method }
+func (t methodDesc) ParentFile() pref.FileDescriptor { return parentFile(t) }
func (t methodDesc) Parent() (pref.Descriptor, bool) { return t.m.parent, true }
func (t methodDesc) Index() int { return t.m.index }
func (t methodDesc) Syntax() pref.Syntax { return t.m.syntax }
diff --git a/internal/prototype/standalone_type.go b/internal/prototype/standalone_type.go
index 91579da..3bf4115 100644
--- a/internal/prototype/standalone_type.go
+++ b/internal/prototype/standalone_type.go
@@ -15,6 +15,7 @@
type standaloneMessage struct{ m *StandaloneMessage }
+func (t standaloneMessage) ParentFile() pref.FileDescriptor { return nil }
func (t standaloneMessage) Parent() (pref.Descriptor, bool) { return nil, false }
func (t standaloneMessage) Index() int { return 0 }
func (t standaloneMessage) Syntax() pref.Syntax { return t.m.Syntax }
@@ -47,6 +48,7 @@
type standaloneEnum struct{ e *StandaloneEnum }
+func (t standaloneEnum) ParentFile() pref.FileDescriptor { return nil }
func (t standaloneEnum) Parent() (pref.Descriptor, bool) { return nil, false }
func (t standaloneEnum) Index() int { return 0 }
func (t standaloneEnum) Syntax() pref.Syntax { return t.e.Syntax }
@@ -65,6 +67,7 @@
type standaloneExtension struct{ x *StandaloneExtension }
+func (t standaloneExtension) ParentFile() pref.FileDescriptor { return nil }
func (t standaloneExtension) Parent() (pref.Descriptor, bool) { return nil, false }
func (t standaloneExtension) Index() int { return 0 }
func (t standaloneExtension) Syntax() pref.Syntax { return pref.Proto2 }
diff --git a/internal/typefmt/desc_test.go b/internal/typefmt/desc_test.go
index 346362b..296131c 100644
--- a/internal/typefmt/desc_test.go
+++ b/internal/typefmt/desc_test.go
@@ -11,6 +11,7 @@
// TestDescriptorAccessors tests that descriptorAccessors is up-to-date.
func TestDescriptorAccessors(t *testing.T) {
ignore := map[string]bool{
+ "ParentFile": true,
"Parent": true,
"Index": true,
"Syntax": true,
diff --git a/reflect/protodesc/protodesc.go b/reflect/protodesc/protodesc.go
index 4fcd648..7bc2191 100644
--- a/reflect/protodesc/protodesc.go
+++ b/reflect/protodesc/protodesc.go
@@ -375,26 +375,6 @@
return md, nil
}
-func validateFileInImports(d protoreflect.Descriptor, imps importSet) error {
- fd := fileDescriptor(d)
- if fd == nil {
- return errors.New("%v has no parent FileDescriptor", d.FullName())
- }
- if !imps[fd.Path()] {
- return errors.New("reference to type %v without import of %v", d.FullName(), fd.Path())
- }
- return nil
-}
-
-func fileDescriptor(d protoreflect.Descriptor) protoreflect.FileDescriptor {
- for ; d != nil; d, _ = d.Parent() {
- if fd, ok := d.(protoreflect.FileDescriptor); ok {
- return fd
- }
- }
- return nil
-}
-
func findEnumDescriptor(s string, imps importSet, r *protoregistry.Files) (protoreflect.EnumDescriptor, error) {
if !strings.HasPrefix(s, ".") {
return nil, errors.New("identifier name must be fully qualified with a leading dot: %v", s)
@@ -409,3 +389,14 @@
}
return ed, nil
}
+
+func validateFileInImports(d protoreflect.Descriptor, imps importSet) error {
+ fd := d.ParentFile()
+ if fd == nil {
+ return errors.New("%v has no parent FileDescriptor", d.FullName())
+ }
+ if !imps[fd.Path()] {
+ return errors.New("reference to type %v without import of %v", d.FullName(), fd.Path())
+ }
+ return nil
+}
diff --git a/reflect/protoreflect/type.go b/reflect/protoreflect/type.go
index 490bfff..3971b44 100644
--- a/reflect/protoreflect/type.go
+++ b/reflect/protoreflect/type.go
@@ -24,6 +24,14 @@
// This can occur if a descriptor type is created dynamically, or multiple
// versions of the same proto type are linked into the Go binary.
type Descriptor interface {
+ // ParentFile returns the parent file descriptor that this descriptor
+ // is declared within. The parent file for the file descriptor is itself.
+ //
+ // Support for this functionality is optional and may return nil.
+ ParentFile() FileDescriptor
+
+ // TODO: Switch the signature of Parent to drop the bool.
+
// Parent returns the parent containing this descriptor declaration.
// The following shows the mapping from child type to possible parent types:
//