Include elements of oneofs in custom option scopes.

This prevents usages of such options being flagged as errors and enables
autocompletion of them.

Change-Id: I80cbd7cfb6f99066f80626b831a6a23dec4b3e4b
Signed-off-by: John Glassmyer <jogl@google.com>
diff --git a/com.google.eclipse.protobuf.integration.test/src/com/google/eclipse/protobuf/scoping/ProtobufScopeProvider_scope_OptionField_target_with_MessageOptionField_Test.java b/com.google.eclipse.protobuf.integration.test/src/com/google/eclipse/protobuf/scoping/ProtobufScopeProvider_scope_OptionField_target_with_MessageOptionField_Test.java
index 7b8b51e..f46338f 100644
--- a/com.google.eclipse.protobuf.integration.test/src/com/google/eclipse/protobuf/scoping/ProtobufScopeProvider_scope_OptionField_target_with_MessageOptionField_Test.java
+++ b/com.google.eclipse.protobuf.integration.test/src/com/google/eclipse/protobuf/scoping/ProtobufScopeProvider_scope_OptionField_target_with_MessageOptionField_Test.java
@@ -15,6 +15,7 @@
 import static com.google.eclipse.protobuf.junit.core.SearchOption.IGNORE_CASE;
 import static com.google.eclipse.protobuf.junit.core.XtextRule.overrideRuntimeModuleWith;
 import static com.google.eclipse.protobuf.junit.matchers.ContainAllFieldsInMessage.containAllFieldsIn;
+import static com.google.eclipse.protobuf.junit.matchers.ContainAllNames.containAll;
 
 import org.eclipse.emf.ecore.EReference;
 import org.eclipse.xtext.scoping.IScope;
@@ -182,4 +183,32 @@
     Group groupMessage = xtext.find("Type", " =", Group.class);
     assertThat(descriptionsIn(scope), containAllFieldsIn(groupMessage));
   }
+
+  // syntax = "proto2";
+  //
+  // package com.google.proto;
+  //
+  // import 'google/protobuf/descriptor.proto';
+  //
+  // message Type {
+  //   bool foo = 1;
+  //   oneof choose_one {
+  //     bool bar = 2;
+  //     bool baz = 3;
+  //   }
+  // }
+  //
+  // extend google.protobuf.FieldOptions {
+  //   optional Type type = 1000;
+  // }
+  //
+  // message Person {
+  //   optional bool active = 1 [(type).bar = true];
+  // }
+  @Test public void should_provide_field_option_from_oneof() {
+    CustomFieldOption option = xtext.find("type", ")", CustomFieldOption.class);
+    MessageOptionField codeOptionField = (MessageOptionField) option.getFields().get(0);
+    IScope scope = scopeProvider.scope_OptionField_target(codeOptionField, reference);
+    assertThat(descriptionsIn(scope), containAll("foo", "bar", "baz"));
+  }
 }
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/MessageFieldFinderStrategy.java b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/MessageFieldFinderStrategy.java
index 7329bfb..f54118a 100644
--- a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/MessageFieldFinderStrategy.java
+++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/MessageFieldFinderStrategy.java
@@ -12,7 +12,9 @@
 
 import static com.google.common.collect.Sets.newHashSet;
 
+import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.Set;
 
 import org.eclipse.emf.ecore.EObject;
@@ -21,11 +23,10 @@
 import com.google.eclipse.protobuf.model.util.MessageFields;
 import com.google.eclipse.protobuf.model.util.Options;
 import com.google.eclipse.protobuf.protobuf.Group;
-import com.google.eclipse.protobuf.protobuf.GroupElement;
 import com.google.eclipse.protobuf.protobuf.IndexedElement;
 import com.google.eclipse.protobuf.protobuf.Message;
-import com.google.eclipse.protobuf.protobuf.MessageElement;
 import com.google.eclipse.protobuf.protobuf.MessageField;
+import com.google.eclipse.protobuf.protobuf.OneOf;
 import com.google.inject.Inject;
 
 /**
@@ -36,27 +37,44 @@
   @Inject private Options options;
 
   @Override public Collection<IEObjectDescription> findOptionFields(IndexedElement reference) {
-    Set<IEObjectDescription> descriptions = newHashSet();
+    Collection<? extends EObject> elements;
     if (reference instanceof MessageField) {
       Message fieldType = messageFields.messageTypeOf((MessageField) reference);
-      for (MessageElement element : fieldType.getElements()) {
-        IEObjectDescription d = describe(element);
-        if (d != null) {
-          descriptions.add(d);
-        }
+      if (fieldType != null) {
+        elements = fieldType.getElements();
+      } else {
+        elements = Collections.emptySet();
       }
+    } else if (reference instanceof Group) {
+      elements = ((Group) reference).getElements();
+    } else {
+      elements = Collections.emptySet();
     }
-    if (reference instanceof Group) {
-      for (GroupElement element : ((Group) reference).getElements()) {
-        IEObjectDescription d = describe(element);
-        if (d != null) {
-          descriptions.add(d);
-        }
+
+    Set<IEObjectDescription> descriptions = newHashSet();
+    Collection<EObject> expandedElements = expandOneOfs(elements);
+    for (EObject element : expandedElements) {
+      IEObjectDescription d = describe(element);
+      if (d != null) {
+        descriptions.add(d);
       }
     }
     return descriptions;
   }
 
+  private Collection<EObject> expandOneOfs(Collection<? extends EObject> elements) {
+    Collection<EObject> expandedElements = new ArrayList<>(elements.size());
+    for (EObject element : elements) {
+      if (element instanceof OneOf) {
+        expandedElements.addAll(((OneOf) element).getElements());
+      }
+      else {
+        expandedElements.add(element);
+      }
+    }
+    return expandedElements;
+  }
+
   private IEObjectDescription describe(EObject e) {
     if (!(e instanceof IndexedElement)) {
       return null;