Prohibit extensions other than non-map message fields and groups.

Change-Id: Ib7d91b5ba5f1d76a92c49952bcc27359c0790978
diff --git a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/validation/ProtobufJavaValidator_checkMapNotWithinTypeExtension.java b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/validation/ProtobufJavaValidator_checkMapNotWithinTypeExtension.java
new file mode 100644
index 0000000..13afc7e
--- /dev/null
+++ b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/validation/ProtobufJavaValidator_checkMapNotWithinTypeExtension.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2015 Google Inc.
+ *
+ * All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License v1.0 which accompanies this distribution, and is available at
+ *
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package com.google.eclipse.protobuf.validation;
+
+import static com.google.eclipse.protobuf.junit.core.UnitTestModule.unitTestModule;
+import static com.google.eclipse.protobuf.junit.core.XtextRule.overrideRuntimeModuleWith;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import com.google.eclipse.protobuf.junit.core.XtextRule;
+import com.google.eclipse.protobuf.protobuf.MapType;
+import com.google.eclipse.protobuf.protobuf.MapTypeLink;
+import com.google.eclipse.protobuf.protobuf.MessageField;
+import com.google.inject.Inject;
+
+import org.eclipse.xtext.validation.ValidationMessageAcceptor;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+/**
+ * Tests for <code>{@link ProtobufJavaValidator#checkMapNotWithinTypeExtension(MapType)}</code>
+ */
+public class ProtobufJavaValidator_checkMapNotWithinTypeExtension {
+  @Rule public XtextRule xtext = overrideRuntimeModuleWith(unitTestModule());
+
+  @Inject private ProtobufJavaValidator validator;
+  private ValidationMessageAcceptor messageAcceptor;
+
+  @Before public void setUp() {
+    messageAcceptor = mock(ValidationMessageAcceptor.class);
+    validator.setMessageAcceptor(messageAcceptor);
+  }
+
+  // syntax = "proto2";
+  //
+  // extend proto2.FieldOptions {
+  //   map<string, string> bar = 1;
+  // }
+  @Test public void should_create_error_if_map_within_type_extension() {
+    MessageField field = xtext.find("bar", MessageField.class);
+    MapType map = ((MapTypeLink) field.getType()).getTarget();
+    validator.checkMapIsNotWithinExtension(map);
+    verify(messageAcceptor).acceptError(
+        Messages.mapWithinTypeExtension,
+        map,
+        null,
+        ValidationMessageAcceptor.INSIGNIFICANT_INDEX,
+        null);
+  }
+
+  // syntax = "proto2";
+  //
+  // extend proto2.FieldOptions {
+  //   optional group my_group = 1 {
+  //     map<string, string> bar = 2;
+  //   }
+  // }
+  @Test public void should_create_error_if_map_within_group_within_type_extension() {
+    MessageField field = xtext.find("bar", MessageField.class);
+    MapType map = ((MapTypeLink) field.getType()).getTarget();
+    validator.checkMapIsNotWithinExtension(map);
+    verify(messageAcceptor).acceptError(
+        Messages.mapWithinTypeExtension,
+        map,
+        null,
+        ValidationMessageAcceptor.INSIGNIFICANT_INDEX,
+        null);
+  }
+}
diff --git a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/editor/syntaxcoloring/ProtobufSemanticHighlightingCalculator.java b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/editor/syntaxcoloring/ProtobufSemanticHighlightingCalculator.java
index 0f48e9b..95ab1bb 100644
--- a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/editor/syntaxcoloring/ProtobufSemanticHighlightingCalculator.java
+++ b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/editor/syntaxcoloring/ProtobufSemanticHighlightingCalculator.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011 Google Inc.
+ * Copyright (c) 2011, 2015 Google Inc.
  *
  * All rights reserved. This program and the accompanying materials are made
  * available under the terms of the Eclipse Public License v1.0 which
