diff options
author | Stephan Herrmann | 2021-10-17 21:43:36 +0000 |
---|---|---|
committer | Stephan Herrmann | 2021-10-21 19:11:23 +0000 |
commit | ce59f244f3c0c78d1953b77e8d3652368d985c3a (patch) | |
tree | a548ce602330fbadb6cf326ca7b7db36c142f9e5 | |
parent | 437144de085992ac398c6582dce05c16b64324db (diff) | |
download | eclipse.jdt.core-ce59f244f3c0c78d1953b77e8d3652368d985c3a.tar.gz eclipse.jdt.core-ce59f244f3c0c78d1953b77e8d3652368d985c3a.tar.xz eclipse.jdt.core-ce59f244f3c0c78d1953b77e8d3652368d985c3a.zip |
Bug 575631 - [content assist] missing static method proposals in
conditional, before variable declaration
Change-Id: Ib6cb4b149bd2fa720103383c43dabb2e25a9b5a7
Reviewed-on: https://git.eclipse.org/r/c/jdt/eclipse.jdt.core/+/186576
Tested-by: JDT Bot <jdt-bot@eclipse.org>
Reviewed-by: Stephan Herrmann <stephan.herrmann@berlin.de>
8 files changed, 559 insertions, 104 deletions
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/parser/AllocationExpressionCompletionTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/parser/AllocationExpressionCompletionTest.java index 847f7721ef..30694438a4 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/parser/AllocationExpressionCompletionTest.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/parser/AllocationExpressionCompletionTest.java @@ -28,7 +28,40 @@ public static Test suite() { /* * Completion inside an if statement. */ -public void testInIfStatement() { +public void testInIfStatement1() { + this.runTestCheckMethodParse( + // compilationUnit: + "class Bar { \n" + + " void foo() { \n" + + " if (true) { \n" + + " new z.y.X(1, 2, i); \n" + + " } \n" + + " } \n" + + "}\n", + // completeBehind: + "X(", + // expectedCompletionNodeToString: + "<CompleteOnAllocationExpression:new z.y.X(<CompleteOnName:>, 2, i)>", + // expectedUnitDisplayString: + "class Bar {\n" + + " Bar() {\n" + + " }\n" + + " void foo() {\n" + + " if (true)\n" + + " {\n" + + " <CompleteOnAllocationExpression:new z.y.X(<CompleteOnName:>, 2, i)>;\n" + + " }\n" + + " }\n" + + "}\n", + // expectedCompletionIdentifier: + "", + // expectedReplacedSource: + "new z.y.X(1, 2, i)", + // test name + "<complete inside an if statement>" + ); +} +public void testInIfStatement2() { this.runTestCheckMethodParse( // compilationUnit: "class Bar { \n" + @@ -41,15 +74,16 @@ public void testInIfStatement() { // completeBehind: "X(1, 2,", // expectedCompletionNodeToString: - "<CompleteOnAllocationExpression:new z.y.X(1, 2)>", + "<CompleteOnName:>", // expectedUnitDisplayString: "class Bar {\n" + " Bar() {\n" + " }\n" + " void foo() {\n" + - " {\n" + - " <CompleteOnAllocationExpression:new z.y.X(1, 2)>;\n" + - " }\n" + + " if (true)\n" + + " {\n" + + " new z.y.X(1, 2, <CompleteOnName:>, i);\n" + + " }\n" + " }\n" + "}\n", // expectedCompletionIdentifier: @@ -66,7 +100,35 @@ public void testInIfStatement() { * ie. ClassInstanceCreationExpression ::= 'new' ClassType '(' ArgumentListopt ')' ClassBodyopt * where ClassType is a qualified type name */ -public void testNoQualificationQualifiedTypeName() { +public void testNoQualificationQualifiedTypeName1() { + this.runTestCheckMethodParse( + // compilationUnit: + "class Bar { \n" + + " void foo() { \n" + + " new z.y.X(1, 2, i); \n" + + " } \n" + + "}\n", + // completeBehind: + "X(", + // expectedCompletionNodeToString: + "<CompleteOnAllocationExpression:new z.y.X()>", + // expectedUnitDisplayString: + "class Bar {\n" + + " Bar() {\n" + + " }\n" + + " void foo() {\n" + + " <CompleteOnAllocationExpression:new z.y.X()>;\n" + + " }\n" + + "}\n", + // expectedCompletionIdentifier: + "", + // expectedReplacedSource: + "", + // test name + "<complete on non qualified instance creation with qualified type name>" + ); +} +public void testNoQualificationQualifiedTypeName2() { this.runTestCheckMethodParse( // compilationUnit: "class Bar { \n" + @@ -77,13 +139,13 @@ public void testNoQualificationQualifiedTypeName() { // completeBehind: "X(1, 2,", // expectedCompletionNodeToString: - "<CompleteOnAllocationExpression:new z.y.X(1, 2)>", + "<CompleteOnName:>", // expectedUnitDisplayString: "class Bar {\n" + " Bar() {\n" + " }\n" + " void foo() {\n" + - " <CompleteOnAllocationExpression:new z.y.X(1, 2)>;\n" + + " new z.y.X(1, 2, <CompleteOnName:>, i);\n" + " }\n" + "}\n", // expectedCompletionIdentifier: @@ -100,7 +162,35 @@ public void testNoQualificationQualifiedTypeName() { * ie. ClassInstanceCreationExpression ::= 'new' ClassType '(' ArgumentListopt ')' ClassBodyopt * where ClassType is a simple type name */ -public void testNoQualificationSimpleTypeName() { +public void testNoQualificationSimpleTypeName1() { + this.runTestCheckMethodParse( + // compilationUnit: + "class Bar { \n" + + " void foo() { \n" + + " new X(1, 2, i); \n" + + " } \n" + + "}\n", + // completeBehind: + "X(", + // expectedCompletionNodeToString: + "<CompleteOnAllocationExpression:new X()>", + // expectedUnitDisplayString: + "class Bar {\n" + + " Bar() {\n" + + " }\n" + + " void foo() {\n" + + " <CompleteOnAllocationExpression:new X()>;\n" + + " }\n" + + "}\n", + // expectedCompletionIdentifier: + "", + // expectedReplacedSource: + "", + // test name + "<complete on non qualified instance creation with simple type name>" + ); +} +public void testNoQualificationSimpleTypeName2() { this.runTestCheckMethodParse( // compilationUnit: "class Bar { \n" + @@ -111,13 +201,13 @@ public void testNoQualificationSimpleTypeName() { // completeBehind: "X(1, 2,", // expectedCompletionNodeToString: - "<CompleteOnAllocationExpression:new X(1, 2)>", + "<CompleteOnName:>", // expectedUnitDisplayString: "class Bar {\n" + " Bar() {\n" + " }\n" + " void foo() {\n" + - " <CompleteOnAllocationExpression:new X(1, 2)>;\n" + + " new X(1, 2, <CompleteOnName:>, i);\n" + " }\n" + "}\n", // expectedCompletionIdentifier: @@ -133,7 +223,35 @@ public void testNoQualificationSimpleTypeName() { * * ie. ClassInstanceCreationExpression ::= ClassInstanceCreationExpressionName 'new' SimpleName '(' ArgumentListopt ')' ClassBodyopt */ -public void testQualifiedWithName() { +public void testQualifiedWithName1() { + this.runTestCheckMethodParse( + // compilationUnit: + "class Bar {\n" + + " void foo() { \n" + + " Buz.x.new X(1, 2, i); \n" + + " } \n" + + "}\n", + // completeBehind: + "X(", + // expectedCompletionNodeToString: + "<CompleteOnQualifiedAllocationExpression:Buz.x.new X(<CompleteOnName:>, 2, i)>", + // expectedUnitDisplayString: + "class Bar {\n" + + " Bar() {\n" + + " }\n" + + " void foo() {\n" + + " <CompleteOnQualifiedAllocationExpression:Buz.x.new X(<CompleteOnName:>, 2, i)>;\n" + + " }\n" + + "}\n", + // expectedCompletionIdentifier: + "", + // expectedReplacedSource: + "Buz.x.new X(1, 2, i)", + // test name + "<complete on name qualified instance creation>" + ); +} +public void testQualifiedWithName2() { this.runTestCheckMethodParse( // compilationUnit: "class Bar {\n" + @@ -144,13 +262,13 @@ public void testQualifiedWithName() { // completeBehind: "X(1, 2,", // expectedCompletionNodeToString: - "<CompleteOnQualifiedAllocationExpression:Buz.x.new X(1, 2)>", + "<CompleteOnName:>", // expectedUnitDisplayString: "class Bar {\n" + " Bar() {\n" + " }\n" + " void foo() {\n" + - " <CompleteOnQualifiedAllocationExpression:Buz.x.new X(1, 2)>;\n" + + " Buz.x.new X(1, 2, <CompleteOnName:>, i);\n" + " }\n" + "}\n", // expectedCompletionIdentifier: @@ -166,7 +284,35 @@ public void testQualifiedWithName() { * * ie. ClassInstanceCreationExpression ::= Primary '.' 'new' SimpleName '(' ArgumentListopt ')' ClassBodyopt */ -public void testQualifiedWithPrimary() { +public void testQualifiedWithPrimary1() { + this.runTestCheckMethodParse( + // compilationUnit: + "class Bar { \n" + + " void foo() { \n" + + " primary().new X(1, 2, i); \n" + + " } \n" + + "}\n", + // completeBehind: + "X(", + // expectedCompletionNodeToString: + "<CompleteOnQualifiedAllocationExpression:primary().new X(<CompleteOnName:>, 2, i)>", + // expectedUnitDisplayString: + "class Bar {\n" + + " Bar() {\n" + + " }\n" + + " void foo() {\n" + + " <CompleteOnQualifiedAllocationExpression:primary().new X(<CompleteOnName:>, 2, i)>;\n" + + " }\n" + + "}\n", + // expectedCompletionIdentifier: + "", + // expectedReplacedSource: + "primary().new X(1, 2, i)", + // test name + "<complete on primary qualified instance creation>" + ); +} +public void testQualifiedWithPrimary2() { this.runTestCheckMethodParse( // compilationUnit: "class Bar { \n" + @@ -177,13 +323,13 @@ public void testQualifiedWithPrimary() { // completeBehind: "X(1, 2,", // expectedCompletionNodeToString: - "<CompleteOnQualifiedAllocationExpression:primary().new X(1, 2)>", + "<CompleteOnName:>", // expectedUnitDisplayString: "class Bar {\n" + " Bar() {\n" + " }\n" + " void foo() {\n" + - " <CompleteOnQualifiedAllocationExpression:primary().new X(1, 2)>;\n" + + " primary().new X(1, 2, <CompleteOnName:>, i);\n" + " }\n" + "}\n", // expectedCompletionIdentifier: diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/parser/ExplicitConstructorInvocationCompletionTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/parser/ExplicitConstructorInvocationCompletionTest.java index c0a0849653..2d89e672e6 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/parser/ExplicitConstructorInvocationCompletionTest.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/parser/ExplicitConstructorInvocationCompletionTest.java @@ -49,7 +49,7 @@ public void testPrimarySuper() { // completeBehind: "super(1, 2,", // expectedCompletionNodeToString: - "<CompleteOnExplicitConstructorCall:primary().super(1, 2)>;", + "<CompleteOnName:>", // expectedUnitDisplayString: "class Bar {\n" + " public class InnerBar {\n" + @@ -58,7 +58,7 @@ public void testPrimarySuper() { " }\n" + " public class SubInnerBar extends InnerBar {\n" + " SubInnerBar(Bar x) {\n" + - " <CompleteOnExplicitConstructorCall:primary().super(1, 2)>;\n" + + " primary().super(1, 2, <CompleteOnName:>, i);\n" + " }\n" + " }\n" + " static Bar x;\n" + @@ -98,7 +98,7 @@ public void testPrimaryThis() { // completeBehind: "this(1, 2,", // expectedCompletionNodeToString: - "<CompleteOnExplicitConstructorCall:primary().this(1, 2)>;", + "<CompleteOnName:>", // expectedUnitDisplayString: "class Bar {\n" + " public class InnerBar {\n" + @@ -107,7 +107,7 @@ public void testPrimaryThis() { " }\n" + " public class SubInnerBar extends InnerBar {\n" + " SubInnerBar(Bar x) {\n" + - " <CompleteOnExplicitConstructorCall:primary().this(1, 2)>;\n" + + " primary().this(1, 2, <CompleteOnName:>, i);\n" + " }\n" + " }\n" + " static Bar x;\n" + @@ -140,11 +140,11 @@ public void testSuper() { // completeBehind: "super(1, 2,", // expectedCompletionNodeToString: - "<CompleteOnExplicitConstructorCall:super(1, 2)>;", + "<CompleteOnName:>", // expectedUnitDisplayString: "class Bar {\n" + " Bar() {\n" + - " <CompleteOnExplicitConstructorCall:super(1, 2)>;\n" + + " super(1, 2, <CompleteOnName:>, i);\n" + " }\n" + "}\n", // expectedCompletionIdentifier: @@ -171,11 +171,11 @@ public void testThis() { // completeBehind: "this(1, 2,", // expectedCompletionNodeToString: - "<CompleteOnExplicitConstructorCall:this(1, 2)>;", + "<CompleteOnName:>", // expectedUnitDisplayString: "class Bar {\n" + " Bar() {\n" + - " <CompleteOnExplicitConstructorCall:this(1, 2)>;\n" + + " this(1, 2, <CompleteOnName:>, i);\n" + " }\n" + "}\n", // expectedCompletionIdentifier: 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 38dca1c05b..3f061ee575 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 @@ -395,19 +395,19 @@ public void testBeforeLastParameter() { // completeBehind: "fred(1, 2,", // expectedCompletionNodeToString: - "<CompleteOnMessageSend:this.fred(1, 2)>", + "<CompleteOnName:>", // expectedUnitDisplayString: "class Bar {\n" + " Bar() {\n" + " }\n" + " void foo() {\n" + - " <CompleteOnMessageSend:this.fred(1, 2)>;\n" + + " this.fred(1, 2, <CompleteOnName:>, i);\n" + " }\n" + "}\n", // expectedCompletionIdentifier: "", // expectedReplacedSource: - "fred(1, 2,", + "", // test name "<completion just before last parameter>" ); @@ -555,19 +555,19 @@ public void testLabeledWithExpressionReceiver() { // completeBehind: "fred(1, 2,", // expectedCompletionNodeToString: - "<CompleteOnMessageSend:bar().fred(1, 2)>", + "<CompleteOnName:>", // expectedUnitDisplayString: "class X {\n" + " X() {\n" + " }\n" + " void foo() {\n" + - " <CompleteOnMessageSend:bar().fred(1, 2)>;\n" + + " label1: bar().fred(1, 2, <CompleteOnName:>, o);\n" + " }\n" + "}\n", // expectedCompletionIdentifier: "", // expectedReplacedSource: - "fred(1, 2,", + "", // expectedLabels: new String[] {"label1"}, // test name @@ -588,19 +588,19 @@ public void testLabeledWithoutReceiver() { // completeBehind: "fred(1, 2,", // expectedCompletionNodeToString: - "<CompleteOnMessageSend:fred(1, 2)>", + "<CompleteOnName:>", // expectedUnitDisplayString: "class Bar {\n" + " Bar() {\n" + " }\n" + " void foo() {\n" + - " <CompleteOnMessageSend:fred(1, 2)>;\n" + + " label1: fred(1, 2, <CompleteOnName:>, o);\n" + " }\n" + "}\n", // expectedCompletionIdentifier: "", // expectedReplacedSource: - "fred(1, 2,", + "", // expectedLabels: new String[] {"label1"}, // test name @@ -813,20 +813,20 @@ public void testWithNameReceiverAndTwoArgs() { // completeBehind: "x.fred(1, 2,", // expectedCompletionNodeToString: - "<CompleteOnMessageSend:x.fred(1, 2)>", + "<CompleteOnName:>", // expectedUnitDisplayString: "class Bar {\n" + " Bar() {\n" + " }\n" + " void foo() {\n" + " X x;\n" + - " <CompleteOnMessageSend:x.fred(1, 2)>;\n" + + " x.fred(1, 2, <CompleteOnName:>, o);\n" + " }\n" + "}\n", // expectedCompletionIdentifier: "", // expectedReplacedSource: - "fred(1, 2,", + "", // test name "<completion with name receiver and 2 arguments>" ); @@ -846,20 +846,20 @@ public void testWithQualifiedNameReceiver() { // completeBehind: "x.fred(1, 2,", // expectedCompletionNodeToString: - "<CompleteOnMessageSend:y.x.fred(1, 2)>", + "<CompleteOnName:>", // expectedUnitDisplayString: "class Bar {\n" + " Bar() {\n" + " }\n" + " void foo() {\n" + " X x;\n" + - " <CompleteOnMessageSend:y.x.fred(1, 2)>;\n" + + " y.x.fred(1, 2, <CompleteOnName:>, o);\n" + " }\n" + "}\n", // expectedCompletionIdentifier: "", // expectedReplacedSource: - "fred(1, 2,", + "", // test name "<completion with qualified name receiver>" ); 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 eaff34a610..b0e1172b81 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 @@ -19295,9 +19295,7 @@ public void testStaticMembers1() throws JavaModelException { "StaticMembers.StaticClazz[TYPE_REF]{StaticClazz, test, Ltest.StaticMembers$StaticClazz;, null, null, " + (R_DEFAULT + R_RESOLVED + R_INTERESTING + R_CASE + R_NON_INHERITED + R_NON_RESTRICTED) + "}\n" + "class[FIELD_REF]{class, null, Ljava.lang.Class;, class, null, " + (R_DEFAULT + R_RESOLVED + R_INTERESTING + R_CASE + R_NON_INHERITED + R_NON_RESTRICTED) + "}\n" + "staticField[FIELD_REF]{staticField, Ltest.StaticMembers;, I, staticField, null, " + (R_DEFAULT + R_RESOLVED + R_INTERESTING + R_CASE + R_NON_INHERITED + R_NON_RESTRICTED) + "}\n" + - "staticMethod[METHOD_REF]{staticMethod(), Ltest.StaticMembers;, ()I, staticMethod, null, " + (R_DEFAULT + R_RESOLVED + R_INTERESTING + R_CASE + R_NON_INHERITED + R_NON_RESTRICTED) + "}\n" + - "super[KEYWORD]{super, null, null, super, null, " + (R_DEFAULT + R_RESOLVED + R_INTERESTING + R_CASE + R_NON_INHERITED + R_NON_RESTRICTED) + "}\n" + - "this[KEYWORD]{this, null, null, this, null, " + (R_DEFAULT + R_RESOLVED + R_INTERESTING + R_CASE + R_NON_INHERITED + R_NON_RESTRICTED) + "}", + "staticMethod[METHOD_REF]{staticMethod(), Ltest.StaticMembers;, ()I, staticMethod, null, " + (R_DEFAULT + R_RESOLVED + R_INTERESTING + R_CASE + R_NON_INHERITED + R_NON_RESTRICTED) + "}", requestor.getResults()); } //https://bugs.eclipse.org/bugs/show_bug.cgi?id=99631 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 d6b8dd60ff..a18ac50a12 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 @@ -1190,6 +1190,7 @@ public void testBug575397a() throws Exception { int cursorLocation = str.indexOf(completeAfter) + completeAfter.length(); this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); assertResults( + "class[FIELD_REF]{class, null, Ljava.lang.Class<LThread;>;, class, null, 51}\n" + "sleep[METHOD_REF]{sleep(), LThread;, (I)V, sleep, (millis), 51}", requestor.getResults()); } finally { @@ -1255,6 +1256,7 @@ public void testBug575397c() throws Exception { "serialVersionUID[FIELD_REF]{serialVersionUID, Ljava.lang.Enum<LThread$State;>;, J, serialVersionUID, null, 49}\n" + "BLOCKED[FIELD_REF]{BLOCKED, LThread$State;, LThread$State;, BLOCKED, null, 51}\n" + "NEW[FIELD_REF]{NEW, LThread$State;, LThread$State;, NEW, null, 51}\n" + + "class[FIELD_REF]{class, null, Ljava.lang.Class<LThread$State;>;, class, null, 51}\n" + "valueOf[METHOD_REF]{valueOf(), LThread$State;, (Ljava.lang.String;)LThread$State;, valueOf, (arg0), 51}\n" + "values[METHOD_REF]{values(), LThread$State;, ()[LThread$State;, values, null, 51}", requestor.getResults()); @@ -1262,4 +1264,259 @@ public void testBug575397c() throws Exception { deleteProject("P"); } } +public void testBug575631_comment0() throws Exception { + try { + createJavaProject("P", new String[] {"src"}, new String[]{"JCL11_LIB"}, "bin", "11"); + this.workingCopies = new ICompilationUnit[1]; + this.workingCopies[0] = getWorkingCopy( + "/P/src/ContentAssist.java", + "import java.util.Calendar;\n" + + "class ZoneId {}\n" + + "class LocalDateTime {\n" + + " static LocalDateTime now() { return null; }\n" + + " static LocalDateTime now(ZoneId id) { return null; }\n" + + "}\n" + + "public class ContentAssist {\n" + + " public static void staticMethod() {\n" + + " if (true) {\n" + + " LocalDateTime.now\n" + + " Calendar calendar = Calendar.getInstance();\n" + + " }\n" + + " }\n" + + "}\n"); + CompletionTestsRequestor2 requestor = new CompletionTestsRequestor2(true); + String str = this.workingCopies[0].getSource(); + String completeAfter = "LocalDateTime.now"; + int cursorLocation = str.indexOf(completeAfter) + completeAfter.length(); + this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); + assertResults( + "now[METHOD_REF]{now(), LLocalDateTime;, ()LLocalDateTime;, now, null, 55}\n" + + "now[METHOD_REF]{now(), LLocalDateTime;, (LZoneId;)LLocalDateTime;, now, (id), 55}", + requestor.getResults()); + } finally { + deleteProject("P"); + } +} +public void testBug575631_comment1a() throws Exception { + try { + createJavaProject("P", new String[] {"src"}, new String[]{"JCL11_LIB"}, "bin", "11"); + this.workingCopies = new ICompilationUnit[1]; + this.workingCopies[0] = getWorkingCopy( + "/P/src/missing_proposals_for_static_fields_and_methods.java", + "\n" + + "class System {\n" + + " static Object out;\n" + + " static Object getEnv() { return null; }\n" + + "}\n" + + "class missing_proposals_for_static_fields_and_methods {\n" + + " void sample(String foo) {\n" + + " if (foo == null) {\n" + + " System. // <- missing: \"out\", \"getenv()\", etc. (similar to bug 574267)\n" + + " System.out.println();\n" + + " }\n" + + " System. // <- here content assist works fine\n" + + " }\n" + + "}\n"); + CompletionTestsRequestor2 requestor = new CompletionTestsRequestor2(true); + String str = this.workingCopies[0].getSource(); + String completeAfter = "System."; + int cursorLocation = str.indexOf(completeAfter) + completeAfter.length(); + this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); + assertResults( + "class[FIELD_REF]{class, null, Ljava.lang.Class<LSystem;>;, class, null, 51}\n" + + "getEnv[METHOD_REF]{getEnv(), LSystem;, ()Ljava.lang.Object;, getEnv, null, 51}\n" + + "out[FIELD_REF]{out, LSystem;, Ljava.lang.Object;, out, null, 51}", + requestor.getResults()); + } finally { + deleteProject("P"); + } +} +public void testBug575631_comment1b() throws Exception { + try { + createJavaProject("P", new String[] {"src"}, new String[]{"JCL11_LIB"}, "bin", "11"); + this.workingCopies = new ICompilationUnit[1]; + this.workingCopies[0] = getWorkingCopy( + "/P/src/missing_proposals_for_static_fields_and_methods.java", + "\n" + + "class System {\n" + + " static Object out;\n" + + " static Object getEnv() { return null; }\n" + + "}\n" + + "class missing_proposals_for_static_fields_and_methods {\n" + + " void sample(String foo) {\n" + + " if (foo == null) {\n" + + " sample(\"\");\n" + + " } else {\n" + + " System. // <- missing: \"out\", \"getenv()\", etc. (similar to bug 574215)\n" + + " System.out.println();\n" + + " }\n" + + " System. // <- here content assist works fine\n" + + " }\n" + + "}\n"); + CompletionTestsRequestor2 requestor = new CompletionTestsRequestor2(true); + String str = this.workingCopies[0].getSource(); + String completeAfter = "System."; + int cursorLocation = str.indexOf(completeAfter) + completeAfter.length(); + this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); + assertResults( + "class[FIELD_REF]{class, null, Ljava.lang.Class<LSystem;>;, class, null, 51}\n" + + "getEnv[METHOD_REF]{getEnv(), LSystem;, ()Ljava.lang.Object;, getEnv, null, 51}\n" + + "out[FIELD_REF]{out, LSystem;, Ljava.lang.Object;, out, null, 51}", + requestor.getResults()); + } finally { + deleteProject("P"); + } +} +public void testBug575631_comment3() throws Exception { + try { + createJavaProject("P", new String[] {"src"}, new String[]{"JCL11_LIB"}, "bin", "11"); + this.workingCopies = new ICompilationUnit[1]; + this.workingCopies[0] = getWorkingCopy( + "/P/src/X.java", + "class OutputStream {\n" + + " void println() {}\n" + + "}\n" + + "interface Runnable { void run(); }\n" + + "class System {\n" + + " static OutputStream out;\n" + + " static Object getEnv() { return null; }\n" + + "}\n" + + "class X {\n" + + " void foo() {\n" + + " Runnable r = () -> {\n" + + " System.out.\n" + + " System.out.println();\n" + + " };\n" + + " }\n" + + "}\n"); + CompletionTestsRequestor2 requestor = new CompletionTestsRequestor2(true); + String str = this.workingCopies[0].getSource(); + String completeAfter = "System.out."; + int cursorLocation = str.indexOf(completeAfter) + completeAfter.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" + + "finalize[METHOD_REF]{finalize(), Ljava.lang.Object;, ()V, finalize, 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" + + "println[METHOD_REF]{println(), LOutputStream;, ()V, println, 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()); + } finally { + deleteProject("P"); + } +} +public void testBug575631_comment3b() throws Exception { + // method invocation inside lambda in field initializer + try { + createJavaProject("P", new String[] {"src"}, new String[]{"JCL11_LIB"}, "bin", "11"); + this.workingCopies = new ICompilationUnit[1]; + this.workingCopies[0] = getWorkingCopy( + "/P/src/X.java", + "class OutputStream {\n" + + " void println() {}\n" + + "}\n" + + "interface Runnable { void run(); }\n" + + "class System {\n" + + " static OutputStream out;\n" + + " static Object getEnv() { return null; }\n" + + "}\n" + + "class X {\n" + + " Runnable r = () -> {\n" + + " System.out.\n" + + " System.out.println();\n" + + " };\n" + + "}\n"); + CompletionTestsRequestor2 requestor = new CompletionTestsRequestor2(true); + String str = this.workingCopies[0].getSource(); + String completeAfter = "System.out."; + int cursorLocation = str.indexOf(completeAfter) + completeAfter.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" + + "finalize[METHOD_REF]{finalize(), Ljava.lang.Object;, ()V, finalize, 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" + + "println[METHOD_REF]{println(), LOutputStream;, ()V, println, 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()); + } finally { + deleteProject("P"); + } +} +public void testBug575631_comment3c() throws Exception { + // variable declaration in lambda in field initializer + try { + createJavaProject("P", new String[] {"src"}, new String[]{"JCL11_LIB"}, "bin", "11"); + this.workingCopies = new ICompilationUnit[1]; + this.workingCopies[0] = getWorkingCopy( + "/P/src/X.java", + "class OutputStream {\n" + + " void println() {}\n" + + "}\n" + + "interface Consumer { void consume(int); }\n" + + "class Number{}\n" + + "class System {\n" + + " static OutputStream out;\n" + + " static Object getEnv() { return null; }\n" + + "}\n" + + "class X {\n" + + " Consumer r = (int number) -> {\n" + + " Number \n" + + " System.out.println();\n" + + " };\n" + + "}\n"); + CompletionTestsRequestor2 requestor = new CompletionTestsRequestor2(true); + String str = this.workingCopies[0].getSource(); + String completeAfter = "Number "; + int cursorLocation = str.indexOf(completeAfter) + completeAfter.length(); + this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); + assertResults( + "number[VARIABLE_DECLARATION]{number, null, LNumber;, number, null, 48}", // FIXME: should be number2 => https://bugs.eclipse.org/576781 + requestor.getResults()); + } finally { + deleteProject("P"); + } +} +public void testBug575631_comment3d() throws Exception { + // first of two arguments in method invocation in lambda in field initializer + // overloads should be selected by the existing second argument + // no separating ',' yet. + try { + createJavaProject("P", new String[] {"src"}, new String[]{"JCL11_LIB"}, "bin", "11"); + this.workingCopies = new ICompilationUnit[1]; + this.workingCopies[0] = getWorkingCopy( + "/P/src/X.java", + "interface BiConsumer { void consume(int,boolean); }\n" + + "class X {\n" + + " BiConsumer r = (int number, boolean bool) -> {\n" + + " bar( number);\n" + + " };\n" + + " void bar(int i, String s) {}\n" + + " void bar(boolean b, int j) {}\n" + + "}\n"); + CompletionTestsRequestor2 requestor = new CompletionTestsRequestor2(true); + String str = this.workingCopies[0].getSource(); + String completeAfter = "bar("; + int cursorLocation = str.indexOf(completeAfter) + completeAfter.length(); + this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); + assertResults( + "bar[METHOD_REF]{, LX;, (ZI)V, bar, (b, j), 56}", // select overload with int as 2nd arg + 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 8e9491893c..234c953c78 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 @@ -2000,7 +2000,7 @@ public final class CompletionEngine } else if (astNode instanceof CompletionOnQualifiedNameReference) { completionOnQualifiedNameReference(astNode, enclosingNode, qualifiedBinding, scope, insideTypeAnnotation); } else if (astNode instanceof CompletionOnQualifiedTypeReference) { - completionOnQualifiedTypeReference(astNode, astNodeParent, qualifiedBinding, scope); + completionOnQualifiedTypeReference(astNode, astNodeParent, enclosingNode, qualifiedBinding, scope); } else if (astNode instanceof CompletionOnMemberAccess) { completionOnMemberAccess(astNode, enclosingNode, qualifiedBinding, scope, insideTypeAnnotation); } else if (astNode instanceof CompletionOnMessageSend) { @@ -3581,8 +3581,15 @@ public final class CompletionEngine CompletionOnQualifiedNameReference ref = (CompletionOnQualifiedNameReference) astNode; this.completionToken = ref.completionIdentifier; - long completionPosition = ref.sourcePositions[ref.sourcePositions.length - 1]; + internalCompletionOnQualifiedReference(astNode, enclosingNode, qualifiedBinding, scope, insideTypeAnnotation, + ref.isInsideAnnotationAttribute, ref, ref.tokens, ref.sourcePositions); + } + /** Unified handling for true QualifiedNameReference and misclassified QualifiedTypeReference. */ + private void internalCompletionOnQualifiedReference(ASTNode ref, ASTNode enclosingNode, Binding qualifiedBinding, Scope scope, + boolean insideTypeAnnotation, boolean isInsideAnnotationAttribute, InvocationSite site, char[][] tokens, long[] sourcePositions) + { + long completionPosition = sourcePositions[sourcePositions.length - 1]; if (qualifiedBinding.problemId() == ProblemReasons.NotFound) { setSourceAndTokenRange((int) (completionPosition >>> 32), (int) completionPosition); // complete field members with missing fields type @@ -3596,20 +3603,20 @@ public final class CompletionEngine (this.requestor.isAllowingRequiredProposals(CompletionProposal.FIELD_REF, CompletionProposal.TYPE_REF) || this.requestor.isAllowingRequiredProposals(CompletionProposal.METHOD_REF, CompletionProposal.TYPE_REF) || this.requestor.isAllowingRequiredProposals(CompletionProposal.TYPE_REF, CompletionProposal.TYPE_REF))) { - if(ref.tokens.length == 1) { - boolean foundSomeFields = findFieldsAndMethodsFromMissingFieldType(ref.tokens[0], scope, ref, insideTypeAnnotation); + if(tokens.length == 1) { + boolean foundSomeFields = findFieldsAndMethodsFromMissingFieldType(tokens[0], scope, site, insideTypeAnnotation); if (!foundSomeFields) { checkCancel(); findMembersFromMissingType( - ref.tokens[0], - ref.sourcePositions[0], + tokens[0], + sourcePositions[0], null, scope, - ref, - ref.isInsideAnnotationAttribute); + site, + isInsideAnnotationAttribute); } } } @@ -3626,7 +3633,7 @@ public final class CompletionEngine scope, fieldsFound, methodsFound, - ref, + site, scope, false, false, @@ -3640,15 +3647,17 @@ public final class CompletionEngine checkCancel(); - findFieldsAndMethodsFromCastedReceiver( + if (ref instanceof Expression) { + findFieldsAndMethodsFromCastedReceiver( enclosingNode, qualifiedBinding, scope, fieldsFound, methodsFound, - ref, + site, scope, - ref); + (Expression) ref); + } } else if (this.assistNodeInJavadoc == 0 && (this.requestor.isAllowingRequiredProposals(CompletionProposal.FIELD_REF, CompletionProposal.TYPE_REF) || @@ -3656,7 +3665,7 @@ public final class CompletionEngine boolean proposeField = !this.requestor.isIgnored(CompletionProposal.FIELD_REF); boolean proposeMethod = !this.requestor.isIgnored(CompletionProposal.METHOD_REF); if (proposeField || proposeMethod) { - if(ref.tokens.length == 1) { + if(tokens.length == 1) { if (qualifiedBinding instanceof LocalVariableBinding) { // complete local variable members with missing variables type // class X { @@ -3669,7 +3678,7 @@ public final class CompletionEngine findFieldsAndMethodsFromMissingType( localVariableBinding.declaration.type, localVariableBinding.declaringScope, - ref, + site, scope); } else { // complete field members with missing fields type @@ -3679,7 +3688,7 @@ public final class CompletionEngine // f.| // } // } - findFieldsAndMethodsFromMissingFieldType(ref.tokens[0], scope, ref, insideTypeAnnotation); + findFieldsAndMethodsFromMissingFieldType(tokens[0], scope, site, insideTypeAnnotation); } } @@ -3687,7 +3696,6 @@ public final class CompletionEngine } } else if (qualifiedBinding instanceof ReferenceBinding && !(qualifiedBinding instanceof TypeVariableBinding)) { - boolean isInsideAnnotationAttribute = ref.isInsideAnnotationAttribute; ReferenceBinding receiverType = (ReferenceBinding) qualifiedBinding; setSourceAndTokenRange((int) (completionPosition >>> 32), (int) completionPosition); @@ -3695,7 +3703,7 @@ public final class CompletionEngine this.completionToken, receiverType, scope, - ref, + site, isInsideAnnotationAttribute, null, null, @@ -3704,7 +3712,7 @@ public final class CompletionEngine } else if (qualifiedBinding instanceof PackageBinding) { - setSourceRange(astNode.sourceStart, (int) completionPosition); + setSourceRange(ref.sourceStart, (int) completionPosition); setTokenRange((int) (completionPosition >>> 32), (int) completionPosition); // replace to the end of the completion identifier @@ -3712,7 +3720,7 @@ public final class CompletionEngine } } - private void completionOnQualifiedTypeReference(ASTNode astNode, ASTNode astNodeParent, Binding qualifiedBinding, + private void completionOnQualifiedTypeReference(ASTNode astNode, ASTNode astNodeParent, ASTNode enclosingNode, Binding qualifiedBinding, Scope scope) { this.insideQualifiedReference = true; @@ -3730,6 +3738,8 @@ public final class CompletionEngine this.completionToken = ref.completionIdentifier; long completionPosition = ref.sourcePositions[ref.tokens.length]; + boolean haveTypeProposals = false; + // get the source positions of the completion identifier if (qualifiedBinding.problemId() == ProblemReasons.NotFound) { setSourceAndTokenRange((int) (completionPosition >>> 32), (int) completionPosition); @@ -3744,16 +3754,6 @@ public final class CompletionEngine } } else if (qualifiedBinding instanceof ReferenceBinding && !(qualifiedBinding instanceof TypeVariableBinding)) { ReferenceBinding receiverType = (ReferenceBinding) qualifiedBinding; - if (astNodeParent instanceof LocalDeclaration && ref.nextToken == TerminalTokens.TokenNameLPAREN && !this.assistNodeIsConstructor) { - // the subsequent '(' makes the interpretation as LocalDeclaration illegal (unless it's "new prefix.token()"). - // therefore we assume that "name(" (where name is LocalDeclaration.name) is the start of a new statement, - // and propose *everything* that can be referenced via the receiverType: - findMethods(this.completionToken, null, null, receiverType, scope, new ObjectVector(), true/*onlyStatic*/, false, - FakeInvocationSite, scope, false, false, false, null, null, null, false, null, -1, -1); - findFields(this.completionToken, receiverType, scope, new ObjectVector(), new ObjectVector(), true/*onlyStatic*/, - FakeInvocationSite, scope, false, false, null, null, null, false, null, -1, -1); - // fall through to propose member types - } if (!this.requestor.isIgnored(CompletionProposal.TYPE_REF)) { setSourceAndTokenRange((int) (completionPosition >>> 32), (int) completionPosition); @@ -3782,6 +3782,7 @@ public final class CompletionEngine null, null, false); + haveTypeProposals = typesFound.size() > 0; } } else if (qualifiedBinding instanceof PackageBinding) { @@ -3790,24 +3791,17 @@ public final class CompletionEngine // replace to the end of the completion identifier findTypesAndSubpackages(this.completionToken, (PackageBinding) qualifiedBinding, scope); } - ASTNode parentNode = this.parser.assistNodeParent; - if (ref.tokens.length > 0 && parentNode instanceof LocalDeclaration && ((LocalDeclaration) parentNode).type == ref) { - // additionally check if 'prefix.' should be interpreted as a variable receiver rather then part of a type reference: - Binding variable = scope.getBinding(ref.tokens[0], Binding.VARIABLE, FakeInvocationSite, true); - lookupViaVariable: if (variable instanceof VariableBinding) { - TypeBinding receiverType = ((VariableBinding) variable).type; - int len = ref.tokens.length; - for (int i=1; i<len; i++) { - // lookup subsequent fields in 'prefix.q.r.' - if (!(receiverType instanceof ReferenceBinding && receiverType.isValidBinding())) - break lookupViaVariable; - FieldBinding field = scope.getField(receiverType, ref.tokens[i], FakeInvocationSite); - if (!field.isValidBinding()) - break lookupViaVariable; - receiverType = field.type; - } - if (receiverType instanceof ReferenceBinding && receiverType.isValidBinding()) { - findFieldsAndMethods(this.completionToken, receiverType, scope, new ObjectVector(), new ObjectVector(), FakeInvocationSite, scope, false, false, null, null, null, false, null, ref.sourceStart, (int)ref.sourcePositions[0]); + // alternatively interpret tokens in a misclassified LocalDeclaration like a QualifiedNameReference: + if (astNodeParent instanceof LocalDeclaration && enclosingNode != null) { // enclosingNode == null when called from completionOnProvidesInterfacesQualifiedTypeReference + if (scope instanceof BlockScope) { + // resolve tokens like it's done in CompletionOnQualifiedNameReference: + qualifiedBinding = ((BlockScope) scope).getBinding(ref.tokens, FakeInvocationSite); + boolean ignoreType = this.requestor.isIgnored(CompletionProposal.TYPE_REF); + try { + this.requestor.setIgnored(CompletionProposal.TYPE_REF, haveTypeProposals); // temp ignore types if already proposed above + internalCompletionOnQualifiedReference(ref, enclosingNode, qualifiedBinding, scope, false, false, FakeInvocationSite, ref.tokens, ref.sourcePositions); + } finally { + this.requestor.setIgnored(CompletionProposal.TYPE_REF, ignoreType); } } } @@ -3815,7 +3809,7 @@ public final class CompletionEngine private void completionOnProvidesInterfacesQualifiedTypeReference(ASTNode astNode, ASTNode astNodeParent, Binding qualifiedBinding, Scope scope) { // TODO: Filter the results wrt accessibility and add relevance to the results. - completionOnQualifiedTypeReference(astNode, astNodeParent, qualifiedBinding, scope); + completionOnQualifiedTypeReference(astNode, astNodeParent, null, qualifiedBinding, scope); } private void completionOnProvidesImplementationsQualifiedTypeReference(ASTNode astNode, ASTNode astNodeParent, Binding qualifiedBinding, Scope scope) { @@ -7260,7 +7254,7 @@ public final class CompletionEngine int nextPosition = 0; do { ReferenceBinding[] itsInterfaces = currentType.superInterfaces(); - if (notInJavadoc && itsInterfaces != Binding.NO_SUPERINTERFACES) { + if (notInJavadoc && itsInterfaces != null && itsInterfaces != Binding.NO_SUPERINTERFACES) { if (interfacesToVisit == null) { interfacesToVisit = itsInterfaces; nextPosition = interfacesToVisit.length; @@ -10080,7 +10074,9 @@ public final class CompletionEngine isInterface = receiverType.isInterface(); } if (!isInterface) { - findKeywords(token, new char[][] { Keywords.THIS, Keywords.SUPER }, true, false); + if (hasCompatibleEnclosing(scope, receiverType)) { + findKeywords(token, new char[][] { Keywords.THIS, Keywords.SUPER }, true, false); + } } else { boolean isEqual = false; char[] enclosingSourceName = null; @@ -10161,6 +10157,16 @@ public final class CompletionEngine } } + private boolean hasCompatibleEnclosing(Scope scope, ReferenceBinding receiverType) { + ReferenceBinding enclosing = scope.enclosingSourceType(); + while (enclosing != null) { + if (enclosing.isCompatibleWith(receiverType, scope)) + return true; + enclosing = enclosing.enclosingType(); + } + return false; + } + private void findMembersFromMissingType( final char[] token, final long pos, diff --git a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/complete/CompletionOnQualifiedTypeReference.java b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/complete/CompletionOnQualifiedTypeReference.java index 4ab064be45..c69faafc84 100644 --- a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/complete/CompletionOnQualifiedTypeReference.java +++ b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/complete/CompletionOnQualifiedTypeReference.java @@ -40,7 +40,6 @@ public class CompletionOnQualifiedTypeReference extends QualifiedTypeReference { public char[] completionIdentifier; public boolean isConstructorType; - public int nextToken; public CompletionOnQualifiedTypeReference(char[][] previousIdentifiers, char[] completionIdentifier, long[] positions) { this(previousIdentifiers, completionIdentifier, positions, K_TYPE); @@ -50,10 +49,6 @@ public CompletionOnQualifiedTypeReference(char[][] previousIdentifiers, char[] c this.completionIdentifier = completionIdentifier; this.kind = kind; } -public CompletionOnQualifiedTypeReference(char[][] previousIdentifiers, char[] assistName, long[] positions, int kind, int nextToken) { - this(previousIdentifiers, assistName, positions, kind); - this.nextToken = nextToken; -} @Override public void aboutToResolve(Scope scope) { getTypeBinding(scope); 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 310dbc5f38..7485157341 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 @@ -19,6 +19,7 @@ package org.eclipse.jdt.internal.codeassist.complete; import java.util.ArrayList; +import java.util.Arrays; /* * Parser able to build specific completion parse nodes, given a cursorLocation. @@ -4020,11 +4021,22 @@ protected int fetchNextToken() throws InvalidInputException { 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)) - { - // <emptyAssistIdentifier> <someLiteral> is illegal and most likely the literal should be replaced => discard it now - return fetchNextToken(); + && this.identifierStack[this.identifierPtr].length == 0) { + if (Scanner.isLiteral(token)) { + // <emptyAssistIdentifier> <someLiteral> is illegal and most likely the literal should be replaced => discard it now + return fetchNextToken(); + } + if (token == TerminalTokens.TokenNameIdentifier) { + // empty completion identifier followed by another identifier likely means the 2nd id should start another parse node. + // To cleanly separate them find a suitable separator: + this.scanner.currentPosition = this.scanner.startPosition; // return to startPosition after this charade + if (this.unstackedAct < ERROR_ACTION) { + if ("LocalVariableDeclaration".equals(reduce(TokenNameSEMICOLON))) //$NON-NLS-1$ + return TokenNameSEMICOLON; // send ';' to terminate a local variable declaration + if ("ArgumentList".equals(reduce(TokenNameCOMMA))) //$NON-NLS-1$ + return TokenNameCOMMA; // send ',' to terminate an argument in the list + } + } } } if (!this.diet) { // only when parsing a method body: @@ -4043,6 +4055,47 @@ protected int fetchNextToken() throws InvalidInputException { } return token; } +/* + * Variant of parse() without side effects that stops when another token would need to be fetched. + * Returns the name of the last reduced rule, or empty string if nothing reduced, or null if error was detected. + */ +String reduce(int token) { + int myStackTop = this.stateStackTop; + int[] myStack = Arrays.copyOf(this.stack, this.stack.length); + int act = this.unstackedAct; + String reduceName = ""; //$NON-NLS-1$ + for (;;) { + int stackLength = myStack.length; + if (++myStackTop >= stackLength) { + System.arraycopy( + myStack, 0, + myStack = new int[stackLength + StackIncrement], 0, + stackLength); + } + myStack[myStackTop] = act; + act = tAction(act, token); + if (act == ERROR_ACTION) + return null; + if (act <= NUM_RULES) { // reduce + myStackTop--; + } else if (act > ERROR_ACTION) { // shift-reduce + return reduceName; // ready to accept the next token + } else { + if (act < ACCEPT_ACTION) { // shift + return reduceName; // ready to accept the next token + } + return reduceName; // ? + } + do { + reduceName = name[non_terminal_index[lhs[act]]]; + myStackTop -= (rhs[act] - 1); + act = ntAction(myStack[myStackTop], lhs[act]); + if (act == ACCEPT_ACTION) { + return reduceName; + } + } while (act <= NUM_RULES); + } +} @Override protected void consumeToken(int token) { if(this.isFirst) { @@ -5062,7 +5115,7 @@ public TypeReference createQualifiedAssistTypeReference(char[][] previousIdentif if (ref != null) return ref; return new CompletionOnQualifiedTypeReference(previousIdentifiers, assistName, positions, - CompletionOnQualifiedTypeReference.K_TYPE, this.currentToken); + CompletionOnQualifiedTypeReference.K_TYPE); } } @Override |