Bug 574704 - [content assist] incomplete content assist before cast
Change-Id: Ifb779371547dc6fcd6f5b1b3c6188417a1c8faa7
Reviewed-on: https://git.eclipse.org/r/c/jdt/eclipse.jdt.core/+/182908
Tested-by: JDT Bot <jdt-bot@eclipse.org>
Reviewed-by: Stephan Herrmann <stephan.herrmann@berlin.de>
diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/CompletionTests3.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/CompletionTests3.java
index cd57046..c0ebd5b 100644
--- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/CompletionTests3.java
+++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/CompletionTests3.java
@@ -928,4 +928,80 @@
deleteProject("P");
}
}
+public void testBug574704() throws Exception {
+ try {
+ createJavaProject("P", new String[] {"src"}, new String[]{"JCL17_LIB"}, "bin", "1.7");
+ this.workingCopies = new ICompilationUnit[2];
+ this.workingCopies[0] = getWorkingCopy(
+ "/P/src/Cast.java",
+ "public class Cast {\n" +
+ "\n" +
+ " Object field;\n" +
+ "\n" +
+ " void test(Object o) {\n" +
+ " if (true) {\n" +
+ " // content assist here does not offer o or field\n" +
+ " ((String) o).toCharArray();\n" +
+ " }\n" +
+ " }\n" +
+ "}\n");
+
+ CompletionTestsRequestor2 requestor = new CompletionTestsRequestor2(true);
+ String str = this.workingCopies[0].getSource();
+ String completeBefore = " // content assist here";
+ int cursorLocation = str.indexOf(completeBefore);
+ this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner);
+ int relevance = R_DEFAULT + R_INTERESTING + R_RESOLVED + R_CASE + R_UNQUALIFIED + R_NON_RESTRICTED;
+ assertResults(
+ "clone[METHOD_REF]{clone(), Ljava.lang.Object;, ()Ljava.lang.Object;, clone, null, " + relevance + "}\n" +
+ "equals[METHOD_REF]{equals(), Ljava.lang.Object;, (Ljava.lang.Object;)Z, equals, (obj), " + relevance + "}\n" +
+ "field[FIELD_REF]{field, LCast;, Ljava.lang.Object;, field, null, " + relevance + "}\n" +
+ "finalize[METHOD_REF]{finalize(), Ljava.lang.Object;, ()V, finalize, null, " + relevance + "}\n" +
+ "getClass[METHOD_REF]{getClass(), Ljava.lang.Object;, ()Ljava.lang.Class<+Ljava.lang.Object;>;, getClass, null, " + relevance + "}\n" +
+ "hashCode[METHOD_REF]{hashCode(), Ljava.lang.Object;, ()I, hashCode, null, " + relevance + "}\n" +
+ "notify[METHOD_REF]{notify(), Ljava.lang.Object;, ()V, notify, null, " + relevance + "}\n" +
+ "notifyAll[METHOD_REF]{notifyAll(), Ljava.lang.Object;, ()V, notifyAll, null, " + relevance + "}\n" +
+ "o[LOCAL_VARIABLE_REF]{o, null, Ljava.lang.Object;, o, null, "+relevance+"}\n" +
+ "test[METHOD_REF]{test(), LCast;, (Ljava.lang.Object;)V, test, (o), " + relevance + "}\n" +
+ "toString[METHOD_REF]{toString(), Ljava.lang.Object;, ()Ljava.lang.String;, toString, null, " + relevance + "}\n" +
+ "wait[METHOD_REF]{wait(), Ljava.lang.Object;, ()V, wait, null, " + relevance + "}\n" +
+ "wait[METHOD_REF]{wait(), Ljava.lang.Object;, (J)V, wait, (millis), " + relevance + "}\n" +
+ "wait[METHOD_REF]{wait(), Ljava.lang.Object;, (JI)V, wait, (millis, nanos), " + relevance + "}",
+ requestor.getResults());
+ } finally {
+ deleteProject("P");
+ }
+}
+public void testBug574704_withPrefix() throws Exception {
+ try {
+ createJavaProject("P", new String[] {"src"}, new String[]{"JCL17_LIB"}, "bin", "1.7");
+ this.workingCopies = new ICompilationUnit[2];
+ this.workingCopies[0] = getWorkingCopy(
+ "/P/src/Cast.java",
+ "public class Cast {\n" +
+ "\n" +
+ " Object oField;\n" +
+ "\n" +
+ " void test(Object oArg, String wrongArg) {\n" +
+ " if (true) {\n" +
+ " o // content assist here does not offer oArg or oField\n" +
+ " ((String) oArg).toCharArray();\n" +
+ " }\n" +
+ " }\n" +
+ "}\n");
+
+ CompletionTestsRequestor2 requestor = new CompletionTestsRequestor2(true);
+ String str = this.workingCopies[0].getSource();
+ String completeBefore = " // content assist here";
+ int cursorLocation = str.indexOf(completeBefore);
+ this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner);
+ int relevance = R_DEFAULT + R_INTERESTING + R_RESOLVED + R_CASE + R_UNQUALIFIED + R_NON_RESTRICTED;
+ assertResults(
+ "oArg[LOCAL_VARIABLE_REF]{oArg, null, Ljava.lang.Object;, oArg, null, "+relevance+"}\n" +
+ "oField[FIELD_REF]{oField, LCast;, Ljava.lang.Object;, oField, null, " + relevance + "}",
+ requestor.getResults());
+ } finally {
+ deleteProject("P");
+ }
+}
}
diff --git a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/CompletionEngine.java b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/CompletionEngine.java
index 556a4e2..44c68ce 100644
--- a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/CompletionEngine.java
+++ b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/CompletionEngine.java
@@ -3225,9 +3225,16 @@
if (messageSend.statementEnd > messageSend.sourceStart)
setSourceRange(messageSend.sourceStart, messageSend.statementEnd);
- this.insideQualifiedReference = true;
this.completionToken = messageSend.selector;
+ if (messageSend.nextIsCast) {
+ // optionalPrefix|((String) s) was mistaken as a messageSend(?). Treat like beginning of statement.
+ findVariablesAndMethods(this.completionToken, scope, messageSend, scope, false, false);
+ return;
+ }
+
+ this.insideQualifiedReference = true;
+
TypeBinding receiverType = (TypeBinding)qualifiedBinding;
if(receiverType != null && receiverType instanceof ReferenceBinding) {
diff --git a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/complete/CompletionOnMessageSendName.java b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/complete/CompletionOnMessageSendName.java
index 179cffc..16bc25f 100644
--- a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/complete/CompletionOnMessageSendName.java
+++ b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/complete/CompletionOnMessageSendName.java
@@ -25,12 +25,16 @@
* and signals that the selector is to be matched inexactly (in contrast to CompletionOnMessageSend)..
*/
public class CompletionOnMessageSendName extends MessageSend {
- public CompletionOnMessageSendName(char[] selector, int start, int end) {
+
+ public boolean nextIsCast;
+
+ public CompletionOnMessageSendName(char[] selector, int start, int end, boolean nextIsCast) {
super();
this.selector = selector;
this.sourceStart = start;
this.sourceEnd = end;
this.nameSourcePosition = end;
+ this.nextIsCast = nextIsCast;
}
@Override
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 39342f2..3609511 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
@@ -2047,7 +2047,7 @@
this.identifierLengthPtr--;
int end = (int) position;
int start = (int) (position >>> 32);
- m = new CompletionOnMessageSendName(selector, start, end);
+ m = new CompletionOnMessageSendName(selector, start, end, false);
// handle type arguments
int length = this.genericsLengthStack[this.genericsLengthPtr--];
@@ -2066,7 +2066,7 @@
this.identifierLengthPtr--;
int end = (int) position;
int start = (int) (position >>> 32);
- m = new CompletionOnMessageSendName(selector, start, end);
+ m = new CompletionOnMessageSendName(selector, start, end, false);
// handle type arguments
int length = this.genericsLengthStack[this.genericsLengthPtr--];
@@ -2083,7 +2083,7 @@
this.identifierLengthPtr--;
int end = (int) position;
int start = (int) (position >>> 32);
- m = new CompletionOnMessageSendName(selector, start, end);
+ m = new CompletionOnMessageSendName(selector, start, end, false);
// handle type arguments
int length = this.genericsLengthStack[this.genericsLengthPtr--];
@@ -5671,7 +5671,8 @@
MessageSend m = null;
long nameStart = this.identifierPositionStack[this.identifierPtr] >>> 32;
if (this.assistNode == null && this.lParenPos > this.cursorLocation && nameStart <= this.cursorLocation + 1) {
- m = new CompletionOnMessageSendName(null, 0, 0); // positions will be set in consumeMethodInvocationName(), if that's who called us
+ boolean nextIsCast = this.expressionPtr > -1 && this.expressionStack[this.expressionPtr] instanceof CastExpression;
+ m = new CompletionOnMessageSendName(null, 0, 0, nextIsCast); // positions will be set in consumeMethodInvocationName(), if that's who called us
} else if (this.assistNode != null && this.lParenPos == this.assistNode.sourceEnd) {
// this branch corresponds to work done in checkParemeterizedMethodName(), just the latter isn't called in absence of a syntax error
if (this.expressionPtr != -1 && this.expressionStack[this.expressionPtr] == this.assistNode) {