From d1ef8821e29f1715cc22930ed75843e2c88bb58c Mon Sep 17 00:00:00 2001 From: peter Date: Fri, 28 Dec 2018 18:24:59 +0100 Subject: [PATCH] support production-on-test dependencies in use scopes (IDEA-203841) --- .../java/psi/search/ClassInheritorsTest.java | 14 ++++++ .../scopes/ModuleWithDependentsScope.java | 45 +++++++++++-------- 2 files changed, 41 insertions(+), 18 deletions(-) diff --git a/java/java-tests/testSrc/com/intellij/java/psi/search/ClassInheritorsTest.java b/java/java-tests/testSrc/com/intellij/java/psi/search/ClassInheritorsTest.java index 7893b9120d2a..2b208d9a9e96 100644 --- a/java/java-tests/testSrc/com/intellij/java/psi/search/ClassInheritorsTest.java +++ b/java/java-tests/testSrc/com/intellij/java/psi/search/ClassInheritorsTest.java @@ -24,6 +24,7 @@ import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.progress.util.StandardProgressIndicatorBase; import com.intellij.openapi.roots.DependencyScope; import com.intellij.openapi.roots.ModuleRootModificationUtil; +import com.intellij.openapi.roots.impl.ModuleOrderEntryImpl; import com.intellij.psi.PsiClass; import com.intellij.psi.PsiJavaFile; import com.intellij.psi.impl.compiled.ClsClassImpl; @@ -140,6 +141,19 @@ public class ClassInheritorsTest extends JavaCodeInsightFixtureTestCase { assertSize(2, ClassInheritorsSearch.search(myFixture.findClass("A")).findAll()); } + public void testInheritorsInAnotherModuleWithProductionOnTestDependency() throws IOException { + myFixture.addFileToProject("tests/B.java", "class B {}"); + myFixture.addFileToProject("mod2/C.java", "class C extends B {}"); + + PsiTestUtil.addSourceRoot(myModule, myFixture.getTempDirFixture().findOrCreateDir("tests"), true); + Module mod2 = PsiTestUtil.addModule(getProject(), StdModuleTypes.JAVA, "mod2", myFixture.getTempDirFixture().findOrCreateDir("mod2")); + + ModuleRootModificationUtil.updateModel(mod2, model -> + ((ModuleOrderEntryImpl)model.addModuleOrderEntry(myModule)).setProductionOnTestDependency(true)); + + assertSize(1, ClassInheritorsSearch.search(myFixture.findClass("B")).findAll()); + } + public void testSpaceBeforeSuperTypeGenerics() { myFixture.addFileToProject("A.java", "interface A {}"); myFixture.addFileToProject("B.java", "class B implements A {}"); diff --git a/platform/indexing-impl/src/com/intellij/openapi/module/impl/scopes/ModuleWithDependentsScope.java b/platform/indexing-impl/src/com/intellij/openapi/module/impl/scopes/ModuleWithDependentsScope.java index d185dc8043d9..105ea83ba742 100644 --- a/platform/indexing-impl/src/com/intellij/openapi/module/impl/scopes/ModuleWithDependentsScope.java +++ b/platform/indexing-impl/src/com/intellij/openapi/module/impl/scopes/ModuleWithDependentsScope.java @@ -22,6 +22,7 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.*; import com.intellij.openapi.roots.impl.DirectoryIndex; import com.intellij.openapi.roots.impl.DirectoryInfo; +import com.intellij.openapi.roots.impl.ModuleOrderEntryImpl; import com.intellij.openapi.roots.impl.ProjectFileIndexImpl; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.search.GlobalSearchScope; @@ -44,7 +45,8 @@ class ModuleWithDependentsScope extends GlobalSearchScope { private final Module myModule; private final ProjectFileIndexImpl myProjectFileIndex; - private final Set myModules; + private final Set myModules = new THashSet<>(); + private final Set myProductionOnTestModules = new THashSet<>(); ModuleWithDependentsScope(@NotNull Module module) { super(module.getProject()); @@ -52,30 +54,28 @@ class ModuleWithDependentsScope extends GlobalSearchScope { myProjectFileIndex = (ProjectFileIndexImpl)ProjectRootManager.getInstance(module.getProject()).getFileIndex(); - myModules = buildDependents(myModule); - } - - @NotNull - private static Set buildDependents(@NotNull Module module) { - Set result = new THashSet<>(); - result.add(module); + myModules.add(module); ModuleIndex index = getModuleIndex(module.getProject()); HashSetQueue walkingQueue = new HashSetQueue<>(); walkingQueue.add(module); for (Module current : walkingQueue) { - result.addAll(index.plainUsages.get(current)); - Collection exported = index.exportingUsages.get(current); - walkingQueue.addAll(exported); - result.addAll(exported); + Collection usages = index.allUsages.get(current); + myModules.addAll(usages); + walkingQueue.addAll(index.exportingUsages.get(current)); + + if (myProductionOnTestModules.contains(current)) { + myProductionOnTestModules.addAll(usages); + } + myProductionOnTestModules.addAll(index.productionOnTestUsages.get(current)); } - return result; } private static class ModuleIndex { - final MultiMap plainUsages = MultiMap.create(); + final MultiMap allUsages = MultiMap.create(); final MultiMap exportingUsages = MultiMap.create(); + final MultiMap productionOnTestUsages = MultiMap.create(); } @NotNull @@ -87,8 +87,13 @@ class ModuleWithDependentsScope extends GlobalSearchScope { if (orderEntry instanceof ModuleOrderEntry) { Module referenced = ((ModuleOrderEntry)orderEntry).getModule(); if (referenced != null) { - MultiMap map = ((ModuleOrderEntry)orderEntry).isExported() ? index.exportingUsages : index.plainUsages; - map.putValue(referenced, module); + index.allUsages.putValue(referenced, module); + if (((ModuleOrderEntry)orderEntry).isExported()) { + index.exportingUsages.putValue(referenced, module); + } + if (orderEntry instanceof ModuleOrderEntryImpl && ((ModuleOrderEntryImpl)orderEntry).isProductionOnTestDependency()) { + index.productionOnTestUsages.putValue(referenced, module); + } } } } @@ -102,12 +107,16 @@ class ModuleWithDependentsScope extends GlobalSearchScope { return contains(file, false); } - boolean contains(@NotNull VirtualFile file, boolean myOnlyTests) { + boolean contains(@NotNull VirtualFile file, boolean fromTests) { // optimization: fewer calls to getInfoForFileOrDirectory() DirectoryInfo info = myProjectFileIndex.getInfoForFileOrDirectory(file); Module moduleOfFile = info.getModule(); if (moduleOfFile == null || !myModules.contains(moduleOfFile)) return false; - if (myOnlyTests && !TestSourcesFilter.isTestSources(file, moduleOfFile.getProject())) return false; + if (fromTests && + !myProductionOnTestModules.contains(moduleOfFile) && + !TestSourcesFilter.isTestSources(file, moduleOfFile.getProject())) { + return false; + } return ProjectFileIndexImpl.isFileInContent(file, info); }