Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorssankaran2014-10-23 12:07:44 +0000
committerssankaran2014-10-23 13:30:59 +0000
commite060e0428f7f92647e77ad8da2c7818eb4c4e3a7 (patch)
tree89b4b8533a22bf7d42adf459dc027ffbc069dbf5
parent027433d18c80815de3076b7fd489422a48401f1a (diff)
downloadeclipse.jdt.core-e060e0428f7f92647e77ad8da2c7818eb4c4e3a7.tar.gz
eclipse.jdt.core-e060e0428f7f92647e77ad8da2c7818eb4c4e3a7.tar.xz
eclipse.jdt.core-e060e0428f7f92647e77ad8da2c7818eb4c4e3a7.zip
Bug 446765 - [1.8][content assist] Completion does not work with both
lambdas and anonymous classes in the picture
-rw-r--r--org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/parser/CompletionParserTest18.java242
-rw-r--r--org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/CompletionTests18.java3
-rw-r--r--org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/InternalExtendedCompletionContext.java5
-rw-r--r--org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/complete/CompletionParser.java25
-rw-r--r--org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/impl/AssistParser.java28
-rw-r--r--org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/select/SelectionParser.java22
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Block.java5
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/LambdaExpression.java2
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/CommitRollbackParser.java23
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/Parser.java25
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/RecoveredBlock.java57
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/RecoveredType.java10
12 files changed, 341 insertions, 106 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 bba49fa8bc..a1cdbef2df 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
@@ -1710,7 +1710,6 @@ public void test430656() {
"diet ast");
}
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=438952, [1.8][content assist] StackOverflowError at org.eclipse.jdt.internal.compiler.ast.SingleTypeReference.traverse(SingleTypeReference.java:108)
-// FIXME: Recovered parse tree isn't quite correct, but is harmless.
public void test438952() {
String string =
"import java.util.function.Supplier;\n" +
@@ -1738,22 +1737,7 @@ public void test438952() {
"class SO {\n" +
" {\n" +
" int Supplier;\n" +
- " new SO() {\n" +
- " {\n" +
- " }\n" +
- " void test() {\n" +
- " <CompleteOnName:>;\n" +
- " }\n" +
- " void test() {\n" +
- " <CompleteOnName:>;\n" +
- " }\n" +
- " };\n" +
" m6 = () -> new SO() {\n" +
- " {\n" +
- " }\n" +
- " void test() {\n" +
- " <CompleteOnName:>;\n" +
- " }\n" +
" void test() {\n" +
" <CompleteOnName:>;\n" +
" }\n" +
@@ -2068,4 +2052,230 @@ public void test430667d() {
expectedReplacedSource,
"diet ast");
}
+// https://bugs.eclipse.org/bugs/show_bug.cgi?id=446765,
+public void test446765() {
+ String string =
+ "class Stepper<T> {\n" +
+ " public interface Step<T> {\n" +
+ " void run();\n" +
+ " }\n" +
+ " public Stepper(Handler<AsyncResult<T>> handler) {}\n" +
+ "\n" +
+ " @SafeVarargs\n" +
+ " public final void run(Step<T> ... steps) {}\n" +
+ "}\n" +
+ "interface AsyncResult<T> {}\n" +
+ "interface Handler<E> {\n" +
+ " void handle(E event);\n" +
+ "}\n" +
+ "class Z {\n" +
+ " void foo() {}\n" +
+ "}\n" +
+ "interface I {\n" +
+ " void foo(Z z);\n" +
+ "}\n" +
+ "class Y {\n" +
+ " void request(I i) {}\n" +
+ "}\n" +
+ "public class X {\n" +
+ " void test() {\n" +
+ " new Stepper<Void>(r -> {}) {\n" +
+ " private void step1() {\n" +
+ " Y y = new Y();\n" +
+ " y.request(response -> {\n" +
+ " if (response.)\n" +
+ " });\n" +
+ " }\n" +
+ " }.run(); \n" +
+ " } \n" +
+ "}\n";
+
+ String completeBehind = "response.";
+ int cursorLocation = string.indexOf(completeBehind) + completeBehind.length() - 1;
+
+ String expectedCompletionNodeToString = "<CompleteOnName:response.>";
+ String expectedParentNodeToString = "if (<CompleteOnName:response.>)\n" +
+ " ;";
+ String completionIdentifier = "";
+ String expectedReplacedSource = "response.";
+ String expectedUnitDisplayString =
+ "class Stepper<T> {\n" +
+ " public interface Step<T> {\n" +
+ " void run();\n" +
+ " }\n" +
+ " public Stepper(Handler<AsyncResult<T>> handler) {\n" +
+ " }\n" +
+ " public final @SafeVarargs void run(Step<T>... steps) {\n" +
+ " }\n" +
+ "}\n" +
+ "interface AsyncResult<T> {\n" +
+ "}\n" +
+ "interface Handler<E> {\n" +
+ " void handle(E event);\n" +
+ "}\n" +
+ "class Z {\n" +
+ " Z() {\n" +
+ " }\n" +
+ " void foo() {\n" +
+ " }\n" +
+ "}\n" +
+ "interface I {\n" +
+ " void foo(Z z);\n" +
+ "}\n" +
+ "class Y {\n" +
+ " Y() {\n" +
+ " }\n" +
+ " void request(I i) {\n" +
+ " }\n" +
+ "}\n" +
+ "public class X {\n" +
+ " public X() {\n" +
+ " }\n" +
+ " void test() {\n" +
+ " new Stepper<Void>((<no type> r) -> {\n" +
+ "}) {\n" +
+ " private void step1() {\n" +
+ " Y y;\n" +
+ " y.request((<no type> response) -> {\n" +
+ " <CompleteOnName:response.>;\n" +
+ "});\n" +
+ " }\n" +
+ " };\n" +
+ " }\n" +
+ "}\n";
+
+ checkMethodParse(
+ string.toCharArray(),
+ cursorLocation,
+ expectedCompletionNodeToString,
+ expectedParentNodeToString,
+ expectedUnitDisplayString,
+ completionIdentifier,
+ expectedReplacedSource,
+ "diet ast");
+}
+// https://bugs.eclipse.org/bugs/show_bug.cgi?id=428735, [1.8][assist] Missing completion proposals inside lambda body expression - other than first token
+public void test428735h() {
+ String string =
+ "import java.util.List;\n" +
+ "class Person {\n" +
+ " String getLastName() { return null; }\n" +
+ "}\n" +
+ "public class X {\n" +
+ " void test2(List<Person> people) {\n" +
+ " people.sort((x,y) -> {\n" +
+ " if (true) return \"\" + x.get); \n" +
+ " else return \"\";\n" +
+ " }\n" +
+ "}\n";
+
+ String completeBehind = "x.get";
+ int cursorLocation = string.indexOf(completeBehind) + completeBehind.length() - 1;
+
+ String expectedCompletionNodeToString = "<CompleteOnName:x.get>";
+ String expectedParentNodeToString = "(\"\" + <CompleteOnName:x.get>)";
+ String completionIdentifier = "get";
+ String expectedReplacedSource = "x.get";
+ String expectedUnitDisplayString =
+ "import java.util.List;\n" +
+ "class Person {\n" +
+ " Person() {\n" +
+ " }\n" +
+ " String getLastName() {\n" +
+ " }\n" +
+ "}\n" +
+ "public class X {\n" +
+ " public X() {\n" +
+ " }\n" +
+ " void test2(List<Person> people) {\n" +
+ " people.sort((<no type> x, <no type> y) -> {\n" +
+ " if (true)\n" +
+ " return (\"\" + <CompleteOnName:x.get>);\n" +
+ " ;\n" +
+ " return \"\";\n" +
+ "});\n" +
+ " }\n" +
+ "}\n";
+
+ checkMethodParse(
+ string.toCharArray(),
+ cursorLocation,
+ expectedCompletionNodeToString,
+ expectedParentNodeToString,
+ expectedUnitDisplayString,
+ completionIdentifier,
+ expectedReplacedSource,
+ "diet ast");
+}
+// https://bugs.eclipse.org/bugs/show_bug.cgi?id=422468, [1.8][assist] Code assist issues with type elided lambda parameters
+public void test422468() { // computing visible elements in lambda scope.
+ String string =
+ "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";
+ String completeBehind = "xyz.";
+ int cursorLocation = string.indexOf(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(X x);\n" +
+ "}\n" +
+ "public class X {\n" +
+ " static X xField;\n" +
+ " public X() {\n" +
+ " }\n" +
+ " <clinit>() {\n" +
+ " }\n" +
+ " static X goo(String s) {\n" +
+ " }\n" +
+ " static void goo(I i) {\n" +
+ " }\n" +
+ " public static void main(String[] args) {\n" +
+ " X xLocal;\n" +
+ " {\n" +
+ " {\n" +
+ " goo((<no type> xyz) -> {\n" +
+ " X xLambdaLocal;\n" +
+ " System.out.println(<CompleteOnName:xyz.>);\n" +
+ "});\n" +
+ " }\n" +
+ " }\n" +
+ " }\n" +
+ "}\n";
+
+ checkMethodParse(
+ string.toCharArray(),
+ cursorLocation,
+ expectedCompletionNodeToString,
+ expectedParentNodeToString,
+ expectedUnitDisplayString,
+ completionIdentifier,
+ expectedReplacedSource,
+ "diet ast");
+}
}
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 c4b5da913e..0df34f1fd9 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
@@ -689,8 +689,9 @@ public void test018a() throws JavaModelException { // computing visible elements
"expectedTypesKeys={Z,C,I,J,F,D,[C,Ljava/lang/String;,Ljava/lang/Object;}\n" +
"completion token location=UNKNOWN\n" +
"visibleElements={\n" +
+ " xLambdaLocal [in main(String[]) [in X [in [Working copy] X.java [in <default> [in src [in Completion]]]]]],\n" +
+ " xyz [in main(String[]) [in X [in [Working copy] X.java [in <default> [in src [in Completion]]]]]],\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());
diff --git a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/InternalExtendedCompletionContext.java b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/InternalExtendedCompletionContext.java
index 6a02d3bb87..d74665ff65 100644
--- a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/InternalExtendedCompletionContext.java
+++ b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/InternalExtendedCompletionContext.java
@@ -19,6 +19,7 @@ import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.ITypeRoot;
import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.core.Signature;
import org.eclipse.jdt.core.WorkingCopyOwner;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.internal.codeassist.complete.CompletionNodeDetector;
@@ -261,7 +262,7 @@ public class InternalExtendedCompletionContext {
LocalDeclaration local = binding.declaration;
JavaElement parent = null;
- ReferenceContext referenceContext = binding.declaringScope.referenceContext();
+ ReferenceContext referenceContext = binding.declaringScope.isLambdaSubscope() ? binding.declaringScope.namedMethodScope().referenceContext() : binding.declaringScope.referenceContext();
if (referenceContext instanceof AbstractMethodDeclaration) {
AbstractMethodDeclaration methodDeclaration = (AbstractMethodDeclaration) referenceContext;
parent = this.getJavaElementOfCompilationUnit(methodDeclaration, methodDeclaration.binding);
@@ -281,7 +282,7 @@ public class InternalExtendedCompletionContext {
local.declarationSourceEnd,
local.sourceStart,
local.sourceEnd,
- Util.typeSignature(local.type),
+ local.type == null ? Signature.createTypeSignature(binding.type.readableName(), true) : Util.typeSignature(local.type),
binding.declaration.annotations,
local.modifiers,
local.getKind() == AbstractVariableDeclaration.PARAMETER);
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 26f51b64b0..c745f5e09a 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
@@ -2426,9 +2426,18 @@ protected void consumeEmptyStatement() {
decide whether to call contactNodeLists. See Parser.consumeBlockStatement(s)
*/
if (this.shouldStackAssistNode && this.assistNode != null)
- this.astStack[this.astPtr] = this.assistNode;
+ this.astStack[this.astPtr] = this.assistNodeParent instanceof MessageSend ? this.assistNodeParent : this.assistNode;
this.shouldStackAssistNode = false;
}
+@Override
+protected void consumeBlockStatement() {
+ super.consumeBlockStatement();
+ if (this.shouldStackAssistNode && this.assistNode != null) {
+ Statement stmt = (Statement) this.astStack[this.astPtr];
+ if (stmt.sourceStart <= this.assistNode.sourceStart && stmt.sourceEnd >= this.assistNode.sourceEnd)
+ this.shouldStackAssistNode = false;
+ }
+}
protected void consumeEnhancedForStatement() {
super.consumeEnhancedForStatement();
@@ -2608,8 +2617,6 @@ protected void consumeExitVariableWithInitialization() {
if (this.currentElement != null) {
this.restartRecovery = true;
}
- if (!isInsideMethod())
- popElement(K_FIELD_INITIALIZER_DELIMITER);
}
}
protected void consumeExitVariableWithoutInitialization() {
@@ -3269,6 +3276,13 @@ protected void consumeLabel() {
pushOnLabelStack(this.identifierStack[this.identifierPtr]);
this.pushOnElementStack(K_LABEL, this.labelPtr);
}
+@Override
+protected void consumeLambdaExpression() {
+ super.consumeLambdaExpression();
+ Expression expression = this.expressionStack[this.expressionPtr];
+ if (this.assistNode == null || !(this.assistNode.sourceStart >= expression.sourceStart && this.assistNode.sourceEnd <= expression.sourceEnd))
+ popElement(K_LAMBDA_EXPRESSION_DELIMITER);
+}
protected void consumeMarkerAnnotation(boolean isTypeAnnotation) {
if (this.topKnownElementKind(COMPLETION_OR_ASSIST_PARSER) == K_BETWEEN_ANNOTATION_NAME_AND_RPAREN &&
(this.topKnownElementInfo(COMPLETION_OR_ASSIST_PARSER) & ANNOTATION_NAME_COMPLETION) != 0 ) {
@@ -5153,6 +5167,11 @@ protected void shouldStackAssistNode() {
this.shouldStackAssistNode = true;
}
+@Override
+protected boolean assistNodeNeedsStacking() {
+ return this.shouldStackAssistNode;
+}
+
public String toString() {
StringBuffer buffer = new StringBuffer();
buffer.append("elementKindStack : int[] = {"); //$NON-NLS-1$
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 223e5f334a..9d083f4b7f 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
@@ -232,7 +232,7 @@ public RecoveredElement buildInitialRecoveryState(){
break;
}
if (this.blockStarts[j] != lastStart){ // avoid multiple block if at same position
- block = new Block(0, lastNode instanceof LambdaExpression);
+ block = new Block(0);
block.sourceStart = lastStart = this.blockStarts[j];
element = element.add(block, 1);
}
@@ -333,8 +333,11 @@ public RecoveredElement buildInitialRecoveryState(){
this.lastCheckPoint = importRef.declarationSourceEnd + 1;
}
}
- if (this.currentToken == TokenNameRBRACE && !isIndirectlyInsideLambdaExpression()) {
- this.currentToken = 0; // closing brace has already been taken care of
+ if (this.currentToken == TokenNameRBRACE) {
+ if (isIndirectlyInsideLambdaExpression())
+ this.ignoreNextClosingBrace = true;
+ else
+ this.currentToken = 0; // closing brace has already been taken care of
}
/* might need some extra block (after the last reduced node) */
@@ -344,7 +347,7 @@ public RecoveredElement buildInitialRecoveryState(){
for (int j = blockIndex; j <= this.realBlockPtr; j++){
if (this.blockStarts[j] >= 0) {
if ((this.blockStarts[j] < pos || createLambdaBlock) && (this.blockStarts[j] != lastStart)){ // avoid multiple block if at same position
- block = new Block(0, createLambdaBlock);
+ block = new Block(0);
block.sourceStart = lastStart = this.blockStarts[j];
element = element.add(block, 1);
createLambdaBlock = false;
@@ -477,14 +480,15 @@ protected boolean triggerRecoveryUponLambdaClosure(Statement statement, boolean
See also that this concern does not arise in the case of field/local initialization since the initializer is replaced with full tree by consumeExitVariableWithInitialization.
*/
RecoveredBlock recoveredBlock = (RecoveredBlock) (this.currentElement instanceof RecoveredBlock ? this.currentElement :
- (this.currentElement.parent instanceof RecoveredBlock) ? this.currentElement.parent : null);
+ (this.currentElement.parent instanceof RecoveredBlock) ? this.currentElement.parent :
+ this.currentElement instanceof RecoveredMethod ? ((RecoveredMethod) this.currentElement).methodBody : null);
if (recoveredBlock != null) {
RecoveredStatement recoveredStatement = recoveredBlock.statementCount > 0 ? recoveredBlock.statements[recoveredBlock.statementCount - 1] : null;
ASTNode parseTree = recoveredStatement != null ? recoveredStatement.updatedStatement(0, new HashSet()) : null;
if (parseTree != null) {
if ((parseTree.sourceStart == 0 || parseTree.sourceEnd == 0) || (parseTree.sourceStart >= statementStart && parseTree.sourceEnd <= statementEnd)) {
- recoveredBlock.statements[--recoveredBlock.statementCount] = null;
- this.currentElement = recoveredBlock;
+ recoveredBlock.statements[recoveredBlock.statementCount - 1] = new RecoveredStatement(statement, recoveredBlock, 0);
+ statement = null;
} else if (recoveredStatement instanceof RecoveredLocalVariable && statement instanceof Expression) {
RecoveredLocalVariable local = (RecoveredLocalVariable) recoveredStatement;
if (local.localDeclaration != null && local.localDeclaration.initialization != null) {
@@ -793,6 +797,7 @@ protected void consumeRestoreDiet() {
// if we are not in a method (i.e. we were not in a local variable initializer)
// then we are exiting a field initializer
if (!isInsideMethod()) {
+ popUntilElement(K_FIELD_INITIALIZER_DELIMITER);
popElement(K_FIELD_INITIALIZER_DELIMITER);
}
}
@@ -1697,12 +1702,7 @@ protected void popElement(int kind) {
return;
int stackPointer = this.elementPtr;
-
- if (this.elementKindStack[stackPointer] == K_LAMBDA_EXPRESSION_DELIMITER) {
- if (kind == K_FIELD_INITIALIZER_DELIMITER) // wait until lambda is reduced.
- return;
- }
-
+
if (kind != K_LAMBDA_EXPRESSION_DELIMITER) {
while (this.elementKindStack[stackPointer] == K_LAMBDA_EXPRESSION_DELIMITER) {
stackPointer --;
@@ -1887,6 +1887,8 @@ protected int resumeAfterRecovery() {
return mode;
// else fall through and RESTART
} else {
+ if (this.currentToken == TokenNameLBRACE)
+ this.ignoreNextOpeningBrace = true; // already accounted for in recovery token check.
return RESUME;
}
}
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 196826481f..d72c51aabd 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
@@ -118,8 +118,14 @@ protected void attachOrphanCompletionNode(){
this.currentElement = this.currentElement.add(statement, 0);
}
}
- if (!isIndirectlyInsideLambdaExpression())
+ if (isIndirectlyInsideLambdaExpression()) {
+ if (this.currentToken == TokenNameLBRACE)
+ this.ignoreNextOpeningBrace = true;
+ else if (this.currentToken == TokenNameRBRACE)
+ this.ignoreNextClosingBrace = true;
+ } else {
this.currentToken = 0; // given we are not on an eof, we do not want side effects caused by looked-ahead token
+ }
}
}
private void buildMoreCompletionContext(Expression expression) {
@@ -552,8 +558,10 @@ protected void consumeEnterAnonymousClassBody(boolean qualified) {
if (!this.diet){
this.restartRecovery = true; // force to restart in recovery mode
this.lastIgnoredToken = -1;
- if (!isIndirectlyInsideLambdaExpression())
- this.currentToken = 0; // opening brace already taken into account
+ if (isIndirectlyInsideLambdaExpression())
+ this.ignoreNextOpeningBrace = true;
+ else
+ this.currentToken = 0; // opening brace already taken into account.
this.hasReportedError = true;
}
@@ -563,8 +571,10 @@ protected void consumeEnterAnonymousClassBody(boolean qualified) {
if (this.currentElement != null){
this.lastCheckPoint = anonymousType.bodyStart;
this.currentElement = this.currentElement.add(anonymousType, 0);
- if (!isIndirectlyInsideLambdaExpression())
- this.currentToken = 0; // opening brace already taken into account
+ if (isIndirectlyInsideLambdaExpression())
+ this.ignoreNextOpeningBrace = true;
+ else
+ this.currentToken = 0; // opening brace already taken into account.
this.lastIgnoredToken = -1;
}
}
@@ -769,6 +779,8 @@ protected void consumeLambdaExpression() {
this.expressionStack[this.expressionPtr] = new SelectionOnLambdaExpression(expression);
}
}
+ if (!(this.selectionStart >= expression.sourceStart && this.selectionEnd <= expression.sourceEnd))
+ popElement(K_LAMBDA_EXPRESSION_DELIMITER);
}
@Override
protected void consumeReferenceExpression(ReferenceExpression referenceExpression) {
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 19c912c2a0..e7af82fa5f 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
@@ -27,12 +27,7 @@ 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 cc20877713..58faea9258 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
@@ -120,7 +120,7 @@ public class LambdaExpression extends FunctionalExpression implements ReferenceC
private Set thrownExceptions;
public char[] text; // source representation of the lambda.
private static final SyntheticArgumentBinding [] NO_SYNTHETIC_ARGUMENTS = new SyntheticArgumentBinding[0];
- private static final Block NO_BODY = new Block(0, true);
+ private static final Block NO_BODY = new Block(0);
public LambdaExpression(CompilationResult compilationResult, boolean assistNode, boolean requiresGenericSignature) {
super(compilationResult);
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
index ec3036dcae..ac09e687d5 100644
--- 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
@@ -56,28 +56,28 @@ public abstract class CommitRollbackParser implements TerminalTokens, ParserBasi
// 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.currentToken == TokenNameEOF) {
if (this.scanner.eofPosition < this.scanner.source.length) {
- atCompletionSite = true;
+ shouldStackAssistNode();
this.scanner.eofPosition = this.scanner.source.length;
nextToken = getNextToken();
if (automatonWillShift(nextToken, automatonState)) {
this.currentToken = nextToken;
return RESUME;
}
+ this.scanner.ungetToken(nextToken); // spit out what has been bitten more than we can chew.
} else {
- nextToken = TokenNameEOF;
+ return HALT; // don't know how to proceed.
}
} else {
nextToken = this.currentToken;
+ this.scanner.ungetToken(nextToken);
+ if (nextToken == TokenNameRBRACE)
+ ignoreNextClosingBrace(); // having ungotten it, recoveryTokenCheck will see this again.
}
- 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)) {
@@ -90,15 +90,22 @@ public abstract class CommitRollbackParser implements TerminalTokens, ParserBasi
return RESTART;
this.copyState(this.snapShot);
- if (atCompletionSite) {
+ if (assistNodeNeedsStacking()) {
this.currentToken = TokenNameSEMICOLON;
- shouldStackAssistNode();
return RESUME;
}
this.currentToken = this.scanner.fastForward(unused);
return RESUME;
}
+ protected void ignoreNextClosingBrace() {
+ return;
+ }
+
+ protected boolean assistNodeNeedsStacking() {
+ return false;
+ }
+
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 5d9ef401a6..8c4d9d8eb2 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
@@ -942,6 +942,7 @@ public class Parser extends CommitRollbackParser implements ConflictedParser, Op
protected int identifierPtr;
protected char[][] identifierStack;
protected boolean ignoreNextOpeningBrace;
+ protected boolean ignoreNextClosingBrace;
//positions , dimensions , .... (int stacks)
protected int intPtr;
@@ -1539,7 +1540,9 @@ protected void consumeAllocationHeader() {
this.lastCheckPoint = anonymousType.bodyStart = this.scanner.currentPosition;
this.currentElement = this.currentElement.add(anonymousType, 0);
this.lastIgnoredToken = -1;
- if (!isIndirectlyInsideLambdaExpression())
+ if (isIndirectlyInsideLambdaExpression())
+ this.ignoreNextOpeningBrace = true;
+ else
this.currentToken = 0; // opening brace already taken into account
return;
}
@@ -3473,7 +3476,9 @@ protected void consumeEnterAnonymousClassBody(boolean qualified) {
this.lastCheckPoint = anonymousType.bodyStart;
this.currentElement = this.currentElement.add(anonymousType, 0);
if (!(this.currentElement instanceof RecoveredAnnotation)) {
- if (!isIndirectlyInsideLambdaExpression())
+ if (isIndirectlyInsideLambdaExpression())
+ this.ignoreNextOpeningBrace = true;
+ else
this.currentToken = 0; // opening brace already taken into account
} else {
this.ignoreNextOpeningBrace = true;
@@ -3675,8 +3680,10 @@ protected void consumeEnumConstantHeader() {
this.currentElement = this.currentElement.add(anonymousType, 0);
this.lastCheckPoint = anonymousType.bodyStart;
this.lastIgnoredToken = -1;
- if (!isIndirectlyInsideLambdaExpression())
- this.currentToken = 0; // opening brace already taken into account
+ if (isIndirectlyInsideLambdaExpression())
+ this.ignoreNextOpeningBrace = true;
+ else
+ this.currentToken = 0; // opening brace already taken into account
} else {
if(this.currentToken == TokenNameSEMICOLON) {
RecoveredType currentType = currentRecoveryType();
@@ -8020,7 +8027,6 @@ protected void consumeLambdaExpression() {
body.sourceStart = oldBody.sourceStart;
body.sourceEnd = oldBody.sourceEnd;
}
- ((Block) body).lambdaBody = true; // for consistency's sakes.
}
LambdaExpression lexp = (LambdaExpression) this.astStack[this.astPtr--];
@@ -10543,6 +10549,11 @@ public boolean hasLeadingTagComment(char[] commentPrefixTag, int rangeEnd) {
}
return false;
}
+
+@Override
+protected void ignoreNextClosingBrace() {
+ this.ignoreNextClosingBrace = true;
+}
protected void ignoreExpressionAssignment() {
// Assignment ::= InvalidArrayInitializerAssignement
// encoded operator would be: this.intStack[this.intPtr]
@@ -12151,6 +12162,10 @@ public void recoveryTokenCheck() {
break;
case TokenNameRBRACE :
+ if (this.ignoreNextClosingBrace) {
+ this.ignoreNextClosingBrace = false;
+ break;
+ }
this.rBraceStart = this.scanner.startPosition - 1;
this.rBraceEnd = this.scanner.currentPosition - 1;
this.endPosition = flushCommentsDefinedPriorTo(this.rBraceEnd);
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 8638c9bafd..58c2ef4df9 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
@@ -18,7 +18,6 @@ import org.eclipse.jdt.internal.compiler.ast.Argument;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.Block;
import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
-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;
@@ -155,9 +154,6 @@ public RecoveredElement add(Statement stmt, int bracketBalanceValue) {
*/
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)
@@ -292,11 +288,6 @@ 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;
@@ -337,9 +328,19 @@ public Block updatedBlock(int depth, Set knownTypes){
int lastEnd = this.blockDeclaration.sourceStart;
// only collect the non-null updated statements
+ next:
for (int i = 0; i < this.statementCount; i++){
Statement updatedStatement = this.statements[i].updatedStatement(depth, knownTypes);
- if (updatedStatement != null){
+ if (updatedStatement != null) {
+ for (int j = 0; j < i; j++) {
+ if (updatedStatements[j] instanceof LocalDeclaration) {
+ LocalDeclaration local = (LocalDeclaration) updatedStatements[j];
+ if (local.initialization != null) {
+ if (updatedStatement.sourceStart >= local.initialization.sourceStart && updatedStatement.sourceEnd <= local.initialization.sourceEnd)
+ continue next;
+ }
+ }
+ }
updatedStatements[updatedCount++] = updatedStatement;
if (updatedStatement instanceof LocalDeclaration) {
@@ -426,42 +427,6 @@ public void updateParseTree(){
updatedBlock(0, new HashSet());
}
/*
- * Rebuild a flattened block from the nested structure which is in scope
- */
-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;
-
- // only collect the non-null updated statements
- for (int i = 0; i < this.statementCount; i++){
- Statement updatedStatement = this.statements[i].updatedStatement(depth, knownTypes);
- if (updatedStatement != null){
- updatedStatements[updatedCount++] = updatedStatement;
- }
- }
- if (updatedCount == 0) return null; // not interesting block
-
- // resize statement collection if necessary
- if (updatedCount != this.statementCount){
- this.blockDeclaration.statements = new Statement[updatedCount];
- System.arraycopy(updatedStatements, 0, this.blockDeclaration.statements, 0, updatedCount);
- } else {
- this.blockDeclaration.statements = updatedStatements;
- }
-
- return this.blockDeclaration;
-}
-
-/*
* Record a field declaration
*/
public RecoveredElement add(FieldDeclaration fieldDeclaration, int bracketBalanceValue) {
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/RecoveredType.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/RecoveredType.java
index 8638f8a4ed..df8c5a00a4 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/RecoveredType.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/RecoveredType.java
@@ -579,12 +579,20 @@ public TypeDeclaration updatedTypeDeclaration(int depth, Set knownTypes){
this.methods[this.methodCount - 1].methodDeclaration.declarationSourceEnd = bodyEndValue;
this.methods[this.methodCount - 1].methodDeclaration.bodyEnd = bodyEndValue;
}
+ int totalMethods = existingCount;
+ next:
for (int i = 0; i < this.methodCount; i++){
+ for (int j = 0; j < existingCount; j++) {
+ if (methodDeclarations[j] == this.methods[i].methodDeclaration)
+ continue next;
+ }
AbstractMethodDeclaration updatedMethod = this.methods[i].updatedMethodDeclaration(depth, knownTypes);
if (updatedMethod.isConstructor()) hasRecoveredConstructor = true;
if (updatedMethod.isAbstract()) hasAbstractMethods = true;
- methodDeclarations[existingCount + i] = updatedMethod;
+ methodDeclarations[totalMethods ++] = updatedMethod;
}
+ if (totalMethods != methodDeclarations.length)
+ System.arraycopy(methodDeclarations, 0, methodDeclarations = new AbstractMethodDeclaration[totalMethods], 0, totalMethods);
this.typeDeclaration.methods = methodDeclarations;
if(methodDeclarations[methodDeclarations.length - 1].declarationSourceEnd > lastEnd) {
lastEnd = methodDeclarations[methodDeclarations.length - 1].declarationSourceEnd;

Back to the top