inheritance transitivity with several versions of same library in classpath (IDEA...
authorpeter <peter@jetbrains.com>
Thu, 2 Aug 2012 12:36:51 +0000 (14:36 +0200)
committerpeter <peter@jetbrains.com>
Thu, 2 Aug 2012 12:41:29 +0000 (14:41 +0200)
java/java-psi-impl/src/com/intellij/psi/impl/InheritanceImplUtil.java
java/java-psi-impl/src/com/intellij/psi/impl/PsiClassImplUtil.java
java/java-tests/testSrc/com/intellij/psi/resolve/ResolveInLibrariesTest.groovy
platform/core-api/src/com/intellij/openapi/roots/FileIndexFacade.java
platform/core-impl/src/com/intellij/mock/MockFileIndexFacade.java
platform/projectModel-impl/src/com/intellij/openapi/roots/impl/ProjectFileIndexFacade.java
platform/vcs-impl/src/com/intellij/openapi/vcs/impl/DefaultFileIndexFacade.java

index 28fc636cbb26be044164d93bf84e095e4a6e0534..205facdaadfa3a073454139b9d32f434ef78e5da 100644 (file)
@@ -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;
index ee44391808255389311e7d4bdd0cc9e0cec9836a..8bfd2f8c168e8dfee789719afbb2d8b86a596262 100644 (file)
@@ -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) {
index ceb2bbd93df6fe43525f21d15a92488bc8a4347d..2f7fe69025d027a05ae9292ec53b9014eb529cd2 100644 (file)
@@ -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)
+      }
+    }
+  }
+
 }
index 35dc94d204a04dd9e16c5aeab7498bc156052aaa..75e693f140e8cfc36d3240fc7676fc3889f6f160 100644 (file)
@@ -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);
 
index a2f565b6f520f20aeaa67f3bb31944bfc5d70388..173cd687e6ba248392d242bf514f08e563775e04 100644 (file)
@@ -62,6 +62,11 @@ public class MockFileIndexFacade extends FileIndexFacade {
   }
 
   @Override
+  public boolean isInSdkClasses(VirtualFile file) {
+    return false;
+  }
+
+  @Override
   public boolean isInLibrarySource(VirtualFile file) {
     return false;
   }
index 476fc854f8629c185de3163ec6776cc209643206..0e4aa7dc83ffab00933a99e28185bbcbe22e118e 100644 (file)
@@ -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;
 
 /**
@@ -57,6 +59,11 @@ public class ProjectFileIndexFacade extends FileIndexFacade {
   }
 
   @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);
   }
index 8e6bd1d2453e57ca46649816bba8c0c96bda9087..92c0b7ba9a343e1d581858bc72870bf2655ddf49 100644 (file)
@@ -55,6 +55,11 @@ public class DefaultFileIndexFacade extends FileIndexFacade {
   }
 
   @Override
+  public boolean isInSdkClasses(VirtualFile file) {
+    return false;
+  }
+
+  @Override
   public boolean isInLibrarySource(VirtualFile file) {
     return false;
   }