diff --git a/java/java-impl/src/com/intellij/codeInsight/TargetElementUtil.java b/java/java-impl/src/com/intellij/codeInsight/TargetElementUtil.java index 6284094443ad..35d333a28910 100644 --- a/java/java-impl/src/com/intellij/codeInsight/TargetElementUtil.java +++ b/java/java-impl/src/com/intellij/codeInsight/TargetElementUtil.java @@ -262,23 +262,33 @@ public class TargetElementUtil extends TargetElementUtilBase { return ApplicationManager.getApplication().runReadAction(new Computable() { @Override public Boolean compute() { + PsiClass containingClass = ((PsiMember)element).getContainingClass(); final PsiExpression expression = ((PsiReferenceExpression)reference).getQualifierExpression(); - final PsiClass psiClass; + PsiClass psiClass; if (expression != null) { psiClass = PsiUtil.resolveClassInType(expression.getType()); } else { if (element instanceof PsiClass) { + psiClass = (PsiClass)element; final PsiElement resolve = reference.resolve(); if (resolve instanceof PsiClass) { - return InheritanceUtil.isInheritorOrSelf((PsiClass)resolve, (PsiClass)element, true) || - InheritanceUtil.isInheritorOrSelf((PsiClass)element, (PsiClass)resolve, true); + containingClass = (PsiClass)resolve; } + } else { + psiClass = PsiTreeUtil.getParentOfType((PsiReferenceExpression)reference, PsiClass.class); } - psiClass = PsiTreeUtil.getParentOfType((PsiReferenceExpression)reference, PsiClass.class); } - final PsiClass containingClass = ((PsiMember)element).getContainingClass(); - if (containingClass == psiClass) return true; - return psiClass != null && containingClass != null && (containingClass.isInheritor(psiClass, true) || psiClass.isInheritor(containingClass, true)); + + if (containingClass == null && psiClass == null) return true; + if (containingClass != null) { + while (psiClass != null) { + if (InheritanceUtil.isInheritorOrSelf(containingClass, psiClass, true) || InheritanceUtil.isInheritorOrSelf(psiClass, containingClass, true)) { + return true; + } + psiClass = psiClass.getContainingClass(); + } + } + return false; } }); } diff --git a/java/java-tests/testSrc/com/intellij/navigation/GotoImplementationHandlerTest.java b/java/java-tests/testSrc/com/intellij/navigation/GotoImplementationHandlerTest.java new file mode 100644 index 000000000000..984f65bcc776 --- /dev/null +++ b/java/java-tests/testSrc/com/intellij/navigation/GotoImplementationHandlerTest.java @@ -0,0 +1,100 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.intellij.navigation; + + +import com.intellij.codeInsight.navigation.GotoImplementationHandler; +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiMethod; +import com.intellij.testFramework.fixtures.JavaCodeInsightFixtureTestCase; + +public class GotoImplementationHandlerTest extends JavaCodeInsightFixtureTestCase { + + public void testMultipleImplsFromAbstractCall() throws Throwable { + PsiFile file = myFixture.addFileToProject("Foo.java", "public abstract class Hello {\n" + + " abstract void foo();\n" + + "\n" + + " class A {\n" + + " {\n" + + " foo();\n" + + " }\n" + + " }\n" + + " class Hello1 extends Hello {\n" + + " void foo() {}\n" + + " }\n" + + " class Hello2 extends Hello {\n" + + " void foo() {}\n" + + " }\n" + + "}\n" + + "\n"); + myFixture.configureFromExistingVirtualFile(file.getVirtualFile()); + + final PsiElement[] impls = new GotoImplementationHandler().getSourceAndTargetElements(myFixture.getEditor(), file).targets; + assertEquals(2, impls.length); + } + + public void testMultipleImplsFromStaticCall() throws Throwable { + PsiFile file = myFixture.addFileToProject("Foo.java", "public abstract class Hello {\n" + + " static void bar (){}\n" + + " class Hello1 extends Hello {\n" + + " }\n" + + " class Hello2 extends Hello {\n" + + " }\n" + + "class D {\n" + + " {\n" + + " Hello.bar();\n" + + " }\n" + + "}"); + myFixture.configureFromExistingVirtualFile(file.getVirtualFile()); + + final PsiElement[] impls = new GotoImplementationHandler().getSourceAndTargetElements(myFixture.getEditor(), file).targets; + assertEquals(2, impls.length); + } + + public void testFilterOutImpossibleVariants() throws Throwable { + PsiFile file = myFixture.addFileToProject("Foo.java", "interface A {\n" + + " void save();\n" + + "}\n" + + "interface B extends A {\n" + + " void foo();\n" + + "}\n" + + "class X implements B {\n" + + " public void foo() { }\n" + + " public void save(){}\n" + + "}\n" + + "class Y implements A {\n" + + " public void save(){}\n" + + "}\n" + + "class App {\n" + + " private B b;\n" + + " private void some() {\n" + + " b.save();\n" + + " }\n" + + "}"); + myFixture.configureFromExistingVirtualFile(file.getVirtualFile()); + + final PsiElement[] impls = new GotoImplementationHandler().getSourceAndTargetElements(myFixture.getEditor(), file).targets; + assertEquals(1, impls.length); + final PsiElement meth = impls[0]; + assertTrue(meth instanceof PsiMethod); + final PsiClass aClass = ((PsiMethod)meth).getContainingClass(); + assertNotNull(aClass); + assertEquals(aClass.getName(), "X"); + } +} \ No newline at end of file diff --git a/platform/lang-impl/src/com/intellij/codeInsight/navigation/GotoImplementationHandler.java b/platform/lang-impl/src/com/intellij/codeInsight/navigation/GotoImplementationHandler.java index c8bf13ee83a5..36b3f210710b 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/navigation/GotoImplementationHandler.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/navigation/GotoImplementationHandler.java @@ -66,7 +66,7 @@ public class GotoImplementationHandler extends GotoTargetHandler { protected PsiElement[] filterElements(PsiElement element, PsiElement[] targetElements, int offset) { final List result = new ArrayList(); for (PsiElement targetElement : targetElements) { - if (instance.acceptImplementationForReference(reference, element)) { + if (instance.acceptImplementationForReference(reference, targetElement)) { result.add(targetElement); } }