JSDoc type parser - improve handling of union types
diff --git a/plugins/org.eclipse.dltk.javascript.core/src/org/eclipse/dltk/javascript/typeinfo/JSDocTypeParser.java b/plugins/org.eclipse.dltk.javascript.core/src/org/eclipse/dltk/javascript/typeinfo/JSDocTypeParser.java
index 2b35aeb..768650e 100644
--- a/plugins/org.eclipse.dltk.javascript.core/src/org/eclipse/dltk/javascript/typeinfo/JSDocTypeParser.java
+++ b/plugins/org.eclipse.dltk.javascript.core/src/org/eclipse/dltk/javascript/typeinfo/JSDocTypeParser.java
@@ -65,7 +65,7 @@
public JSType parse(String input) throws ParseException {
final ANTLRStringStream stream = new ANTLRStringStream(input);
- final JSType type = parse(stream);
+ final JSType type = parse(stream, true);
if (stream.LT(1) != CharStream.EOF) {
throw new ParseException("Unexpected "
+ stream.substring(stream.index(), stream.size() - 1),
@@ -74,7 +74,19 @@
return type;
}
- protected JSType parse(CharStream input) throws ParseException {
+ /**
+ * Parses the next type expression. {@link UnionType}s are handled at this
+ * level, parsing of parts is delegated to {@link #parseType(CharStream)}.
+ *
+ * @param input
+ * @param autoUnion
+ * if <code>true</code> then it is allowed to parse all parts of
+ * the union type, but if <code>false</code> then unit type
+ * declaration should be enclosed in parenthesis -
+ * <code>'('</code> and <code>')'</code>.
+ */
+ protected JSType parse(CharStream input, boolean autoUnion)
+ throws ParseException {
skipSpaces(input);
final List<JSType> types = new ArrayList<JSType>();
final boolean inParenthese = input.LT(1) == '(';
@@ -87,7 +99,7 @@
if (type != null) {
types.add(type);
skipSpaces(input);
- if (input.LT(1) == '|') {
+ if ((inParenthese || autoUnion) && input.LT(1) == '|') {
input.consume();
skipSpaces(input);
continue;
@@ -154,7 +166,7 @@
final int baseEnd = input.index();
final String baseType = input.substring(start, baseEnd - 1);
input.consume();
- final List<JSType> typeParams = parseParams(input);
+ final List<JSType> typeParams = parseTypeParams(input);
match(input, '>');
JSType type = createGenericType(baseType, typeParams);
if (extension != null) {
@@ -166,7 +178,7 @@
final String baseType = input.substring(start, baseEnd - 1);
input.consume();
input.consume();
- final List<JSType> typeParams = parseParams(input);
+ final List<JSType> typeParams = parseTypeParams(input);
match(input, '>');
JSType type = createGenericType(baseType, typeParams);
if (extension != null) {
@@ -185,7 +197,7 @@
if (input.LT(1) == ':') {
input.consume();
skipSpaces(input);
- functionType.setReturnType(parseType(input));
+ functionType.setReturnType(parse(input, false));
}
return checkIfArray(input, functionType);
} else if (ch == '[') {
@@ -326,10 +338,11 @@
return TypeUtil.ref(baseType);
}
- protected List<JSType> parseParams(CharStream input) throws ParseException {
+ protected List<JSType> parseTypeParams(CharStream input)
+ throws ParseException {
final List<JSType> types = new ArrayList<JSType>();
for (;;) {
- final JSType type = parse(input);
+ final JSType type = parse(input, true);
if (type != null) {
types.add(type);
skipSpaces(input);
@@ -360,7 +373,7 @@
squareBracket = true;
}
}
- final JSType type = parse(input);
+ final JSType type = parse(input, true);
if (type != null) {
final Parameter parameter = TypeInfoModelFactory.eINSTANCE
.createParameter();
@@ -435,7 +448,7 @@
if (input.LT(1) == ':') {
input.consume();
- final JSType memberType = parse(input);
+ final JSType memberType = parse(input, true);
ch = input.LT(1);
if (ch == '=') {
input.consume();
diff --git a/tests/org.eclipse.dltk.javascript.core.tests/src/org/eclipse/dltk/javascript/core/tests/typeinfo/JSDocTypeParserTests.java b/tests/org.eclipse.dltk.javascript.core.tests/src/org/eclipse/dltk/javascript/core/tests/typeinfo/JSDocTypeParserTests.java
index 3d8e30f..d555413 100644
--- a/tests/org.eclipse.dltk.javascript.core.tests/src/org/eclipse/dltk/javascript/core/tests/typeinfo/JSDocTypeParserTests.java
+++ b/tests/org.eclipse.dltk.javascript.core.tests/src/org/eclipse/dltk/javascript/core/tests/typeinfo/JSDocTypeParserTests.java
@@ -28,6 +28,7 @@
import org.eclipse.dltk.javascript.typeinfo.model.RecordType;
import org.eclipse.dltk.javascript.typeinfo.model.SimpleType;
import org.eclipse.dltk.javascript.typeinfo.model.UnionType;
+import org.eclipse.emf.common.util.EList;
@SuppressWarnings("restriction")
public class JSDocTypeParserTests extends TestCase {
@@ -196,4 +197,34 @@
assertRef("Number", type.getReturnType());
}
+ public void testUnitonWithFunction1() {
+ final UnionType type = (UnionType) parse("String|function(Number):Number|String");
+ final EList<JSType> parts = type.getTargets();
+ assertEquals(3, parts.size());
+
+ final SimpleType part0 = (SimpleType) parts.get(0);
+ assertEquals("String", part0.getName());
+
+ final FunctionType part1 = (FunctionType) parts.get(1);
+ assertEquals("Number", part1.getReturnType().getName());
+
+ final SimpleType part2 = (SimpleType) parts.get(2);
+ assertEquals("String", part2.getName());
+ }
+
+ public void testUnitonWithFunction2() {
+ final UnionType type = (UnionType) parse("String|function(Number):(Number|String)");
+ final EList<JSType> parts = type.getTargets();
+ assertEquals(2, parts.size());
+
+ final SimpleType part0 = (SimpleType) parts.get(0);
+ assertEquals("String", part0.getName());
+
+ final FunctionType part1 = (FunctionType) parts.get(1);
+ final UnionType resultType = (UnionType) part1.getReturnType();
+ assertEquals(2, resultType.getTargets().size());
+ assertEquals("Number", resultType.getTargets().get(0).getName());
+ assertEquals("String", resultType.getTargets().get(1).getName());
+ }
+
}