@@ -115,7 +115,7 @@
 
   private void highlight(TypeExtension extension, IHighlightedPositionAcceptor acceptor) {
     highlightFirstFeature(extension, TYPE_EXTENSION__TYPE, acceptor, MESSAGE_ID);
-    for (MessageElement element : extension.getElements()) {
+    for (IndexedElement element : extension.getElements()) {
       highlight(element, acceptor);
     }
   }
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/Protobuf.xtext b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/Protobuf.xtext
index a4caeed..0627375 100644
--- a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/Protobuf.xtext
+++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/Protobuf.xtext
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011 Google Inc.
+ * Copyright (c) 2011, 2015 Google Inc.
  *
  * All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse
  * Public License v1.0 which accompanies this distribution, and is available at
@@ -126,7 +126,7 @@
 
 TypeExtension:
   ->'extend' type=ExtensibleTypeLink '{'
-  elements+=MessageElement*
+  elements+=IndexedElement*
   '}' (';')?;
 
 ExtensibleTypeLink:
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/CustomOptionFinderStrategy.java b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/CustomOptionFinderStrategy.java
index 2188e5a..bae454f 100644
--- a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/CustomOptionFinderStrategy.java
+++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/CustomOptionFinderStrategy.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011 Google Inc.
+ * Copyright (c) 2011, 2015 Google Inc.
  *
  * All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse
  * Public License v1.0 which accompanies this distribution, and is available at
@@ -25,8 +25,8 @@
 import com.google.eclipse.protobuf.naming.LocalNamesProvider;
 import com.google.eclipse.protobuf.naming.OptionNamingStrategy;
 import com.google.eclipse.protobuf.protobuf.Import;
+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.Package;
 import com.google.eclipse.protobuf.protobuf.TypeExtension;
 import com.google.inject.Inject;
@@ -47,7 +47,7 @@
     }
     Set<IEObjectDescription> descriptions = newHashSet();
     TypeExtension extension = (TypeExtension) target;
-    for (MessageElement e : extension.getElements()) {
+    for (IndexedElement e : extension.getElements()) {
       descriptions.addAll(qualifiedNamesDescriptions.qualifiedNames(e, namingStrategy));
     }
     return descriptions;
@@ -63,7 +63,7 @@
     }
     Set<IEObjectDescription> descriptions = newHashSet();
     TypeExtension extension = (TypeExtension) target;
