diff options
author | Stephan Herrmann | 2021-05-20 20:25:08 +0000 |
---|---|---|
committer | Jay Arthanareeswaran | 2021-05-24 04:37:34 +0000 |
commit | 0904799f18b393b84b7d7f69b95f1a1cd7534343 (patch) | |
tree | 85a96cebd770ed92a194cd58e4e7064f663b3dd4 | |
parent | f405e82c76aed68376e8e17e0ebf3b01f613d19e (diff) | |
download | eclipse.jdt.core-0904799f18b393b84b7d7f69b95f1a1cd7534343.tar.gz eclipse.jdt.core-0904799f18b393b84b7d7f69b95f1a1cd7534343.tar.xz eclipse.jdt.core-0904799f18b393b84b7d7f69b95f1a1cd7534343.zip |
Bug 573632 - Content assist is not working anymore after "if"
Change-Id: I3eb8a982fda8fbf5f70be5ce4dff647b94184d13
Signed-off-by: Stephan Herrmann <stephan.herrmann@berlin.de>
Reviewed-on: https://git.eclipse.org/r/c/jdt/eclipse.jdt.core/+/180822
Tested-by: Jay Arthanareeswaran <jarthana@in.ibm.com>
Reviewed-by: Jay Arthanareeswaran <jarthana@in.ibm.com>
3 files changed, 226 insertions, 6 deletions
diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/CompletionTests.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/CompletionTests.java index ec723935d3..4d57bdc8a3 100644 --- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/CompletionTests.java +++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/CompletionTests.java @@ -26078,4 +26078,157 @@ public void testBug496354() throws Exception { "val2[ANNOTATION_ATTRIBUTE_REF]{val2=, LT;, I, val2, null, 52}", requestor.getResults()); } +public void testBug573632() throws Exception { + this.workingCopies = new ICompilationUnit[1]; + this.workingCopies[0] = getWorkingCopy( + "Completion/src/Foo.java", + "package test;\n" + + "public class Foo {\n" + + " Foo f;\n" + + " public void foo() {\n" + + " if (f != null) {\n" + + " f.\n" + + " f = null;\n" + + " }\n" + + " };\n" + + "}\n"); + + CompletionTestsRequestor2 requestor = new CompletionTestsRequestor2(true); + requestor.allowAllRequiredProposals(); + String str = this.workingCopies[0].getSource(); + String completeBehind = "f."; + int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); + this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); + assertResults( + "clone[METHOD_REF]{clone(), Ljava.lang.Object;, ()Ljava.lang.Object;, clone, null, 60}\n" + + "equals[METHOD_REF]{equals(), Ljava.lang.Object;, (Ljava.lang.Object;)Z, equals, (obj), 60}\n" + + "f[FIELD_REF]{f, LFoo;, LFoo;, f, null, 60}\n" + + "finalize[METHOD_REF]{finalize(), Ljava.lang.Object;, ()V, finalize, null, 60}\n" + + "foo[METHOD_REF]{foo(), LFoo;, ()V, foo, null, 60}\n" + + "getClass[METHOD_REF]{getClass(), Ljava.lang.Object;, ()Ljava.lang.Class;, getClass, null, 60}\n" + + "hashCode[METHOD_REF]{hashCode(), Ljava.lang.Object;, ()I, hashCode, null, 60}\n" + + "notify[METHOD_REF]{notify(), Ljava.lang.Object;, ()V, notify, null, 60}\n" + + "notifyAll[METHOD_REF]{notifyAll(), Ljava.lang.Object;, ()V, notifyAll, null, 60}\n" + + "toString[METHOD_REF]{toString(), Ljava.lang.Object;, ()Ljava.lang.String;, toString, null, 60}\n" + + "wait[METHOD_REF]{wait(), Ljava.lang.Object;, ()V, wait, null, 60}\n" + + "wait[METHOD_REF]{wait(), Ljava.lang.Object;, (J)V, wait, (millis), 60}\n" + + "wait[METHOD_REF]{wait(), Ljava.lang.Object;, (JI)V, wait, (millis, nanos), 60}", + requestor.getResults()); +} +public void testBug573632a() throws Exception { + // variation: nested ifs + this.workingCopies = new ICompilationUnit[1]; + this.workingCopies[0] = getWorkingCopy( + "Completion/src/Foo.java", + "package test;\n" + + "public class Foo {\n" + + " Foo f;\n" + + " public void foo() {\n" + + " if (f != null) {\n" + + " if (f != null) {\n" + + " f.\n" + + " f = null;\n" + + " }\n" + + " }\n" + + " };\n" + + "}\n"); + + CompletionTestsRequestor2 requestor = new CompletionTestsRequestor2(true); + requestor.allowAllRequiredProposals(); + String str = this.workingCopies[0].getSource(); + String completeBehind = "f."; + int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); + this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); + assertResults( + "clone[METHOD_REF]{clone(), Ljava.lang.Object;, ()Ljava.lang.Object;, clone, null, 60}\n" + + "equals[METHOD_REF]{equals(), Ljava.lang.Object;, (Ljava.lang.Object;)Z, equals, (obj), 60}\n" + + "f[FIELD_REF]{f, LFoo;, LFoo;, f, null, 60}\n" + + "finalize[METHOD_REF]{finalize(), Ljava.lang.Object;, ()V, finalize, null, 60}\n" + + "foo[METHOD_REF]{foo(), LFoo;, ()V, foo, null, 60}\n" + + "getClass[METHOD_REF]{getClass(), Ljava.lang.Object;, ()Ljava.lang.Class;, getClass, null, 60}\n" + + "hashCode[METHOD_REF]{hashCode(), Ljava.lang.Object;, ()I, hashCode, null, 60}\n" + + "notify[METHOD_REF]{notify(), Ljava.lang.Object;, ()V, notify, null, 60}\n" + + "notifyAll[METHOD_REF]{notifyAll(), Ljava.lang.Object;, ()V, notifyAll, null, 60}\n" + + "toString[METHOD_REF]{toString(), Ljava.lang.Object;, ()Ljava.lang.String;, toString, null, 60}\n" + + "wait[METHOD_REF]{wait(), Ljava.lang.Object;, ()V, wait, null, 60}\n" + + "wait[METHOD_REF]{wait(), Ljava.lang.Object;, (J)V, wait, (millis), 60}\n" + + "wait[METHOD_REF]{wait(), Ljava.lang.Object;, (JI)V, wait, (millis, nanos), 60}", + requestor.getResults()); +} +public void testBug573632b() throws Exception { + // variation: for + this.workingCopies = new ICompilationUnit[1]; + this.workingCopies[0] = getWorkingCopy( + "Completion/src/Foo.java", + "package test;\n" + + "public class Foo {\n" + + " Foo f;\n" + + " public void foo() {\n" + + " for (int i = 0; f != null; i++) {\n" + + " f.\n" + + " f = null;\n" + + " }\n" + + " };\n" + + "}\n"); + + CompletionTestsRequestor2 requestor = new CompletionTestsRequestor2(true); + requestor.allowAllRequiredProposals(); + String str = this.workingCopies[0].getSource(); + String completeBehind = "f."; + int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); + this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); + assertResults( + "clone[METHOD_REF]{clone(), Ljava.lang.Object;, ()Ljava.lang.Object;, clone, null, 60}\n" + + "equals[METHOD_REF]{equals(), Ljava.lang.Object;, (Ljava.lang.Object;)Z, equals, (obj), 60}\n" + + "f[FIELD_REF]{f, LFoo;, LFoo;, f, null, 60}\n" + + "finalize[METHOD_REF]{finalize(), Ljava.lang.Object;, ()V, finalize, null, 60}\n" + + "foo[METHOD_REF]{foo(), LFoo;, ()V, foo, null, 60}\n" + + "getClass[METHOD_REF]{getClass(), Ljava.lang.Object;, ()Ljava.lang.Class;, getClass, null, 60}\n" + + "hashCode[METHOD_REF]{hashCode(), Ljava.lang.Object;, ()I, hashCode, null, 60}\n" + + "notify[METHOD_REF]{notify(), Ljava.lang.Object;, ()V, notify, null, 60}\n" + + "notifyAll[METHOD_REF]{notifyAll(), Ljava.lang.Object;, ()V, notifyAll, null, 60}\n" + + "toString[METHOD_REF]{toString(), Ljava.lang.Object;, ()Ljava.lang.String;, toString, null, 60}\n" + + "wait[METHOD_REF]{wait(), Ljava.lang.Object;, ()V, wait, null, 60}\n" + + "wait[METHOD_REF]{wait(), Ljava.lang.Object;, (J)V, wait, (millis), 60}\n" + + "wait[METHOD_REF]{wait(), Ljava.lang.Object;, (JI)V, wait, (millis, nanos), 60}", + requestor.getResults()); +} +public void testBug573632c() throws Exception { + // variation: while + this.workingCopies = new ICompilationUnit[1]; + this.workingCopies[0] = getWorkingCopy( + "Completion/src/Foo.java", + "package test;\n" + + "public class Foo {\n" + + " Foo f;\n" + + " public void foo() {\n" + + " while (f != null) {\n" + + " f.\n" + + " f = null;\n" + + " }\n" + + " };\n" + + "}\n"); + + CompletionTestsRequestor2 requestor = new CompletionTestsRequestor2(true); + requestor.allowAllRequiredProposals(); + String str = this.workingCopies[0].getSource(); + String completeBehind = "f."; + int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); + this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); + assertResults( + "clone[METHOD_REF]{clone(), Ljava.lang.Object;, ()Ljava.lang.Object;, clone, null, 60}\n" + + "equals[METHOD_REF]{equals(), Ljava.lang.Object;, (Ljava.lang.Object;)Z, equals, (obj), 60}\n" + + "f[FIELD_REF]{f, LFoo;, LFoo;, f, null, 60}\n" + + "finalize[METHOD_REF]{finalize(), Ljava.lang.Object;, ()V, finalize, null, 60}\n" + + "foo[METHOD_REF]{foo(), LFoo;, ()V, foo, null, 60}\n" + + "getClass[METHOD_REF]{getClass(), Ljava.lang.Object;, ()Ljava.lang.Class;, getClass, null, 60}\n" + + "hashCode[METHOD_REF]{hashCode(), Ljava.lang.Object;, ()I, hashCode, null, 60}\n" + + "notify[METHOD_REF]{notify(), Ljava.lang.Object;, ()V, notify, null, 60}\n" + + "notifyAll[METHOD_REF]{notifyAll(), Ljava.lang.Object;, ()V, notifyAll, null, 60}\n" + + "toString[METHOD_REF]{toString(), Ljava.lang.Object;, ()Ljava.lang.String;, toString, null, 60}\n" + + "wait[METHOD_REF]{wait(), Ljava.lang.Object;, ()V, wait, null, 60}\n" + + "wait[METHOD_REF]{wait(), Ljava.lang.Object;, (J)V, wait, (millis), 60}\n" + + "wait[METHOD_REF]{wait(), Ljava.lang.Object;, (JI)V, wait, (millis, nanos), 60}", + requestor.getResults()); +} } diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/CompletionTests9.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/CompletionTests9.java index 6a9596b017..a9420b7ba8 100644 --- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/CompletionTests9.java +++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/CompletionTests9.java @@ -1636,4 +1636,42 @@ public void testBug560606() throws CoreException { deleteProject(project1); } } +public void testBug573632d() throws Exception { + IJavaProject project1 = createJavaProject("Completion9_1", new String[] {"src"}, new String[] {"JCL19_LIB", "org.eclipse.jdt.core.tests.model.TEST_CONTAINER"}, "bin", "9"); + try { + project1.open(null); + CompletionResult result = complete( + "/Completion9_1/src/Foo.java", + "package test;\n" + + "import java.io.InputStream;\n" + + "public class Foo {\n" + + " Foo f;\n" + + " public void foo() {\n" + + " InputStream is = new InputStream(null);\n" + + " try (is) {\n" + + " f.\n" + + " f = null;\n" + + " }\n" + + " };\n" + + "}\n", + "f."); + assertResults( + "clone[METHOD_REF]{clone(), Ljava.lang.Object;, ()Ljava.lang.Object;, clone, null, 60}\n" + + "equals[METHOD_REF]{equals(), Ljava.lang.Object;, (Ljava.lang.Object;)Z, equals, (obj), 60}\n" + + "f[FIELD_REF]{f, LFoo;, LFoo;, f, null, 60}\n" + + "finalize[METHOD_REF]{finalize(), Ljava.lang.Object;, ()V, finalize, null, 60}\n" + + "foo[METHOD_REF]{foo(), LFoo;, ()V, foo, null, 60}\n" + + "getClass[METHOD_REF]{getClass(), Ljava.lang.Object;, ()Ljava.lang.Class<+Ljava.lang.Object;>;, getClass, null, 60}\n" + + "hashCode[METHOD_REF]{hashCode(), Ljava.lang.Object;, ()I, hashCode, null, 60}\n" + + "notify[METHOD_REF]{notify(), Ljava.lang.Object;, ()V, notify, null, 60}\n" + + "notifyAll[METHOD_REF]{notifyAll(), Ljava.lang.Object;, ()V, notifyAll, null, 60}\n" + + "toString[METHOD_REF]{toString(), Ljava.lang.Object;, ()Ljava.lang.String;, toString, null, 60}\n" + + "wait[METHOD_REF]{wait(), Ljava.lang.Object;, ()V, wait, null, 60}\n" + + "wait[METHOD_REF]{wait(), Ljava.lang.Object;, (J)V, wait, (millis), 60}\n" + + "wait[METHOD_REF]{wait(), Ljava.lang.Object;, (JI)V, wait, (millis, nanos), 60}", + result.proposals); + } finally { + deleteProject(project1); + } +} }
\ No newline at end of file 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 de79ff84c0..ace2ddef20 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 @@ -4012,8 +4012,8 @@ protected void consumeSwitchLabeledBlock() { protected int fetchNextToken() throws InvalidInputException { int token = this.scanner.getNextToken(); if (!this.diet && token != TerminalTokens.TokenNameEOF) { - if (!requireExtendedRecovery() && this.expressionPtr == -1) { - if (this.scanner.currentPosition > this.cursorLocation) { + if (!requireExtendedRecovery() && this.scanner.currentPosition > this.cursorLocation) { + if (!hasPendingExpression(token)) { this.scanner.eofPosition = this.cursorLocation + 1; // revert to old strategy where we stop parsing right at the cursor // stop immediately or deferred? @@ -4028,6 +4028,26 @@ protected int fetchNextToken() throws InvalidInputException { } return token; } +private boolean hasPendingExpression(int token) { + if (this.expressionPtr == -1) + return false; + if (token == TerminalTokens.TokenNameDOT) { + // at '.' we are more eager to send early EOF to avoid seeing a qualified type reference in this pattern: + // foo.| + // bar ... + Expression expression = this.expressionStack[this.expressionPtr]; + int elPtr = this.elementPtr; + while (elPtr >= 0) { + if (this.elementKindStack[elPtr] == K_BLOCK_DELIMITER) { + if (this.elementObjectInfoStack[elPtr] == expression) { + return false; // top expr on expressionStack belongs to a block statement (e.g., an if-condition) + } + } + elPtr--; + } + } + return true; +} @Override protected void consumeToken(int token) { if(this.isFirst) { @@ -4291,10 +4311,19 @@ protected void consumeToken(int token) { if (kind == K_CONTROL_STATEMENT_DELIMITER) { int info = topKnownElementInfo(COMPLETION_OR_ASSIST_PARSER); popElement(K_CONTROL_STATEMENT_DELIMITER); - if (info == IF) { - pushOnElementStack(K_BLOCK_DELIMITER, IF, this.expressionStack[this.expressionPtr]); - } else { - pushOnElementStack(K_BLOCK_DELIMITER, info); + switch (info) { + case IF: + // include top-expression of these just for the benefit of hasPendingExpression(): + // (TRY is not included, even Java9-t-w-r doesn't own an *expression*) + case FOR: + case WHILE: + if (this.expressionPtr > -1) { + pushOnElementStack(K_BLOCK_DELIMITER, info, this.expressionStack[this.expressionPtr]); + break; + } + //$FALL-THROUGH$ + default: + pushOnElementStack(K_BLOCK_DELIMITER, info); } } else { switch(previous) { |