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/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)
+			}
+		})
+	}
+}