Java: Avoid duplicates for shadowed members in completion list for arguments of reflection calls. Simplify building the list of method names with PsiClass.getVisibleSignatures() (IDEA-167250)

This commit is contained in:
Pavel Dolgov
2017-03-02 17:23:42 +03:00
parent 9f2db277e9
commit aedc783354
25 changed files with 213 additions and 29 deletions

View File

@@ -20,6 +20,7 @@ import com.intellij.codeInsight.completion.InsertionContext;
import com.intellij.codeInsight.completion.JavaLookupElementBuilder;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.psi.*;
import com.intellij.psi.util.MethodSignatureBackedByPsiMethod;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtilCore;
import com.intellij.util.IncorrectOperationException;
@@ -30,7 +31,6 @@ import org.jetbrains.annotations.Nullable;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
import static com.intellij.psi.impl.source.resolve.reference.impl.JavaReflectionReferenceUtil.*;
@@ -119,16 +119,19 @@ public class JavaLangClassMemberReference extends PsiReferenceBase<PsiLiteralExp
case DECLARED_FIELD:
return Arrays.stream(psiClass.getFields())
.filter(field -> field.getName() != null)
.sorted(Comparator.comparing(PsiField::getName))
.map(field -> lookupField(field))
.toArray();
case FIELD:
case FIELD: {
final Set<String> uniqueNames = new THashSet<>();
return Arrays.stream(psiClass.getAllFields())
.filter(field -> isPotentiallyAccessible(field, psiClass))
.filter(field -> isPotentiallyAccessible(field, psiClass) && field.getName() != null && uniqueNames.add(field.getName()))
.sorted(Comparator.comparingInt((PsiField field) -> isPublic(field) ? 0 : 1).thenComparing(PsiField::getName))
.map(field -> withPriority(lookupField(field), isPublic(field)))
.toArray();
}
case DECLARED_METHOD:
return Arrays.stream(psiClass.getMethods())
@@ -138,16 +141,10 @@ public class JavaLangClassMemberReference extends PsiReferenceBase<PsiLiteralExp
.toArray();
case METHOD: {
final List<PsiMethod> methods = ContainerUtil.filter(
psiClass.getAllMethods(), method -> isRegularMethod(method) && isPotentiallyAccessible(method, psiClass));
final Set<PsiMethod> superMethods = new THashSet<>();
for (PsiMethod method : methods) {
ContainerUtil.addAll(superMethods, method.findSuperMethods());
}
return methods.stream()
.filter(method -> !superMethods.contains(method))
return psiClass.getVisibleSignatures()
.stream()
.map(MethodSignatureBackedByPsiMethod::getMethod)
.filter(method -> isRegularMethod(method) && isPotentiallyAccessible(method, psiClass))
.sorted(Comparator.comparingInt((PsiMethod method) -> getMethodSortOrder(method)).thenComparing(PsiMethod::getName))
.map(method -> withPriority(lookupMethod(method), -getMethodSortOrder(method)))
.toArray();

View File

@@ -23,4 +23,5 @@ class Test {
public int num;
public int num2;
int num1;
private int num0;
}

View File

@@ -23,4 +23,5 @@ class Test {
public int num;
public int num2;
int num1;
private int num0;
}

View File

@@ -23,6 +23,7 @@ class Test {
public void method(){}
public void method2(A a, B b){}
void method1(){}
private void method0(C c) {}
}
class A {}

View File

@@ -23,6 +23,7 @@ class Test {
public void method(){}
public void method2(A a, B b){}
void method1(){}
private void method0(C c) {}
}
class A {}

View File

@@ -7,9 +7,11 @@ class Main {
class Test extends Parent {
public int num;
int num3;
private int num5;
}
class Parent {
public int num2;
int num4;
private int num6;
}

View File

@@ -7,9 +7,11 @@ class Main {
class Test extends Parent {
public int num;
int num3;
private int num5;
}
class Parent {
public int num2;
int num4;
private int num6;
}

View File

@@ -7,9 +7,11 @@ class Main {
class Test extends Parent {
public void method(){}
void method3(){}
private void method5(){}
}
class Parent {
public void method2(){}
void method4(){}
private void method6(){}
}

View File

@@ -7,9 +7,11 @@ class Main {
class Test extends Parent {
public void method(){}
void method3(){}
private void method5(){}
}
class Parent {
public void method2(){}
void method4(){}
private void method6(){}
}

View File

@@ -7,9 +7,11 @@ class Main {
class Test extends Parent {
public int num;
void int num3;
private int num5;
}
class Parent {
public int num2;
int num4;
private int num6;
}

View File

@@ -7,9 +7,11 @@ class Main {
class Test extends Parent {
public int num;
void int num3;
private int num5;
}
class Parent {
public int num2;
int num4;
private int num6;
}

View File

@@ -7,9 +7,11 @@ class Main {
class Test extends Parent {
public void method(){}
void method3(){}
private void method5(){}
}
class Parent {
public void method2(){}
void method4(){}
private void method6(){}
}

View File

@@ -7,9 +7,11 @@ class Main {
class Test extends Parent {
public void method(){}
void method3(){}
private void method5(){}
}
class Parent {
public void method2(){}
void method4(){}
private void method6(){}
}

View File

@@ -0,0 +1,13 @@
class Main {
void foo() {
Test.class.getField("<caret>");
}
}
class Test extends Parent {
public int shadowed;
}
class Parent {
public int shadowed;
}

View File

@@ -0,0 +1,13 @@
class Main {
void foo() {
Test.class.getField("shadowed");
}
}
class Test extends Parent {
public int shadowed;
}
class Parent {
public int shadowed;
}

View File

@@ -0,0 +1,20 @@
class Main {
void foo() {
Test.class.getField("<caret>");
}
}
class Test extends Parent {
}
class Parent extends Grand {
public int shadowed;
}
class Grand extends GrandGrand {
public int shadowed;
}
class GrandGrand {
public int shadowed;
}

View File

@@ -0,0 +1,20 @@
class Main {
void foo() {
Test.class.getField("shadowed");
}
}
class Test extends Parent {
}
class Parent extends Grand {
public int shadowed;
}
class Grand extends GrandGrand {
public int shadowed;
}
class GrandGrand {
public int shadowed;
}

View File

@@ -0,0 +1,13 @@
class Main {
void foo() {
Test.class.getMethod("<caret>");
}
}
class Test extends Parent {
public static void overloaded(int n){}
}
class Parent {
public static void overloaded(String s){}
}

View File

@@ -0,0 +1,13 @@
class Main {
void foo() {
Test.class.getMethod("overloaded", String.class);
}
}
class Test extends Parent {
public static void overloaded(int n){}
}
class Parent {
public static void overloaded(String s){}
}

View File

@@ -0,0 +1,10 @@
class Main {
void foo() {
Test.class.getMethod("<caret>");
}
}
class Test {
public void overloaded(){}
public void overloaded(int n){}
}

View File

@@ -0,0 +1,10 @@
class Main {
void foo() {
Test.class.getMethod("overloaded", int.class);
}
}
class Test {
public void overloaded(){}
public void overloaded(int n){}
}

View File

@@ -0,0 +1,13 @@
class Main {
void foo() {
Test.class.getMethod("<caret>");
}
}
class Test extends Parent {
public static void shadowed(){}
}
class Parent {
public static void shadowed(){}
}

View File

@@ -0,0 +1,13 @@
class Main {
void foo() {
Test.class.getMethod("shadowed");
}
}
class Test extends Parent {
public static void shadowed(){}
}
class Parent {
public static void shadowed(){}
}

View File

@@ -45,6 +45,18 @@ class JavaReflectionCompletionOverloadTest : LightFixtureCompletionTestCase() {
"gpMethod(A a,B b)", "method()", "pMethod(C c)",
"equals(java.lang.Object obj)")
fun testShadowedMethod() = doTest(0,
"shadowed()",
"equals(java.lang.Object obj)")
fun testOverloadedMethod() = doTest(1,
"overloaded()", "overloaded(int n)",
"equals(java.lang.Object obj)")
fun testOverloadedInheritedMethod() = doTest(1,
"overloaded(int n)", "overloaded(java.lang.String s)",
"equals(java.lang.Object obj)")
private fun doTest(index: Int, vararg expected: String) {
configureByFile(getTestName(false) + ".java")

View File

@@ -18,6 +18,7 @@ package com.intellij.codeInsight.completion;
import com.intellij.JavaTestUtil;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.testFramework.IdeaTestUtil;
import com.intellij.util.ArrayUtil;
/**
* @author Konstantin Bulenkov
@@ -34,11 +35,11 @@ public class JavaReflectionCompletionTest extends LightFixtureCompletionTestCase
}
public void testDeclaredField() throws Exception {
doTest(2, "num", "num1", "num2");
doTest(2, "num", "num1", "num2", "num0");
}
public void testDeclaredMethod() throws Exception {
doTest(2, "method", "method1", "method2");
doTest(2, "method", "method1", "method2", "method0");
}
public void testDeclaredMethod2() throws Exception {
@@ -46,7 +47,7 @@ public class JavaReflectionCompletionTest extends LightFixtureCompletionTestCase
}
public void testMethod() throws Exception {
doTest(1, "method", "method2", "method3");
doTestFirst(1, "method", "method2", "method3");
}
public void testForNameDeclaredMethod() throws Exception {
@@ -54,7 +55,7 @@ public class JavaReflectionCompletionTest extends LightFixtureCompletionTestCase
}
public void testForNameMethod() throws Exception {
doTest(1, "method", "method2", "method3");
doTestFirst(1, "method", "method2", "method3");
}
public void testForNameField() throws Exception {
@@ -71,31 +72,39 @@ public class JavaReflectionCompletionTest extends LightFixtureCompletionTestCase
public void testGenerics() throws Exception {
myFixture.addFileToProject("a.properties", "foo=bar"); // check that property variants don't override reflection ones
doTest(0, "foo");
doTestFirst(0, "foo");
}
public void testInheritedMethod() throws Exception {
doTest(1, "method", "method2", "method3");
doTestFirst(1, "method", "method2", "method3", "method5");
}
public void testInheritedDeclaredMethod() throws Exception {
doTest(1, "method", "method3");
doTest(1, "method", "method3", "method5");
}
public void testInheritedField() throws Exception {
doTest(1, "num", "num2", "num3");
doTest(1, "num", "num2", "num3", "num5");
}
public void testInheritedDeclaredField() throws Exception {
doTest(1, "num", "num3");
doTest(1, "num", "num3", "num5");
}
public void testShadowedField() {
doTest(0, "shadowed");
}
public void testShadowedSuperField() {
doTest(0, "shadowed");
}
public void testInitRaw() throws Exception {
doTest(1, "method", "method2");
doTestFirst(1, "method", "method2");
}
public void testInitWithType() throws Exception {
doTest(1, "method", "method2");
doTestFirst(1, "method", "method2");
}
public void testInitChain() throws Exception {
@@ -115,15 +124,15 @@ public class JavaReflectionCompletionTest extends LightFixtureCompletionTestCase
}
public void testJdk14() throws Exception {
IdeaTestUtil.withLevel(myFixture.getModule(), LanguageLevel.JDK_1_4, () -> doTest(0, "method"));
IdeaTestUtil.withLevel(myFixture.getModule(), LanguageLevel.JDK_1_4, () -> doTestFirst(0, "method"));
}
public void testWildcard() throws Exception {
doTest(0, "method");
doTestFirst(0, "method");
}
public void testConstantMethod() throws Exception {
doTest(0, "method", "method2");
doTestFirst(0, "method", "method2");
}
public void testDistantDefinition() throws Exception {
@@ -166,13 +175,21 @@ public class JavaReflectionCompletionTest extends LightFixtureCompletionTestCase
}
public void testHasConstructor() {
doTest(2, "method", "method2", "method1");
doTestFirst(2, "method", "method2", "method1");
}
private void doTest(int index, String... expected) {
doTest(index, () -> assertStringItems(expected));
}
private void doTestFirst(int index, String... expected) {
doTest(index, () -> assertFirstStringItems(ArrayUtil.mergeArrays(expected, "equals")));
}
private void doTest(int index, Runnable assertion) {
configureByFile(getTestName(false) + ".java");
assertFirstStringItems(expected);
assertion.run();
if (index >= 0) selectItem(getLookup().getItems().get(index));
myFixture.checkResultByFile(getTestName(false) + "_after.java");
}