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 }