Add support for omitting modifiers for proto3
Change-Id: I4c74d0a31cc4b94c0e48d94409eebc5d941d60f9
Signed-off-by: Harry Terkelsen <het@google.com>
diff --git a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/validation/ProtobufJavaValidator_checkFieldModifiers_Test.java b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/validation/ProtobufJavaValidator_checkFieldModifiers_Test.java
new file mode 100644
index 0000000..c8c87a7
--- /dev/null
+++ b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/validation/ProtobufJavaValidator_checkFieldModifiers_Test.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2014 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 com.google.eclipse.protobuf.protobuf.ProtobufPackage.Literals.MESSAGE_FIELD__MODIFIER;
+import static com.google.eclipse.protobuf.validation.Messages.mapWithModifier;
+import static com.google.eclipse.protobuf.validation.Messages.missingModifier;
+import static com.google.eclipse.protobuf.validation.ProtobufJavaValidator.MAP_WITH_MODIFIER_ERROR;
+import static com.google.eclipse.protobuf.validation.ProtobufJavaValidator.MISSING_MODIFIER_ERROR;
+import static org.eclipse.xtext.validation.ValidationMessageAcceptor.INSIGNIFICANT_INDEX;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+
+import com.google.eclipse.protobuf.junit.core.XtextRule;
+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#checkFieldModifiers(MessageField)}</code>
+ */
+public class ProtobufJavaValidator_checkFieldModifiers_Test {
+ @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";
+ //
+ // message Foo {
+ // string bar = 1;
+ // }
+ @Test public void should_create_error_if_no_modifier_in_proto2() {
+ MessageField field = xtext.find("bar", MessageField.class);
+ validator.checkFieldModifiers(field);
+ verify(messageAcceptor).acceptError(missingModifier, field, MESSAGE_FIELD__MODIFIER,
+ INSIGNIFICANT_INDEX, MISSING_MODIFIER_ERROR);
+ }
+
+ // syntax = "proto3";
+ //
+ // message Foo {
+ // string bar = 1;
+ // }
+ @Test public void should_not_create_error_if_no_modifier_in_proto3() {
+ MessageField field = xtext.find("bar", MessageField.class);
+ validator.checkFieldModifiers(field);
+ verifyZeroInteractions(messageAcceptor);
+ }
+
+ // syntax = "proto3";
+ //
+ // message Foo {
+ // required string bar = 1;
+ // }
+ @Test public void should_create_error_if_required_modifier_in_proto3() {
+ MessageField field = xtext.find("bar", MessageField.class);
+ validator.checkFieldModifiers(field);
+ verify(messageAcceptor).acceptError(Messages.requiredInProto3, field, MESSAGE_FIELD__MODIFIER,
+ INSIGNIFICANT_INDEX, ProtobufJavaValidator.REQUIRED_IN_PROTO3_ERROR);
+ }
+
+ // syntax = "proto2";
+ //
+ // message Foo {
+ // optional map<string, string> bar = 1;
+ // }
+ @Test public void should_create_error_if_modifier_on_map_in_proto2() {
+ MessageField field = xtext.find("bar", MessageField.class);
+ validator.checkFieldModifiers(field);
+ verify(messageAcceptor).acceptError(mapWithModifier, field, MESSAGE_FIELD__MODIFIER,
+ INSIGNIFICANT_INDEX, MAP_WITH_MODIFIER_ERROR);
+ }
+
+ // syntax = "proto3";
+ //
+ // message Foo {
+ // optional map<string, string> bar = 1;
+ // }
+ @Test public void should_create_error_if_modifier_on_map_in_proto3() {
+ MessageField field = xtext.find("bar", MessageField.class);
+ validator.checkFieldModifiers(field);
+ verify(messageAcceptor).acceptError(mapWithModifier, field, MESSAGE_FIELD__MODIFIER,
+ INSIGNIFICANT_INDEX, MAP_WITH_MODIFIER_ERROR);
+ }
+
+ // syntax = "proto2";
+ //
+ // message Foo {
+ // map<string, string> bar = 1;
+ // }
+ @Test public void should_not_create_error_if_no_modifier_on_map_in_proto2() {
+ MessageField field = xtext.find("bar", MessageField.class);
+ validator.checkFieldModifiers(field);
+ verifyZeroInteractions(messageAcceptor);
+ }
+}
diff --git a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/validation/ProtobufJavaValidator_checkSyntaxIsKnown_Test.java b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/validation/ProtobufJavaValidator_checkSyntaxIsKnown_Test.java
index ff0ae0c..4ec6ede 100644
--- a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/validation/ProtobufJavaValidator_checkSyntaxIsKnown_Test.java
+++ b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/validation/ProtobufJavaValidator_checkSyntaxIsKnown_Test.java
@@ -42,8 +42,10 @@
@Test public void should_create_error_if_syntax_is_not_proto2_or_proto3() {
when(syntax.getName()).thenReturn("proto5");
validator.checkSyntaxIsKnown(syntax);
- String message = "Unrecognized syntax identifier \"proto5\". This parser only recognizes \"proto2\" or \"proto3\".";
- verify(messageAcceptor).acceptError(message, syntax, SYNTAX__NAME, INSIGNIFICANT_INDEX, SYNTAX_IS_NOT_KNOWN_ERROR);
+ String message = "Unrecognized syntax identifier \"proto5\". "
+ + "This parser only recognizes \"proto2\" and \"proto3\".";
+ verify(messageAcceptor).acceptError(message, syntax, SYNTAX__NAME, INSIGNIFICANT_INDEX,
+ SYNTAX_IS_NOT_KNOWN_ERROR);
}
@Test public void should_not_create_error_if_syntax_is_proto2() {
diff --git a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/contentassist/ProtobufProposalProvider.java b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/contentassist/ProtobufProposalProvider.java
index bbbce17..3a2fbfa 100644
--- a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/contentassist/ProtobufProposalProvider.java
+++ b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/contentassist/ProtobufProposalProvider.java
@@ -18,8 +18,8 @@
import static com.google.eclipse.protobuf.grammar.CommonKeyword.OPENING_CURLY_BRACKET;
import static com.google.eclipse.protobuf.grammar.CommonKeyword.SYNTAX;
import static com.google.eclipse.protobuf.grammar.CommonKeyword.TRUE;
-import static com.google.eclipse.protobuf.protobuf.Modifier.OPTIONAL;
-import static com.google.eclipse.protobuf.protobuf.Modifier.REPEATED;
+import static com.google.eclipse.protobuf.protobuf.ModifierEnum.OPTIONAL;
+import static com.google.eclipse.protobuf.protobuf.ModifierEnum.REPEATED;
import static com.google.eclipse.protobuf.protobuf.ProtobufPackage.Literals.LITERAL;
import static com.google.eclipse.protobuf.protobuf.ProtobufPackage.Literals.OPTION;
import static com.google.eclipse.protobuf.ui.grammar.CompoundElement.DEFAULT_EQUAL;
@@ -28,8 +28,8 @@
import static com.google.eclipse.protobuf.ui.grammar.CompoundElement.DEFAULT_EQUAL_STRING_IN_BRACKETS;
import static com.google.eclipse.protobuf.ui.grammar.CompoundElement.EMPTY_STRING;
import static com.google.eclipse.protobuf.ui.grammar.CompoundElement.EQUAL_PROTO2_IN_QUOTES;
-import static com.google.eclipse.protobuf.ui.grammar.CompoundElement.PROTO2_IN_QUOTES;
import static com.google.eclipse.protobuf.ui.grammar.CompoundElement.EQUAL_PROTO3_IN_QUOTES;
+import static com.google.eclipse.protobuf.ui.grammar.CompoundElement.PROTO2_IN_QUOTES;
import static com.google.eclipse.protobuf.ui.grammar.CompoundElement.PROTO3_IN_QUOTES;
import static com.google.eclipse.protobuf.util.CommonWords.space;
import static java.lang.String.valueOf;
@@ -56,7 +56,7 @@
import com.google.eclipse.protobuf.protobuf.IndexedElement;
import com.google.eclipse.protobuf.protobuf.Literal;
import com.google.eclipse.protobuf.protobuf.MessageField;
-import com.google.eclipse.protobuf.protobuf.Modifier;
+import com.google.eclipse.protobuf.protobuf.ModifierEnum;
import com.google.eclipse.protobuf.protobuf.NativeFieldOption;
import com.google.eclipse.protobuf.protobuf.NativeOption;
import com.google.eclipse.protobuf.protobuf.Option;
@@ -314,7 +314,7 @@
}
if (model instanceof MessageField) {
MessageField field = (MessageField) model;
- Modifier modifier = field.getModifier();
+ ModifierEnum modifier = field.getModifier();
if (OPTIONAL.equals(modifier)) {
CompoundElement display = DEFAULT_EQUAL_IN_BRACKETS;
int cursorPosition = display.indexOf(CLOSING_BRACKET);
diff --git a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/labeling/Images.java b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/labeling/Images.java
index ae435bf..b0dbee3 100644
--- a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/labeling/Images.java
+++ b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/labeling/Images.java
@@ -9,9 +9,9 @@
package com.google.eclipse.protobuf.ui.labeling;
import static com.google.common.collect.Sets.newHashSet;
-import static com.google.eclipse.protobuf.protobuf.Modifier.OPTIONAL;
-import static com.google.eclipse.protobuf.protobuf.Modifier.REPEATED;
-import static com.google.eclipse.protobuf.protobuf.Modifier.REQUIRED;
+import static com.google.eclipse.protobuf.protobuf.ModifierEnum.OPTIONAL;
+import static com.google.eclipse.protobuf.protobuf.ModifierEnum.REPEATED;
+import static com.google.eclipse.protobuf.protobuf.ModifierEnum.REQUIRED;
import static com.google.eclipse.protobuf.protobuf.ProtobufPackage.Literals.ENUM;
import static com.google.eclipse.protobuf.protobuf.ProtobufPackage.Literals.EXTENSIONS;
import static com.google.eclipse.protobuf.protobuf.ProtobufPackage.Literals.GROUP;
@@ -35,7 +35,7 @@
import com.google.eclipse.protobuf.grammar.CommonKeyword;
import com.google.eclipse.protobuf.protobuf.Import;
import com.google.eclipse.protobuf.protobuf.MessageField;
-import com.google.eclipse.protobuf.protobuf.Modifier;
+import com.google.eclipse.protobuf.protobuf.ModifierEnum;
import com.google.eclipse.protobuf.protobuf.Option;
import com.google.inject.Singleton;
@@ -56,8 +56,8 @@
addImages("imports", "options");
}
- private static void addImages(Modifier...modifiers) {
- for (Modifier m : modifiers) {
+ private static void addImages(ModifierEnum...modifiers) {
+ for (ModifierEnum m : modifiers) {
addImage(imageNameFrom(m));
}
}
@@ -90,7 +90,7 @@
imageName = (String) o;
} else if (o instanceof MessageField) {
MessageField field = (MessageField) o;
- Modifier modifier = field.getModifier();
+ ModifierEnum modifier = field.getModifier();
imageName = imageNameFrom(modifier);
} else if (o instanceof Option) {
imageName = imageNameFrom(OPTION);
@@ -110,7 +110,7 @@
return (IMAGES.contains(imageFileName)) ? imageFileName : defaultImage();
}
- private static String imageNameFrom(Modifier modifier) {
+ private static String imageNameFrom(ModifierEnum modifier) {
return modifier.getName().toLowerCase();
}
diff --git a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/quickfix/Messages.properties b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/quickfix/Messages.properties
index efbe0b8..66492dd 100644
--- a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/quickfix/Messages.properties
+++ b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/quickfix/Messages.properties
@@ -2,4 +2,4 @@
changeValueLabel=Change value to '%s'
regenerateTagNumberLabel=Regenerate tag number
regenerateTagNumberDescription=Regenerate tag number.
-removeDuplicatePackageLabel=Remove duplicate package declaration
\ No newline at end of file
+removeDuplicatePackageLabel=Remove duplicate package declaration
diff --git a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/quickfix/ProtobufQuickfixProvider.java b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/quickfix/ProtobufQuickfixProvider.java
index 32e012c..aa8f353 100644
--- a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/quickfix/ProtobufQuickfixProvider.java
+++ b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/quickfix/ProtobufQuickfixProvider.java
@@ -21,7 +21,9 @@
import static com.google.eclipse.protobuf.validation.DataTypeValidator.EXPECTED_BOOL_ERROR;
import static com.google.eclipse.protobuf.validation.DataTypeValidator.EXPECTED_STRING_ERROR;
import static com.google.eclipse.protobuf.validation.ProtobufJavaValidator.INVALID_FIELD_TAG_NUMBER_ERROR;
+import static com.google.eclipse.protobuf.validation.ProtobufJavaValidator.MISSING_MODIFIER_ERROR;
import static com.google.eclipse.protobuf.validation.ProtobufJavaValidator.MORE_THAN_ONE_PACKAGE_ERROR;
+import static com.google.eclipse.protobuf.validation.ProtobufJavaValidator.REQUIRED_IN_PROTO3_ERROR;
import static com.google.eclipse.protobuf.validation.ProtobufJavaValidator.SYNTAX_IS_NOT_KNOWN_ERROR;
import static org.eclipse.emf.ecore.util.EcoreUtil.remove;
import static org.eclipse.xtext.nodemodel.util.NodeModelUtils.findActualNodeFor;
@@ -34,11 +36,14 @@
import com.google.eclipse.protobuf.protobuf.BooleanLink;
import com.google.eclipse.protobuf.protobuf.FieldOption;
import com.google.eclipse.protobuf.protobuf.IndexedElement;
+import com.google.eclipse.protobuf.protobuf.MessageField;
+import com.google.eclipse.protobuf.protobuf.ModifierEnum;
import com.google.eclipse.protobuf.protobuf.Package;
import com.google.eclipse.protobuf.protobuf.ProtobufFactory;
import com.google.eclipse.protobuf.protobuf.StringLink;
import com.google.eclipse.protobuf.protobuf.Syntax;
import com.google.eclipse.protobuf.protobuf.Value;
+import com.google.eclipse.protobuf.validation.ProtobufJavaValidator;
import com.google.inject.Inject;
import org.eclipse.emf.ecore.EObject;
@@ -119,6 +124,62 @@
acceptor.accept(issue, removeDuplicatePackageLabel, description, "remove.gif", modification);
}
+ @Fix(MISSING_MODIFIER_ERROR)
+ public void changeModifierToRequired(Issue issue, IssueResolutionAcceptor acceptor) {
+ ISemanticModification modification = new ISemanticModification() {
+ @Override
+ public void apply(EObject element, IModificationContext context) throws Exception {
+ MessageField field = (MessageField) element;
+ field.setModifier(ModifierEnum.REQUIRED);
+ }
+ };
+ String description = String.format(changeValueDescription, "modifier", "required");
+ String label = String.format(changeValueLabel, "required");
+ acceptor.accept(issue, label, description, ICON_FOR_CHANGE, modification);
+ }
+
+ @Fix(MISSING_MODIFIER_ERROR)
+ public void changeModifierToRepeated(Issue issue, IssueResolutionAcceptor acceptor) {
+ ISemanticModification modification = new ISemanticModification() {
+ @Override
+ public void apply(EObject element, IModificationContext context) throws Exception {
+ MessageField field = (MessageField) element;
+ field.setModifier(ModifierEnum.REPEATED);
+ }
+ };
+ String description = String.format(changeValueDescription, "modifier", "repeated");
+ String label = String.format(changeValueLabel, "repeated");
+ acceptor.accept(issue, label, description, ICON_FOR_CHANGE, modification);
+ }
+
+ @Fix(MISSING_MODIFIER_ERROR)
+ public void changeModifierToOptional(Issue issue, IssueResolutionAcceptor acceptor) {
+ ISemanticModification modification = new ISemanticModification() {
+ @Override
+ public void apply(EObject element, IModificationContext context) throws Exception {
+ MessageField field = (MessageField) element;
+ field.setModifier(ModifierEnum.OPTIONAL);
+ }
+ };
+ String description = String.format(changeValueDescription, "modifier", "optional");
+ String label = String.format(changeValueLabel, "optional");
+ acceptor.accept(issue, label, description, ICON_FOR_CHANGE, modification);
+ }
+
+ @Fix(REQUIRED_IN_PROTO3_ERROR)
+ public void changeModifierToOptionalOnRequired(Issue issue, IssueResolutionAcceptor acceptor) {
+ ISemanticModification modification = new ISemanticModification() {
+ @Override
+ public void apply(EObject element, IModificationContext context) throws Exception {
+ MessageField field = (MessageField) element;
+ field.setModifier(ModifierEnum.OPTIONAL);
+ }
+ };
+ String description = String.format(changeValueDescription, "modifier", "optional");
+ String label = String.format(changeValueLabel, "optional");
+ acceptor.accept(issue, label, description, ICON_FOR_CHANGE, modification);
+ }
+
@Fix(EXPECTED_BOOL_ERROR)
public void changeValueToTrue(Issue issue, IssueResolutionAcceptor acceptor) {
EObject element = elementIn(issue);
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 0ce144f..fb0a8e6 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
@@ -45,7 +45,7 @@
Enum | ExtensibleType;
Message:
- 'message' name=Name '{'
+ ->'message' name=Name '{'
elements+=MessageElement*
'}' (';')?;
@@ -58,43 +58,38 @@
RangeMax:
LONG | 'max';
-Group:
- modifier=Modifier 'group' name=Name '=' index=(LONG | HEX)
- ('[' (fieldOptions+=FieldOption (',' fieldOptions+=FieldOption)*) ']')? '{'
- elements+=GroupElement*
- '}' (';')?;
+// Hack to make the default modifier 'unspecified'
+enum ModifierEnum:
+ unspecified | optional | required | repeated
+;
-UnmodifiedGroup returns Group:
- =>'group' name=Name '=' index=(LONG | HEX)
+enum Modifier returns ModifierEnum:
+ optional | required | repeated;
+
+Group:
+ (modifier=Modifier)? =>'group' name=Name '=' index=(LONG | HEX)
('[' (fieldOptions+=FieldOption (',' fieldOptions+=FieldOption)*) ']')? '{'
elements+=GroupElement*
'}' (';')?;
GroupElement:
- Option | IndexedElement | ComplexType | TypeExtension | Extensions;
+ Option | MessageField | ComplexType | TypeExtension | Extensions;
OneOf:
- (isRepeated?='repeated')? 'oneof' name=Name '{'
+ (isRepeated?='repeated')? =>'oneof' name=Name '{'
elements+=OneOfElement+
'}' (';')?;
OneOfElement returns MessageElement:
- Extensions | UnmodifiedGroup | UnmodifiedMessageField;
+ Extensions | Group;
Extensions:
- 'extensions' ranges+=Range (',' ranges+=Range)* (';')+;
+ ->'extensions' ranges+=Range (',' ranges+=Range)* (';')+;
MessageField:
- =>modifier=Modifier type=TypeLink name=Name '=' index=(LONG | HEX)
+ (modifier=Modifier)? =>type=TypeLink name=Name '=' index=(LONG | HEX)
('[' (fieldOptions+=FieldOption (',' fieldOptions+=FieldOption)*)? ']')? (';')+;
-UnmodifiedMessageField returns MessageField:
- type=TypeLink name=Name '=' index=(LONG | HEX)
- ('[' (fieldOptions+=FieldOption (',' fieldOptions+=FieldOption)*)? ']')? (';')+;
-
-enum Modifier:
- optional | required | repeated;
-
TypeLink:
ScalarTypeLink | ComplexTypeLink | MapTypeLink;
@@ -107,15 +102,15 @@
ComplexTypeLink:
target=[ComplexType|QualifiedName];
-
+
MapTypeLink:
target=MapType;
MapType:
- =>'map' '<' fromType=TypeLink ',' toType=TypeLink '>';
+ ->'map' '<' fromType=TypeLink ',' toType=TypeLink '>';
Enum:
- =>'enum' name=Name '{'
+ ->'enum' name=Name '{'
elements+=EnumElement*
'}' ';'?;
@@ -123,14 +118,14 @@
Option | Literal;
Literal:
- name=Name '=' index=(LONG | HEX)
+ name=Name '=' index=(LONG | HEX)
('[' fieldOptions+=FieldOption (',' fieldOptions+=FieldOption)* ']')? (';')+;
terminal HEX returns ecore::ELong:
- ('-')? '0' ('x' | 'X') (NUMBER | 'a'..'f' | 'A'..'F')+;
+ ('-')? '0' ('x' | 'X') (DIGIT | 'a'..'f' | 'A'..'F')+;
TypeExtension:
- =>'extend' type=ExtensibleTypeLink '{'
+ ->'extend' type=ExtensibleTypeLink '{'
elements+=MessageElement*
'}' (';')?;
@@ -141,7 +136,7 @@
Message | Group;
Service:
- 'service' name=Name '{'
+ ->'service' name=Name '{'
(elements+=ServiceElement)*
'}' (';')?;
@@ -149,17 +144,17 @@
Option | Rpc | Stream;
Rpc:
- 'rpc' name=Name '(' argType=MessageLink ')' 'returns' '(' returnType=MessageLink ')'
+ ->'rpc' name=Name '(' argType=MessageLink ')' 'returns' '(' returnType=MessageLink ')'
(('{' options+=Option* '}') (';')? | (';')+);
Stream:
- 'stream' name=Name '(' clientMessage=MessageLink ',' serverMessage=MessageLink ')'
+ ->'stream' name=Name '(' clientMessage=MessageLink ',' serverMessage=MessageLink ')'
(('{' options+=Option* '}') (';')? | (';')+);
Name:
IdOrReservedWord;
-IdOrReservedWord:
+IdOrReservedWord:
ID | ReservedWord;
MessageLink:
@@ -178,9 +173,9 @@
'option' source=OptionSource '=' value=Value (';')+;
CustomOption:
- 'option' '(' source=OptionSource ')'
+ 'option' =>'(' source=OptionSource ')'
('.' fields+=OptionField ('.' fields+=OptionField)*)? '=' value=Value (';')+;
-
+
FieldOption:
DefaultValueFieldOption | NativeFieldOption | CustomFieldOption;
@@ -191,7 +186,7 @@
source=OptionSource '=' value=Value;
CustomFieldOption:
- '(' source=OptionSource ')'
+ ->'(' source=OptionSource ')'
('.' fields+=OptionField ('.' fields+=OptionField)*)? '=' value=Value;
OptionSource:
@@ -201,7 +196,7 @@
MessageOptionField | '(' ExtensionOptionField ')';
MessageOptionField:
- target=[IndexedElement|Name];
+ target=[IndexedElement|Name];
ExtensionOptionField:
target=[IndexedElement|QualifiedName];
@@ -217,11 +212,11 @@
// { foo: 1, bar: 2 }
ComplexValueCurlyBracket:
- '{' {ComplexValueCurlyBracket} (fields+=ValueField (',')?)* '}';
+ ->'{' {ComplexValueCurlyBracket} (fields+=ValueField (',')?)* '}';
// < foo: 1, bar: 2 >
ComplexValueAngleBracket:
- '<' {ComplexValueAngleBracket} (fields+=ValueField (',')?)* '>';
+ ->'<' {ComplexValueAngleBracket} (fields+=ValueField (',')?)* '>';
ValueField:
SimpleValueField | ComplexValueField;
@@ -278,18 +273,18 @@
target=LONG;
terminal LONG returns ecore::ELong:
- ('-')? (NUMBER)+;
+ ('-')? (DIGIT)+;
DoubleLink:
target=DOUBLE;
terminal DOUBLE returns ecore::EDouble:
- ('-')? (NUMBER)* ('.' (NUMBER)+)? |
- ('-')? (NUMBER)+ ('.') |
- ('-')? (NUMBER)+ ('.' (NUMBER)*)? (('e'|'E')('-'|'+')? (NUMBER)+) |
+ ('-')? (DIGIT)* ('.' (DIGIT)+)? |
+ ('-')? (DIGIT)+ ('.') |
+ ('-')? (DIGIT)+ ('.' (DIGIT)*)? (('e'|'E')('-'|'+')? (DIGIT)+) |
'nan' | 'inf' | '-inf';
-terminal NUMBER:
+terminal DIGIT:
'0'..'9';
StringLink:
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/model/util/MessageFields.java b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/model/util/MessageFields.java
index 3f500e2..3c09143 100644
--- a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/model/util/MessageFields.java
+++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/model/util/MessageFields.java
@@ -23,7 +23,7 @@
import static com.google.eclipse.protobuf.grammar.CommonKeyword.STRING;
import static com.google.eclipse.protobuf.grammar.CommonKeyword.UINT32;
import static com.google.eclipse.protobuf.grammar.CommonKeyword.UINT64;
-import static com.google.eclipse.protobuf.protobuf.Modifier.OPTIONAL;
+import static com.google.eclipse.protobuf.protobuf.ModifierEnum.OPTIONAL;
import com.google.eclipse.protobuf.grammar.CommonKeyword;
import com.google.eclipse.protobuf.protobuf.ComplexType;
@@ -31,7 +31,6 @@
import com.google.eclipse.protobuf.protobuf.Enum;
import com.google.eclipse.protobuf.protobuf.Message;
import com.google.eclipse.protobuf.protobuf.MessageField;
-import com.google.eclipse.protobuf.protobuf.Modifier;
import com.google.eclipse.protobuf.protobuf.ScalarType;
import com.google.eclipse.protobuf.protobuf.ScalarTypeLink;
import com.google.eclipse.protobuf.protobuf.TypeLink;
@@ -44,7 +43,7 @@
*/
@Singleton public class MessageFields {
/**
- * Indicates whether the modifier of the given field is <code>{@link Modifier#OPTIONAL}</code>.
+ * Indicates whether the modifier of the given field is <code>{@link ModifierEnum#OPTIONAL}</code>.
* @param field the given field.
* @return {@code true} if the modifier of the given field is "optional," {@code false} otherwise.
*/
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 66eee36..184ec86 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
@@ -28,8 +28,11 @@
public static String importingUnsupportedSyntax;
public static String importNotFound;
public static String literalNotInEnum;
+ public static String mapWithModifier;
public static String missingFieldNumber;
+ public static String missingModifier;
public static String multiplePackages;
+ public static String requiredInProto3;
public static String scopingError;
public static String unknownSyntax;
public static String unrecognizedSyntaxIdentifier;
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 38bccda..1c061db 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
@@ -12,8 +12,11 @@
importingUnsupportedSyntax = Importing unsupported file (directly or indirectly.) This may cause errors related to unresolved references.
importNotFound = Import \"%s\" was not found.
literalNotInEnum = Enum type \"%s\" has no value named \"%s\".
+mapWithModifier = Field labels (required/optional/repeated) are not allowed on map fields.
missingFieldNumber = Missing field number.
+missingModifier = Missing modifier: \"required\", \"optional\", or \"repeated\".
multiplePackages = Multiple package definitions.
+requiredInProto3 = Required fields are not allowed in proto3.
scopingError = It may be caused by an imported non-proto2 file.
unknownSyntax = Unknown syntax. This parser only recognizes \"proto2\" or \"proto3\".
unrecognizedSyntaxIdentifier = Unrecognized syntax identifier \"%s\". This parser only recognizes \"proto2\" and \"proto3\".
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 9bc3f57..3a83d80 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
@@ -8,31 +8,31 @@
*/
package com.google.eclipse.protobuf.validation;
-import static java.lang.String.format;
-
+import static com.google.eclipse.protobuf.protobuf.ProtobufPackage.Literals.MESSAGE_FIELD__MODIFIER;
import static com.google.eclipse.protobuf.protobuf.ProtobufPackage.Literals.PACKAGE__NAME;
import static com.google.eclipse.protobuf.protobuf.ProtobufPackage.Literals.SYNTAX__NAME;
import static com.google.eclipse.protobuf.validation.Messages.expectedFieldNumber;
import static com.google.eclipse.protobuf.validation.Messages.expectedSyntaxIdentifier;
import static com.google.eclipse.protobuf.validation.Messages.fieldNumberAlreadyUsed;
import static com.google.eclipse.protobuf.validation.Messages.fieldNumbersMustBePositive;
+import static com.google.eclipse.protobuf.validation.Messages.mapWithModifier;
+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.requiredInProto3;
import static com.google.eclipse.protobuf.validation.Messages.unknownSyntax;
import static com.google.eclipse.protobuf.validation.Messages.unrecognizedSyntaxIdentifier;
-
-import org.eclipse.emf.ecore.EObject;
-import org.eclipse.xtext.naming.IQualifiedNameProvider;
-import org.eclipse.xtext.naming.QualifiedName;
-import org.eclipse.xtext.validation.Check;
-import org.eclipse.xtext.validation.ComposedChecks;
+import static java.lang.String.format;
import com.google.eclipse.protobuf.grammar.Syntaxes;
import com.google.eclipse.protobuf.model.util.IndexedElements;
import com.google.eclipse.protobuf.model.util.Protobufs;
import com.google.eclipse.protobuf.naming.NameResolver;
import com.google.eclipse.protobuf.protobuf.IndexedElement;
+import com.google.eclipse.protobuf.protobuf.MapTypeLink;
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.ModifierEnum;
import com.google.eclipse.protobuf.protobuf.OneOf;
import com.google.eclipse.protobuf.protobuf.Package;
import com.google.eclipse.protobuf.protobuf.Protobuf;
@@ -40,6 +40,12 @@
import com.google.eclipse.protobuf.protobuf.Syntax;
import com.google.inject.Inject;
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.xtext.naming.IQualifiedNameProvider;
+import org.eclipse.xtext.naming.QualifiedName;
+import org.eclipse.xtext.validation.Check;
+import org.eclipse.xtext.validation.ComposedChecks;
+
/**
* @author alruiz@google.com (Alex Ruiz)
*/
@@ -48,6 +54,9 @@
public static final String SYNTAX_IS_NOT_KNOWN_ERROR = "syntaxIsNotProto2";
public static final String INVALID_FIELD_TAG_NUMBER_ERROR = "invalidFieldTagNumber";
public static final String MORE_THAN_ONE_PACKAGE_ERROR = "moreThanOnePackage";
+ public static final String MISSING_MODIFIER_ERROR = "noModifier";
+ public static final String MAP_WITH_MODIFIER_ERROR = "mapWithModifier";
+ public static final String REQUIRED_IN_PROTO3_ERROR = "requiredInProto3";
@Inject private IndexedElements indexedElements;
@Inject private NameResolver nameResolver;
@@ -85,6 +94,47 @@
}
}
+ @Check public void checkFieldModifiers(MessageField field) {
+ if (field.getType() instanceof MapTypeLink) {
+ checkMapField(field);
+ return;
+ }
+ if (field.getModifier() == ModifierEnum.UNSPECIFIED && isProto2Field(field)) {
+ error(missingModifier, field, MESSAGE_FIELD__MODIFIER, MISSING_MODIFIER_ERROR);
+ } else if (field.getModifier() == ModifierEnum.REQUIRED && isProto3Field(field)) {
+ error(requiredInProto3, field, MESSAGE_FIELD__MODIFIER, REQUIRED_IN_PROTO3_ERROR);
+ }
+ }
+
+ private void checkMapField(MessageField field) {
+ // TODO(het): Add quickfix to delete the modifier
+ if (field.getModifier() != ModifierEnum.UNSPECIFIED) {
+ error(mapWithModifier, field, MESSAGE_FIELD__MODIFIER, MAP_WITH_MODIFIER_ERROR);
+ }
+ }
+
+ private boolean isProto2Field(MessageField field) {
+ EObject container = field.eContainer();
+ if (container != null && !(container instanceof Protobuf)) {
+ container = container.eContainer();
+ }
+ if (container instanceof Protobuf) {
+ return Syntaxes.isSpecifyingProto2Syntax(((Protobuf) container).getSyntax().getName());
+ }
+ return false;
+ }
+
+ private boolean isProto3Field(MessageField field) {
+ EObject container = field.eContainer();
+ while (container != null && !(container instanceof Protobuf)) {
+ container = container.eContainer();
+ }
+ if (container instanceof Protobuf) {
+ return Syntaxes.isSpecifyingProto3Syntax(((Protobuf) container).getSyntax().getName());
+ }
+ return false;
+ }
+
private boolean checkTagNumberIsUnique(IndexedElement e, EObject message,
Iterable<MessageElement> elements) {
long index = indexedElements.indexOf(e);