internal/impl: handle legacy ExtensionDesc form
Add support for an ExtensionDesc with only Field populated as returned by
protoV1.ExtensionDescs. Rather than panicking when TypeDescriptor is called,
return a placeholder that preserves the name and/or number.
Change-Id: I60352a7aec8ccd8a0c1fb08db5891043a441695f
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/193725
Reviewed-by: Damien Neil <dneil@google.com>
diff --git a/internal/impl/extension.go b/internal/impl/extension.go
index a895763..723bdb0 100644
--- a/internal/impl/extension.go
+++ b/internal/impl/extension.go
@@ -140,10 +140,12 @@
if xi.desc.ExtensionDescriptor == nil {
xi.initFromLegacy()
}
- if xi.ExtensionType == nil {
- xi.initToLegacy()
+ if !xi.desc.ExtensionDescriptor.IsPlaceholder() {
+ if xi.ExtensionType == nil {
+ xi.initToLegacy()
+ }
+ xi.conv = NewConverter(xi.goType, xi.desc)
}
- xi.conv = NewConverter(xi.goType, xi.desc)
}
type extensionTypeDescriptor struct {
diff --git a/internal/impl/legacy_aberrant_test.go b/internal/impl/legacy_aberrant_test.go
index cca88d4..da9c8c1 100644
--- a/internal/impl/legacy_aberrant_test.go
+++ b/internal/impl/legacy_aberrant_test.go
@@ -180,7 +180,7 @@
type AberrantEnum int32
-func TestAberrant(t *testing.T) {
+func TestAberrantMessages(t *testing.T) {
want := new(descriptorpb.DescriptorProto)
if err := prototext.Unmarshal([]byte(`
name: "AberrantMessage"
@@ -320,3 +320,34 @@
t.Errorf("mismatching exact message descriptors")
}
}
+
+func TestAberrantExtensions(t *testing.T) {
+ tests := []struct {
+ in *impl.ExtensionInfo
+ wantName protoreflect.FullName
+ wantNumber protoreflect.FieldNumber
+ wantPlaceholder bool
+ }{{
+ in: &impl.ExtensionInfo{Field: 500},
+ wantNumber: 500,
+ wantPlaceholder: true,
+ }, {
+ in: &impl.ExtensionInfo{Name: "foo.bar.baz"},
+ wantName: "foo.bar.baz",
+ wantPlaceholder: true,
+ }}
+
+ for _, tt := range tests {
+ t.Run("", func(t *testing.T) {
+ xtd := tt.in.TypeDescriptor()
+ switch {
+ case xtd.FullName() != tt.wantName:
+ t.Errorf("FullName() = %v, want %v", xtd.FullName(), tt.wantName)
+ case xtd.Number() != tt.wantNumber:
+ t.Errorf("Number() = %v, want %v", xtd.Number(), tt.wantNumber)
+ case xtd.IsPlaceholder() != tt.wantPlaceholder:
+ t.Errorf("IsPlaceholder() = %v, want %v", xtd.IsPlaceholder(), tt.wantPlaceholder)
+ }
+ })
+ }
+}
diff --git a/internal/impl/legacy_extension.go b/internal/impl/legacy_extension.go
index 0979ec0..93c318f 100644
--- a/internal/impl/legacy_extension.go
+++ b/internal/impl/legacy_extension.go
@@ -7,9 +7,11 @@
import (
"reflect"
+ "google.golang.org/protobuf/internal/descopts"
"google.golang.org/protobuf/internal/encoding/messageset"
ptag "google.golang.org/protobuf/internal/encoding/tag"
"google.golang.org/protobuf/internal/filedesc"
+ "google.golang.org/protobuf/internal/pragma"
pref "google.golang.org/protobuf/reflect/protoreflect"
preg "google.golang.org/protobuf/reflect/protoregistry"
piface "google.golang.org/protobuf/runtime/protoiface"
@@ -71,6 +73,17 @@
// initFromLegacy initializes an ExtensionInfo from
// the contents of the deprecated exported fields of the type.
func (xi *ExtensionInfo) initFromLegacy() {
+ // The v1 API returns "type incomplete" descriptors where only the
+ // field number is specified. In such a case, use a placeholder.
+ if xi.ExtendedType == nil || xi.ExtensionType == nil {
+ xd := placeholderExtension{
+ name: pref.FullName(xi.Name),
+ number: pref.FieldNumber(xi.Field),
+ }
+ xi.desc = extensionTypeDescriptor{xd, xi}
+ return
+ }
+
// Resolve enum or message dependencies.
var ed pref.EnumDescriptor
var md pref.MessageDescriptor
@@ -123,3 +136,38 @@
xi.goType = tt
xi.desc = extensionTypeDescriptor{xd, xi}
}
+
+type placeholderExtension struct {
+ name pref.FullName
+ number pref.FieldNumber
+}
+
+func (x placeholderExtension) ParentFile() pref.FileDescriptor { return nil }
+func (x placeholderExtension) Parent() pref.Descriptor { return nil }
+func (x placeholderExtension) Index() int { return 0 }
+func (x placeholderExtension) Syntax() pref.Syntax { return 0 }
+func (x placeholderExtension) Name() pref.Name { return x.name.Name() }
+func (x placeholderExtension) FullName() pref.FullName { return x.name }
+func (x placeholderExtension) IsPlaceholder() bool { return true }
+func (x placeholderExtension) Options() pref.ProtoMessage { return descopts.Field }
+func (x placeholderExtension) Number() pref.FieldNumber { return x.number }
+func (x placeholderExtension) Cardinality() pref.Cardinality { return 0 }
+func (x placeholderExtension) Kind() pref.Kind { return 0 }
+func (x placeholderExtension) HasJSONName() bool { return false }
+func (x placeholderExtension) JSONName() string { return "" }
+func (x placeholderExtension) IsExtension() bool { return true }
+func (x placeholderExtension) IsWeak() bool { return false }
+func (x placeholderExtension) IsPacked() bool { return false }
+func (x placeholderExtension) IsList() bool { return false }
+func (x placeholderExtension) IsMap() bool { return false }
+func (x placeholderExtension) MapKey() pref.FieldDescriptor { return nil }
+func (x placeholderExtension) MapValue() pref.FieldDescriptor { return nil }
+func (x placeholderExtension) HasDefault() bool { return false }
+func (x placeholderExtension) Default() pref.Value { return pref.Value{} }
+func (x placeholderExtension) DefaultEnumValue() pref.EnumValueDescriptor { return nil }
+func (x placeholderExtension) ContainingOneof() pref.OneofDescriptor { return nil }
+func (x placeholderExtension) ContainingMessage() pref.MessageDescriptor { return nil }
+func (x placeholderExtension) Enum() pref.EnumDescriptor { return nil }
+func (x placeholderExtension) Message() pref.MessageDescriptor { return nil }
+func (x placeholderExtension) ProtoType(pref.FieldDescriptor) { return }
+func (x placeholderExtension) ProtoInternal(pragma.DoNotImplement) { return }