-    for (MessageElement e : extension.getElements()) {
+    for (IndexedElement e : extension.getElements()) {
       List<QualifiedName> names = localNamesProvider.localNames(e, namingStrategy);
       int nameCount = names.size();
       for (int i = level; i < nameCount; i++) {
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ExtensionFieldFinderStrategy.java b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ExtensionFieldFinderStrategy.java
index 52970e4..eca6a62 100644
--- a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ExtensionFieldFinderStrategy.java
+++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ExtensionFieldFinderStrategy.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011 Google Inc.
+ * Copyright (c) 2011, 2015 Google Inc.
  *
  * All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse
  * Public License v1.0 which accompanies this distribution, and is available at
@@ -25,7 +25,6 @@
 import com.google.eclipse.protobuf.naming.OptionNamingStrategy;
 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.TypeExtension;
 import com.google.inject.Inject;
@@ -50,14 +49,10 @@
     }
     Set<IEObjectDescription> descriptions = newHashSet();
     for (TypeExtension extension : messages.localExtensionsOf(fieldType)) {
-      for (MessageElement element : extension.getElements()) {
-        if (!(element instanceof IndexedElement)) {
-          continue;
-        }
-        IndexedElement current = (IndexedElement) element;
-        descriptions.addAll(qualifiedNameDescriptions.qualifiedNames(current, namingStrategy));
-        String name = options.nameForOption(current);
-        descriptions.add(create(name, current));
+      for (IndexedElement element : extension.getElements()) {
+        descriptions.addAll(qualifiedNameDescriptions.qualifiedNames(element, namingStrategy));
+        String name = options.nameForOption(element);
+        descriptions.add(create(name, element));
       }
     }
     return descriptions;
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ExtensionFieldNameFinderStrategy.java b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ExtensionFieldNameFinderStrategy.java
index 39cbfbd..4d84140 100644
--- a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ExtensionFieldNameFinderStrategy.java
+++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ExtensionFieldNameFinderStrategy.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012 Google Inc.
+ * Copyright (c) 2012, 2015 Google Inc.
  *
  * All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse
  * Public License v1.0 which accompanies this distribution, and is available at
@@ -23,7 +23,6 @@
 import com.google.eclipse.protobuf.protobuf.Group;
 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.TypeExtension;
 import com.google.inject.Inject;
@@ -44,10 +43,8 @@
       Message type = messageFields.messageTypeOf((MessageField) reference);
       // check first in descriptor.proto
       for (TypeExtension extension : messages.extensionsOf(type, modelObjects.rootOf(reference))) {
-        for (MessageElement element : extension.getElements()) {
-          if (element instanceof IndexedElement) {
-            descriptions.addAll(qualifiedNameDescriptions.qualifiedNames(element, namingStrategy));
-          }
+        for (IndexedElement element : extension.getElements()) {
+          descriptions.addAll(qualifiedNameDescriptions.qualifiedNames(element, namingStrategy));
         }
       }
     } else if (reference instanceof Group) {
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/validation/Messages.java b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/validation/Messages.java
index 8f80459..3f6c7da 100644
--- a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/validation/Messages.java
+++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/validation/Messages.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011 Google Inc.
+ * Copyright (c) 2011, 2015 Google Inc.
  *
  * All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse
  * Public License v1.0 which accompanies this distribution, and is available at
@@ -31,6 +31,7 @@
   public static String invalidMapValueType;
   public static String literalNotInEnum;
   public static String mapWithModifier;
+  public static String mapWithinTypeExtension;
   public static String missingFieldNumber;
   public static String missingModifier;
   public static String multiplePackages;
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/validation/Messages.properties b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/validation/Messages.properties
index 9c15670..1357817 100644
--- a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/validation/Messages.properties
+++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/validation/Messages.properties
@@ -15,6 +15,7 @@
 invalidMapValueType = Value in a map field cannot be a map.
 literalNotInEnum = Enum type \"%s\" has no value named \"%s\".
 mapWithModifier = Field labels (required/optional/repeated) are not allowed on map fields.
+mapWithinTypeExtension = Map fields are not allowed as extensions.
 missingFieldNumber = Missing field number.
 missingModifier = Missing modifier: \"required\", \"optional\", or \"repeated\".
 oneofFieldWithModifier = Fields inside "oneof" cannot have a modifier.
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/validation/ProtobufJavaValidator.java b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/validation/ProtobufJavaValidator.java
index 286c736..bcbbab0 100644
--- a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/validation/ProtobufJavaValidator.java
+++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/validation/ProtobufJavaValidator.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014 Google Inc.
+ * Copyright (c) 2014, 2015 Google Inc.
  *
  * All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse
  * Public License v1.0 which accompanies this distribution, and is available at
@@ -20,6 +20,7 @@
 import static com.google.eclipse.protobuf.validation.Messages.invalidMapKeyType;
 import static com.google.eclipse.protobuf.validation.Messages.invalidMapValueType;
 import static com.google.eclipse.protobuf.validation.Messages.mapWithModifier;
+import static com.google.eclipse.protobuf.validation.Messages.mapWithinTypeExtension;
 import static com.google.eclipse.protobuf.validation.Messages.missingModifier;
 import static com.google.eclipse.protobuf.validation.Messages.multiplePackages;
 import static com.google.eclipse.protobuf.validation.Messages.oneofFieldWithModifier;
@@ -46,10 +47,12 @@
 import com.google.eclipse.protobuf.protobuf.ScalarType;
 import com.google.eclipse.protobuf.protobuf.ScalarTypeLink;
 import com.google.eclipse.protobuf.protobuf.Syntax;
+import com.google.eclipse.protobuf.protobuf.TypeExtension;
 import com.google.eclipse.protobuf.protobuf.TypeLink;
 import com.google.inject.Inject;
 
 import org.eclipse.emf.ecore.EObject;
+import org.eclipse.xtext.EcoreUtil2;
 import org.eclipse.xtext.naming.IQualifiedNameProvider;
 import org.eclipse.xtext.naming.QualifiedName;
 import org.eclipse.xtext.validation.Check;
@@ -243,4 +246,10 @@
       return;
     }
   }
+
+  @Check public void checkMapIsNotWithinExtension(MapType map) {
+    if (EcoreUtil2.getContainerOfType(map, TypeExtension.class) != null) {
+      error(mapWithinTypeExtension, map, null);
+    }
+  }
 }