diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/CompletionTests14.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/CompletionTests14.java index acdfc80f037..0d14b8063db 100644 --- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/CompletionTests14.java +++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/CompletionTests14.java @@ -983,4 +983,147 @@ public void testBugGH1095_2() throws Exception { "implementMe[METHOD_DECLARATION]{public void implementMe(), LBaseInterface;, ()V, implementMe, null, " + (R_DEFAULT + R_RESOLVED + R_INTERESTING + R_CASE + R_ABSTRACT_METHOD + R_UNQUALIFIED + R_NON_RESTRICTED) + "}", requestor.getResults()); } + + public void testGH1561_CompletionInIfConditionInsideASwitchStatement() throws JavaModelException { + this.workingCopies = new ICompilationUnit[1]; + this.workingCopies[0] = getWorkingCopy("/Completion/src/SwitchIf.java", + """ + public class SwitchIf { + final String name = "test"; + boolean namedFlag; + enum Type { A } + private void foo(Type input) { + switch (input) { + case A: + if (nam) + break; + } + } + } + """); + CompletionTestsRequestor2 requestor = new CompletionTestsRequestor2(true, true, true, false); + String str = this.workingCopies[0].getSource(); + String completeBehind = "if (nam"; + int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); + this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); + assertResults( + "name[FIELD_REF]{name, LSwitchIf;, Ljava.lang.String;, null, null, name, null, [165, 168], " + + (R_DEFAULT + R_RESOLVED + R_INTERESTING + R_CASE + R_UNQUALIFIED + R_NON_RESTRICTED) + "}\n" + + "namedFlag[FIELD_REF]{namedFlag, LSwitchIf;, Z, null, null, namedFlag, null, [165, 168], " + + (R_DEFAULT + R_RESOLVED + R_INTERESTING + R_CASE + R_UNQUALIFIED + R_NON_RESTRICTED + R_EXACT_EXPECTED_TYPE)+"}", + requestor.getResults()); + } + + public void testGH1561_CompletionInWhileConditionInsideASwitchStatement() throws JavaModelException { + this.workingCopies = new ICompilationUnit[1]; + this.workingCopies[0] = getWorkingCopy("/Completion/src/SwitchIf.java", + """ + public class SwitchIf { + final String name = "test"; + enum Type { A } + private void foo(Type input) { + switch (input) { + case A: + while (nam) + break; + } + } + } + """); + CompletionTestsRequestor2 requestor = new CompletionTestsRequestor2(true, true, true, false); + String str = this.workingCopies[0].getSource(); + String completeBehind = "while (nam"; + int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); + this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); + assertResults( + "name[FIELD_REF]{name, LSwitchIf;, Ljava.lang.String;, null, null, name, null, [147, 150], " + + (R_DEFAULT + R_RESOLVED + R_INTERESTING + R_CASE + R_UNQUALIFIED + R_NON_RESTRICTED) + "}", + requestor.getResults()); + } + + public void testGH1561_CompletionInForConditionInsideASwitchStatement() throws JavaModelException { + this.workingCopies = new ICompilationUnit[1]; + this.workingCopies[0] = getWorkingCopy("/Completion/src/SwitchIf.java", + """ + public class SwitchIf { + final String name = "test"; + enum Type { A } + private void foo(Type input) { + switch (input) { + case A: + for (int i = 0; i < nam) + break; + } + } + } + """); + CompletionTestsRequestor2 requestor = new CompletionTestsRequestor2(true, true, true, false); + String str = this.workingCopies[0].getSource(); + String completeBehind = "for (int i = 0; i < nam"; + int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); + this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); + assertResults( + "name[FIELD_REF]{name, LSwitchIf;, Ljava.lang.String;, null, null, name, null, [160, 163], " + + (R_DEFAULT + R_RESOLVED + R_INTERESTING + R_CASE + R_UNQUALIFIED + R_NON_RESTRICTED) + "}", + requestor.getResults()); + } + + public void testGH1561_CompletionInSwitchInsideASwitchStatement() throws JavaModelException { + this.workingCopies = new ICompilationUnit[1]; + this.workingCopies[0] = getWorkingCopy("/Completion/src/SwitchIf.java", + """ + public class SwitchIf { + final String name = "test"; + enum Type { A } + private void foo(Type input) { + switch (input) { + case A: + switch (nam + break; + } + } + } + """); + CompletionTestsRequestor2 requestor = new CompletionTestsRequestor2(true, true, true, false); + String str = this.workingCopies[0].getSource(); + String completeBehind = "switch (nam"; + int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); + this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); + assertResults( + "name[FIELD_REF]{name, LSwitchIf;, Ljava.lang.String;, null, null, name, null, [148, 151], " + + (R_DEFAULT + R_RESOLVED + R_INTERESTING + R_CASE + R_UNQUALIFIED + R_NON_RESTRICTED) + "}", + requestor.getResults()); + } + + public void testGH1561_CompletionInForInsideASwitchStatement() throws JavaModelException { + this.workingCopies = new ICompilationUnit[1]; + this.workingCopies[0] = getWorkingCopy("/Completion/src/SwitchIf.java", + """ + public class SwitchIf { + final String name = "test"; + enum Type { A } + private void foo(Type input) { + switch (input) { + case A: + for (int i = 0; nam) + break; + } + } + private boolean nameContains(String name) { + return false; + } + } + """); + CompletionTestsRequestor2 requestor = new CompletionTestsRequestor2(true, true, true, false); + String str = this.workingCopies[0].getSource(); + String completeBehind = "for (int i = 0; nam"; + int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); + this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); + assertResults("name[FIELD_REF]{name, LSwitchIf;, Ljava.lang.String;, null, null, name, null, [156, 159], " + + (R_DEFAULT + R_RESOLVED + R_INTERESTING + R_CASE + R_UNQUALIFIED + R_NON_RESTRICTED) + "}\n" + + "nameContains[METHOD_REF]{nameContains(), LSwitchIf;, (Ljava.lang.String;)Z, null, null, nameContains, (name), [156, 159], " + + (R_DEFAULT + R_RESOLVED + R_INTERESTING + R_CASE + R_UNQUALIFIED + R_NON_RESTRICTED + + R_EXACT_EXPECTED_TYPE) + + "}", requestor.getResults()); + } } 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 5813614b4c2..4a8cd647bd0 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 @@ -5950,8 +5950,10 @@ public void testBug574823_completeOn_methodInvocationWithParams_inIfConidtion_in this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); String result = requestor.getResults(); - assertTrue(String.format("Result doesn't contain method forEach (%s)", result), - result.contains("forEach[METHOD_REF]{forEach(), Ljava.lang.Iterable;, (Ljava.util.function.Consumer<-Ljava.lang.String;>;)V, null, null, forEach, (arg0), replace[149, 149], token[149, 149], 60}")); + int relevance = R_DEFAULT + R_RESOLVED + R_INTERESTING + R_CASE + R_VOID + R_NON_STATIC + R_NON_RESTRICTED; + assertTrue(String.format("Result doesn't contain method forEach (%s)", result), + result.contains("forEach[METHOD_REF]{forEach(), Ljava.lang.Iterable;, (Ljava.util.function.Consumer<-Ljava.lang.String;>;)V," + + " null, null, forEach, (arg0), replace[149, 149], token[149, 149], "+relevance+"}")); } public void testBug574823_completeOn_methodInvocationWithParams_inIfConidtion_insideIf_followedByChainedStatment() throws Exception { this.workingCopies = new ICompilationUnit[1]; @@ -5974,8 +5976,10 @@ public void testBug574823_completeOn_methodInvocationWithParams_inIfConidtion_in this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); String result = requestor.getResults(); - assertTrue(String.format("Result doesn't contain method forEach (%s)", result), - result.contains("forEach[METHOD_REF]{forEach(), Ljava.lang.Iterable;, (Ljava.util.function.Consumer<-Ljava.lang.String;>;)V, null, null, forEach, (arg0), replace[149, 149], token[149, 149], 60}")); + int relevance = R_DEFAULT + R_RESOLVED + R_INTERESTING + R_CASE + R_VOID + R_NON_STATIC + R_NON_RESTRICTED; + assertTrue(String.format("Result doesn't contain method forEach (%s)", result), + result.contains("forEach[METHOD_REF]{forEach(), Ljava.lang.Iterable;, (Ljava.util.function.Consumer<-Ljava.lang.String;>;)V," + + " null, null, forEach, (arg0), replace[149, 149], token[149, 149], "+relevance+"}")); } public void testBug574823_completeOn_methodInvocationWithParams_inWhileConidtion_insideWhileBlock_followedByChainedStatment() throws Exception { this.workingCopies = new ICompilationUnit[1]; @@ -5999,8 +6003,10 @@ public void testBug574823_completeOn_methodInvocationWithParams_inWhileConidtion this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); String result = requestor.getResults(); - assertTrue(String.format("Result doesn't contain method forEach (%s)", result), - result.contains("forEach[METHOD_REF]{forEach(), Ljava.lang.Iterable;, (Ljava.util.function.Consumer<-Ljava.lang.String;>;)V, null, null, forEach, (arg0), replace[152, 152], token[152, 152], 60}")); + int relevance = R_DEFAULT + R_RESOLVED + R_INTERESTING + R_CASE + R_VOID + R_NON_STATIC + R_NON_RESTRICTED; + assertTrue(String.format("Result doesn't contain method forEach (%s)", result), + result.contains("forEach[METHOD_REF]{forEach(), Ljava.lang.Iterable;, (Ljava.util.function.Consumer<-Ljava.lang.String;>;)V," + + " null, null, forEach, (arg0), replace[152, 152], token[152, 152], "+relevance+"}")); } public void testBug574823_completeOn_methodInvocationWithParams_inIfConidtionWithExpression_insideIfBlock_followedByChainedStatment() throws Exception { this.workingCopies = new ICompilationUnit[1]; diff --git a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/complete/CompletionNodeDetector.java b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/complete/CompletionNodeDetector.java index 3f2f490fa32..1841b12e7cb 100644 --- a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/complete/CompletionNodeDetector.java +++ b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/complete/CompletionNodeDetector.java @@ -40,6 +40,10 @@ protected boolean visitNode(ASTNode node) { return !this.found; } } + + /** Sentinel that signals when searchNode was found as a child of a potential parent but in an unsupported location of it. */ + private static final ASTNode NOT_A_PARENT = new NullLiteral(0, 0); + public static boolean findAny(CompilationUnitDeclaration unit, ASTNode searchFor) { FindAny visitor = new FindAny(searchFor); unit.traverse(visitor, (CompilationUnitScope)null, false); @@ -82,6 +86,8 @@ public boolean containsCompletionNode() { } public ASTNode getCompletionNodeParent() { + if (this.parent == NOT_A_PARENT) + return null; return this.parent; } public Expression getCompletionNodeOuterExpression() { @@ -172,6 +178,12 @@ public void endVisit(GuardedPattern guardedPattern, BlockScope scope) { @Override public void endVisit(IfStatement ifStatement, BlockScope scope) { this.interestingEnclosings.pop(); + endVisit(ifStatement); + if (this.parent == ifStatement && this.searchedNode != ifStatement.condition) { + // searchNode was found as a child of the ifStatement, but in a wrong position (only condition is supported) + // Remove the unwanted parent, but at the same time signal that we should not look for a parent in any enclosing ASTNode: + this.parent = NOT_A_PARENT; + } } @Override public void endVisit(InstanceOfExpression instanceOfExpression, BlockScope scope) { @@ -299,6 +311,15 @@ public void endVisit(ConstructorDeclaration constructorDeclaration, ClassScope s throw new StopTraversal(); // don't associate with out-of-scope outer expression } @Override + public void endVisit(WhileStatement whileStatement, BlockScope scope) { + endVisit(whileStatement); + if (this.parent == whileStatement && this.searchedNode != whileStatement.condition) { + // searchNode was found as a child of the whileStatement, but in a wrong position (only condition is supported) + // Remove the unwanted parent, but at the same time signal that we should not look for a parent in any enclosing ASTNode: + this.parent = NOT_A_PARENT; + } + } + @Override public boolean visit(AllocationExpression allocationExpression, BlockScope scope) { return this.visit(allocationExpression); }