Bug 574912 - [content assist] Content assist in lambda hangs worker
threads or runs into error dialog
reopened at comment 6:
- discard unhelpful literal after empty assist identifier
in order to get real completion proposals
Change-Id: I54024f0a14138cde3112dd1c697cc72b3e06d465
Reviewed-on: https://git.eclipse.org/r/c/jdt/eclipse.jdt.core/+/183344
Tested-by: Stephan Herrmann <stephan.herrmann@berlin.de>
Reviewed-by: Stephan Herrmann <stephan.herrmann@berlin.de>
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/parser/MethodInvocationCompletionTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/parser/MethodInvocationCompletionTest.java
index 4c61320..38dca1c 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/parser/MethodInvocationCompletionTest.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/parser/MethodInvocationCompletionTest.java
@@ -301,20 +301,20 @@
// completeBehind:
"fred(new Runnable() {}, ",
// expectedCompletionNodeToString:
- "<CompleteOnMessageSend:this.fred(new Runnable() {\n})>",
+ "<CompleteOnName:>",
// expectedUnitDisplayString:
"class Bar {\n" +
" Bar() {\n" +
" }\n" +
" void foo() {\n" +
- " <CompleteOnMessageSend:this.fred(new Runnable() {\n" +
- "})>;\n" +
+ " this.fred(new Runnable() {\n" +
+ "}, <CompleteOnName:>, i);\n" +
" }\n" +
"}\n",
// expectedCompletionIdentifier:
"",
// expectedReplacedSource:
- "fred(new Runnable() {}, ",
+ "",
// test name
"<completion just before second parameter, the first parameter being an empty anonymous class>"
);
@@ -364,19 +364,19 @@
// completeBehind:
"fred(",
// expectedCompletionNodeToString:
- "<CompleteOnMessageSend:this.fred()>",
+ "<CompleteOnMessageSend:this.fred(<CompleteOnName:>, 2, i)>",
// expectedUnitDisplayString:
"class Bar {\n" +
" Bar() {\n" +
" }\n" +
" void foo() {\n" +
- " <CompleteOnMessageSend:this.fred()>;\n" +
+ " <CompleteOnMessageSend:this.fred(<CompleteOnName:>, 2, i)>;\n" +
" }\n" +
"}\n",
// expectedCompletionIdentifier:
"",
// expectedReplacedSource:
- "fred(",
+ "fred(1, 2, i)",
// test name
"<completion just before first parameter>"
);
@@ -426,19 +426,19 @@
// completeBehind:
"fred(1, ",
// expectedCompletionNodeToString:
- "<CompleteOnMessageSend:this.fred(1)>",
+ "<CompleteOnName:>",
// expectedUnitDisplayString:
"class Bar {\n" +
" Bar() {\n" +
" }\n" +
" void foo() {\n" +
- " <CompleteOnMessageSend:this.fred(1)>;\n" +
+ " this.fred(1, <CompleteOnName:>, i);\n" +
" }\n" +
"}\n",
// expectedCompletionIdentifier:
"",
// expectedReplacedSource:
- "fred(1, ",
+ "",
// test name
"<completion just before second parameter>"
);
@@ -652,19 +652,19 @@
// completeBehind:
"fred( ",
// expectedCompletionNodeToString:
- "<CompleteOnMessageSend:this.fred()>",
+ "<CompleteOnName:>",
// expectedUnitDisplayString:
"class Bar {\n" +
" Bar() {\n" +
" }\n" +
" void foo() {\n" +
- " <CompleteOnMessageSend:this.fred()>;\n" +
+ " this.fred(<CompleteOnName:>, 2, i);\n" +
" }\n" +
"}\n",
// expectedCompletionIdentifier:
"",
// expectedReplacedSource:
- "fred( ",
+ "",
// test name
"<completion just before first parameter with a space after open parenthesis>"
);
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 b2864a6..342d842 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
@@ -5601,9 +5601,9 @@
int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length();
this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner, new NullProgressMonitor());
- String result = requestor.getResults();
- assertTrue(String.format("Result doesn't contain expected constructor (%s)", result),
- result.contains("Temp.Enclosed<java.lang.String>[ANONYMOUS_CLASS_DECLARATION]{, LTemp$Enclosed<Ljava.lang.String;>;, ()V, null, null, 39}"));
+ assertResults(
+ "Temp.Enclosed<>[ANONYMOUS_CLASS_DECLARATION]{, LTemp$Enclosed<>;, ()V, null, null, 39}",
+ requestor.getResults());
}
public void testBug563020_lambdaWithMethodRef_overloadedMethodRef_expectCompletions() throws JavaModelException {
this.workingCopies = new ICompilationUnit[1];
@@ -5926,12 +5926,13 @@
public void testBug574912_comment6() throws JavaModelException {
this.workingCopies = new ICompilationUnit[1];
this.workingCopies[0] = getWorkingCopy(
- "Completion/src/LambdaFreeze.java",
+ "Completion/src/LambdaFreeze2.java",
"import java.util.Calendar;\n" +
"import java.util.Date;\n" +
"import java.util.function.Supplier;\n" +
"\n" +
"public class LambdaFreeze2 {\n" +
+ " static int num = 13;\n" +
"\n" +
" public static final Supplier<Date> SUPPLIER = () -> {\n" +
" Calendar calendar = Calendar.getInstance();\n" +
@@ -5947,7 +5948,35 @@
int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length();
this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner);
String result = requestor.getResults();
- assertResults("",
+ assertResults("getMinimum[METHOD_REF]{, Ljava.util.Calendar;, (I)I, getMinimum, (arg0), 86}",
+ result);
+}
+public void testBug574912_comment6b() throws JavaModelException {
+ this.workingCopies = new ICompilationUnit[1];
+ this.workingCopies[0] = getWorkingCopy(
+ "Completion/src/LambdaFreeze2.java",
+ "import java.util.Calendar;\n" +
+ "import java.util.Date;\n" +
+ "import java.util.function.Supplier;\n" +
+ "\n" +
+ "public class LambdaFreeze2 {\n" +
+ " static int xyz = 13;\n" +
+ "\n" +
+ " public static final Supplier<Date> SUPPLIER = () -> {\n" +
+ " Calendar calendar = Calendar.getInstance();\n" +
+ " calendar.set(Calendar.ALL_STYLES, calendar.getMinimum(xy0));\n" + // once we have a non-empty assist id, use it!
+ " return calendar.getTime();\n" +
+ " };\n" +
+ "}\n");
+
+ CompletionTestsRequestor2 requestor = new CompletionTestsRequestor2(true);
+ requestor.allowAllRequiredProposals();
+ String str = this.workingCopies[0].getSource();
+ String completeBehind = "calendar.getMinimum(xy";
+ int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length();
+ this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner);
+ String result = requestor.getResults();
+ assertResults("xyz[FIELD_REF]{xyz, LLambdaFreeze2;, I, xyz, null, 82}",
result);
}
}
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 690f31d..277ea02 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
@@ -122,6 +122,7 @@
import org.eclipse.jdt.internal.compiler.parser.RecoveredProvidesStatement;
import org.eclipse.jdt.internal.compiler.parser.RecoveredType;
import org.eclipse.jdt.internal.compiler.parser.RecoveredUnit;
+import org.eclipse.jdt.internal.compiler.parser.Scanner;
import org.eclipse.jdt.internal.compiler.parser.TerminalTokens;
import org.eclipse.jdt.internal.compiler.problem.AbortCompilation;
import org.eclipse.jdt.internal.compiler.problem.ProblemReporter;
@@ -4011,19 +4012,27 @@
@Override
protected int fetchNextToken() throws InvalidInputException {
int token = this.scanner.getNextToken();
- if (!this.diet && token != TerminalTokens.TokenNameEOF) {
- if (this.scanner.currentPosition > this.cursorLocation
- && this.expressionPtr <= -1
- && !requireExtendedRecovery())
- {
- this.scanner.eofPosition = this.cursorLocation + 1; // revert to old strategy where we stop parsing right at the cursor
-
- // stop immediately or deferred?
- if (topKnownElementKind(COMPLETION_OR_ASSIST_PARSER) == K_BLOCK_DELIMITER // -> not within a complex structure?
- && this.scanner.startPosition > this.cursorLocation + 1 // -> is current token entirely beyond cursorLocation?
- && this.currentElement == null) // -> clean slate?
+ if (token != TerminalTokens.TokenNameEOF && this.scanner.currentPosition > this.cursorLocation) {
+ if (!this.diet || this.dietInt != 0) { // do this also when parsing field initializers:
+ if (this.currentToken == TerminalTokens.TokenNameIdentifier
+ && this.identifierStack[this.identifierPtr].length == 0
+ && Scanner.isLiteral(token))
{
- return TerminalTokens.TokenNameEOF; // no need to look further
+ // <emptyAssistIdentifier> <someLiteral> is illegal and most likely the literal should be replaced => discard it now
+ return fetchNextToken();
+ }
+ }
+ if (!this.diet) { // only when parsing a method body:
+ if (this.expressionPtr <= -1 && !requireExtendedRecovery()) {
+ this.scanner.eofPosition = this.cursorLocation + 1; // revert to old strategy where we stop parsing right at the cursor
+
+ // stop immediately or deferred?
+ if (topKnownElementKind(COMPLETION_OR_ASSIST_PARSER) == K_BLOCK_DELIMITER // -> not within a complex structure?
+ && this.scanner.startPosition > this.cursorLocation + 1 // -> is current token entirely beyond cursorLocation?
+ && this.currentElement == null) // -> clean slate?
+ {
+ return TerminalTokens.TokenNameEOF; // no need to look further
+ }
}
}
}