diff --git a/java/java-impl/src/com/intellij/ide/hierarchy/call/CallerMethodsTreeStructure.java b/java/java-impl/src/com/intellij/ide/hierarchy/call/CallerMethodsTreeStructure.java index ef3f343ab807..e06f4c8f6925 100644 --- a/java/java-impl/src/com/intellij/ide/hierarchy/call/CallerMethodsTreeStructure.java +++ b/java/java-impl/src/com/intellij/ide/hierarchy/call/CallerMethodsTreeStructure.java @@ -105,8 +105,7 @@ public final class CallerMethodsTreeStructure extends HierarchyTreeStructure { if (receiverClass != null && expectedQualifierClass != null - && !InheritanceUtil.isInheritorOrSelf(expectedQualifierClass, receiverClass, true) - && !InheritanceUtil.isInheritorOrSelf(receiverClass, expectedQualifierClass, true) + && !areClassesRelated(expectedQualifierClass, receiverClass) ) { // ignore impossible candidates. E.g. when A < B,A < C and we invoked call hierarchy for method in C we should filter out methods in B because B and C are assignment-incompatible return true; @@ -130,6 +129,22 @@ public final class CallerMethodsTreeStructure extends HierarchyTreeStructure { .map(e -> new CallHierarchyNodeDescriptor(myProject, nodeDescriptor, e, false, false)).toArray(); } + private static boolean areClassesRelated(@NotNull PsiClass expectedQualifierClass, @NotNull PsiClass receiverClass) { + if (InheritanceUtil.isInheritorOrSelf(expectedQualifierClass, receiverClass, true) + || InheritanceUtil.isInheritorOrSelf(receiverClass, expectedQualifierClass, true)) { + return true; + } + if (receiverClass instanceof PsiTypeParameter) { + // in case of "T extends S", it should be related to SImpl, even though T is not superclass of SImpl + for (PsiClass receiverExtends : receiverClass.getSupers()) { + if (areClassesRelated(expectedQualifierClass, receiverExtends)) { + return true; + } + } + } + return false; + } + private static boolean isLocalOrAnonymousClass(PsiMember enclosingElement) { return enclosingElement instanceof PsiClass && ((PsiClass)enclosingElement).getQualifiedName() == null; } diff --git a/java/java-tests/testData/ide/hierarchy/call/Wildcards/A.java b/java/java-tests/testData/ide/hierarchy/call/Wildcards/A.java new file mode 100644 index 000000000000..eeaa313cda2d --- /dev/null +++ b/java/java-tests/testData/ide/hierarchy/call/Wildcards/A.java @@ -0,0 +1,15 @@ +package p; + +interface Board { + int getCount(); +} +class BoardImpl implements Board { + @Override + public int getCount() {return 0;} +} +class King { + B board; + boolean isLast() { + return board.getCount() == 1; + } +} diff --git a/java/java-tests/testData/ide/hierarchy/call/Wildcards/verification.xml b/java/java-tests/testData/ide/hierarchy/call/Wildcards/verification.xml new file mode 100644 index 000000000000..a7d65544b182 --- /dev/null +++ b/java/java-tests/testData/ide/hierarchy/call/Wildcards/verification.xml @@ -0,0 +1,3 @@ + + + diff --git a/java/java-tests/testSrc/com/intellij/java/ide/hierarchy/JavaCallHierarchyTest.java b/java/java-tests/testSrc/com/intellij/java/ide/hierarchy/JavaCallHierarchyTest.java index d1baaab15ef1..f8d135b17e66 100644 --- a/java/java-tests/testSrc/com/intellij/java/ide/hierarchy/JavaCallHierarchyTest.java +++ b/java/java-tests/testSrc/com/intellij/java/ide/hierarchy/JavaCallHierarchyTest.java @@ -144,4 +144,7 @@ public class JavaCallHierarchyTest extends HierarchyViewTestBase { public void testThroughAnonymousCalledByOther() throws Exception { doJavaCallerTypeHierarchyTest("x.AcmClientImpl", "returnSomething", "X.java"); } + public void testWildcards() throws Exception { + doJavaCallerTypeHierarchyTest("p.BoardImpl", "getCount", "A.java"); + } }