diff options
author | ssankaran | 2014-10-23 12:07:44 +0000 |
---|---|---|
committer | ssankaran | 2014-10-23 13:30:59 +0000 |
commit | e060e0428f7f92647e77ad8da2c7818eb4c4e3a7 (patch) | |
tree | 89b4b8533a22bf7d42adf459dc027ffbc069dbf5 | |
parent | 027433d18c80815de3076b7fd489422a48401f1a (diff) | |
download | eclipse.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
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; |