diff options
| author | ssankaran | 2013-12-13 02:29:23 +0000 |
|---|---|---|
| committer | ssankaran | 2013-12-13 07:25:14 +0000 |
| commit | a98d7d87df415161ba75f53acbdbe8d316ea160c (patch) | |
| tree | 4d0eaf38a577b60e8dcdf7fa232a13916f75ada2 | |
| parent | f338c86815ffb4c165b8024ba4f28cade0b2b75f (diff) | |
| download | eclipse.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
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; + } + } +} } |
