Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorssankaran2013-12-13 02:29:23 +0000
committerssankaran2013-12-13 07:25:14 +0000
commita98d7d87df415161ba75f53acbdbe8d316ea160c (patch)
tree4d0eaf38a577b60e8dcdf7fa232a13916f75ada2
parentf338c86815ffb4c165b8024ba4f28cade0b2b75f (diff)
downloadeclipse.jdt.core-a98d7d87df415161ba75f53acbdbe8d316ea160c.tar.gz
eclipse.jdt.core-a98d7d87df415161ba75f53acbdbe8d316ea160c.tar.xz
eclipse.jdt.core-a98d7d87df415161ba75f53acbdbe8d316ea160c.zip
Fixed Bug 422468 - [1.8][assist] Code assist issues with type elided
lambda parameters
-rw-r--r--org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/parser/CompletionParserTest18.java486
-rw-r--r--org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/parser/CompletionRecoveryTest.java2
-rw-r--r--org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/RunOnlyAssistTests18.java69
-rw-r--r--org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/CompletionTests18.java276
-rw-r--r--org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ResolveTests18.java121
-rw-r--r--org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/complete/CompletionParser.java62
-rw-r--r--org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/complete/CompletionScanner.java10
-rw-r--r--org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/impl/AssistParser.java216
-rw-r--r--org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/select/SelectionParser.java75
-rw-r--r--org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/select/SelectionScanner.java4
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Block.java9
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/LambdaExpression.java83
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/CommitRollbackParser.java111
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/Parser.java240
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/RecoveredBlock.java24
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/RecoveredElement.java14
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/RecoveredField.java24
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/RecoveredLambdaExpression.java126
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/RecoveredLocalVariable.java19
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/RecoveredStatement.java7
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/Scanner.java113
21 files changed, 1648 insertions, 443 deletions
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/parser/CompletionParserTest18.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/parser/CompletionParserTest18.java
index 6a3bb73054..836af64ce7 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/parser/CompletionParserTest18.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/parser/CompletionParserTest18.java
@@ -74,9 +74,9 @@ public void test0001() {
" static void goo(J i) {\n" +
" }\n" +
" public static void main(String[] args) {\n" +
- " (<no type> first, <no type> second) -> {\n" +
- " (<no type> xyz, <no type> pqr) -> <CompleteOnName:first.>;\n" +
- " };\n" +
+ " goo((<no type> first, <no type> second) -> {\n" +
+ " return (<no type> xyz, <no type> pqr) -> <CompleteOnName:first.>;\n" +
+ "});\n" +
" }\n" +
"}\n";
@@ -177,7 +177,9 @@ public void test0004() {
int cursorLocation = string.lastIndexOf(completeBehind) + completeBehind.length() - 1;
String expectedCompletionNodeToString = "<CompleteOnName:x>";
- String expectedParentNodeToString = "<NONE>";
+ String expectedParentNodeToString = "static Foo f = (<no type> x5, <no type> x6) -> {\n" +
+ " <CompleteOnName:x>;\n" +
+ "};";
String completionIdentifier = "x";
String expectedReplacedSource = "x";
String expectedUnitDisplayString =
@@ -233,10 +235,10 @@ public void test0005() {
" }\n" +
" void go() {\n" +
" I i = (<no type> argument) -> {\n" +
- " {\n" +
- " if (true)\n" +
+ " if (true)\n" +
+ " {\n" +
" return <CompleteOnName:arg>;\n" +
- " }\n" +
+ " }\n" +
" };\n" +
" }\n" +
"}\n";
@@ -345,4 +347,474 @@ public void test0007() {
expectedReplacedSource,
"diet ast");
}
+public void test0010() {
+ String string =
+ "interface I {\n" +
+ " void foo(String x);\n" +
+ "}\n" +
+ "public class X {\n" +
+ " String xField;\n" +
+ " static void goo(String s) {\n" +
+ " }\n" +
+ " static void goo(I i) {\n" +
+ " }\n" +
+ " public static void main(String[] args) {\n" +
+ " goo((xyz) -> {\n" +
+ " System.out.println(xyz.);\n" +
+ " });\n" +
+ " }\n" +
+ "}\n";
+
+ String completeBehind = "xyz.";
+ int cursorLocation = string.lastIndexOf(completeBehind) + completeBehind.length() - 1;
+
+ String expectedCompletionNodeToString = "<CompleteOnName:xyz.>";
+ String expectedParentNodeToString = "System.out.println(<CompleteOnName:xyz.>)";
+ String completionIdentifier = "";
+ String expectedReplacedSource = "xyz.";
+ String expectedUnitDisplayString =
+ "interface I {\n" +
+ " void foo(String x);\n" +
+ "}\n" +
+ "public class X {\n" +
+ " String xField;\n" +
+ " public X() {\n" +
+ " }\n" +
+ " static void goo(String s) {\n" +
+ " }\n" +
+ " static void goo(I i) {\n" +
+ " }\n" +
+ " public static void main(String[] args) {\n" +
+ " goo((<no type> xyz) -> {\n" +
+ " System.out.println(<CompleteOnName:xyz.>);\n" +
+ "});\n" +
+ " }\n" +
+ "}\n";
+
+ checkMethodParse(
+ string.toCharArray(),
+ cursorLocation,
+ expectedCompletionNodeToString,
+ expectedParentNodeToString,
+ expectedUnitDisplayString,
+ completionIdentifier,
+ expectedReplacedSource,
+ "diet ast");
+}
+// https://bugs.eclipse.org/bugs/show_bug.cgi?id=417935, [1.8][code select] ICU#codeSelect doesn't work on reference to lambda parameter
+public void test417935() {
+ String string =
+ "import java.util.ArrayList;\n" +
+ "import java.util.Arrays;\n" +
+ "import java.util.Collections;\n" +
+ "import java.util.Comparator;\n" +
+ "public class X {\n" +
+ " int compareTo(X x) { return 0; }\n" +
+ " void foo() {\n" +
+ " Collections.sort(new ArrayList<X>(Arrays.asList(new X(), new X(), new X())),\n" +
+ " (X o1, X o2) -> o1.compa); //[2]\n" +
+ " }\n" +
+ "}\n";
+
+ String completeBehind = "compa";
+ int cursorLocation = string.lastIndexOf(completeBehind) + completeBehind.length() - 1;
+
+ String expectedCompletionNodeToString = "<CompleteOnName:o1.compa>";
+ String expectedParentNodeToString = "<NONE>";
+ String completionIdentifier = "compa";
+ String expectedReplacedSource = "o1.compa";
+ String expectedUnitDisplayString =
+ "import java.util.ArrayList;\n" +
+ "import java.util.Arrays;\n" +
+ "import java.util.Collections;\n" +
+ "import java.util.Comparator;\n" +
+ "public class X {\n" +
+ " public X() {\n" +
+ " }\n" +
+ " int compareTo(X x) {\n" +
+ " }\n" +
+ " void foo() {\n" +
+ " Collections.sort(new ArrayList<X>(Arrays.asList(new X(), new X(), new X())), (X o1, X o2) -> <CompleteOnName:o1.compa>);\n" +
+ " }\n" +
+ "}\n";
+
+ checkMethodParse(
+ string.toCharArray(),
+ cursorLocation,
+ expectedCompletionNodeToString,
+ expectedParentNodeToString,
+ expectedUnitDisplayString,
+ completionIdentifier,
+ expectedReplacedSource,
+ "diet ast");
+}
+// https://bugs.eclipse.org/bugs/show_bug.cgi?id=405126, [1.8][code assist] Lambda parameters incorrectly recovered as fields.
+public void test405126() {
+ String string =
+ "public interface Foo { \n" +
+ " int run(int s1, int s2); \n" +
+ "}\n" +
+ "interface X {\n" +
+ " static Foo f = (int x5, int x11) -> x\n" +
+ " static int x1 = 2;\n" +
+ "}\n" +
+ "class C {\n" +
+ " void method1(){\n" +
+ " int p = X.\n" +
+ " }\n" +
+ "}\n";
+
+ String completeBehind = "X.";
+ int cursorLocation = string.lastIndexOf(completeBehind) + completeBehind.length() - 1;
+
+ String expectedCompletionNodeToString = "<CompleteOnName:X.>";
+ String expectedParentNodeToString = "int p = <CompleteOnName:X.>;";
+ String completionIdentifier = "";
+ String expectedReplacedSource = "X.";
+ String expectedUnitDisplayString =
+ "public interface Foo {\n" +
+ " int run(int s1, int s2);\n" +
+ "}\n" +
+ "interface X {\n" +
+ " static Foo f;\n" +
+ " static int x1;\n" +
+ " <clinit>() {\n" +
+ " }\n" +
+ "}\n" +
+ "class C {\n" +
+ " C() {\n" +
+ " }\n" +
+ " void method1() {\n" +
+ " int p = <CompleteOnName:X.>;\n" +
+ " }\n" +
+ "}\n";
+
+ checkMethodParse(
+ string.toCharArray(),
+ cursorLocation,
+ expectedCompletionNodeToString,
+ expectedParentNodeToString,
+ expectedUnitDisplayString,
+ completionIdentifier,
+ expectedReplacedSource,
+ "diet ast");
+}
+// Verify that locals inside a lambda block don't get promoted to the parent block.
+public void testLocalsPromotion() {
+ String string =
+ "interface I {\n" +
+ " void foo(int x);\n" +
+ "}\n" +
+ "public class X {\n" +
+ " static void goo(I i) {}\n" +
+ " public static void main(String[] args) {\n" +
+ " int outerLocal;\n" +
+ " goo ((x) -> {\n" +
+ " int lambdaLocal = 10;\n" +
+ " System.out.println(\"Statement inside lambda\");\n" +
+ " lam\n" +
+ " });\n" +
+ " }\n" +
+ "}\n";
+
+ String completeBehind = "lam";
+ int cursorLocation = string.lastIndexOf(completeBehind) + completeBehind.length() - 1;
+
+ String expectedCompletionNodeToString = "<CompleteOnName:lam>";
+ String expectedParentNodeToString = "<NONE>";
+ String completionIdentifier = "lam";
+ String expectedReplacedSource = "lam";
+ String expectedUnitDisplayString =
+ "interface I {\n" +
+ " void foo(int x);\n" +
+ "}\n" +
+ "public class X {\n" +
+ " public X() {\n" +
+ " }\n" +
+ " static void goo(I i) {\n" +
+ " }\n" +
+ " public static void main(String[] args) {\n" +
+ " int outerLocal;\n" +
+ " goo((<no type> x) -> {\n" +
+ " int lambdaLocal;\n" +
+ " System.out.println(\"Statement inside lambda\");\n" +
+ " <CompleteOnName:lam>;\n" +
+ "});\n" +
+ " }\n" +
+ "}\n";
+
+ checkMethodParse(
+ string.toCharArray(),
+ cursorLocation,
+ expectedCompletionNodeToString,
+ expectedParentNodeToString,
+ expectedUnitDisplayString,
+ completionIdentifier,
+ expectedReplacedSource,
+ "diet ast");
+}
+
+// https://bugs.eclipse.org/bugs/show_bug.cgi?id=422107, [1.8][code assist] Invoking code assist just before and after a variable initialized using lambda gives different result
+public void testCompletionLocation() {
+ String string =
+ "interface I {\n" +
+ " void doit();\n" +
+ "}\n" +
+ "interface J {\n" +
+ "}\n" +
+ "public class X { \n" +
+ " Object o = (I & J) () -> {};\n" +
+ " /* AFTER */\n" +
+ "}\n";
+
+ String completeBehind = "/* AFTER */";
+ int cursorLocation = string.lastIndexOf(completeBehind) + completeBehind.length() - 1;
+
+ String expectedCompletionNodeToString = "<CompleteOnType:>";
+ String expectedParentNodeToString = "<NONE>";
+ String completionIdentifier = "";
+ String expectedReplacedSource = "";
+ String expectedUnitDisplayString =
+ "interface I {\n" +
+ " void doit();\n" +
+ "}\n" +
+ "interface J {\n" +
+ "}\n" +
+ "public class X {\n" +
+ " Object o;\n" +
+ " <CompleteOnType:>;\n" +
+ " public X() {\n" +
+ " }\n" +
+ "}\n";
+
+ checkMethodParse(
+ string.toCharArray(),
+ cursorLocation,
+ expectedCompletionNodeToString,
+ expectedParentNodeToString,
+ expectedUnitDisplayString,
+ completionIdentifier,
+ expectedReplacedSource,
+ "diet ast");
+}
+public void testElidedCompletion() {
+ String string =
+ "class Collections {\n" +
+ " public static void sort(ArrayList list, Comparator c) {\n" +
+ " }\n" +
+ "}\n" +
+ "interface Comparator {\n" +
+ " int compareTo(X t, X s);\n" +
+ "}\n" +
+ "class ArrayList {\n" +
+ "}\n" +
+ "public class X {\n" +
+ " int compareTo(X x) { return 0; }\n" +
+ " void foo() {\n" +
+ " Collections.sort(new ArrayList(), (X o1, X o2) -> o1.compa);\n" +
+ " }\n" +
+ "}\n";
+
+ String completeBehind = "compa";
+ int cursorLocation = string.lastIndexOf(completeBehind) + completeBehind.length() - 1;
+
+ String expectedCompletionNodeToString = "<CompleteOnName:o1.compa>";
+ String expectedParentNodeToString = "<NONE>";
+ String completionIdentifier = "compa";
+ String expectedReplacedSource = "o1.compa";
+ String expectedUnitDisplayString =
+ "class Collections {\n" +
+ " Collections() {\n" +
+ " }\n" +
+ " public static void sort(ArrayList list, Comparator c) {\n" +
+ " }\n" +
+ "}\n" +
+ "interface Comparator {\n" +
+ " int compareTo(X t, X s);\n" +
+ "}\n" +
+ "class ArrayList {\n" +
+ " ArrayList() {\n" +
+ " }\n" +
+ "}\n" +
+ "public class X {\n" +
+ " public X() {\n" +
+ " }\n" +
+ " int compareTo(X x) {\n" +
+ " }\n" +
+ " void foo() {\n" +
+ " Collections.sort(new ArrayList(), (X o1, X o2) -> <CompleteOnName:o1.compa>);\n" +
+ " }\n" +
+ "}\n";
+
+ checkMethodParse(
+ string.toCharArray(),
+ cursorLocation,
+ expectedCompletionNodeToString,
+ expectedParentNodeToString,
+ expectedUnitDisplayString,
+ completionIdentifier,
+ expectedReplacedSource,
+ "diet ast");
+}
+public void testElidedCompletion2() {
+ String string =
+ "class Collections {\n" +
+ " public static void sort(ArrayList list, Comparator c) {\n" +
+ " }\n" +
+ "}\n" +
+ "interface Comparator {\n" +
+ " int compareTo(X t, X s);\n" +
+ "}\n" +
+ "class ArrayList {\n" +
+ "}\n" +
+ "public class X {\n" +
+ " int compareTo(X x) { return 0; }\n" +
+ " void foo() {\n" +
+ " Collections.sort(new ArrayList(), (o1, o2) -> o1.compa);\n" +
+ " }\n" +
+ "}\n";
+
+ String completeBehind = "compa";
+ int cursorLocation = string.lastIndexOf(completeBehind) + completeBehind.length() - 1;
+
+ String expectedCompletionNodeToString = "<CompleteOnName:o1.compa>";
+ String expectedParentNodeToString = "<NONE>";
+ String completionIdentifier = "compa";
+ String expectedReplacedSource = "o1.compa";
+ String expectedUnitDisplayString =
+ "class Collections {\n" +
+ " Collections() {\n" +
+ " }\n" +
+ " public static void sort(ArrayList list, Comparator c) {\n" +
+ " }\n" +
+ "}\n" +
+ "interface Comparator {\n" +
+ " int compareTo(X t, X s);\n" +
+ "}\n" +
+ "class ArrayList {\n" +
+ " ArrayList() {\n" +
+ " }\n" +
+ "}\n" +
+ "public class X {\n" +
+ " public X() {\n" +
+ " }\n" +
+ " int compareTo(X x) {\n" +
+ " }\n" +
+ " void foo() {\n" +
+ " Collections.sort(new ArrayList(), (<no type> o1, <no type> o2) -> <CompleteOnName:o1.compa>);\n" +
+ " }\n" +
+ "}\n";
+
+ checkMethodParse(
+ string.toCharArray(),
+ cursorLocation,
+ expectedCompletionNodeToString,
+ expectedParentNodeToString,
+ expectedUnitDisplayString,
+ completionIdentifier,
+ expectedReplacedSource,
+ "diet ast");
+}
+public void testUnspecifiedReference() { // verify that completion works on unspecified reference and finds types and names.
+ String string =
+ "interface I {\n" +
+ " void doit(X x);\n" +
+ "}\n" +
+ "class String {\n" +
+ "}\n" +
+ "public class X { \n" +
+ " static void goo(I i) {\n" +
+ " }\n" +
+ " public static void main(String[] args) {\n" +
+ " goo((StringParameter) -> {\n" +
+ " Str\n" +
+ " });\n" +
+ " } \n" +
+ "}\n";
+
+ String completeBehind = "Str";
+ int cursorLocation = string.lastIndexOf(completeBehind) + completeBehind.length() - 1;
+
+ String expectedCompletionNodeToString = "<CompleteOnName:Str>";
+ String expectedParentNodeToString = "<NONE>";
+ String completionIdentifier = "Str";
+ String expectedReplacedSource = "Str";
+ String expectedUnitDisplayString =
+ "interface I {\n" +
+ " void doit(X x);\n" +
+ "}\n" +
+ "class String {\n" +
+ " String() {\n" +
+ " }\n" +
+ "}\n" +
+ "public class X {\n" +
+ " public X() {\n" +
+ " }\n" +
+ " static void goo(I i) {\n" +
+ " }\n" +
+ " public static void main(String[] args) {\n" +
+ " goo((<no type> StringParameter) -> {\n" +
+ " <CompleteOnName:Str>;\n" +
+ "});\n" +
+ " }\n" +
+ "}\n";
+
+ checkMethodParse(
+ string.toCharArray(),
+ cursorLocation,
+ expectedCompletionNodeToString,
+ expectedParentNodeToString,
+ expectedUnitDisplayString,
+ completionIdentifier,
+ expectedReplacedSource,
+ "diet ast");
+}
+public void testBrokenMethodCall() { // verify that completion works when the call containing the lambda is broken - i.e missing a semicolon.
+ String string =
+ "interface I {\n" +
+ " void doit(X x);\n" +
+ "}\n" +
+ "public class X { \n" +
+ " static void goo(I i) {\n" +
+ " }\n" +
+ " public static void main(String[] args) {\n" +
+ " goo((StringParameter) -> {\n" +
+ " Str\n" +
+ " })\n" +
+ " } \n" +
+ "}\n";
+
+ String completeBehind = "Str";
+ int cursorLocation = string.lastIndexOf(completeBehind) + completeBehind.length() - 1;
+
+ String expectedCompletionNodeToString = "<CompleteOnName:Str>";
+ String expectedParentNodeToString = "<NONE>";
+ String completionIdentifier = "Str";
+ String expectedReplacedSource = "Str";
+ String expectedUnitDisplayString =
+ "interface I {\n" +
+ " void doit(X x);\n" +
+ "}\n" +
+ "public class X {\n" +
+ " public X() {\n" +
+ " }\n" +
+ " static void goo(I i) {\n" +
+ " }\n" +
+ " public static void main(String[] args) {\n" +
+ " goo((<no type> StringParameter) -> {\n" +
+ " <CompleteOnName:Str>;\n" +
+ "});\n" +
+ " }\n" +
+ "}\n";
+
+ checkMethodParse(
+ string.toCharArray(),
+ cursorLocation,
+ expectedCompletionNodeToString,
+ expectedParentNodeToString,
+ expectedUnitDisplayString,
+ completionIdentifier,
+ expectedReplacedSource,
+ "diet ast");
+}
}
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/parser/CompletionRecoveryTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/parser/CompletionRecoveryTest.java
index abb6bbd64c..cfb6b33d88 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/parser/CompletionRecoveryTest.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/parser/CompletionRecoveryTest.java
@@ -1071,8 +1071,6 @@ public void test26() {
" e.gc.setLineCap(<CompleteOnName:SWT.CAP_>);\n" +
" }\n" +
" };\n" +
- " new ControlAdapter() {\n" +
- " };\n" +
" }\n" +
"}\n",
// expectedCompletionIdentifier:
diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/RunOnlyAssistTests18.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/RunOnlyAssistTests18.java
new file mode 100644
index 0000000000..b4392f784d
--- /dev/null
+++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/RunOnlyAssistTests18.java
@@ -0,0 +1,69 @@
+/*******************************************************************************
+ * Copyright (c) 2013 IBM Corporation and others.
+ * 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
+ *
+ * This is an implementation of an early-draft specification developed under the Java
+ * Community Process (JCP) and is made available for testing and evaluation purposes
+ * only. The code is not compatible with any specification of the JCP.
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jdt.core.tests;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.eclipse.jdt.core.tests.compiler.parser.CompletionParserTest18;
+import org.eclipse.jdt.core.tests.model.CompletionTests18;
+import org.eclipse.jdt.core.tests.model.ResolveTests18;
+
+public class RunOnlyAssistTests18 extends TestCase {
+
+ public RunOnlyAssistTests18(String name) {
+ super(name);
+ }
+ public static Class[] getAllTestClasses() {
+ return new Class[] {
+ ResolveTests18.class,
+ CompletionParserTest18.class,
+ CompletionTests18.class,
+ };
+ }
+
+ public static Test suite() {
+ TestSuite ts = new TestSuite(RunOnlyAssistTests18.class.getName());
+
+ Class[] testClasses = getAllTestClasses();
+ addTestsToSuite(ts, testClasses);
+ return ts;
+ }
+ public static void addTestsToSuite(TestSuite suite, Class[] testClasses) {
+
+ for (int i = 0; i < testClasses.length; i++) {
+ Class testClass = testClasses[i];
+ // call the suite() method and add the resulting suite to the suite
+ try {
+ Method suiteMethod = testClass.getDeclaredMethod("suite", new Class[0]); //$NON-NLS-1$
+ Test test = (Test)suiteMethod.invoke(null, new Object[0]);
+ suite.addTest(test);
+ } catch (IllegalAccessException e) {
+ e.printStackTrace();
+ } catch (InvocationTargetException e) {
+ e.getTargetException().printStackTrace();
+ } catch (NoSuchMethodException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ }
+}
diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/CompletionTests18.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/CompletionTests18.java
index 9b21be217c..dee7603fb8 100644
--- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/CompletionTests18.java
+++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/CompletionTests18.java
@@ -115,12 +115,12 @@ public void test003() throws JavaModelException {
int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length();
this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner);
assertResults(
- // INTERIM RESULTS, WILL FAIL ONCE ELIDED TYPE IS CORRECTLY INFERRED.
"clone[METHOD_REF]{clone(), Ljava.lang.Object;, ()Ljava.lang.Object;, clone, null, 35}\n" +
"equals[METHOD_REF]{equals(), Ljava.lang.Object;, (Ljava.lang.Object;)Z, equals, (obj), 35}\n" +
"finalize[METHOD_REF]{finalize(), Ljava.lang.Object;, ()V, finalize, null, 35}\n" +
"getClass[METHOD_REF]{getClass(), Ljava.lang.Object;, ()Ljava.lang.Class<+Ljava.lang.Object;>;, getClass, null, 35}\n" +
"hashCode[METHOD_REF]{hashCode(), Ljava.lang.Object;, ()I, hashCode, null, 35}\n" +
+ "length[METHOD_REF]{length(), Ljava.lang.String;, ()I, length, null, 35}\n" +
"notify[METHOD_REF]{notify(), Ljava.lang.Object;, ()V, notify, null, 35}\n" +
"notifyAll[METHOD_REF]{notifyAll(), Ljava.lang.Object;, ()V, notifyAll, null, 35}\n" +
"toString[METHOD_REF]{toString(), Ljava.lang.Object;, ()Ljava.lang.String;, toString, null, 35}\n" +
@@ -331,12 +331,12 @@ public void test009() throws JavaModelException {
"toString[METHOD_DECLARATION]{public String toString(), Ljava.lang.Object;, ()Ljava.lang.String;, toString, null, 27}",
requestor.getResults());
}
-public void _test010() throws JavaModelException {
+public void test010() throws JavaModelException {
this.workingCopies = new ICompilationUnit[1];
this.workingCopies[0] = getWorkingCopy(
"/Completion/src/X.java",
"interface I {\n" +
- " String foo(String x, Integer i); \n" +
+ " String foo(X x, X i); \n" +
"} \n" +
"public class X {\n" +
" static void goo(I i) {\n" +
@@ -358,29 +358,20 @@ public void _test010() throws JavaModelException {
int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length();
this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner);
assertResults(
- "[POTENTIAL_METHOD_DECLARATION]{, LX;, ()V, , null, 14}\n" +
- "abstract[KEYWORD]{abstract, null, null, abstract, null, 24}\n" +
- "class[KEYWORD]{class, null, null, class, null, 24}\n" +
- "enum[KEYWORD]{enum, null, null, enum, null, 24}\n" +
- "final[KEYWORD]{final, null, null, final, null, 24}\n" +
- "interface[KEYWORD]{interface, null, null, interface, null, 24}\n" +
- "native[KEYWORD]{native, null, null, native, null, 24}\n" +
- "private[KEYWORD]{private, null, null, private, null, 24}\n" +
- "protected[KEYWORD]{protected, null, null, protected, null, 24}\n" +
- "public[KEYWORD]{public, null, null, public, null, 24}\n" +
- "static[KEYWORD]{static, null, null, static, null, 24}\n" +
- "strictfp[KEYWORD]{strictfp, null, null, strictfp, null, 24}\n" +
- "synchronized[KEYWORD]{synchronized, null, null, synchronized, null, 24}\n" +
- "transient[KEYWORD]{transient, null, null, transient, null, 24}\n" +
- "volatile[KEYWORD]{volatile, null, null, volatile, null, 24}\n" +
- "I[TYPE_REF]{I, , LI;, null, null, 27}\n" +
- "J[TYPE_REF]{J, , LJ;, null, null, 27}\n" +
- "X[TYPE_REF]{X, , LX;, null, null, 27}\n" +
- "clone[METHOD_DECLARATION]{protected Object clone() throws CloneNotSupportedException, Ljava.lang.Object;, ()Ljava.lang.Object;, clone, null, 27}\n" +
- "equals[METHOD_DECLARATION]{public boolean equals(Object obj), Ljava.lang.Object;, (Ljava.lang.Object;)Z, equals, (obj), 27}\n" +
- "finalize[METHOD_DECLARATION]{protected void finalize() throws Throwable, Ljava.lang.Object;, ()V, finalize, null, 27}\n" +
- "hashCode[METHOD_DECLARATION]{public int hashCode(), Ljava.lang.Object;, ()I, hashCode, null, 27}\n" +
- "toString[METHOD_DECLARATION]{public String toString(), Ljava.lang.Object;, ()Ljava.lang.String;, toString, null, 27}",
+ "goo[METHOD_REF]{goo(), LX;, (LI;)V, goo, (i), 24}\n" +
+ "goo[METHOD_REF]{goo(), LX;, (Ljava.lang.String;)V, goo, (s), 24}\n" +
+ "main[METHOD_REF]{main(), LX;, ([Ljava.lang.String;)V, main, (args), 24}\n" +
+ "clone[METHOD_REF]{clone(), Ljava.lang.Object;, ()Ljava.lang.Object;, clone, null, 35}\n" +
+ "equals[METHOD_REF]{equals(), Ljava.lang.Object;, (Ljava.lang.Object;)Z, equals, (obj), 35}\n" +
+ "finalize[METHOD_REF]{finalize(), Ljava.lang.Object;, ()V, finalize, null, 35}\n" +
+ "getClass[METHOD_REF]{getClass(), Ljava.lang.Object;, ()Ljava.lang.Class<+Ljava.lang.Object;>;, getClass, null, 35}\n" +
+ "hashCode[METHOD_REF]{hashCode(), Ljava.lang.Object;, ()I, hashCode, null, 35}\n" +
+ "notify[METHOD_REF]{notify(), Ljava.lang.Object;, ()V, notify, null, 35}\n" +
+ "notifyAll[METHOD_REF]{notifyAll(), Ljava.lang.Object;, ()V, notifyAll, null, 35}\n" +
+ "toString[METHOD_REF]{toString(), Ljava.lang.Object;, ()Ljava.lang.String;, toString, null, 35}\n" +
+ "wait[METHOD_REF]{wait(), Ljava.lang.Object;, ()V, wait, null, 35}\n" +
+ "wait[METHOD_REF]{wait(), Ljava.lang.Object;, (J)V, wait, (millis), 35}\n" +
+ "wait[METHOD_REF]{wait(), Ljava.lang.Object;, (JI)V, wait, (millis, nanos), 35}",
requestor.getResults());
}
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=422901, [1.8][code assist] Code assistant sensitive to scope.referenceContext type identity.
@@ -570,7 +561,7 @@ public void test017() throws JavaModelException { // ensure higher relevance for
" public static void main(String[] args) {\n" +
" I i = () -> {\n" +
" xyz\n" +
- " }\n" +
+ " }\n" +
" }\n" +
"}\n");
@@ -597,4 +588,235 @@ public void test017() throws JavaModelException { // ensure higher relevance for
" goo() {key=LX;.goo()LX;} [in X [in [Working copy] X.java [in <default> [in src [in Completion]]]]],\n" +
"}" , requestor.getContext());
}
+// https://bugs.eclipse.org/bugs/show_bug.cgi?id=422468, [1.8][assist] Code assist issues with type elided lambda parameters
+public void test018() throws JavaModelException { // computing visible elements in lambda scope.
+ this.workingCopies = new ICompilationUnit[1];
+ this.workingCopies[0] = getWorkingCopy(
+ "/Completion/src/X.java",
+ "interface I {\n" +
+ " void foo(String x);\n" +
+ "}\n" +
+ "public class X {\n" +
+ " static X xField;\n" +
+ " static X goo(String s) {\n" +
+ " return null;\n" +
+ " }\n" +
+ " static void goo(I i) {\n" +
+ " }\n" +
+ " public static void main(String[] args) {\n" +
+ " goo((xyz) -> {\n" +
+ " System.out.println(xyz.);\n" +
+ " });\n" +
+ " }\n" +
+ "}\n");
+
+ CompletionTestsRequestor2 requestor = new CompletionTestsRequestor2(true, true, true, false);
+ requestor.allowAllRequiredProposals();
+ requestor.setRequireExtendedContext(true);
+ requestor.setComputeEnclosingElement(false);
+ requestor.setComputeVisibleElements(true);
+ requestor.setAssignableType("LX;");
+
+ String str = this.workingCopies[0].getSource();
+ String completeBehind = "xyz.";
+ int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length();
+ this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner);
+ assertEquals("completion offset=233\n" +
+ "completion range=[233, 232]\n" +
+ "completion token=\"\"\n" +
+ "completion token kind=TOKEN_KIND_NAME\n" +
+ "expectedTypesSignatures=null\n" +
+ "expectedTypesKeys=null\n" +
+ "completion token location=UNKNOWN\n" +
+ "visibleElements={\n" +
+ " xField {key=LX;.xField)LX;} [in X [in [Working copy] X.java [in <default> [in src [in Completion]]]]],\n" +
+ " goo(String) {key=LX;.goo(Ljava/lang/String;)LX;} [in X [in [Working copy] X.java [in <default> [in src [in Completion]]]]],\n" +
+ "}" , requestor.getContext());
+}
+// https://bugs.eclipse.org/bugs/show_bug.cgi?id=422468, [1.8][assist] Code assist issues with type elided lambda parameters
+public void test018a() throws JavaModelException { // computing visible elements in lambda scope.
+ this.workingCopies = new ICompilationUnit[1];
+ this.workingCopies[0] = getWorkingCopy(
+ "/Completion/src/X.java",
+ "interface I {\n" +
+ " void foo(X x);\n" +
+ "}\n" +
+ "public class X {\n" +
+ " static X xField;\n" +
+ " static X goo(String s) {\n" +
+ " return null;\n" +
+ " }\n" +
+ " static void goo(I i) {\n" +
+ " }\n" +
+ " public static void main(String[] args) {\n" +
+ " X xLocal = null;\n" +
+ " args = null;\n" +
+ " if (args != null) {\n" +
+ " xField = null;\n" +
+ " else \n" +
+ " xField = null;\n" +
+ " while (true);\n" +
+ " goo((xyz) -> {\n" +
+ " X xLambdaLocal = null;\n" +
+ " System.out.println(xyz.)\n" +
+ " });\n" +
+ " }\n" +
+ "}\n");
+
+ CompletionTestsRequestor2 requestor = new CompletionTestsRequestor2(true, true, true, false);
+ requestor.allowAllRequiredProposals();
+ requestor.setRequireExtendedContext(true);
+ requestor.setComputeEnclosingElement(false);
+ requestor.setComputeVisibleElements(true);
+ requestor.setAssignableType("LX;");
+
+ String str = this.workingCopies[0].getSource();
+ String completeBehind = "xyz.";
+ int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length();
+ this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner);
+ assertEquals(
+ "completion offset=419\n" +
+ "completion range=[419, 418]\n" +
+ "completion token=\"\"\n" +
+ "completion token kind=TOKEN_KIND_NAME\n" +
+ "expectedTypesSignatures=null\n" +
+ "expectedTypesKeys=null\n" +
+ "completion token location=UNKNOWN\n" +
+ "visibleElements={\n" +
+ " xLocal [in main(String[]) [in X [in [Working copy] X.java [in <default> [in src [in Completion]]]]]],\n" +
+ " xLambdaLocal [in main(String[]) [in X [in [Working copy] X.java [in <default> [in src [in Completion]]]]]],\n" +
+ " xField {key=LX;.xField)LX;} [in X [in [Working copy] X.java [in <default> [in src [in Completion]]]]],\n" +
+ " goo(String) {key=LX;.goo(Ljava/lang/String;)LX;} [in X [in [Working copy] X.java [in <default> [in src [in Completion]]]]],\n" +
+ "}" , requestor.getContext());
+}
+public void testUnspecifiedReference() throws JavaModelException { // ensure completion on ambiguous reference works and shows both types and names.
+ this.workingCopies = new ICompilationUnit[1];
+ this.workingCopies[0] = getWorkingCopy(
+ "/Completion/src/X.java",
+ "interface I {\n" +
+ " void doit(X x);\n" +
+ "}\n" +
+ "public class X { \n" +
+ " static void goo(I i) {\n" +
+ " }\n" +
+ " public static void main(String[] args) {\n" +
+ " goo((StringParameter) -> {\n" +
+ " Str\n" +
+ " });\n" +
+ " } \n" +
+ "}\n");
+
+ CompletionTestsRequestor2 requestor = new CompletionTestsRequestor2(true, true, true, false);
+ requestor.allowAllRequiredProposals();
+ String str = this.workingCopies[0].getSource();
+ String completeBehind = "Str";
+ int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length();
+ this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner);
+ assertResults("String[TYPE_REF]{String, java.lang, Ljava.lang.String;, null, null, null, null, [155, 158], 27}\n" +
+ "StringParameter[LOCAL_VARIABLE_REF]{StringParameter, null, LX;, null, null, StringParameter, null, [155, 158], 27}", requestor.getResults());
+}
+public void testBrokenMethodCall() throws JavaModelException { // ensure completion works when the containing call is not terminated properly.
+ this.workingCopies = new ICompilationUnit[1];
+ this.workingCopies[0] = getWorkingCopy(
+ "/Completion/src/X.java",
+ "interface I {\n" +
+ " void doit(X x);\n" +
+ "}\n" +
+ "public class X { \n" +
+ " static void goo(I i) {\n" +
+ " }\n" +
+ " public static void main(String[] args) {\n" +
+ " goo((StringParameter) -> {\n" +
+ " Str\n" +
+ " })\n" +
+ " } \n" +
+ "}\n");
+
+ CompletionTestsRequestor2 requestor = new CompletionTestsRequestor2(true, true, true, false);
+ requestor.allowAllRequiredProposals();
+ String str = this.workingCopies[0].getSource();
+ String completeBehind = "Str";
+ int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length();
+ this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner);
+ assertResults("String[TYPE_REF]{String, java.lang, Ljava.lang.String;, null, null, null, null, [155, 158], 27}\n" +
+ "StringParameter[LOCAL_VARIABLE_REF]{StringParameter, null, LX;, null, null, StringParameter, null, [155, 158], 27}", requestor.getResults());
+}
+public void testExpressionBody() throws JavaModelException {
+ this.workingCopies = new ICompilationUnit[1];
+ this.workingCopies[0] = getWorkingCopy(
+ "/Completion/src/X.java",
+ "interface I {\n" +
+ " void doit(X x);\n" +
+ "}\n" +
+ "public class X { \n" +
+ " void foo() {}\n" +
+ " int field;\n" +
+ " static void goo(I i) {\n" +
+ " }\n" +
+ " public static void main(String[] args) {\n" +
+ " goo((xyz) -> xyz.)\n" +
+ " } \n" +
+ "}\n");
+
+ CompletionTestsRequestor2 requestor = new CompletionTestsRequestor2(true, true, true, false);
+ requestor.allowAllRequiredProposals();
+ String str = this.workingCopies[0].getSource();
+ String completeBehind = "xyz.";
+ int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length();
+ this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner);
+ assertResults("goo[METHOD_REF]{goo(), LX;, (LI;)V, null, null, goo, (i), [173, 173], 24}\n" +
+ "main[METHOD_REF]{main(), LX;, ([Ljava.lang.String;)V, null, null, main, (args), [173, 173], 24}\n" +
+ "clone[METHOD_REF]{clone(), Ljava.lang.Object;, ()Ljava.lang.Object;, null, null, clone, null, [173, 173], 35}\n" +
+ "equals[METHOD_REF]{equals(), Ljava.lang.Object;, (Ljava.lang.Object;)Z, null, null, equals, (obj), [173, 173], 35}\n" +
+ "field[FIELD_REF]{field, LX;, I, null, null, field, null, [173, 173], 35}\n" +
+ "finalize[METHOD_REF]{finalize(), Ljava.lang.Object;, ()V, null, null, finalize, null, [173, 173], 35}\n" +
+ "foo[METHOD_REF]{foo(), LX;, ()V, null, null, foo, null, [173, 173], 35}\n" +
+ "getClass[METHOD_REF]{getClass(), Ljava.lang.Object;, ()Ljava.lang.Class<+Ljava.lang.Object;>;, null, null, getClass, null, [173, 173], 35}\n" +
+ "hashCode[METHOD_REF]{hashCode(), Ljava.lang.Object;, ()I, null, null, hashCode, null, [173, 173], 35}\n" +
+ "notify[METHOD_REF]{notify(), Ljava.lang.Object;, ()V, null, null, notify, null, [173, 173], 35}\n" +
+ "notifyAll[METHOD_REF]{notifyAll(), Ljava.lang.Object;, ()V, null, null, notifyAll, null, [173, 173], 35}\n" +
+ "toString[METHOD_REF]{toString(), Ljava.lang.Object;, ()Ljava.lang.String;, null, null, toString, null, [173, 173], 35}\n" +
+ "wait[METHOD_REF]{wait(), Ljava.lang.Object;, ()V, null, null, wait, null, [173, 173], 35}\n" +
+ "wait[METHOD_REF]{wait(), Ljava.lang.Object;, (J)V, null, null, wait, (millis), [173, 173], 35}\n" +
+ "wait[METHOD_REF]{wait(), Ljava.lang.Object;, (JI)V, null, null, wait, (millis, nanos), [173, 173], 35}", requestor.getResults());
+}
+public void testExpressionBody2() throws JavaModelException {
+ this.workingCopies = new ICompilationUnit[1];
+ this.workingCopies[0] = getWorkingCopy(
+ "/Completion/src/X.java",
+ "interface I {\n" +
+ " void doit(X x);\n" +
+ "}\n" +
+ "public class X { \n" +
+ " void foo() {}\n" +
+ " int field;\n" +
+ " static void goo(I i) {\n" +
+ " }\n" +
+ " public static void main(String[] args) {\n" +
+ " goo(xyz -> xyz.)\n" +
+ " } \n" +
+ "}\n");
+
+ CompletionTestsRequestor2 requestor = new CompletionTestsRequestor2(true, true, true, false);
+ requestor.allowAllRequiredProposals();
+ String str = this.workingCopies[0].getSource();
+ String completeBehind = "xyz.";
+ int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length();
+ this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner);
+ assertResults("goo[METHOD_REF]{goo(), LX;, (LI;)V, null, null, goo, (i), [173, 173], 24}\n" +
+ "main[METHOD_REF]{main(), LX;, ([Ljava.lang.String;)V, null, null, main, (args), [173, 173], 24}\n" +
+ "clone[METHOD_REF]{clone(), Ljava.lang.Object;, ()Ljava.lang.Object;, null, null, clone, null, [173, 173], 35}\n" +
+ "equals[METHOD_REF]{equals(), Ljava.lang.Object;, (Ljava.lang.Object;)Z, null, null, equals, (obj), [173, 173], 35}\n" +
+ "field[FIELD_REF]{field, LX;, I, null, null, field, null, [173, 173], 35}\n" +
+ "finalize[METHOD_REF]{finalize(), Ljava.lang.Object;, ()V, null, null, finalize, null, [173, 173], 35}\n" +
+ "foo[METHOD_REF]{foo(), LX;, ()V, null, null, foo, null, [173, 173], 35}\n" +
+ "getClass[METHOD_REF]{getClass(), Ljava.lang.Object;, ()Ljava.lang.Class<+Ljava.lang.Object;>;, null, null, getClass, null, [173, 173], 35}\n" +
+ "hashCode[METHOD_REF]{hashCode(), Ljava.lang.Object;, ()I, null, null, hashCode, null, [173, 173], 35}\n" +
+ "notify[METHOD_REF]{notify(), Ljava.lang.Object;, ()V, null, null, notify, null, [173, 173], 35}\n" +
+ "notifyAll[METHOD_REF]{notifyAll(), Ljava.lang.Object;, ()V, null, null, notifyAll, null, [173, 173], 35}\n" +
+ "toString[METHOD_REF]{toString(), Ljava.lang.Object;, ()Ljava.lang.String;, null, null, toString, null, [173, 173], 35}\n" +
+ "wait[METHOD_REF]{wait(), Ljava.lang.Object;, ()V, null, null, wait, null, [173, 173], 35}\n" +
+ "wait[METHOD_REF]{wait(), Ljava.lang.Object;, (J)V, null, null, wait, (millis), [173, 173], 35}\n" +
+ "wait[METHOD_REF]{wait(), Ljava.lang.Object;, (JI)V, null, null, wait, (millis, nanos), [173, 173], 35}", requestor.getResults());
+}
}
diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ResolveTests18.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ResolveTests18.java
index e12991fa44..63b20f38f8 100644
--- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ResolveTests18.java
+++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ResolveTests18.java
@@ -1349,20 +1349,22 @@ public void testBug408230n() throws CoreException {
}
}
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=417935, [1.8][code select] ICU#codeSelect doesn't work on reference to lambda parameter
-public void test417935() throws JavaModelException {
+public void test417935() throws JavaModelException { // JCL_MIN does not have the relevant classes - these are needed to handle lambda. Use local versions.
this.wc = getWorkingCopy(
"/Resolve/src/X.java",
- "import java.util.ArrayList;\n" +
- "import java.util.Arrays;\n" +
- "import java.util.Collections;\n" +
- "import java.util.Comparator;\n" +
+ "class Collections {\n" +
+ " public static void sort(ArrayList list, Comparator c) {\n" +
+ " }\n" +
+ "}\n" +
+ "interface Comparator {\n" +
+ " int compareTo(X t, X s);\n" +
+ "}\n" +
+ "class ArrayList {\n" +
+ "}\n" +
"public class X {\n" +
- " int compareTo(X x) { return 0; }\n" +
+ " int compareTo(X x) { return 0; }\n" +
" void foo() {\n" +
- " Collections.sort(new ArrayList<X>(Arrays.asList(new X(), new X(), new X())),\n" +
- " (X o1, X o2) -> o1.compareTo(o2)); //[2]\n" +
- " }\n" +
- "\n" +
+ " Collections.sort(new ArrayList(), (X o1, X o2) -> o1.compareTo(o2));\n" +
" }\n" +
"}\n");
@@ -1531,4 +1533,103 @@ public void test422468c() throws JavaModelException {
elements
);
}
+// https://bugs.eclipse.org/bugs/show_bug.cgi?id=422468, [1.8][assist] Code assist issues with type elided lambda parameters
+public void test422468d() throws JavaModelException {
+ this.wc = getWorkingCopy(
+ "/Resolve/src/X.java",
+ "interface I {\n" +
+ " J foo(String x, String y);\n" +
+ "}\n" +
+ "interface J {\n" +
+ " K foo(String x, String y);\n" +
+ "}\n" +
+ "interface K {\n" +
+ " int foo(String x, int y);\n" +
+ "}\n" +
+ "public class X {\n" +
+ " static void goo(K i) {}\n" +
+ " public static void main(String[] args) {\n" +
+ " I i = (x, y) -> { return (a, b) -> (p, q) -> a.length(); };\n" +
+ " }\n" +
+ "}\n");
+
+ String str = this.wc.getSource();
+ String selection = "a.length";
+ int start = str.lastIndexOf(selection);
+ int length = selection.length();
+
+ IJavaElement[] elements = this.wc.codeSelect(start, length);
+ assertElementsEqual(
+ "Unexpected elements",
+ "length() [in String [in String.class [in java.lang [in "+ getExternalPath() + "jclMin1.8.jar]]]]",
+ elements
+ );
+}
+// https://bugs.eclipse.org/bugs/show_bug.cgi?id=422468, [1.8][assist] Code assist issues with type elided lambda parameters
+public void test422468e() throws JavaModelException {
+ this.wc = getWorkingCopy(
+ "/Resolve/src/X.java",
+ "interface I {\n" +
+ " J foo(String x, String y);\n" +
+ "}\n" +
+ "interface J {\n" +
+ " K foo(String x, String y);\n" +
+ "}\n" +
+ "interface K {\n" +
+ " int foo(String x, int y);\n" +
+ "}\n" +
+ "public class X {\n" +
+ " static void goo(K i) {}\n" +
+ " public static void main(String[] args) {\n" +
+ " I i = (x, y) -> { return (a, b) -> (p, q) -> a.length(); };\n" +
+ " }\n" +
+ "}\n");
+
+ String str = this.wc.getSource();
+ String selection = "q";
+ int start = str.lastIndexOf(selection);
+ int length = selection.length();
+
+ IJavaElement[] elements = this.wc.codeSelect(start, length);
+ assertElementsEqual(
+ "Unexpected elements",
+ "q [in main(String[]) [in X [in [Working copy] X.java [in <default> [in src [in Resolve]]]]]]",
+ elements
+ );
+}
+// https://bugs.eclipse.org/bugs/show_bug.cgi?id=422468, [1.8][assist] Code assist issues with type elided lambda parameters
+public void testParser() throws JavaModelException {
+ this.wc = getWorkingCopy(
+ "/Resolve/src/X.java",
+ "interface I {\n" +
+ " int foo(String x, Integer y);\n" +
+ "}\n" +
+ "public class X {\n" +
+ " public static void main(String[] args) {\n" +
+ " I i = (x, y) -> {\n" +
+ " x = \"Hello\"\n" +
+ " y = 10; \n" +
+ " if (x.length() > y) {\n" +
+ " System.out.println(\"if\");\n" +
+ " } else {\n" +
+ " System.out.println(\"else\");\n" +
+ " }\n" +
+ " return x.length();\n" +
+ " };\n" +
+ " // System.out.println((I) (p, q) -> { return q.\n" +
+ " }\n" +
+ "}\n");
+
+ String str = this.wc.getSource();
+ String selection = "x";
+ int start = str.lastIndexOf(selection);
+ int length = selection.length();
+
+ IJavaElement[] elements = this.wc.codeSelect(start, length);
+ assertElementsEqual(
+ "Unexpected elements",
+ "x [in main(String[]) [in X [in [Working copy] X.java [in <default> [in src [in Resolve]]]]]]",
+ elements
+ );
+}
}
diff --git a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/complete/CompletionParser.java b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/complete/CompletionParser.java
index b8cbc0e800..96023c96ba 100644
--- a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/complete/CompletionParser.java
+++ b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/complete/CompletionParser.java
@@ -31,7 +31,6 @@ import java.util.HashSet;
import org.eclipse.jdt.internal.compiler.*;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.env.*;
-
import org.eclipse.jdt.internal.compiler.ast.*;
import org.eclipse.jdt.internal.compiler.parser.*;
import org.eclipse.jdt.internal.compiler.problem.*;
@@ -156,6 +155,7 @@ public class CompletionParser extends AssistParser {
int labelPtr = -1;
boolean isAlreadyAttached;
+ boolean shouldStackAssistNode;
public boolean record = false;
public boolean skipRecord = false;
@@ -2407,6 +2407,16 @@ protected void consumeDimWithOrWithOutExpr() {
// DimWithOrWithOutExpr ::= '[' ']'
pushOnExpressionStack(null);
}
+protected void consumeEmptyStatement() {
+ super.consumeEmptyStatement();
+ /* Sneak in the assist node. The reason we can't do that when we see the assist node is that
+ we don't know whether it is the first or subsequent statement in a block to be able to
+ decide whether to call contactNodeLists. See Parser.consumeBlockStatement(s)
+ */
+ if (this.shouldStackAssistNode && this.assistNode != null)
+ this.astStack[this.astPtr] = this.assistNode;
+ this.shouldStackAssistNode = false;
+}
protected void consumeEnhancedForStatement() {
super.consumeEnhancedForStatement();
@@ -2580,7 +2590,7 @@ protected void consumeExitVariableWithInitialization() {
this.cursorLocation > variable.initialization.sourceEnd) {
variable.initialization = null;
} else if (this.assistNode != null && this.assistNode == variable.initialization) {
- this.assistNodeParent = variable;
+ this.assistNodeParent = variable;
}
}
protected void consumeExitVariableWithoutInitialization() {
@@ -3470,6 +3480,8 @@ protected void consumeToken(int token) {
case K_MEMBER_VALUE_ARRAY_INITIALIZER:
popElement(K_MEMBER_VALUE_ARRAY_INITIALIZER);
break;
+ case K_LAMBDA_EXPRESSION_DELIMITER:
+ break; // will be popped when the containing block statement is reduced.
default:
popElement(K_ARRAY_INITIALIZER);
break;
@@ -3676,6 +3688,8 @@ protected void consumeToken(int token) {
case TokenNamedo:
pushOnElementStack(K_BLOCK_DELIMITER, DO);
break;
+ case TokenNameARROW:
+ break;
default :
pushOnElementStack(K_BLOCK_DELIMITER);
break;
@@ -4609,6 +4623,18 @@ public void initialize(boolean parsingCompilationUnit) {
this.labelPtr = -1;
initializeForBlockStatements();
}
+public void copyState(CommitRollbackParser from) {
+
+ super.copyState(from);
+
+ CompletionParser parser = (CompletionParser) from;
+
+ this.invocationType = parser.invocationType;
+ this.qualifier = parser.qualifier;
+ this.inReferenceExpression = parser.inReferenceExpression;
+ this.hasUnusedModifiers = parser.hasUnusedModifiers;
+ this.canBeExplicitConstructor = parser.canBeExplicitConstructor;
+}
/*
* Initializes the state of the parser that is about to go for BlockStatements.
*/
@@ -4954,6 +4980,10 @@ public void recoveryTokenCheck() {
break;
}
}
+
+protected CommitRollbackParser createSnapShotParser() {
+ return new CompletionParser(this.problemReporter, this.storeSourceEnds);
+}
/*
* Reset internal state after completion is over
*/
@@ -4988,13 +5018,21 @@ public void restoreAssistParser(Object parserState) {
* Move checkpoint location, reset internal stacks and
* decide which grammar goal is activated.
*/
-protected boolean resumeAfterRecovery() {
+protected int resumeAfterRecovery() {
this.hasUnusedModifiers = false;
if (this.assistNode != null) {
+
+ if (requireExtendedRecovery()) {
+ if (this.unstackedAct != ERROR_ACTION) {
+ return RESUME;
+ }
+ return super.resumeAfterRecovery();
+ }
+
/* if reached [eof] inside method body, but still inside nested type,
or inside a field initializer, should continue in diet mode until
the end of the method body or compilation unit */
- if ((this.scanner.eofPosition == this.cursorLocation+1)
+ if ((this.scanner.eofPosition >= this.cursorLocation+1)
&& (!(this.referenceContext instanceof CompilationUnitDeclaration)
|| isIndirectlyInsideFieldInitialization()
|| this.assistNodeParent instanceof FieldDeclaration && !(this.assistNodeParent instanceof Initializer))) {
@@ -5022,6 +5060,7 @@ protected boolean resumeAfterRecovery() {
}
}
*/
+
/* restart in diet mode for finding sibling constructs */
if (this.currentElement instanceof RecoveredType
|| this.currentElement.enclosingType() != null){
@@ -5035,7 +5074,7 @@ protected boolean resumeAfterRecovery() {
this.scanner.eofPosition = end < Integer.MAX_VALUE ? end + 1 : end;
} else {
resetStacks();
- return false;
+ return HALT;
}
}
}
@@ -5044,6 +5083,11 @@ protected boolean resumeAfterRecovery() {
public void setAssistIdentifier(char[] assistIdent){
((CompletionScanner)this.scanner).completionIdentifier = assistIdent;
}
+
+protected void shouldStackAssistNode() {
+ this.shouldStackAssistNode = true;
+}
+
public String toString() {
StringBuffer buffer = new StringBuffer();
buffer.append("elementKindStack : int[] = {"); //$NON-NLS-1$
@@ -5070,7 +5114,15 @@ protected void updateRecoveryState() {
/* may be able to retrieve completionNode as an orphan, and then attach it */
completionIdentifierCheck();
+ // attachOrphanCompletionNode pops various stacks to construct astNodeParent and enclosingNode. This does not gel well with extended recovery.
+ CommitRollbackParser parser = null;
+ if (lastIndexOfElement(K_LAMBDA_EXPRESSION_DELIMITER) >= 0) {
+ parser = createSnapShotParser();
+ parser.copyState(this);
+ }
attachOrphanCompletionNode();
+ if (parser != null)
+ this.copyState(parser);
// if an assist node has been found and a recovered element exists,
// mark enclosing blocks as to be preserved
diff --git a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/complete/CompletionScanner.java b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/complete/CompletionScanner.java
index 61a375ab7e..368879509d 100644
--- a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/complete/CompletionScanner.java
+++ b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/complete/CompletionScanner.java
@@ -56,6 +56,15 @@ public CompletionScanner(long sourceLevel) {
null/*taskPriorities*/,
true/*taskCaseSensitive*/);
}
+protected boolean isAtAssistIdentifier() {
+ if (this.cursorLocation < this.startPosition && this.currentPosition == this.startPosition) { // fake empty identifier got issued
+ return true;
+ }
+ if (this.cursorLocation+1 >= this.startPosition && this.cursorLocation < this.currentPosition) {
+ return true;
+ }
+ return false;
+}
/*
* Truncate the current identifier if it is containing the cursor location. Since completion is performed
* on an identifier prefix.
@@ -199,6 +208,7 @@ protected int getNextToken0() throws InvalidInputException {
this.currentPosition = this.startPosition; // for being detected as empty free identifier
return TokenNameIdentifier;
}
+ this.currentPosition = this.startPosition; // fake EOF should not drown the real next token.
return TokenNameEOF;
}
diff --git a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/impl/AssistParser.java b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/impl/AssistParser.java
index 5a08e9a53d..4d663aa1e8 100644
--- a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/impl/AssistParser.java
+++ b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/impl/AssistParser.java
@@ -19,6 +19,7 @@ package org.eclipse.jdt.internal.codeassist.impl;
*
*/
+import org.eclipse.jdt.internal.codeassist.complete.CompletionOnKeyword2;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.AbstractVariableDeclaration;
@@ -44,6 +45,7 @@ import org.eclipse.jdt.internal.compiler.ast.TypeReference;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.lookup.Binding;
import org.eclipse.jdt.internal.compiler.lookup.ExtraCompilerModifiers;
+import org.eclipse.jdt.internal.compiler.parser.CommitRollbackParser;
import org.eclipse.jdt.internal.compiler.parser.Parser;
import org.eclipse.jdt.internal.compiler.parser.RecoveredBlock;
import org.eclipse.jdt.internal.compiler.parser.RecoveredElement;
@@ -58,6 +60,7 @@ import org.eclipse.jdt.internal.compiler.problem.ProblemReporter;
public abstract class AssistParser extends Parser {
public ASTNode assistNode;
public boolean isOrphanCompletionNode;
+ private boolean resumedAfterRepair = false;
// last modifiers info
protected int lastModifiers = ClassFileConstants.AccDefault;
protected int lastModifiersStart = -1;
@@ -93,7 +96,8 @@ public abstract class AssistParser extends Parser {
protected static final int K_FIELD_INITIALIZER_DELIMITER = ASSIST_PARSER + 4; // whether we are inside a field initializer
protected static final int K_ATTRIBUTE_VALUE_DELIMITER = ASSIST_PARSER + 5; // whether we are inside a annotation attribute valuer
protected static final int K_ENUM_CONSTANT_DELIMITER = ASSIST_PARSER + 6; // whether we are inside a field initializer
-
+ protected static final int K_LAMBDA_EXPRESSION_DELIMITER = ASSIST_PARSER + 7; // whether we are inside a lambda expression
+
// selector constants
protected static final int THIS_CONSTRUCTOR = -1;
protected static final int SUPER_CONSTRUCTOR = -2;
@@ -101,9 +105,12 @@ public abstract class AssistParser extends Parser {
// enum constant constants
protected static final int NO_BODY = 0;
protected static final int WITH_BODY = 1;
+
+ protected static final int EXPRESSION_BODY = 0;
+ protected static final int BLOCK_BODY = 1;
protected boolean isFirst = false;
- protected boolean lambdaNeedsClosure = false; // :)
+
public AssistParser(ProblemReporter problemReporter) {
super(problemReporter, true);
@@ -112,8 +119,34 @@ public AssistParser(ProblemReporter problemReporter) {
setMethodsFullRecovery(false);
setStatementsRecovery(false);
}
+
public abstract char[] assistIdentifier();
+public void copyState(CommitRollbackParser from) {
+
+ super.copyState(from);
+
+ AssistParser parser = (AssistParser) from;
+
+ this.previousToken = parser.previousToken;
+ this.previousIdentifierPtr = parser.previousIdentifierPtr;
+
+ this.lastModifiers = parser.lastModifiers;
+ this.lastModifiersStart = parser.lastModifiersStart;
+
+ this.bracketDepth = parser.bracketDepth;
+ this.elementPtr = parser.elementPtr;
+
+ int length;
+ System.arraycopy(parser.blockStarts, 0, this.blockStarts = new int [length = parser.blockStarts.length], 0, length);
+ System.arraycopy(parser.elementKindStack, 0, this.elementKindStack = new int [length = parser.elementKindStack.length], 0, length);
+ System.arraycopy(parser.elementInfoStack, 0, this.elementInfoStack = new int [length = parser.elementInfoStack.length], 0, length);
+ System.arraycopy(parser.elementObjectInfoStack, 0, this.elementObjectInfoStack = new Object [length = parser.elementObjectInfoStack.length], 0, length);
+
+ this.previousKind = parser.previousKind;
+ this.previousInfo = parser.previousInfo;
+ this.previousObjectInfo = parser.previousObjectInfo;
+}
/**
* The parser become a simple parser which behave like a Parser
* @return the state of the assist parser to be able to restore the assist parser state
@@ -144,6 +177,7 @@ public RecoveredElement buildInitialRecoveryState(){
RecoveredElement element = super.buildInitialRecoveryState();
flushAssistState();
flushElementStack();
+ this.snapShot = null;
return element;
}
@@ -184,8 +218,9 @@ public RecoveredElement buildInitialRecoveryState(){
element = element.add(block, 1);
int blockIndex = 1; // ignore first block start, since manually rebuilt here
- for(int i = 0; i <= this.astPtr; i++){
- ASTNode node = this.astStack[i];
+ ASTNode node = null, lastNode = null;
+ for (int i = 0; i <= this.astPtr; i++, lastNode = node) {
+ node = this.astStack[i];
if(node instanceof ForeachStatement && ((ForeachStatement)node).action == null) {
node = ((ForeachStatement)node).elementVariable;
}
@@ -198,7 +233,7 @@ public RecoveredElement buildInitialRecoveryState(){
break;
}
if (this.blockStarts[j] != lastStart){ // avoid multiple block if at same position
- block = new Block(0);
+ block = new Block(0, lastNode instanceof LambdaExpression);
block.sourceStart = lastStart = this.blockStarts[j];
element = element.add(block, 1);
}
@@ -277,12 +312,6 @@ public RecoveredElement buildInitialRecoveryState(){
}
continue;
}
- if (node instanceof LambdaExpression) {
- LambdaExpression lambda = (LambdaExpression) node;
- element = element.add(lambda, 0);
- this.lastCheckPoint = lambda.sourceEnd + 1;
- continue;
- }
if (this.assistNode != null && node instanceof Statement) {
Statement stmt = (Statement) node;
if (!(stmt instanceof Expression) || ((Expression) stmt).statementExpression()) {
@@ -305,13 +334,16 @@ public RecoveredElement buildInitialRecoveryState(){
}
/* might need some extra block (after the last reduced node) */
+ /* For block bodied lambdas we should create a block even though the lambda header appears before it, so elements from within don't get misattributed. */
int pos = this.assistNode == null ? this.lastCheckPoint : this.assistNode.sourceStart;
+ boolean createLambdaBlock = lastNode instanceof LambdaExpression && ((LambdaExpression) node).body() instanceof Block;
for (int j = blockIndex; j <= this.realBlockPtr; j++){
if (this.blockStarts[j] >= 0) {
- if ((this.blockStarts[j] < pos) && (this.blockStarts[j] != lastStart)){ // avoid multiple block if at same position
- block = new Block(0);
+ if ((this.blockStarts[j] < pos || createLambdaBlock) && (this.blockStarts[j] != lastStart)){ // avoid multiple block if at same position
+ block = new Block(0, createLambdaBlock);
block.sourceStart = lastStart = this.blockStarts[j];
element = element.add(block, 1);
+ createLambdaBlock = false;
}
} else {
if ((this.blockStarts[j] < pos)){ // avoid multiple block if at same position
@@ -401,27 +433,68 @@ protected void consumeExitMemberValue() {
protected void consumeExplicitConstructorInvocation(int flag, int recFlag) {
super.consumeExplicitConstructorInvocation(flag, recFlag);
popElement(K_SELECTOR);
- triggerRecoveryUponLambdaClosure();
}
-protected void triggerRecoveryUponLambdaClosure() {
- if (this.assistNode == null || !this.lambdaNeedsClosure)
- return;
- ASTNode node = this.astStack[this.astPtr];
- if (this.assistNode.sourceStart >= node.sourceStart && this.assistNode.sourceEnd <= node.sourceEnd) {
- for (int i = 0; i <= this.astPtr; i++) {
- if (this.astStack[i] instanceof LambdaExpression)
- return;
+
+protected boolean triggerRecoveryUponLambdaClosure(Statement statement, boolean shouldCommit) {
+ // Last block statement reduced is required to be on the AST stack top.
+ boolean lambdaClosed = false;
+ int statementStart, statementEnd;
+ statementStart = statement.sourceStart;
+ statementEnd = statement instanceof AbstractVariableDeclaration ? ((AbstractVariableDeclaration)statement).declarationSourceEnd : statement.sourceEnd;
+ for (int i = this.elementPtr; i >= 0; --i) {
+ if (this.elementKindStack[i] != K_LAMBDA_EXPRESSION_DELIMITER)
+ continue;
+ LambdaExpression expression = (LambdaExpression) this.elementObjectInfoStack[i];
+ if (expression.sourceStart >= statementStart && expression.sourceEnd <= statementEnd) {
+ this.elementPtr = i - 1;
+ lambdaClosed = true;
+ } else {
+ if (shouldCommit) {
+ int stackLength = this.stack.length;
+ if (++this.stateStackTop >= stackLength) {
+ System.arraycopy(
+ this.stack, 0,
+ this.stack = new int[stackLength + StackIncrement], 0,
+ stackLength);
+ }
+ this.stack[this.stateStackTop] = this.unstackedAct;
+ commit();
+ this.stateStackTop --;
+ }
+ return false;
}
+ }
+
+ if (lambdaClosed && this.currentElement != null) {
this.restartRecovery = true;
- this.isOrphanCompletionNode = false;
- this.lambdaNeedsClosure = false;
+ if (!(statement instanceof AbstractVariableDeclaration)) // added already as part of standard recovery since these contribute a name to the scope prevailing at the cursor.
+ this.currentElement.add(statement, 0);
}
+ this.snapShot = null;
+ return lambdaClosed;
}
-protected void consumeExplicitConstructorInvocationWithTypeArguments(int flag, int recFlag) {
- super.consumeExplicitConstructorInvocationWithTypeArguments(flag, recFlag);
- triggerRecoveryUponLambdaClosure();
+protected boolean isAssistParser() {
+ return true;
+}
+protected void consumeInvocationExpression() { // on error, a message send's error reductions will take the expression path rather than the statement path since that is a dead end.
+ super.consumeInvocationExpression();
+ triggerRecoveryUponLambdaClosure(this.expressionStack[this.expressionPtr], false);
+}
+protected void consumeBlockStatement() {
+ super.consumeBlockStatement();
+ triggerRecoveryUponLambdaClosure((Statement) this.astStack[this.astPtr], true);
+}
+protected void consumeBlockStatements() {
+ super.consumeBlockStatements();
+ triggerRecoveryUponLambdaClosure((Statement) this.astStack[this.astPtr], true);
+}
+protected void consumeFieldDeclaration() {
+ super.consumeFieldDeclaration();
+ if (triggerRecoveryUponLambdaClosure((Statement) this.astStack[this.astPtr], true)) {
+ if (this.currentElement instanceof RecoveredType)
+ popUntilElement(K_TYPE_DELIMITER);
+ }
}
-
protected void consumeForceNoDiet() {
super.consumeForceNoDiet();
// if we are not in a method (i.e. we are not in a local variable initializer)
@@ -444,9 +517,10 @@ protected void consumeInterfaceHeader() {
super.consumeInterfaceHeader();
pushOnElementStack(K_TYPE_DELIMITER);
}
-protected void consumeExpressionStatement() {
- super.consumeExpressionStatement();
- triggerRecoveryUponLambdaClosure();
+protected void consumeLambdaHeader() {
+ super.consumeLambdaHeader();
+ LambdaExpression lexp = (LambdaExpression) this.astStack[this.astPtr];
+ pushOnElementStack(K_LAMBDA_EXPRESSION_DELIMITER, EXPRESSION_BODY, lexp);
}
protected void consumeMethodBody() {
super.consumeMethodBody();
@@ -457,7 +531,6 @@ protected void consumeMethodDeclaration(boolean isNotAbstract, boolean isDefault
popElement(K_METHOD_DELIMITER);
}
super.consumeMethodDeclaration(isNotAbstract, isDefaultMethod);
- triggerRecoveryUponLambdaClosure();
}
protected void consumeMethodHeader() {
super.consumeMethodHeader();
@@ -517,16 +590,30 @@ protected void consumeNestedMethod() {
}
protected void consumeOpenBlock() {
// OpenBlock ::= $empty
-
super.consumeOpenBlock();
+
int stackLength = this.blockStarts.length;
if (this.realBlockPtr >= stackLength) {
System.arraycopy(
- this.blockStarts, 0,
- this.blockStarts = new int[stackLength + StackIncrement], 0,
- stackLength);
+ this.blockStarts, 0,
+ this.blockStarts = new int[stackLength + StackIncrement], 0,
+ stackLength);
}
this.blockStarts[this.realBlockPtr] = this.scanner.startPosition;
+ if (requireExtendedRecovery()) {
+ // This is an epsilon production: We are in the state with kernel item: Block ::= .OpenBlock LBRACE BlockStatementsopt RBRACE
+ stackLength = this.stack.length;
+ if (++this.stateStackTop >= stackLength - 1) { // Need two slots.
+ System.arraycopy(
+ this.stack, 0,
+ this.stack = new int[stackLength + StackIncrement], 0,
+ stackLength);
+ }
+ this.stack[this.stateStackTop++] = this.unstackedAct; // transition to Block ::= OpenBlock .LBRACE BlockStatementsopt RBRACE
+ this.stack[this.stateStackTop] = tAction(this.unstackedAct, this.currentToken); // transition to Block ::= OpenBlock LBRACE .BlockStatementsopt RBRACE
+ commit();
+ this.stateStackTop -= 2;
+ }
}
protected void consumeOpenFakeBlock() {
// OpenBlock ::= $empty
@@ -853,6 +940,10 @@ protected void consumeToken(int token) {
}
break;
case TokenNameLBRACE:
+ if (this.previousToken == TokenNameARROW) {
+ popElement(K_LAMBDA_EXPRESSION_DELIMITER);
+ pushOnElementStack(K_LAMBDA_EXPRESSION_DELIMITER, BLOCK_BODY, this.previousObjectInfo);
+ }
this.bracketDepth++;
break;
case TokenNameLBRACKET:
@@ -1264,6 +1355,15 @@ protected boolean isIndirectlyInsideMethod(){
}
return false;
}
+protected boolean isIndirectlyInsideLambdaExpression(){
+ int i = this.elementPtr;
+ while (i > -1) {
+ if (this.elementKindStack[i] == K_LAMBDA_EXPRESSION_DELIMITER)
+ return true;
+ i--;
+ }
+ return false;
+}
protected boolean isIndirectlyInsideType(){
int i = this.elementPtr;
while(i > -1) {
@@ -1597,6 +1697,13 @@ protected void prepareForHeaders() {
flushElementStack();
}
}
+
+public boolean requireExtendedRecovery() {
+ if (this.assistNode instanceof TypeReference || this.assistNode instanceof CompletionOnKeyword2)
+ return false;
+ return lastIndexOfElement(K_LAMBDA_EXPRESSION_DELIMITER) >= 0;
+}
+
protected void pushOnElementStack(int kind){
this.pushOnElementStack(kind, 0, null);
}
@@ -1659,7 +1766,7 @@ public void recoveryTokenCheck() {
break;
case TokenNameRBRACE :
super.recoveryTokenCheck();
- if(this.currentElement != oldElement && !isInsideAttributeValue()) {
+ if(this.currentElement != oldElement && !isInsideAttributeValue() && !isIndirectlyInsideLambdaExpression()) {
if(oldElement instanceof RecoveredInitializer
|| oldElement instanceof RecoveredMethod
|| (oldElement instanceof RecoveredBlock && oldElement.parent instanceof RecoveredInitializer)
@@ -1691,8 +1798,19 @@ public void reset(){
* Move checkpoint location, reset internal stacks and
* decide which grammar goal is activated.
*/
-protected boolean resumeAfterRecovery() {
-
+protected int resumeAfterRecovery() {
+ if (requireExtendedRecovery()) {
+ if (this.unstackedAct == ERROR_ACTION) {
+ int mode = fallBackToSpringForward((Statement) null);
+ this.resumedAfterRepair = mode == RESUME;
+ if (mode == RESUME || mode == HALT)
+ return mode;
+ // else fall through and RESTART
+ } else {
+ return RESUME;
+ }
+ }
+
// reset internal stacks
this.astPtr = -1;
this.astLengthPtr = -1;
@@ -1700,15 +1818,20 @@ protected boolean resumeAfterRecovery() {
this.expressionLengthPtr = -1;
this.typeAnnotationLengthPtr = -1;
this.typeAnnotationPtr = -1;
+
this.identifierPtr = -1;
this.identifierLengthPtr = -1;
this.intPtr = -1;
+
+
this.dimensions = 0 ;
this.recoveredStaticInitializerStart = 0;
this.genericsIdentifiersLengthPtr = -1;
this.genericsLengthPtr = -1;
this.genericsPtr = -1;
+
+ this.valueLambdaNestDepth = -1;
this.modifiers = ClassFileConstants.AccDefault;
this.modifiersSourceStart = -1;
@@ -1717,7 +1840,12 @@ protected boolean resumeAfterRecovery() {
if (this.diet) this.dietInt = 0;
/* attempt to move checkpoint location */
- if (!moveRecoveryCheckpoint()) return false;
+ if (this.unstackedAct != ERROR_ACTION && this.resumedAfterRepair) {
+ this.scanner.ungetToken(this.currentToken); // effectively move recovery checkpoint *backwards*.
+ } else {
+ if (!moveRecoveryCheckpoint()) return HALT;
+ }
+ this.resumedAfterRepair = false;
// only look for headers
if (this.referenceContext instanceof CompilationUnitDeclaration
@@ -1738,7 +1866,7 @@ protected boolean resumeAfterRecovery() {
goForHeaders();
this.diet = true; // passed this point, will not consider method bodies
}
- return true;
+ return RESTART;
}
if (this.referenceContext instanceof AbstractMethodDeclaration
|| this.referenceContext instanceof TypeDeclaration){
@@ -1750,10 +1878,10 @@ protected boolean resumeAfterRecovery() {
prepareForBlockStatements();
goForBlockStatementsOrCatchHeader();
}
- return true;
+ return RESTART;
}
// does not know how to restart
- return false;
+ return HALT;
}
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=292087
// To be implemented in children viz. CompletionParser that are aware of array initializers
@@ -1826,4 +1954,4 @@ protected ASTNode wrapWithExplicitConstructorCallIfNeeded(ASTNode ast) {
return ast;
}
}
-}
+} \ No newline at end of file
diff --git a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/select/SelectionParser.java b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/select/SelectionParser.java
index 357ab583ab..a3eefddaca 100644
--- a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/select/SelectionParser.java
+++ b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/select/SelectionParser.java
@@ -40,7 +40,6 @@ import org.eclipse.jdt.internal.compiler.ast.ExplicitConstructorCall;
import org.eclipse.jdt.internal.compiler.ast.Expression;
import org.eclipse.jdt.internal.compiler.ast.FieldReference;
import org.eclipse.jdt.internal.compiler.ast.ImportReference;
-import org.eclipse.jdt.internal.compiler.ast.LambdaExpression;
import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration;
import org.eclipse.jdt.internal.compiler.ast.MarkerAnnotation;
import org.eclipse.jdt.internal.compiler.ast.MemberValuePair;
@@ -62,6 +61,7 @@ import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.env.ICompilationUnit;
import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
+import org.eclipse.jdt.internal.compiler.parser.CommitRollbackParser;
import org.eclipse.jdt.internal.compiler.parser.JavadocParser;
import org.eclipse.jdt.internal.compiler.parser.RecoveredType;
import org.eclipse.jdt.internal.compiler.problem.ProblemReporter;
@@ -115,8 +115,10 @@ protected void attachOrphanCompletionNode(){
if (orphan instanceof Expression) {
buildMoreCompletionContext((Expression)orphan);
} else {
- Statement statement = (Statement) orphan;
- this.currentElement = this.currentElement.add(statement, 0);
+ if (lastIndexOfElement(K_LAMBDA_EXPRESSION_DELIMITER) < 0) { // lambdas are recovered up to the containing expression statement and will carry along the assist node anyways.
+ Statement statement = (Statement) orphan;
+ this.currentElement = this.currentElement.add(statement, 0);
+ }
}
this.currentToken = 0; // given we are not on an eof, we do not want side effects caused by looked-ahead token
}
@@ -176,12 +178,15 @@ private void buildMoreCompletionContext(Expression expression) {
break nextElement;
}
}
- if(parentNode != null) {
- this.currentElement = this.currentElement.add((Statement)parentNode, 0);
- } else {
- this.currentElement = this.currentElement.add((Statement)wrapWithExplicitConstructorCallIfNeeded(expression), 0);
- if(this.lastCheckPoint < expression.sourceEnd) {
- this.lastCheckPoint = expression.sourceEnd + 1;
+ // Do not add assist node/parent into the recovery system if we are inside a lambda. The lambda will be fully recovered including the containing statement and added.
+ if (lastIndexOfElement(K_LAMBDA_EXPRESSION_DELIMITER) < 0) {
+ if(parentNode != null) {
+ this.currentElement = this.currentElement.add((Statement)parentNode, 0);
+ } else {
+ this.currentElement = this.currentElement.add((Statement)wrapWithExplicitConstructorCallIfNeeded(expression), 0);
+ if(this.lastCheckPoint < expression.sourceEnd) {
+ this.lastCheckPoint = expression.sourceEnd + 1;
+ }
}
}
}
@@ -1184,6 +1189,9 @@ protected void consumeTypeImportOnDemandDeclarationName() {
this.restartRecovery = true; // used to avoid branching back into the regular automaton
}
}
+protected CommitRollbackParser createSnapShotParser() {
+ return new SelectionParser(this.problemReporter);
+}
public ImportReference createAssistImportReference(char[][] tokens, long[] positions, int mod){
return new SelectionOnImportReference(tokens, positions, mod);
}
@@ -1413,28 +1421,6 @@ public CompilationUnitDeclaration parse(ICompilationUnit sourceUnit, Compilation
return super.parse(sourceUnit, compilationResult, -1, -1/*parse without reseting the scanner*/);
}
-protected int resumeOnSyntaxError() {
-
- if (this.referenceContext instanceof CompilationUnitDeclaration)
- return super.resumeOnSyntaxError();
-
- // Defer initial *triggered* recovery if we see a type elided lambda expression on the stack.
- if (this.assistNode != null && this.restartRecovery) {
- this.lambdaNeedsClosure = false;
- for (int i = this.astPtr; i >= 0; i--) {
- if (this.astStack[i] instanceof LambdaExpression) {
- LambdaExpression expression = (LambdaExpression) this.astStack[i];
- if (expression.argumentsTypeElided()) {
- this.restartRecovery = false; // will be restarted in when the containing expression statement or explicit constructor call is reduced.
- this.lambdaNeedsClosure = true;
- return RESUME;
- }
- }
- }
- }
- return super.resumeOnSyntaxError();
-}
-
/*
* Reset context so as to resume to regular parse loop
* If unable to reset for resuming, answers false.
@@ -1442,23 +1428,29 @@ protected int resumeOnSyntaxError() {
* Move checkpoint location, reset internal stacks and
* decide which grammar goal is activated.
*/
-protected boolean resumeAfterRecovery() {
+protected int resumeAfterRecovery() {
/* if reached assist node inside method body, but still inside nested type,
should continue in diet mode until the end of the method body */
if (this.assistNode != null
&& !(this.referenceContext instanceof CompilationUnitDeclaration)){
this.currentElement.preserveEnclosingBlocks();
+ if (requireExtendedRecovery()) {
+ if (this.unstackedAct != ERROR_ACTION) {
+ return RESUME;
+ }
+ return super.resumeAfterRecovery();
+ }
if (this.currentElement.enclosingType() == null) {
- if(!(this.currentElement instanceof RecoveredType)) {
+ if (!(this.currentElement instanceof RecoveredType)) {
resetStacks();
- return false;
- }
+ return HALT;
+ }
- RecoveredType recoveredType = (RecoveredType)this.currentElement;
- if(recoveredType.typeDeclaration != null && recoveredType.typeDeclaration.allocation == this.assistNode){
+ RecoveredType recoveredType = (RecoveredType) this.currentElement;
+ if (recoveredType.typeDeclaration != null && recoveredType.typeDeclaration.allocation == this.assistNode) {
resetStacks();
- return false;
+ return HALT;
}
}
}
@@ -1504,15 +1496,16 @@ protected Argument typeElidedArgument() {
char[] identifierName = this.identifierStack[this.identifierPtr];
long namePositions = this.identifierPositionStack[this.identifierPtr--];
- Argument arg =
+ Argument argument =
new SelectionOnArgumentName(
identifierName,
namePositions,
null, // elided type
ClassFileConstants.AccDefault,
true);
- arg.declarationSourceStart = (int) (namePositions >>> 32);
- return arg;
+ argument.declarationSourceStart = (int) (namePositions >>> 32);
+ this.assistNode = argument;
+ return argument;
}
public String toString() {
String s = Util.EMPTY_STRING;
diff --git a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/select/SelectionScanner.java b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/select/SelectionScanner.java
index 0e81cd0ddc..d08e534e9f 100644
--- a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/select/SelectionScanner.java
+++ b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/select/SelectionScanner.java
@@ -32,6 +32,10 @@ public SelectionScanner(long sourceLevel) {
super(false /*comment*/, false /*whitespace*/, false /*nls*/, sourceLevel, null /*taskTags*/, null/*taskPriorities*/, true/*taskCaseSensitive*/);
}
+protected boolean isAtAssistIdentifier() {
+ return this.selectionStart == this.startPosition && this.selectionEnd == this.currentPosition - 1;
+}
+
public char[] getCurrentIdentifierSource() {
if (this.selectionIdentifier == null){
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Block.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Block.java
index e7af82fa5f..cfcb72e452 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Block.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Block.java
@@ -5,6 +5,10 @@
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
+ * This is an implementation of an early-draft specification developed under the Java
+ * Community Process (JCP) and is made available for testing and evaluation purposes
+ * only. The code is not compatible with any specification of the JCP.
+ *
* Contributors:
* IBM Corporation - initial API and implementation
* Stephan Herrmann - Contributions for
@@ -27,7 +31,12 @@ public class Block extends Statement {
public int explicitDeclarations;
// the number of explicit declaration , used to create scope
public BlockScope scope;
+ public boolean lambdaBody;
+public Block(int explicitDeclarations, boolean lambdaBody) {
+ this.explicitDeclarations = explicitDeclarations;
+ this.lambdaBody = lambdaBody;
+}
public Block(int explicitDeclarations) {
this.explicitDeclarations = explicitDeclarations;
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/LambdaExpression.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/LambdaExpression.java
index 010aacd0d1..ecfd3076d8 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/LambdaExpression.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/LambdaExpression.java
@@ -71,24 +71,25 @@ public class LambdaExpression extends FunctionalExpression implements ReferenceC
private Statement body;
public boolean hasParentheses;
public MethodScope scope;
- private boolean voidCompatible = true;
- private boolean valueCompatible = false;
+ boolean voidCompatible = true;
+ boolean valueCompatible = false;
private boolean shapeAnalysisComplete = false;
- private boolean returnsValue;
- private boolean returnsVoid;
+ boolean returnsValue;
+ boolean returnsVoid;
private LambdaExpression original = this;
private SyntheticArgumentBinding[] outerLocalVariables = NO_SYNTHETIC_ARGUMENTS;
private int outerLocalVariablesSlotSize = 0;
public boolean shouldCaptureInstance = false;
- private boolean shouldUnelideTypes = false;
+ private boolean assistNode = false;
private boolean hasIgnoredMandatoryErrors = false;
private static final SyntheticArgumentBinding [] NO_SYNTHETIC_ARGUMENTS = new SyntheticArgumentBinding[0];
-
- public LambdaExpression(CompilationResult compilationResult, boolean shouldUnelideTypes) {
+ private static final Block NO_BODY = new Block(0, true);
+
+ public LambdaExpression(CompilationResult compilationResult, boolean assistNode) {
super(compilationResult);
- this.shouldUnelideTypes = shouldUnelideTypes;
+ this.assistNode = assistNode;
setArguments(NO_ARGUMENTS);
- setBody(new Block(0));
+ setBody(NO_BODY);
}
public void setArguments(Argument [] arguments) {
@@ -101,7 +102,7 @@ public class LambdaExpression extends FunctionalExpression implements ReferenceC
}
public void setBody(Statement body) {
- this.body = body == null ? new Block(0) : body;
+ this.body = body == null ? NO_BODY : body;
}
public Statement body() {
@@ -112,6 +113,10 @@ public class LambdaExpression extends FunctionalExpression implements ReferenceC
this.arrowPosition = arrowPosition;
}
+ public int getArrowPosition() {
+ return this.arrowPosition;
+ }
+
protected FunctionalExpression original() {
return this.original;
}
@@ -188,7 +193,7 @@ public class LambdaExpression extends FunctionalExpression implements ReferenceC
if (!haveDescriptor) {
if (argumentsTypeElided) {
- if (!this.shouldUnelideTypes)
+ if (!this.assistNode)
return null; // FUBAR, bail out...
// for code assist ONLY, keep the sluice gate shut on bogus errors otherwise.
argumentsTypeElided = false;
@@ -524,6 +529,40 @@ public class LambdaExpression extends FunctionalExpression implements ReferenceC
return false;
}
+ private void analyzeShape() { // simple minded analysis for code assist.
+ class ShapeComputer extends ASTVisitor {
+ public boolean visit(TypeDeclaration type, BlockScope skope) {
+ return false;
+ }
+ public boolean visit(TypeDeclaration type, ClassScope skope) {
+ return false;
+ }
+ public boolean visit(LambdaExpression type, BlockScope skope) {
+ return false;
+ }
+ public boolean visit(ReturnStatement returnStatement, BlockScope skope) {
+ if (returnStatement.expression != null) {
+ LambdaExpression.this.valueCompatible = true;
+ LambdaExpression.this.voidCompatible = false;
+ } else {
+ LambdaExpression.this.voidCompatible = true;
+ LambdaExpression.this.valueCompatible = false;
+ }
+ return false;
+ }
+ }
+ if (this.body instanceof Expression) {
+ this.voidCompatible = ((Expression) this.body).statementExpression();
+ this.valueCompatible = true;
+ } else {
+ // We need to be a bit tolerant/fuzzy here: the code is being written "just now", if we are too pedantic, selection/completion will break;
+ this.voidCompatible = true;
+ this.valueCompatible = true;
+ this.body.traverse(new ShapeComputer(), null);
+ }
+ this.shapeAnalysisComplete = true;
+ }
+
public boolean isCompatibleWith(final TypeBinding left, final Scope someScope) {
final MethodBinding sam = left.getSingleAbstractMethod(this.enclosingScope);
@@ -540,8 +579,19 @@ public class LambdaExpression extends FunctionalExpression implements ReferenceC
compilerOptions.isAnnotationBasedNullAnalysisEnabled = false;
try {
final LambdaExpression copy = copy();
- if (copy == null)
- return false;
+ if (copy == null) {
+ if (this.assistNode) {
+ analyzeShape(); // not on terra firma here !
+ if (sam.returnType.id == TypeIds.T_void) {
+ if (!this.voidCompatible)
+ return false;
+ } else {
+ if (!this.valueCompatible)
+ return false;
+ }
+ }
+ return !isPertinentToApplicability(left);
+ }
copy.setExpressionContext(this.expressionContext);
copy.setExpectedType(left);
this.hasIgnoredMandatoryErrors = false;
@@ -576,9 +626,9 @@ public class LambdaExpression extends FunctionalExpression implements ReferenceC
}
}
- if (!isPertinentToApplicability(left))
+ if (!isPertinentToApplicability(left)) // This check should happen after return type check below, but for buggy javac compatibility we have left it in.
return true;
-
+
if (sam.returnType.id == TypeIds.T_void) {
if (!this.voidCompatible)
return false;
@@ -586,8 +636,7 @@ public class LambdaExpression extends FunctionalExpression implements ReferenceC
if (!this.valueCompatible)
return false;
}
-
- Expression [] returnExpressions = this.resultExpressions;
+ Expression [] returnExpressions = this.resultExpressions;
for (int i = 0, length = returnExpressions.length; i < length; i++) {
if (returnExpressions[i] instanceof FunctionalExpression) { // don't want to use the resolvedType - polluted from some other overload resolution candidate
if (!returnExpressions[i].isCompatibleWith(sam.returnType, this.enclosingScope))
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/CommitRollbackParser.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/CommitRollbackParser.java
new file mode 100644
index 0000000000..8927ec7482
--- /dev/null
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/CommitRollbackParser.java
@@ -0,0 +1,111 @@
+/*******************************************************************************
+ * Copyright (c) 2013 IBM Corporation and others.
+ * 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
+ *
+ * This is an implementation of an early-draft specification developed under the Java
+ * Community Process (JCP) and is made available for testing and evaluation purposes
+ * only. The code is not compatible with any specification of the JCP.
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jdt.internal.compiler.parser;
+
+import org.eclipse.jdt.core.compiler.InvalidInputException;
+import org.eclipse.jdt.internal.compiler.ast.Statement;
+
+public abstract class CommitRollbackParser implements TerminalTokens, ParserBasicInformation {
+
+ // resumeOnSyntaxError codes:
+ protected static final int HALT = 0; // halt and throw up hands.
+ protected static final int RESTART = 1; // stacks adjusted, alternate goal from check point.
+ protected static final int RESUME = 2; // stacks untouched, just continue from where left off.
+
+ public Scanner scanner;
+ public int currentToken;
+ protected int kurrentToken; // copy of currentToken as it is trampled over all over the place :-(
+
+ public CommitRollbackParser snapShot;
+ private static final int[] RECOVERY_TOKENS = new int [] { TokenNameSEMICOLON, TokenNameRPAREN,};
+
+ protected CommitRollbackParser createSnapShotParser() {
+ return new Parser();
+ }
+
+ protected void commit() {
+ if (this.snapShot == null) {
+ this.snapShot = createSnapShotParser();
+ }
+ this.snapShot.copyState(this);
+ }
+
+ public void copyState(CommitRollbackParser commitRollbackParser) {
+ // Subclasses should implement.
+ }
+
+ protected int getNextToken() {
+ try {
+ return this.scanner.getNextToken();
+ } catch (InvalidInputException e) {
+ return TokenNameEOF;
+ }
+ }
+
+ protected void shouldStackAssistNode() {
+ // Not relevant here.
+ }
+
+ // We get here on real syntax error or syntax error triggered by fake EOF at completion site, never due to triggered recovery.
+ protected int fallBackToSpringForward(Statement unused) {
+ int nextToken;
+ boolean atCompletionSite = false;
+ int automatonState = automatonState();
+
+ // If triggered fake EOF at completion site, see if the real next token would have passed muster.
+ if (this.kurrentToken == TokenNameEOF) {
+ if (this.scanner.eofPosition < this.scanner.source.length) {
+ atCompletionSite = true;
+ this.scanner.eofPosition = this.scanner.source.length;
+ nextToken = getNextToken();
+ if (automatonWillShift(nextToken, automatonState)) {
+ this.currentToken = this.kurrentToken = nextToken;
+ return RESUME;
+ }
+ } else {
+ nextToken = TokenNameEOF;
+ }
+ } else {
+ nextToken = this.kurrentToken;
+ }
+ if (nextToken == TokenNameEOF)
+ return HALT; // don't know how to proceed.
+ this.scanner.ungetToken(nextToken); // spit out what has been bitten more than we can chew.
+ // OK, next token is no good to resume "in place", attempt some local repair. FIXME: need to make sure we don't get stuck keep reducing empty statements !!
+ for (int i = 0, length = RECOVERY_TOKENS.length; i < length; i++) {
+ if (automatonWillShift(RECOVERY_TOKENS[i], automatonState)) {
+ this.currentToken = this.kurrentToken = RECOVERY_TOKENS[i];
+ return RESUME;
+ }
+ }
+ // OK, no in place resumption, no local repair, fast forward to next statement.
+ if (this.snapShot == null)
+ return RESTART;
+
+ this.copyState(this.snapShot);
+ if (atCompletionSite) {
+ this.currentToken = TokenNameSEMICOLON;
+ shouldStackAssistNode();
+ return RESUME;
+ }
+ this.currentToken = this.scanner.fastForward(unused);
+ return RESUME;
+ }
+
+ public abstract int automatonState();
+
+ public abstract boolean automatonWillShift(int nextToken, int lastAction);
+}
+
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/Parser.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/Parser.java
index 2fe9401851..26b5e3f77b 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/Parser.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/Parser.java
@@ -45,7 +45,6 @@ import java.util.Properties;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.core.compiler.InvalidInputException;
-import org.eclipse.jdt.internal.codeassist.impl.AssistParser;
import org.eclipse.jdt.internal.compiler.ASTVisitor;
import org.eclipse.jdt.internal.compiler.CompilationResult;
import org.eclipse.jdt.internal.compiler.ast.AND_AND_Expression;
@@ -159,7 +158,7 @@ import org.eclipse.jdt.internal.compiler.problem.ProblemSeverities;
import org.eclipse.jdt.internal.compiler.util.Messages;
import org.eclipse.jdt.internal.compiler.util.Util;
-public class Parser implements ConflictedParser, ParserBasicInformation, TerminalTokens, OperatorIds, TypeIds {
+public class Parser extends CommitRollbackParser implements ConflictedParser, OperatorIds, TypeIds {
protected static final int THIS_CALL = ExplicitConstructorCall.This;
protected static final int SUPER_CALL = ExplicitConstructorCall.Super;
@@ -896,7 +895,7 @@ public class Parser implements ConflictedParser, ParserBasicInformation, Termina
public CompilationUnitDeclaration compilationUnit; /*the result from parse()*/
protected RecoveredElement currentElement;
- public int currentToken;
+
protected boolean diet = false; //tells the scanner to jump over some parts of the code/expressions like method bodies
protected int dietInt = 0; // if > 0 force the none-diet-parsing mode (even if diet if requested) [field parsing with anonymous inner classes...]
protected int endPosition; //accurate only when used ! (the start position is pushed into intStack while the end the current one)
@@ -987,8 +986,7 @@ protected int recoveredTypePtr;
protected int nextTypeStart;
protected TypeDeclaration pendingRecoveredType;
public RecoveryScanner recoveryScanner;
-//scanner token
-public Scanner scanner;
+
protected int[] stack = new int[StackIncrement];
protected int stateStackTop;
protected int synchronizedBlockSourceStart;
@@ -1004,7 +1002,7 @@ public JavadocParser javadocParser;
// used for recovery
protected int lastJavadocEnd;
public org.eclipse.jdt.internal.compiler.ReadManager readManager;
-private int valueLambdaNestDepth = -1;
+protected int valueLambdaNestDepth = -1;
private int stateStackLengthStack[] = new int[0];
protected boolean parsingJava8Plus;
protected int unstackedAct = ERROR_ACTION;
@@ -1013,14 +1011,7 @@ private boolean tolerateDefaultClassMethods = false;
private boolean processingLambdaParameterList = false;
private boolean expectTypeAnnotation = false;
-// resumeOnSyntaxError codes:
-
-protected static final int HALT = 0; // halt and throw up hands.
-protected static final int RESTART = 1; // stacks reset, alternate goal from check point.
-protected static final int RESUME = 2; // stacks untouched, just continue from where left off.
-
-
-protected Parser () {
+public Parser () {
// Caveat Emptor: For inheritance purposes and then only in very special needs. Only minimal state is initialized !
}
public Parser(ProblemReporter problemReporter, boolean optimizeStringLiterals) {
@@ -2251,7 +2242,7 @@ protected void consumeBlock() {
pushOnAstStack(block);
}
protected void consumeBlockStatement() {
- // todo.
+ // for assist parsers.
}
protected void consumeBlockStatements() {
// BlockStatements ::= BlockStatements BlockStatement
@@ -2729,6 +2720,7 @@ protected void consumeClassHeaderName1() {
protected void consumeClassInstanceCreationExpression() {
// ClassInstanceCreationExpression ::= 'new' ClassType '(' ArgumentListopt ')' ClassBodyopt
classInstanceCreation(false);
+ consumeInvocationExpression();
}
protected void consumeClassInstanceCreationExpressionName() {
// ClassInstanceCreationExpressionName ::= Name '.'
@@ -2749,6 +2741,7 @@ protected void consumeClassInstanceCreationExpressionQualified() {
this.expressionStack[this.expressionPtr] = qae;
}
qae.sourceStart = qae.enclosingInstance.sourceStart;
+ consumeInvocationExpression();
}
protected void consumeClassInstanceCreationExpressionQualifiedWithTypeArguments() {
// ClassInstanceCreationExpression ::= Primary '.' 'new' TypeArguments SimpleName '(' ArgumentListopt ')' ClassBodyopt
@@ -2815,6 +2808,7 @@ protected void consumeClassInstanceCreationExpressionQualifiedWithTypeArguments(
this.expressionStack[this.expressionPtr] = qae;
}
qae.sourceStart = qae.enclosingInstance.sourceStart;
+ consumeInvocationExpression();
}
protected void consumeClassInstanceCreationExpressionWithTypeArguments() {
// ClassInstanceCreationExpression ::= 'new' TypeArguments ClassType '(' ArgumentListopt ')' ClassBodyopt
@@ -2870,6 +2864,7 @@ protected void consumeClassInstanceCreationExpressionWithTypeArguments() {
checkForDiamond(allocationExpression.type);
}
}
+ consumeInvocationExpression();
}
protected void consumeClassOrInterface() {
this.genericsIdentifiersLengthStack[this.genericsIdentifiersLengthPtr] += this.identifierLengthStack[this.identifierLengthPtr];
@@ -5212,6 +5207,9 @@ protected void consumeMethodHeaderThrowsClause() {
this.lastCheckPoint = md.bodyStart;
}
}
+protected void consumeInvocationExpression() {
+ // Trap all forms of invocation expressions. Note: Explicit constructor calls are not expressions. Top of expression stack has the MessageSend or AllocationExpression.
+}
protected void consumeMethodInvocationName() {
// MethodInvocation ::= Name '(' ArgumentListopt ')'
@@ -5242,6 +5240,7 @@ protected void consumeMethodInvocationName() {
problemReporter().misplacedTypeAnnotations(typeAnnotations[0], typeAnnotations[typeAnnotations.length - 1]);
}
pushOnExpressionStack(m);
+ consumeInvocationExpression();
}
protected void consumeMethodInvocationNameWithTypeArguments() {
// MethodInvocation ::= Name '.' TypeArguments 'Identifier' '(' ArgumentListopt ')'
@@ -5264,6 +5263,7 @@ protected void consumeMethodInvocationNameWithTypeArguments() {
m.receiver = getUnspecifiedReference();
m.sourceStart = m.receiver.sourceStart;
pushOnExpressionStack(m);
+ consumeInvocationExpression();
}
protected void consumeMethodInvocationPrimary() {
//optimize the push/pop
@@ -5278,6 +5278,7 @@ protected void consumeMethodInvocationPrimary() {
m.sourceStart = m.receiver.sourceStart;
m.sourceEnd = this.rParenPos;
this.expressionStack[this.expressionPtr] = m;
+ consumeInvocationExpression();
}
protected void consumeMethodInvocationPrimaryWithTypeArguments() {
//optimize the push/pop
@@ -5299,6 +5300,7 @@ protected void consumeMethodInvocationPrimaryWithTypeArguments() {
m.sourceStart = m.receiver.sourceStart;
m.sourceEnd = this.rParenPos;
this.expressionStack[this.expressionPtr] = m;
+ consumeInvocationExpression();
}
protected void consumeMethodInvocationSuper() {
// MethodInvocation ::= 'super' '.' 'Identifier' '(' ArgumentListopt ')'
@@ -5311,6 +5313,7 @@ protected void consumeMethodInvocationSuper() {
this.identifierLengthPtr--;
m.receiver = new SuperReference(m.sourceStart, this.endPosition);
pushOnExpressionStack(m);
+ consumeInvocationExpression();
}
protected void consumeMethodInvocationSuperWithTypeArguments() {
// MethodInvocation ::= 'super' '.' TypeArguments 'Identifier' '(' ArgumentListopt ')'
@@ -5330,6 +5333,7 @@ protected void consumeMethodInvocationSuperWithTypeArguments() {
m.receiver = new SuperReference(m.sourceStart, this.endPosition);
pushOnExpressionStack(m);
+ consumeInvocationExpression();
}
protected void consumeModifiers() {
int savedModifiersSourceStart = this.modifiersSourceStart;
@@ -7855,17 +7859,15 @@ protected void consumeExplicitThisParameter(boolean isQualified) {
pushOnIntStack(0); // signal explicit this
}
+protected boolean isAssistParser() {
+ return false;
+}
protected void consumeNestedLambda() {
// NestedLambda ::= $empty - we get here just after the type+parenthesis elided singleton parameter or just before the '(' of the parameter list.
consumeNestedType();
this.nestedMethod[this.nestedType] ++;
- LambdaExpression lambda = new LambdaExpression(this.compilationUnit.compilationResult, this instanceof AssistParser);
+ LambdaExpression lambda = new LambdaExpression(this.compilationUnit.compilationResult, isAssistParser());
pushOnAstStack(lambda);
- if (this.currentElement != null) {
- this.currentElement = this.currentElement.add(lambda, 0);
- this.lastCheckPoint = this.scanner.currentPosition;
- this.lastIgnoredToken = -1;
- }
this.processingLambdaParameterList = true;
}
@@ -7896,16 +7898,15 @@ protected void consumeLambdaHeader() {
}
LambdaExpression lexp = (LambdaExpression) this.astStack[this.astPtr];
lexp.setArguments(arguments);
- lexp.setArrowPosition(arrowPosition); // '->' position
+ lexp.setArrowPosition(arrowPosition);
lexp.sourceEnd = this.intStack[this.intPtr--]; // ')' position or identifier position.
lexp.sourceStart = this.intStack[this.intPtr--]; // '(' position or identifier position.
lexp.hasParentheses = (this.scanner.getSource()[lexp.sourceStart] == '(');
- this.listLength = 0; // reset this.listLength after having read all parameters
+ this.listLength -= arguments == null ? 0 : arguments.length; // not necessary really.
+ this.processingLambdaParameterList = false;
if (this.currentElement != null) {
- this.lastCheckPoint = lexp.sourceEnd + 1;
- this.lastIgnoredToken = -1;
+ this.lastCheckPoint = arrowPosition + 1; // we don't want the typed formal parameters to be processed by recovery.
}
- this.processingLambdaParameterList = false;
}
protected void consumeLambdaExpression() {
@@ -7919,6 +7920,7 @@ protected void consumeLambdaExpression() {
if (this.options.ignoreMethodBodies) {
body = new Block(0);
}
+ ((Block) body).lambdaBody = true; // for consistency's sakes.
}
LambdaExpression lexp = (LambdaExpression) this.astStack[this.astPtr--];
@@ -7935,10 +7937,7 @@ protected void consumeLambdaExpression() {
}
pushOnExpressionStack(lexp);
if (this.currentElement != null) {
- if (this.currentElement.parseTree() == lexp && this.currentElement.parent != null) {
- this.currentElement = this.currentElement.parent;
- }
- this.lastCheckPoint = lexp.sourceEnd + 1;
+ this.lastCheckPoint = body.sourceEnd + 1;
}
}
@@ -7984,9 +7983,7 @@ protected void consumeTypeElidedLambdaParameter(boolean parenthesized) {
pushOnIntStack(arg.declarationSourceEnd);
}
pushOnAstStack(arg);
- /* if incomplete method header, this.listLength counter will not have been reset,
- indicating that some arguments are available on the stack */
- this.listLength++;
+ this.listLength++; // not relevant really.
}
protected void consumeElidedLeftBraceAndReturn() {
/* ElidedLeftBraceAndReturn ::= $empty
@@ -10464,7 +10461,7 @@ public void initialize(boolean parsingCompilationUnit) {
this.referenceContext = null;
this.endStatementPosition = 0;
this.valueLambdaNestDepth = -1;
-
+
//remove objects from stack too, while the same parser/compiler couple is
//re-used between two compilations ....
@@ -10797,41 +10794,14 @@ public boolean atConflictScenario(int token) {
Though this code looks complex, we should exit early in most situations.
*/
- int lastAction = this.unstackedAct;
- if (lastAction == ERROR_ACTION) { // automaton is not running.
+ if (this.unstackedAct == ERROR_ACTION) { // automaton is not running.
return false;
}
- int stackTop = this.stateStackTop; // local copy of stack pointer
- int stackTopState = this.stack[stackTop]; // single cell non write through "alternate stack" - the automaton's stack pointer either stays fixed during this manoeuvre or monotonically decreases.
- int highWaterMark = stackTop;
-
if (token != TokenNameAT) {
token = token == TokenNameLPAREN ? TokenNameBeginLambda : TokenNameBeginTypeArguments;
}
- // A rotated version of the automaton - cf. parse()'s for(;;)
- for (;;) {
- if (lastAction > ERROR_ACTION) {
- lastAction -= ERROR_ACTION; /* shift-reduce on loop entry from above, reduce on loop back */
- do { /* reduce */
- stackTop -= rhs[lastAction] - 1;
- if (stackTop < highWaterMark) {
- stackTopState = this.stack[highWaterMark = stackTop];
- } // else stackTopState is upto date already.
- lastAction = ntAction(stackTopState, lhs[lastAction]);
- } while (lastAction <= NUM_RULES);
- }
- highWaterMark = ++stackTop;
- stackTopState = lastAction; // "push"
- lastAction = tAction(lastAction, token); // can be looked up from a precomputed cache.
- if (lastAction <= NUM_RULES) {
- stackTop --;
- lastAction += ERROR_ACTION;
- continue;
- }
- // Error => false, Shift, Shift/Reduce => true, Accept => impossible.
- return lastAction != ERROR_ACTION;
- }
+ return automatonWillShift(token, this.unstackedAct);
}
/*main loop of the automat
When a rule is reduced, the method consumeRule(int) is called with the number
@@ -10852,6 +10822,7 @@ protected void parse() {
this.hasReportedError = false;
int act = START_STATE;
+ this.unstackedAct = ERROR_ACTION;
this.stateStackTop = -1;
this.currentToken = getFirstToken();
@@ -10866,8 +10837,7 @@ try {
stackLength);
}
this.stack[this.stateStackTop] = act;
-
- act = tAction(act, this.currentToken);
+ this.unstackedAct = act = tAction(act, this.currentToken);
if (act == ERROR_ACTION || this.restartRecovery) {
if (DEBUG_AUTOMATON) {
if (this.restartRecovery) {
@@ -10879,23 +10849,27 @@ try {
int errorPos = this.scanner.currentPosition - 1;
if (!this.hasReportedError) {
- this.hasError = true; // looks incorrect for recovery case ?
+ this.hasError = true;
}
- int previousToken = this.currentToken;
+ this.kurrentToken = this.currentToken;
switch (resumeOnSyntaxError()) {
case HALT:
act = ERROR_ACTION;
break ProcessTerminals;
case RESTART:
- if (act == ERROR_ACTION && previousToken != 0) this.lastErrorEndPosition = errorPos;
+ if (act == ERROR_ACTION && this.kurrentToken != 0) this.lastErrorEndPosition = errorPos;
act = START_STATE;
this.stateStackTop = -1;
this.currentToken = getFirstToken();
continue ProcessTerminals;
case RESUME:
- break; // We presume the world is virgin so we can continue exactly from where we left off.
- default:
- throw new IllegalStateException();
+ if (act == ERROR_ACTION) {
+ act = this.stack[this.stateStackTop--];
+ continue ProcessTerminals;
+ } else {
+ this.currentToken = this.kurrentToken; // Gets trashed all over the place.
+ }
+ // FALL THROUGH.
}
}
if (act <= NUM_RULES) {
@@ -10914,7 +10888,6 @@ try {
this.recordStringLiterals = oldValue;
}
try {
- this.unstackedAct = act;
this.currentToken = this.scanner.getNextToken();
} catch(InvalidInputException e){
if (!this.hasReportedError){
@@ -10924,13 +10897,11 @@ try {
this.lastCheckPoint = this.scanner.currentPosition;
this.currentToken = 0;
this.restartRecovery = true;
- } finally {
- this.unstackedAct = ERROR_ACTION;
- }
+ }
if(this.statementRecoveryActivated) {
jumpOverType();
}
- act -= ERROR_ACTION;
+ this.unstackedAct = act -= ERROR_ACTION;
if (DEBUG_AUTOMATON) {
System.out.print("Shift/Reduce - (" + name[terminal_index[this.currentToken]]+") "); //$NON-NLS-1$ //$NON-NLS-2$
@@ -10946,7 +10917,6 @@ try {
this.recordStringLiterals = oldValue;
}
try{
- this.unstackedAct = act;
this.currentToken = this.scanner.getNextToken();
} catch(InvalidInputException e){
if (!this.hasReportedError){
@@ -10956,8 +10926,6 @@ try {
this.lastCheckPoint = this.scanner.currentPosition;
this.currentToken = 0;
this.restartRecovery = true;
- } finally {
- this.unstackedAct = ERROR_ACTION;
}
if(this.statementRecoveryActivated) {
jumpOverType();
@@ -10978,8 +10946,9 @@ try {
}
this.stateStackTop -= (rhs[act] - 1);
+ this.unstackedAct = ntAction(this.stack[this.stateStackTop], lhs[act]);
consumeRule(act);
- act = ntAction(this.stack[this.stateStackTop], lhs[act]);
+ act = this.unstackedAct;
if (DEBUG_AUTOMATON) {
if (act <= NUM_RULES) {
@@ -10994,6 +10963,7 @@ try {
}
}
} finally {
+ this.unstackedAct = ERROR_ACTION;
this.scanner.setActiveParser(null);
}
@@ -12164,8 +12134,10 @@ protected void resetStacks() {
this.identifierPtr = -1;
this.identifierLengthPtr = -1;
this.intPtr = -1;
+
this.nestedMethod[this.nestedType = 0] = 0; // need to reset for further reuse
this.variablesCounter[this.nestedType] = 0;
+
this.dimensions = 0 ;
this.realBlockStack[this.realBlockPtr = 0] = 0;
this.recoveredStaticInitializerStart = 0;
@@ -12184,7 +12156,7 @@ protected void resetStacks() {
* Move checkpoint location, reset internal stacks and
* decide which grammar goal is activated.
*/
-protected boolean resumeAfterRecovery() {
+protected int resumeAfterRecovery() {
if(!this.methodRecoveryActivated && !this.statementRecoveryActivated) {
// reset internal stacks
@@ -12193,18 +12165,18 @@ protected boolean resumeAfterRecovery() {
/* attempt to move checkpoint location */
if (!moveRecoveryCheckpoint()) {
- return false;
+ return HALT;
}
// only look for headers
if (this.referenceContext instanceof CompilationUnitDeclaration){
goForHeaders();
this.diet = true; // passed this point, will not consider method bodies
- return true;
+ return RESTART;
}
// does not know how to restart
- return false;
+ return HALT;
} else if(!this.statementRecoveryActivated) {
// reset internal stacks
@@ -12213,14 +12185,14 @@ protected boolean resumeAfterRecovery() {
/* attempt to move checkpoint location */
if (!moveRecoveryCheckpoint()) {
- return false;
+ return HALT;
}
// only look for headers
goForHeaders();
- return true;
+ return RESTART;
} else {
- return false;
+ return HALT;
}
}
protected int resumeOnSyntaxError() {
@@ -12260,7 +12232,7 @@ protected int resumeOnSyntaxError() {
}
/* attempt to reset state in order to resume to parse loop */
- return resumeAfterRecovery() ? RESTART : HALT;
+ return resumeAfterRecovery();
}
public void setMethodsFullRecovery(boolean enabled) {
this.options.performMethodsFullRecovery = enabled;
@@ -12368,4 +12340,96 @@ protected void updateSourcePosition(Expression exp) {
exp.sourceEnd = this.intStack[this.intPtr--];
exp.sourceStart = this.intStack[this.intPtr--];
}
+public void copyState(CommitRollbackParser from) {
+
+ Parser parser = (Parser) from;
+
+ // Stack pointers.
+
+ this.stateStackTop = parser.stateStackTop;
+ this.unstackedAct = parser.unstackedAct;
+ this.identifierPtr = parser.identifierPtr;
+ this.identifierLengthPtr = parser.identifierLengthPtr;
+ this.astPtr = parser.astPtr;
+ this.astLengthPtr = parser.astLengthPtr;
+ this.expressionPtr = parser.expressionPtr;
+ this.expressionLengthPtr = parser.expressionLengthPtr;
+ this.genericsPtr = parser.genericsPtr;
+ this.genericsLengthPtr = parser.genericsLengthPtr;
+ this.genericsIdentifiersLengthPtr = parser.genericsIdentifiersLengthPtr;
+ this.typeAnnotationPtr = parser.typeAnnotationPtr;
+ this.typeAnnotationLengthPtr = parser.typeAnnotationLengthPtr;
+ this.intPtr = parser.intPtr;
+ this.nestedType = parser.nestedType;
+ this.realBlockPtr = parser.realBlockPtr;
+ this.valueLambdaNestDepth = parser.valueLambdaNestDepth;
+
+ // Stacks.
+
+ int length;
+ System.arraycopy(parser.stack, 0, this.stack = new int [length = parser.stack.length], 0, length);
+ System.arraycopy(parser.identifierStack, 0, this.identifierStack = new char [length = parser.identifierStack.length][], 0, length);
+ System.arraycopy(parser.identifierLengthStack, 0, this.identifierLengthStack = new int [length = parser.identifierLengthStack.length], 0, length);
+ System.arraycopy(parser.identifierPositionStack, 0, this.identifierPositionStack = new long[length = parser.identifierPositionStack.length], 0, length);
+ System.arraycopy(parser.astStack, 0, this.astStack = new ASTNode [length = parser.astStack.length], 0, length);
+ System.arraycopy(parser.astLengthStack, 0, this.astLengthStack = new int [length = parser.astLengthStack.length], 0, length);
+ System.arraycopy(parser.expressionStack, 0, this.expressionStack = new Expression [length = parser.expressionStack.length], 0, length);
+ System.arraycopy(parser.expressionLengthStack, 0, this.expressionLengthStack = new int [length = parser.expressionLengthStack.length], 0, length);
+ System.arraycopy(parser.genericsStack, 0, this.genericsStack = new ASTNode [length = parser.genericsStack.length], 0, length);
+ System.arraycopy(parser.genericsLengthStack, 0, this.genericsLengthStack = new int [length = parser.genericsLengthStack.length], 0, length);
+ System.arraycopy(parser.genericsIdentifiersLengthStack, 0, this.genericsIdentifiersLengthStack = new int [length = parser.genericsIdentifiersLengthStack.length], 0, length);
+ System.arraycopy(parser.typeAnnotationStack, 0, this.typeAnnotationStack = new Annotation [length = parser.typeAnnotationStack.length], 0, length);
+ System.arraycopy(parser.typeAnnotationLengthStack, 0, this.typeAnnotationLengthStack = new int [length = parser.typeAnnotationLengthStack.length], 0, length);
+ System.arraycopy(parser.intStack, 0, this.intStack = new int [length = parser.intStack.length], 0, length);
+ System.arraycopy(parser.nestedMethod, 0, this.nestedMethod = new int [length = parser.nestedMethod.length], 0, length);
+ System.arraycopy(parser.realBlockStack, 0, this.realBlockStack = new int [length = parser.realBlockStack.length], 0, length);
+ System.arraycopy(parser.stateStackLengthStack, 0, this.stateStackLengthStack = new int [length = parser.stateStackLengthStack.length], 0, length);
+ System.arraycopy(parser.variablesCounter, 0, this.variablesCounter = new int [length = parser.variablesCounter.length], 0, length);
+ System.arraycopy(parser.stack, 0, this.stack = new int [length = parser.stack.length], 0, length);
+ System.arraycopy(parser.stack, 0, this.stack = new int [length = parser.stack.length], 0, length);
+ System.arraycopy(parser.stack, 0, this.stack = new int [length = parser.stack.length], 0, length);
+
+ // Loose variables.
+
+ this.listLength = parser.listLength;
+ this.listTypeParameterLength = parser.listTypeParameterLength;
+ this.dimensions = parser.dimensions;
+ this.recoveredStaticInitializerStart = parser.recoveredStaticInitializerStart;
+
+ // Parser.resetStacks is not clearing the modifiers, but AssistParser.resumeAfterRecovery is - why ? (the former doesn't)
+ // this.modifiers = parser.modifiers;
+ // this.modifiersSourceStart = parser.modifiersSourceStart;
+}
+
+public int automatonState() {
+ return this.stack[this.stateStackTop];
+}
+public boolean automatonWillShift(int token, int lastAction) {
+ int stackTop = this.stateStackTop; // local copy of stack pointer
+ int stackTopState = this.stack[stackTop]; // single cell non write through "alternate stack" - the automaton's stack pointer either stays fixed during this manoeuvre or monotonically decreases.
+ int highWaterMark = stackTop;
+ // A rotated version of the automaton - cf. parse()'s for(;;)
+ for (;;) {
+ if (lastAction > ERROR_ACTION) {
+ lastAction -= ERROR_ACTION; /* shift-reduce on loop entry from above, reduce on loop back */
+ do { /* reduce */
+ stackTop -= rhs[lastAction] - 1;
+ if (stackTop < highWaterMark) {
+ stackTopState = this.stack[highWaterMark = stackTop];
+ } // else stackTopState is upto date already.
+ lastAction = ntAction(stackTopState, lhs[lastAction]);
+ } while (lastAction <= NUM_RULES);
+ }
+ highWaterMark = ++stackTop;
+ stackTopState = lastAction; // "push"
+ lastAction = tAction(lastAction, token); // can be looked up from a precomputed cache.
+ if (lastAction <= NUM_RULES) {
+ stackTop --;
+ lastAction += ERROR_ACTION;
+ continue;
+ }
+ // Error => false, Shift, Shift/Reduce => true, Accept => impossible.
+ return lastAction != ERROR_ACTION;
+ }
+}
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/RecoveredBlock.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/RecoveredBlock.java
index 37fce6fd73..cc01976bd9 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/RecoveredBlock.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/RecoveredBlock.java
@@ -61,14 +61,6 @@ public RecoveredElement add(AbstractMethodDeclaration methodDeclaration, int bra
return super.add(methodDeclaration, bracketBalanceValue);
}
/*
- * Record a Lambda declaration
- */
-public RecoveredElement add(LambdaExpression expression, int bracketBalanceValue) {
- RecoveredLambdaExpression element = new RecoveredLambdaExpression(expression, this, bracketBalanceValue);
- attach(element);
- return element;
-}
-/*
* Record a nested block declaration
*/
public RecoveredElement add(Block nestedBlockDeclaration, int bracketBalanceValue) {
@@ -162,6 +154,10 @@ public RecoveredElement add(Statement stmt, int bracketBalanceValue) {
* Record a statement declaration
*/
public RecoveredElement add(Statement stmt, int bracketBalanceValue, boolean delegatedByParent) {
+
+ if (stmt instanceof LambdaExpression) // lambdas are recovered up to the containing statement anyways.
+ return this;
+
resetPendingModifiers();
/* do not consider a nested block starting passed the block end (if set)
@@ -295,6 +291,11 @@ public Block updatedBlock(int depth, Set knownTypes){
// if block was not marked to be preserved or empty, then ignore it
if (!this.preserveContent || this.statementCount == 0) return null;
+
+ /* If this block stands for the lambda body, trash the contents. Lambda expressions are recovered as part of the enclosing statement.
+ We still have left in a block here to make sure that contained elements can be trapped and tossed out.
+ */
+ if (this.blockDeclaration.lambdaBody) return null;
Statement[] updatedStatements = new Statement[this.statementCount];
int updatedCount = 0;
@@ -340,7 +341,7 @@ public Block updatedBlock(int depth, Set knownTypes){
Statement updatedStatement = this.statements[i].updatedStatement(depth, knownTypes);
if (updatedStatement != null){
updatedStatements[updatedCount++] = updatedStatement;
-
+
if (updatedStatement instanceof LocalDeclaration) {
LocalDeclaration localDeclaration = (LocalDeclaration) updatedStatement;
if(localDeclaration.declarationSourceEnd > lastEnd) {
@@ -431,6 +432,11 @@ public Statement updateStatement(int depth, Set knownTypes){
// if block was closed or empty, then ignore it
if (this.blockDeclaration.sourceEnd != 0 || this.statementCount == 0) return null;
+
+ /* If this block stands for the lambda body, trash the contents. Lambda expressions are recovered as part of the enclosing statement.
+ We still have left in a block here to make sure that contained elements can be trapped and tossed out.
+ */
+ if (this.blockDeclaration.lambdaBody) return null;
Statement[] updatedStatements = new Statement[this.statementCount];
int updatedCount = 0;
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/RecoveredElement.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/RecoveredElement.java
index 68b9688de4..16f0a488eb 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/RecoveredElement.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/RecoveredElement.java
@@ -1,14 +1,10 @@
/*******************************************************************************
- * Copyright (c) 2000, 2013 IBM Corporation and others.
+ * Copyright (c) 2000, 2012 IBM Corporation and others.
* 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
*
- * This is an implementation of an early-draft specification developed under the Java
- * Community Process (JCP) and is made available for testing and evaluation purposes
- * only. The code is not compatible with any specification of the JCP.
- *
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
@@ -23,7 +19,6 @@ import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.Block;
import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
import org.eclipse.jdt.internal.compiler.ast.ImportReference;
-import org.eclipse.jdt.internal.compiler.ast.LambdaExpression;
import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration;
import org.eclipse.jdt.internal.compiler.ast.Statement;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
@@ -105,13 +100,6 @@ public RecoveredElement add(LocalDeclaration localDeclaration, int bracketBalanc
this.updateSourceEndIfNecessary(previousAvailableLineEnd(localDeclaration.declarationSourceStart - 1));
return this.parent.add(localDeclaration, bracketBalanceValue);
}
-
-/*
- * Record a LambdaExpression: Only can occur inside a block. Note: Field initializers are wrapped into a block.
- */
-public RecoveredElement add(LambdaExpression expression, int bracketBalanceValue) {
- return this;
-}
/*
* Record a statement
*/
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/RecoveredField.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/RecoveredField.java
index 6e724a2698..ca7e2a56a8 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/RecoveredField.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/RecoveredField.java
@@ -24,7 +24,6 @@ import org.eclipse.jdt.internal.compiler.ast.ArrayQualifiedTypeReference;
import org.eclipse.jdt.internal.compiler.ast.ArrayTypeReference;
import org.eclipse.jdt.internal.compiler.ast.Expression;
import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
-import org.eclipse.jdt.internal.compiler.ast.LambdaExpression;
import org.eclipse.jdt.internal.compiler.ast.Statement;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
@@ -35,8 +34,6 @@ public class RecoveredField extends RecoveredElement {
public RecoveredAnnotation[] annotations;
public int annotationCount;
-
- public RecoveredLambdaExpression initializerLambda;
public int modifiers;
public int modifiersStart;
@@ -90,24 +87,6 @@ public RecoveredElement add(Statement statement, int bracketBalanceValue) {
}
}
/*
- * Record a lambda expression if field is expecting an initialization expression,
- * used for completion inside field initializers.
- */
-public RecoveredElement add(LambdaExpression expression, int bracketBalanceValue) {
-
- if (this.alreadyCompletedFieldInitialization) {
- return super.add(expression, bracketBalanceValue);
- } else {
- if (expression.sourceEnd > 0)
- this.alreadyCompletedFieldInitialization = true;
- // else we may still be inside the initialization, having parsed only a part of it yet
- this.fieldDeclaration.initialization = expression;
- this.fieldDeclaration.declarationSourceEnd = expression.sourceEnd;
- this.fieldDeclaration.declarationEnd = expression.sourceEnd;
- return this.initializerLambda = new RecoveredLambdaExpression(expression, this, bracketBalanceValue);
- }
-}
-/*
* Record a type declaration if this field is expecting an initialization expression
* and the type is an anonymous type.
* Used for completion inside field initializers.
@@ -269,9 +248,6 @@ public FieldDeclaration updatedFieldDeclaration(int depth, Set knownTypes){
}
}
}
- if (this.initializerLambda != null)
- this.initializerLambda.updateParseTree();
-
return this.fieldDeclaration;
}
/*
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/RecoveredLambdaExpression.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/RecoveredLambdaExpression.java
deleted file mode 100644
index 5e956e097e..0000000000
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/RecoveredLambdaExpression.java
+++ /dev/null
@@ -1,126 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2013 IBM Corporation and others.
- * 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
- *
- * This is an implementation of an early-draft specification developed under the Java
- * Community Process (JCP) and is made available for testing and evaluation purposes
- * only. The code is not compatible with any specification of the JCP.
- *
- * Contributors:
- * IBM Corporation - initial API and implementation
- *******************************************************************************/
-
-package org.eclipse.jdt.internal.compiler.parser;
-
-import java.util.HashSet;
-import java.util.Set;
-
-import org.eclipse.jdt.internal.compiler.ast.ASTNode;
-import org.eclipse.jdt.internal.compiler.ast.Block;
-import org.eclipse.jdt.internal.compiler.ast.LambdaExpression;
-import org.eclipse.jdt.internal.compiler.ast.Statement;
-
-public class RecoveredLambdaExpression extends RecoveredBlock {
-
- private LambdaExpression expression;
- private boolean haveBlockBody = false;
- private boolean haveExpressionBody = false;
- private RecoveredStatement bodyExpression;
-
- public RecoveredLambdaExpression(LambdaExpression expression, RecoveredElement parent, int bracketBalance){
- super(new Block(0), parent, bracketBalance); // don't have a block yet. May never have, in that event will course correct.
- this.expression = expression;
- this.expression.setBody(this.blockDeclaration);
- }
-
- /*
- * Record a nested block declaration
- */
- public RecoveredElement add(Block block, int bracketBalanceValue) {
- if (!this.haveBlockBody && !this.haveExpressionBody) {
- this.haveBlockBody = true;
- this.haveExpressionBody = false;
- this.blockDeclaration = block;
- return this;
- }
- return super.add(block, bracketBalanceValue);
- }
-
- /*
- * Record a nested block declaration
- */
- public RecoveredElement add(LambdaExpression lambda, int bracketBalanceValue) {
- if (!this.haveBlockBody && !this.haveExpressionBody) {
- this.haveBlockBody = false;
- this.haveExpressionBody = true;
- this.bodyExpression = new RecoveredLambdaExpression(lambda, this, bracketBalanceValue);
- this.expression.setBody(lambda);
- return this.bodyExpression;
- }
- return super.add(lambda, bracketBalanceValue);
- }
-
- /*
- * Record a statement declaration
- */
- public RecoveredElement add(Statement stmt, int bracketBalanceValue) {
- return this.add(stmt, bracketBalanceValue, false);
- }
-
- /*
- * Record a statement declaration
- */
- public RecoveredElement add(Statement stmt, int bracketBalanceValue, boolean delegatedByParent) {
- if (!this.haveBlockBody && !this.haveExpressionBody) {
- this.haveBlockBody = false;
- this.haveExpressionBody = true;
- this.bodyExpression = new RecoveredStatement(stmt, this, bracketBalanceValue);
- this.expression.setBody(stmt);
- return this.bodyExpression;
- }
- return super.add(stmt, bracketBalanceValue, delegatedByParent);
- }
-
- /*
- * Answer the associated parsed structure
- */
- public ASTNode parseTree(){
- return updatedLambdaExpression(0, new HashSet());
- }
-
- public LambdaExpression updatedLambdaExpression(int depth, Set knownTypes) {
- if (this.haveBlockBody)
- this.expression.setBody(super.updatedStatement(depth, knownTypes));
- else if (this.bodyExpression != null)
- this.expression.setBody(this.bodyExpression.updatedStatement(depth, knownTypes));
- return this.expression;
- }
- /*
- * Rebuild a statement from the nested structure which is in scope
- */
- public Statement updatedStatement(int depth, Set knownTypes){
- return updatedLambdaExpression(depth, knownTypes);
- }
- /*
- * Final update the corresponding parse node
- */
- public void updateParseTree(){
- updatedLambdaExpression(0, new HashSet());
- }
- /*
- * Rebuild a flattened block from the nested structure which is in scope
- */
- public Statement updateStatement(int depth, Set knownTypes){
- return updatedLambdaExpression(depth, knownTypes);
- }
-
- public String toString(int tab) {
- StringBuffer result = new StringBuffer(tabString(tab));
- result.append("Recovered Lambda Expression:\n"); //$NON-NLS-1$
- this.expression.print(tab + 1, result);
- return result.toString();
- }
-} \ No newline at end of file
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/RecoveredLocalVariable.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/RecoveredLocalVariable.java
index 57bae051f8..7755ca29a8 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/RecoveredLocalVariable.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/RecoveredLocalVariable.java
@@ -21,7 +21,6 @@ import org.eclipse.jdt.internal.compiler.ast.ArrayQualifiedTypeReference;
import org.eclipse.jdt.internal.compiler.ast.ArrayTypeReference;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.Expression;
-import org.eclipse.jdt.internal.compiler.ast.LambdaExpression;
import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration;
import org.eclipse.jdt.internal.compiler.ast.Statement;
@@ -29,7 +28,6 @@ public class RecoveredLocalVariable extends RecoveredStatement {
public RecoveredAnnotation[] annotations;
public int annotationCount;
- private RecoveredLambdaExpression initializer;
public int modifiers;
public int modifiersStart;
@@ -55,21 +53,6 @@ public RecoveredElement add(Statement stmt, int bracketBalanceValue) {
return this;
}
}
-/*
- * Record an expression statement if local variable is expecting an initialization expression.
- */
-public RecoveredElement add(LambdaExpression expression, int bracketBalanceValue) {
-
- if (this.alreadyCompletedLocalInitialization) {
- return this;
- } else {
- this.alreadyCompletedLocalInitialization = true;
- this.localDeclaration.initialization = expression;
- this.localDeclaration.declarationSourceEnd = expression.sourceEnd;
- this.localDeclaration.declarationEnd = expression.sourceEnd;
- return this.initializer = new RecoveredLambdaExpression(expression, this, bracketBalanceValue);
- }
-}
public void attach(RecoveredAnnotation[] annots, int annotCount, int mods, int modsSourceStart) {
if (annotCount > 0) {
Annotation[] existingAnnotations = this.localDeclaration.annotations;
@@ -133,8 +116,6 @@ public Statement updatedStatement(int depth, Set knownTypes){
this.localDeclaration.declarationSourceStart = start;
}
}
- if (this.initializer != null)
- this.initializer.updateParseTree();
return this.localDeclaration;
}
/*
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/RecoveredStatement.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/RecoveredStatement.java
index 326d9854d2..b587af56af 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/RecoveredStatement.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/RecoveredStatement.java
@@ -17,28 +17,21 @@ import java.util.HashSet;
import java.util.Set;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
-import org.eclipse.jdt.internal.compiler.ast.LambdaExpression;
import org.eclipse.jdt.internal.compiler.ast.Statement;
public class RecoveredStatement extends RecoveredElement {
public Statement statement;
- public RecoveredLambdaExpression subExpression;
public RecoveredStatement(Statement statement, RecoveredElement parent, int bracketBalance){
super(parent, bracketBalance);
this.statement = statement;
}
-public RecoveredElement add(LambdaExpression expression, int bracketBalanceValue) {
- return this.subExpression = new RecoveredLambdaExpression(expression, this, bracketBalanceValue);
-}
/*
* Answer the associated parsed structure
*/
public ASTNode parseTree() {
- if (this.subExpression != null)
- this.subExpression.updateParseTree();
return this.statement;
}
/*
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/Scanner.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/Scanner.java
index 9725ebd2ba..91145b9618 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/Scanner.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/Scanner.java
@@ -19,6 +19,7 @@ package org.eclipse.jdt.internal.compiler.parser;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.core.compiler.InvalidInputException;
import org.eclipse.jdt.internal.compiler.CompilationResult;
+import org.eclipse.jdt.internal.compiler.ast.Statement;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.util.Util;
@@ -4241,11 +4242,13 @@ private static final class Goal {
static int IntersectionCastRule = 0;
static int ReferenceExpressionRule = 0;
static int VarargTypeAnnotationsRule = 0;
+ static int BlockStatementoptRule = 0;
static Goal LambdaParameterListGoal;
static Goal IntersectionCastGoal;
static Goal VarargTypeAnnotationGoal;
static Goal ReferenceExpressionGoal;
+ static Goal BlockStatementoptGoal;
static {
@@ -4261,12 +4264,17 @@ private static final class Goal {
else
if ("TypeAnnotations".equals(Parser.name[Parser.non_terminal_index[Parser.lhs[i]]])) //$NON-NLS-1$
VarargTypeAnnotationsRule = i;
+ else
+ if ("BlockStatementopt".equals(Parser.name[Parser.non_terminal_index[Parser.lhs[i]]])) //$NON-NLS-1$
+ BlockStatementoptRule = i;
+
}
LambdaParameterListGoal = new Goal(TokenNameARROW, new int[] { TokenNameARROW }, LambdaParameterListRule);
IntersectionCastGoal = new Goal(TokenNameLPAREN, followSetOfCast(), IntersectionCastRule);
VarargTypeAnnotationGoal = new Goal(TokenNameAT, new int[] { TokenNameELLIPSIS }, VarargTypeAnnotationsRule);
ReferenceExpressionGoal = new Goal(TokenNameLESS, new int[] { TokenNameCOLON_COLON }, ReferenceExpressionRule);
+ BlockStatementoptGoal = new Goal(TokenNameLBRACE, new int [0], BlockStatementoptRule);
}
@@ -4279,10 +4287,13 @@ private static final class Goal {
boolean hasBeenReached(int act, int token) {
/*
System.out.println("[Goal = " + Parser.name[Parser.non_terminal_index[Parser.lhs[this.rule]]] + "] " + "Saw: " + Parser.name[Parser.non_terminal_index[Parser.lhs[act]]] + "::" + //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
- Parser.name[Parser.terminal_index[token]]);
+ Parser.name[Parser.terminal_index[token]]);
*/
if (act == this.rule) {
- for (int i = 0, length = this.follow.length; i < length; i++)
+ final int length = this.follow.length;
+ if (length == 0)
+ return true;
+ for (int i = 0; i < length; i++)
if (this.follow[i] == token)
return true;
}
@@ -4332,7 +4343,7 @@ private static final class VanguardParser extends Parser {
} else if (act > ERROR_ACTION) { /* shift-reduce */
this.unstackedAct = act;
try {
- this.currentToken = this.scanner.getNextToken();
+ this.currentToken = this.scanner.getNextToken();
} finally {
this.unstackedAct = ERROR_ACTION;
}
@@ -4341,7 +4352,7 @@ private static final class VanguardParser extends Parser {
if (act < ACCEPT_ACTION) { /* shift */
this.unstackedAct = act;
try {
- this.currentToken = this.scanner.getNextToken();
+ this.currentToken = this.scanner.getNextToken();
} finally {
this.unstackedAct = ERROR_ACTION;
}
@@ -4493,4 +4504,98 @@ private int disambiguatedToken(int token) {
}
return token;
}
+
+protected boolean isAtAssistIdentifier() {
+ return false;
+}
+
+// Position the scanner at the next block statement and return the start token. We recognize empty statements.
+public int fastForward(Statement unused) {
+
+ int token;
+
+ while (true) {
+ try {
+ token = getNextToken();
+ } catch (InvalidInputException e) {
+ return TokenNameEOF;
+ }
+ /* FOLLOW map of BlockStatement, since the non-terminal is recursive is a super set of its own FIRST set.
+ We use FOLLOW rather than FIRST since we want to recognize empty statements. i.e if (x > 10) { x = 0 }
+ */
+ switch(token) {
+ case TokenNameIdentifier:
+ if (isAtAssistIdentifier()) // do not fast forward past the assist identifier ! We don't handle collections as of now.
+ return token;
+ //$FALL-THROUGH$
+ case TokenNameabstract:
+ case TokenNameassert:
+ case TokenNameboolean:
+ case TokenNamebreak:
+ case TokenNamebyte:
+ case TokenNamecase:
+ case TokenNamechar:
+ case TokenNameclass:
+ case TokenNamecontinue:
+ case TokenNamedefault:
+ case TokenNamedo:
+ case TokenNamedouble:
+ case TokenNameenum:
+ case TokenNamefalse:
+ case TokenNamefinal:
+ case TokenNamefloat:
+ case TokenNamefor:
+ case TokenNameif:
+ case TokenNameint:
+ case TokenNameinterface:
+ case TokenNamelong:
+ case TokenNamenative:
+ case TokenNamenew:
+ case TokenNamenull:
+ case TokenNameprivate:
+ case TokenNameprotected:
+ case TokenNamepublic:
+ case TokenNamereturn:
+ case TokenNameshort:
+ case TokenNamestatic:
+ case TokenNamestrictfp:
+ case TokenNamesuper:
+ case TokenNameswitch:
+ case TokenNamesynchronized:
+ case TokenNamethis:
+ case TokenNamethrow:
+ case TokenNametransient:
+ case TokenNametrue:
+ case TokenNametry:
+ case TokenNamevoid:
+ case TokenNamevolatile:
+ case TokenNamewhile:
+ case TokenNameIntegerLiteral: // ??!
+ case TokenNameLongLiteral:
+ case TokenNameFloatingPointLiteral:
+ case TokenNameDoubleLiteral:
+ case TokenNameCharacterLiteral:
+ case TokenNameStringLiteral:
+ case TokenNamePLUS_PLUS:
+ case TokenNameMINUS_MINUS:
+ case TokenNameLESS:
+ case TokenNameLPAREN:
+ case TokenNameLBRACE:
+ case TokenNameAT:
+ case TokenNameBeginLambda:
+ case TokenNameAT308:
+ if(getVanguardParser().parse(Goal.BlockStatementoptGoal) == VanguardParser.SUCCESS)
+ return token;
+ break;
+ case TokenNameSEMICOLON:
+ case TokenNameEOF:
+ return token;
+ case TokenNameRBRACE: // simulate empty statement.
+ ungetToken(token);
+ return TokenNameSEMICOLON;
+ default:
+ break;
+ }
+ }
+}
}

Back to the top