diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/InheritanceImplUtil.java b/java/java-psi-impl/src/com/intellij/psi/impl/InheritanceImplUtil.java index 28fc636cbb26..205facdaadfa 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/InheritanceImplUtil.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/InheritanceImplUtil.java @@ -19,7 +19,6 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.progress.ProgressIndicatorProvider; import com.intellij.openapi.util.Comparing; import com.intellij.psi.*; -import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.util.InheritanceUtil; import com.intellij.util.containers.HashSet; import gnu.trove.THashSet; @@ -71,12 +70,8 @@ public class InheritanceImplUtil { final boolean bInt = baseClass.isInterface(); if (candidateClass instanceof PsiCompiledElement) { - String baseQName = baseClass.getQualifiedName(); - if (baseQName == null) return false; - - GlobalSearchScope scope = candidateClass.getResolveScope(); - if (cInt == bInt && checkReferenceListWithQualifiedNames(baseQName, candidateClass.getExtendsList(), manager, scope)) return true; - return bInt && !cInt && checkReferenceListWithQualifiedNames(baseQName, candidateClass.getImplementsList(), manager, scope); + if (cInt == bInt && checkReferenceListWithQualifiedNames(candidateClass.getExtendsList(), baseClass)) return true; + return bInt && !cInt && checkReferenceListWithQualifiedNames(candidateClass.getImplementsList(), baseClass); } if (cInt == bInt) { for (PsiClassType type : candidateClass.getExtendsListTypes()) { @@ -103,14 +98,16 @@ public class InheritanceImplUtil { return isInheritorWithoutCaching(candidateClass, baseClass, checkDeep, checkedClasses); } - private static boolean checkReferenceListWithQualifiedNames(final String baseQName, final PsiReferenceList extList, final PsiManager manager, - final GlobalSearchScope scope) { + private static boolean checkReferenceListWithQualifiedNames(final PsiReferenceList extList, PsiClass baseClass) { if (extList != null) { - final PsiJavaCodeReferenceElement[] refs = extList.getReferenceElements(); - for (PsiJavaCodeReferenceElement ref : refs) { - if (Comparing.equal(PsiNameHelper.getQualifiedClassName(ref.getQualifiedName(), false), baseQName) && JavaPsiFacade - .getInstance(manager.getProject()).findClass(baseQName, scope) != null) - return true; + String qname = baseClass.getQualifiedName(); + if (qname != null) { + for (PsiJavaCodeReferenceElement ref : extList.getReferenceElements()) { + if (Comparing.equal(PsiNameHelper.getQualifiedClassName(ref.getQualifiedName(), false), qname) && + baseClass.isEquivalentTo(ref.resolve())) { + return true; + } + } } } return false; diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/PsiClassImplUtil.java b/java/java-psi-impl/src/com/intellij/psi/impl/PsiClassImplUtil.java index ee4439180825..8bfd2f8c168e 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/PsiClassImplUtil.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/PsiClassImplUtil.java @@ -992,8 +992,16 @@ public class PsiClassImplUtil { final FileIndexFacade fileIndex = ServiceManager.getService(file1.getProject(), FileIndexFacade.class); final VirtualFile vfile1 = file1.getViewProvider().getVirtualFile(); final VirtualFile vfile2 = file2.getViewProvider().getVirtualFile(); - return (fileIndex.isInSource(vfile1) || fileIndex.isInLibraryClasses(vfile1)) && - (fileIndex.isInSource(vfile2) || fileIndex.isInLibraryClasses(vfile2)); + boolean lib1 = fileIndex.isInLibraryClasses(vfile1); + boolean lib2 = fileIndex.isInLibraryClasses(vfile2); + if (aClass instanceof PsiCompiledElement && another instanceof PsiCompiledElement && lib1 && lib2) { + if (fileIndex.isInSdkClasses(vfile1) && fileIndex.isInSdkClasses(vfile2)) { + return true; + } + return vfile1.equals(vfile2); + } + + return (fileIndex.isInSource(vfile1) || lib1) && (fileIndex.isInSource(vfile2) || lib2); } private static boolean compareClassSeqNumber(@NotNull PsiClass aClass, @NotNull PsiClass another) { diff --git a/java/java-tests/testSrc/com/intellij/psi/resolve/ResolveInLibrariesTest.groovy b/java/java-tests/testSrc/com/intellij/psi/resolve/ResolveInLibrariesTest.groovy index ceb2bbd93df6..2f7fe69025d0 100644 --- a/java/java-tests/testSrc/com/intellij/psi/resolve/ResolveInLibrariesTest.groovy +++ b/java/java-tests/testSrc/com/intellij/psi/resolve/ResolveInLibrariesTest.groovy @@ -51,4 +51,39 @@ class ResolveInLibrariesTest extends JavaCodeInsightFixtureTestCase { } + public void "test inheritance transitivity"() { + def lib = LocalFileSystem.getInstance().refreshAndFindFileByPath(PathManagerEx.getTestDataPath() + "/../../../lib") + def protoJar = lib.children.find { it.name.startsWith("protobuf") } + + def jarCopy = myFixture.copyFileToProject(protoJar.path, 'lib/protoJar.jar') + + PsiTestUtil.addLibrary(myModule, 'proto1', lib.path, ["/$protoJar.name!/"] as String[], [] as String[]) + PsiTestUtil.addLibrary(myModule, 'proto2', jarCopy.parent.path, ["/$jarCopy.name!/"] as String[], [] as String[]) + + def scope = GlobalSearchScope.allScope(project) + + def bottoms = JavaPsiFacade.getInstance(project).findClasses('com.google.protobuf.AbstractMessage', scope) + assert bottoms.size() == 2 + + def middles = JavaPsiFacade.getInstance(project).findClasses('com.google.protobuf.AbstractMessageLite', scope) + assert middles.size() == 2 + + def intfs = JavaPsiFacade.getInstance(project).findClasses('com.google.protobuf.MessageLite', scope) + assert intfs.size() == 2 + + for (i in 0..1) { + assert middles[i].isInheritor(intfs[i], true) + assert bottoms[i].isInheritor(intfs[i], true) + assert bottoms[i].isInheritor(middles[i], true) + } + + for (deep in [false, true]) { + for (i in 0..1) { + assert !middles[i].isInheritor(intfs[1-i], deep) + assert !bottoms[i].isInheritor(intfs[1-i], deep) + assert !bottoms[i].isInheritor(middles[1-i], deep) + } + } + } + } diff --git a/platform/core-api/src/com/intellij/openapi/roots/FileIndexFacade.java b/platform/core-api/src/com/intellij/openapi/roots/FileIndexFacade.java index 35dc94d204a0..75e693f140e8 100644 --- a/platform/core-api/src/com/intellij/openapi/roots/FileIndexFacade.java +++ b/platform/core-api/src/com/intellij/openapi/roots/FileIndexFacade.java @@ -40,6 +40,7 @@ public abstract class FileIndexFacade { public abstract boolean isInSource(VirtualFile file); public abstract boolean isInSourceContent(VirtualFile file); public abstract boolean isInLibraryClasses(VirtualFile file); + public abstract boolean isInSdkClasses(VirtualFile file); public abstract boolean isInLibrarySource(VirtualFile file); public abstract boolean isExcludedFile(VirtualFile file); diff --git a/platform/core-impl/src/com/intellij/mock/MockFileIndexFacade.java b/platform/core-impl/src/com/intellij/mock/MockFileIndexFacade.java index a2f565b6f520..173cd687e6ba 100644 --- a/platform/core-impl/src/com/intellij/mock/MockFileIndexFacade.java +++ b/platform/core-impl/src/com/intellij/mock/MockFileIndexFacade.java @@ -61,6 +61,11 @@ public class MockFileIndexFacade extends FileIndexFacade { return false; } + @Override + public boolean isInSdkClasses(VirtualFile file) { + return false; + } + @Override public boolean isInLibrarySource(VirtualFile file) { return false; diff --git a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/ProjectFileIndexFacade.java b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/ProjectFileIndexFacade.java index 476fc854f862..0e4aa7dc83ff 100644 --- a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/ProjectFileIndexFacade.java +++ b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/ProjectFileIndexFacade.java @@ -19,9 +19,11 @@ package com.intellij.openapi.roots.impl; import com.intellij.openapi.module.Module; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.FileIndexFacade; +import com.intellij.openapi.roots.JdkOrderEntry; import com.intellij.openapi.roots.ProjectFileIndex; import com.intellij.openapi.roots.ProjectRootManager; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.Nullable; /** @@ -56,6 +58,11 @@ public class ProjectFileIndexFacade extends FileIndexFacade { return myFileIndex.isInLibraryClasses(file); } + @Override + public boolean isInSdkClasses(VirtualFile file) { + return ContainerUtil.findInstance(myFileIndex.getOrderEntriesForFile(file), JdkOrderEntry.class) != null; + } + @Override public boolean isInLibrarySource(VirtualFile file) { return myFileIndex.isInLibrarySource(file); diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/DefaultFileIndexFacade.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/DefaultFileIndexFacade.java index 8e6bd1d2453e..92c0b7ba9a34 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/DefaultFileIndexFacade.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/DefaultFileIndexFacade.java @@ -54,6 +54,11 @@ public class DefaultFileIndexFacade extends FileIndexFacade { return false; } + @Override + public boolean isInSdkClasses(VirtualFile file) { + return false; + } + @Override public boolean isInLibrarySource(VirtualFile file) { return false;