Bug 473654 - Content assist hangs
Change-Id: I85b667d23e394e6d6a3e97d12a7ab0b023ae3977
Signed-off-by: Stephan Herrmann <stephan.herrmann@berlin.de>
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 7ffbefb..59f8068 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
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2013, 2016 IBM Corporation and others.
+ * Copyright (c) 2013, 2018 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
@@ -2134,13 +2134,13 @@
" void test() {\n" +
" new Stepper<Void>((<no type> r) -> {\n" +
"}) {\n" +
- " private void step1() {\n" +
- " Y y;\n" +
- " y.request((<no type> response) -> {\n" +
+ " private void step1() {\n" +
+ " Y y;\n" +
+ " y.request((<no type> response) -> {\n" +
" <CompleteOnName:response.>;\n" +
"});\n" +
- " }\n" +
- " };\n" +
+ " }\n" +
+ "}.run();\n" +
" }\n" +
"}\n";
@@ -2261,7 +2261,8 @@
" {\n" +
" goo((<no type> xyz) -> {\n" +
" X xLambdaLocal;\n" +
- " System.out.println(<CompleteOnName:xyz.>);\n" +
+ " System.out.println(<CompleteOnName:xyz.>);\n" +
+ " ;\n" +
"});\n" +
" }\n" +
" }\n" +
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 07728ab..1c87d9d 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
@@ -212,6 +212,32 @@
"argument[LOCAL_VARIABLE_REF]{argument, null, I, argument, null, " + (R_DEFAULT + 22) + "}",
requestor.getResults());
}
+// corrected syntax (expr w/o enclosing {}) should not give worse result
+public void test006b() throws JavaModelException {
+ this.workingCopies = new ICompilationUnit[1];
+ this.workingCopies[0] = getWorkingCopy(
+ "/Completion/src/X.java",
+ "interface I {\n" +
+ " int foo(int x);\n" +
+ "}\n" +
+ "public class X {\n" +
+ " void go() {\n" +
+ " I i = (argument) -> \n" +
+ " argument == 0 ? arg\n" +
+ " ;\n" +
+ " }\n" +
+ "}\n");
+
+ CompletionTestsRequestor2 requestor = new CompletionTestsRequestor2(true);
+ requestor.allowAllRequiredProposals();
+ String str = this.workingCopies[0].getSource();
+ String completeBehind = "arg";
+ int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length();
+ this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner);
+ assertResults(
+ "argument[LOCAL_VARIABLE_REF]{argument, null, Ljava.lang.Object;, argument, null, " + (R_DEFAULT + 21) + "}", // FIXME should be "I" and 22 like test006
+ requestor.getResults());
+}
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=405126, [1.8][code assist] Lambda parameters incorrectly recovered as fields.
public void test007() throws JavaModelException {
this.workingCopies = new ICompilationUnit[1];
@@ -1284,6 +1310,33 @@
assertResults("getClass[METHOD_REF]{getClass(), Ljava.lang.Object;, ()Ljava.lang.Class<*>;, null, null, getClass, null, [203, 206], " + (R_DEFAULT + 30) + "}\n" +
"getLastName[METHOD_REF]{getLastName(), LPerson;, ()Ljava.lang.String;, null, null, getLastName, null, [203, 206], " + (R_DEFAULT + 60) + "}", requestor.getResults());
}
+//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 test428735f() throws JavaModelException {
+ // copy of test428735e with corrected syntax
+ this.workingCopies = new ICompilationUnit[1];
+ this.workingCopies[0] = getWorkingCopy(
+ "/Completion/src/X.java",
+ "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");
+
+ CompletionTestsRequestor2 requestor = new CompletionTestsRequestor2(true, true, true, false);
+ requestor.allowAllRequiredProposals();
+ String str = this.workingCopies[0].getSource();
+ String completeBehind = "x.get";
+ int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length();
+ this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner);
+ assertResults("getClass[METHOD_REF]{getClass(), Ljava.lang.Object;, ()Ljava.lang.Class<*>;, null, null, getClass, null, [203, 206], " + (R_DEFAULT + 30) + "}\n" +
+ "getLastName[METHOD_REF]{getLastName(), LPerson;, ()Ljava.lang.String;, null, null, getLastName, null, [203, 206], " + (R_DEFAULT + 60) + "}", requestor.getResults());
+}
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=402081, [1.8][code complete] No proposals while completing at method/constructor references
public void test402081() throws JavaModelException {
this.workingCopies = new ICompilationUnit[1];
@@ -3112,4 +3165,100 @@
"SuperSuper[TYPE_REF]{SuperSuper, p, Lp.SuperSuper;, null, null, " + (R_DEFAULT + R_RESOLVED + R_INTERESTING + R_CASE + R_UNQUALIFIED + R_NON_RESTRICTED + R_CLASS) + "}",
requestor.getResults());
}
+public void testBug473654() throws Exception {
+ this.workingCopies = new ICompilationUnit[1];
+ this.workingCopies[0] = getWorkingCopy(
+ "/Completion/src/Foo.java",
+ "class Foo {\n" +
+ " Runnable foo() {\n" +
+ " return () -> new Object() {\n" +
+ " // press Ctrl+Space before the comment\n" +
+ " };\n" +
+ " }\n" +
+ " \n" +
+ " static void bar() { /**/ }\n" +
+ "}\n");
+
+ CompletionTestsRequestor2 requestor = new CompletionTestsRequestor2(true);
+ String str = this.workingCopies[0].getSource();
+ String completeBefore = "// press Ctrl+Space before the comment";
+ int cursorLocation = str.indexOf(completeBefore);
+ this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner);
+
+ int keywordRelevance = R_DEFAULT + R_RESOLVED + R_INTERESTING + R_NON_RESTRICTED + R_CASE;
+ int overrideRelevance = R_DEFAULT + R_RESOLVED + R_INTERESTING + R_NON_RESTRICTED + R_CASE + R_METHOD_OVERIDE;
+
+ assertResults(
+ "[POTENTIAL_METHOD_DECLARATION]{, LObject;, ()V, , null, "+(R_DEFAULT + R_RESOLVED + R_INTERESTING + R_NON_RESTRICTED)+"}\n" +
+ "abstract[KEYWORD]{abstract, null, null, abstract, null, "+keywordRelevance+"}\n" +
+ "class[KEYWORD]{class, null, null, class, null, "+keywordRelevance+"}\n" +
+ "enum[KEYWORD]{enum, null, null, enum, null, "+keywordRelevance+"}\n" +
+ "final[KEYWORD]{final, null, null, final, null, "+keywordRelevance+"}\n" +
+ "interface[KEYWORD]{interface, null, null, interface, null, "+keywordRelevance+"}\n" +
+ "native[KEYWORD]{native, null, null, native, null, "+keywordRelevance+"}\n" +
+ "private[KEYWORD]{private, null, null, private, null, "+keywordRelevance+"}\n" +
+ "protected[KEYWORD]{protected, null, null, protected, null, "+keywordRelevance+"}\n" +
+ "public[KEYWORD]{public, null, null, public, null, "+keywordRelevance+"}\n" +
+ "static[KEYWORD]{static, null, null, static, null, "+keywordRelevance+"}\n" +
+ "strictfp[KEYWORD]{strictfp, null, null, strictfp, null, "+keywordRelevance+"}\n" +
+ "synchronized[KEYWORD]{synchronized, null, null, synchronized, null, "+keywordRelevance+"}\n" +
+ "transient[KEYWORD]{transient, null, null, transient, null, "+keywordRelevance+"}\n" +
+ "volatile[KEYWORD]{volatile, null, null, volatile, null, "+keywordRelevance+"}\n" +
+ "Foo[TYPE_REF]{Foo, , LFoo;, null, null, "+(R_DEFAULT + R_RESOLVED + R_INTERESTING + R_NON_RESTRICTED + R_CASE + R_UNQUALIFIED)+"}\n" +
+ "clone[METHOD_DECLARATION]{protected Object clone() throws CloneNotSupportedException, Ljava.lang.Object;, ()Ljava.lang.Object;, clone, null, "+overrideRelevance+"}\n" +
+ "equals[METHOD_DECLARATION]{public boolean equals(Object obj), Ljava.lang.Object;, (Ljava.lang.Object;)Z, equals, (obj), "+overrideRelevance+"}\n" +
+ "finalize[METHOD_DECLARATION]{protected void finalize() throws Throwable, Ljava.lang.Object;, ()V, finalize, null, "+overrideRelevance+"}\n" +
+ "hashCode[METHOD_DECLARATION]{public int hashCode(), Ljava.lang.Object;, ()I, hashCode, null, "+overrideRelevance+"}\n" +
+ "toString[METHOD_DECLARATION]{public String toString(), Ljava.lang.Object;, ()Ljava.lang.String;, toString, null, "+overrideRelevance+"}",
+ requestor.getResults());
+}
+public void testBug537679() throws JavaModelException {
+ this.workingCopies = new ICompilationUnit[1];
+ this.workingCopies[0] = getWorkingCopy(
+ "/Completion/src/p/SuperSuper.java",
+ "import static java.util.stream.Collectors.toList;\n" +
+ "import java.util.List;\n" +
+ "\n" +
+ "public class Test {\n" +
+ " void foo(List<Object> list) {\n" +
+ " bar(list.stream().map(m -> new Object() {\n" +
+ " // here\n" +
+ " }).collect(toList()));\n" +
+ " }\n" +
+ "\n" +
+ " private void bar(List<Object> collect) {\n" +
+ " }\n" +
+ "}\n");
+
+ CompletionTestsRequestor2 requestor = new CompletionTestsRequestor2(true);
+ String str = this.workingCopies[0].getSource();
+ int cursorLocation = str.lastIndexOf("// here");
+ this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner);
+
+ int keywordRelevance = R_DEFAULT + R_RESOLVED + R_INTERESTING + R_NON_RESTRICTED + R_CASE;
+ int overrideRelevance = R_DEFAULT + R_RESOLVED + R_INTERESTING + R_NON_RESTRICTED + R_CASE + R_METHOD_OVERIDE;
+ assertResults(
+ "[POTENTIAL_METHOD_DECLARATION]{, LObject;, ()V, , null, "+(R_DEFAULT + R_RESOLVED + R_INTERESTING + R_NON_RESTRICTED)+"}\n" +
+ "abstract[KEYWORD]{abstract, null, null, abstract, null, "+keywordRelevance+"}\n" +
+ "class[KEYWORD]{class, null, null, class, null, "+keywordRelevance+"}\n" +
+ "enum[KEYWORD]{enum, null, null, enum, null, "+keywordRelevance+"}\n" +
+ "final[KEYWORD]{final, null, null, final, null, "+keywordRelevance+"}\n" +
+ "interface[KEYWORD]{interface, null, null, interface, null, "+keywordRelevance+"}\n" +
+ "native[KEYWORD]{native, null, null, native, null, "+keywordRelevance+"}\n" +
+ "private[KEYWORD]{private, null, null, private, null, "+keywordRelevance+"}\n" +
+ "protected[KEYWORD]{protected, null, null, protected, null, "+keywordRelevance+"}\n" +
+ "public[KEYWORD]{public, null, null, public, null, "+keywordRelevance+"}\n" +
+ "static[KEYWORD]{static, null, null, static, null, "+keywordRelevance+"}\n" +
+ "strictfp[KEYWORD]{strictfp, null, null, strictfp, null, "+keywordRelevance+"}\n" +
+ "synchronized[KEYWORD]{synchronized, null, null, synchronized, null, "+keywordRelevance+"}\n" +
+ "transient[KEYWORD]{transient, null, null, transient, null, "+keywordRelevance+"}\n" +
+ "volatile[KEYWORD]{volatile, null, null, volatile, null, "+keywordRelevance+"}\n" +
+ "Test[TYPE_REF]{Test, p, Lp.Test;, null, null, "+(R_DEFAULT + R_RESOLVED + R_INTERESTING + R_NON_RESTRICTED + R_CASE + R_UNQUALIFIED)+"}\n" +
+ "clone[METHOD_DECLARATION]{protected Object clone() throws CloneNotSupportedException, Ljava.lang.Object;, ()Ljava.lang.Object;, clone, null, "+overrideRelevance+"}\n" +
+ "equals[METHOD_DECLARATION]{public boolean equals(Object obj), Ljava.lang.Object;, (Ljava.lang.Object;)Z, equals, (obj), "+overrideRelevance+"}\n" +
+ "finalize[METHOD_DECLARATION]{protected void finalize() throws Throwable, Ljava.lang.Object;, ()V, finalize, null, "+overrideRelevance+"}\n" +
+ "hashCode[METHOD_DECLARATION]{public int hashCode(), Ljava.lang.Object;, ()I, hashCode, null, "+overrideRelevance+"}\n" +
+ "toString[METHOD_DECLARATION]{public String toString(), Ljava.lang.Object;, ()Ljava.lang.String;, toString, null, "+overrideRelevance+"}",
+ requestor.getResults());
+}
}
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 6174f59..e216946 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
@@ -5720,6 +5720,23 @@
}
return false;
}
+@Override
+protected int actFromTokenOrSynthetic(int previousAct) {
+ int newAct = tAction(previousAct, this.currentToken);
+ if (this.hasError && !this.diet && newAct == ERROR_ACTION && this.currentToken == TerminalTokens.TokenNameEOF) {
+ if (requireExtendedRecovery()) {
+ // during extended recovery, if EOF would be wrong, try a few things to reduce our stacks:
+ for (int tok : RECOVERY_TOKENS) {
+ newAct = tAction(previousAct, tok);
+ if (newAct != ERROR_ACTION) {
+ this.currentToken = tok; // this worked, pretend we really got this from the Scanner
+ return newAct;
+ }
+ }
+ }
+ }
+ return newAct;
+}
protected boolean isInImportStatement() {
return foundToken(K_INSIDE_IMPORT_STATEMENT);
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 15ca186..dee971c 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
@@ -863,12 +863,31 @@
/* might be completing at very end of file (e.g. behind a dot) */
if (this.completionIdentifier == null &&
this.startPosition == this.cursorLocation + 1){
+ this.endOfEmptyToken = this.currentPosition - 1;
this.currentPosition = this.startPosition; // for being detected as empty free identifier
return TokenNameIdentifier;
}
return TokenNameEOF;
}
@Override
+protected int getNextNotFakedToken() throws InvalidInputException {
+ int token;
+ boolean fromUnget = false;
+ if (this.nextToken != TokenNameNotAToken) {
+ token = this.nextToken;
+ this.nextToken = TokenNameNotAToken;
+ fromUnget = true;
+ } else {
+ token = getNextToken();
+ }
+ if (this.currentPosition == this.startPosition) {
+ if (!fromUnget)
+ this.currentPosition++; // on fake completion identifier
+ return -1;
+ }
+ return token;
+}
+@Override
public final void getNextUnicodeChar() throws InvalidInputException {
int temp = this.currentPosition; // the \ is already read
super.getNextUnicodeChar();
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 afc4777..20b9895 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
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2017 IBM Corporation and others.
+ * Copyright (c) 2000, 2018 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
@@ -116,7 +116,7 @@
protected boolean isFirst = false;
public AssistParser snapShot;
- private static final int[] RECOVERY_TOKENS = new int [] { TokenNameSEMICOLON, TokenNameRPAREN,};
+ protected static final int[] RECOVERY_TOKENS = { TokenNameSEMICOLON, TokenNameRPAREN, TokenNameRBRACE, TokenNameRBRACKET};
public AssistParser(ProblemReporter problemReporter) {
@@ -2230,9 +2230,12 @@
// 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) {
+ int extendedEnd = this.scanner.source.length;
+ if (this.referenceContext instanceof AbstractMethodDeclaration)
+ extendedEnd = ((AbstractMethodDeclaration) this.referenceContext).bodyEnd; // no use parsing beyond the method's body end
+ if (this.scanner.eofPosition < extendedEnd) {
shouldStackAssistNode();
- this.scanner.eofPosition = this.scanner.source.length;
+ this.scanner.eofPosition = extendedEnd;
nextToken = getNextToken();
if (automatonWillShift(nextToken, automatonState)) {
this.currentToken = nextToken;
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 fd47b49..fb1b080 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
@@ -810,6 +810,10 @@
public static int tAction(int state, int sym) {
return term_action[term_check[base_action[state]+sym] == sym ? base_action[state] + sym : base_action[state]];
}
+ /** Overridable hook, to allow CompletionParser to synthesize a few trailing tokens at (faked) EOF. */
+ protected int actFromTokenOrSynthetic(int previousAct) {
+ return tAction(previousAct, this.currentToken);
+ }
protected int astLengthPtr;
protected int[] astLengthStack;
@@ -11344,12 +11348,7 @@
do {
try {
this.scanner.lookBack[0] = this.scanner.lookBack[1] = TokenNameNotAToken; // stay clear of the voodoo in the present method
- this.nextIgnoredToken = this.scanner.getNextToken();
- if(this.scanner.currentPosition == this.scanner.startPosition){
- this.scanner.currentPosition++; // on fake completion identifier
- this.nextIgnoredToken = -1;
- }
-
+ this.nextIgnoredToken = this.scanner.getNextNotFakedToken();
} catch(InvalidInputException e){
pos = this.scanner.currentPosition;
} finally {
@@ -11560,7 +11559,7 @@
stackLength);
}
this.stack[this.stateStackTop] = act;
- this.unstackedAct = act = tAction(act, this.currentToken);
+ this.unstackedAct = act = actFromTokenOrSynthetic(act);
if (act == ERROR_ACTION || this.restartRecovery) {
if (DEBUG_AUTOMATON) {
if (this.restartRecovery) {
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 4d2f75e..655e505 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
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2017 IBM Corporation and others.
+ * Copyright (c) 2000, 2018 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
@@ -209,7 +209,7 @@
newEntry6 = 0;
public boolean insideRecovery = false;
int lookBack[] = new int[2]; // fall back to spring forward.
- int nextToken = TokenNameNotAToken; // allows for one token push back, only the most recent token can be reliably ungotten.
+ protected int nextToken = TokenNameNotAToken; // allows for one token push back, only the most recent token can be reliably ungotten.
private VanguardScanner vanguardScanner;
private VanguardParser vanguardParser;
ConflictedParser activeParser = null;
@@ -4942,4 +4942,9 @@
}
}
}
+
+/** Overridable hook, to allow CompletionScanner to hide a faked identifier token. */
+protected int getNextNotFakedToken() throws InvalidInputException {
+ return getNextToken();
+}
}