Repository: jhump/protoreflect (go)

Issue:
Parser can't recognize custom option for oneof
File `foo.proto`:
```proto
syntax = "proto3";

package foo;

import "google/protobuf/descriptor.proto";

extend google.protobuf.OneofOptions {
  bool my_option = 101400;
}

message MyMessage {
  oneof my_one_of {
    option (my_option) = true;
    int64 my_value = 1;
  }
}
```

Using of `ParseFiles`:
```go
fds, err := protoparse.Parser{}.ParseFiles("foo.proto")
```

After this call `err` contains message:
```
*descriptorpb.OneofDescriptorProto foo.MyMessage.my_one_of: option (my_option): unrecognized extension my_option of google.protobuf.OneofOptions.
```

Pull request description:
protoparse: doh! we weren't resolving options for oneofs during linking
The link step that resolves identifiers into fully-qualified names was failing to ever examine/process custom option names for oneofs.

So this adds a linker test case that includes _every_ kind of option. This reproduced the reported issue and now passes with the main code changes.

Fixes #406.

Patch:
diff --git a/desc/protoparse/linker.go b/desc/protoparse/linker.go
index 0301541..2aa32f0 100644
--- a/desc/protoparse/linker.go
+++ b/desc/protoparse/linker.go
@@ -372,6 +372,14 @@ func (l *linker) resolveMessageTypes(r *parseResult, fd *dpb.FileDescriptorProto
 			return err
 		}
 	}
+	for _, ood := range md.OneofDecl {
+		if ood.Options != nil {
+			ooName := fmt.Sprintf("%s.%s", fqn, ood.GetName())
+			if err := l.resolveOptions(r, fd, "oneof", ooName, proto.MessageName(ood.Options), ood.Options.UninterpretedOption, scopes); err != nil {
+				return err
+			}
+		}
+	}
 	for _, fld := range md.Extension {
 		if err := l.resolveFieldTypes(r, fd, prefix, fld, scopes); err != nil {
 			return err


Test patch:
diff --git a/desc/protoparse/linker_test.go b/desc/protoparse/linker_test.go
index b9ab970..287dc66 100644
--- a/desc/protoparse/linker_test.go
+++ b/desc/protoparse/linker_test.go
@@ -180,6 +180,44 @@ func TestLinkerValidation(t *testing.T) {
 			},
 			"foo.proto:1:70: field fu.baz.foobar.a: unknown type baz; resolved to fu.baz which is not defined; consider using a leading dot",
 		},
+		{
+			map[string]string{
+				"foo.proto": `
+					syntax = "proto2";
+					package foo;
+					import "google/protobuf/descriptor.proto";
+					extend google.protobuf.FileOptions           { optional string fil_foo = 12000; }
+					extend google.protobuf.MessageOptions        { optional string msg_foo = 12000; }
+					extend google.protobuf.FieldOptions          { optional string fld_foo = 12000 [(fld_foo) = "extension"]; }
+					extend google.protobuf.OneofOptions          { optional string oof_foo = 12000; }
+					extend google.protobuf.EnumOptions           { optional string enm_foo = 12000; }
+					extend google.protobuf.EnumValueOptions      { optional string env_foo = 12000; }
+					extend google.protobuf.ExtensionRangeOptions { optional string ext_foo = 12000; }
+					extend google.protobuf.ServiceOptions        { optional string svc_foo = 12000; }
+					extend google.protobuf.MethodOptions         { optional string mtd_foo = 12000; }
+					option (fil_foo) = "file";
+					message Bar {
+						option (msg_foo) = "message";
+						oneof foo {
+							option (oof_foo) = "oneof";
+							string bar = 1 [(fld_foo) = "field"];
+						}
+						extensions 100 to 200 [(ext_foo) = "extensionrange"];
+					}
+					enum Baz {
+						option (enm_foo) = "enum";
+						ZERO = 0 [(env_foo) = "enumvalue"];
+					}
+					service FooService {
+						option (svc_foo) = "service";
+						rpc Bar(Bar) returns (Bar) {
+							option (mtd_foo) = "method";
+						}
+					}
+					`,
+			},
+			"", // should success
+		},
 		{
 			map[string]string{
 				"foo.proto": "package fu.baz; message foobar{ repeated string a = 1 [default = \"abc\"]; }",