diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/MarkerType.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/MarkerType.java index 921250b2dd21..4eda721094a6 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/MarkerType.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/MarkerType.java @@ -19,6 +19,7 @@ import com.intellij.psi.impl.FindSuperElementsHelper; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.search.PsiElementProcessor; import com.intellij.psi.search.PsiElementProcessorAdapter; +import com.intellij.psi.search.PsiSearchHelper; import com.intellij.psi.search.searches.ClassInheritorsSearch; import com.intellij.psi.search.searches.FunctionalExpressionSearch; import com.intellij.psi.search.searches.OverridingMethodsSearch; @@ -391,7 +392,7 @@ public class MarkerType { @Override public void run(@NotNull final ProgressIndicator indicator) { super.run(indicator); - ClassInheritorsSearch.search(myClass, ReadAction.compute(myClass::getUseScope), true).forEach( + ClassInheritorsSearch.search(myClass, ReadAction.compute(() -> PsiSearchHelper.getInstance(myProject).getUseScope(myClass)), true).forEach( new CommonProcessors.CollectProcessor<>() { @Override public boolean process(final PsiClass o) { diff --git a/java/java-indexing-api/src/com/intellij/psi/search/searches/ClassInheritorsSearch.java b/java/java-indexing-api/src/com/intellij/psi/search/searches/ClassInheritorsSearch.java index d8723dfd4cca..3508de065e19 100644 --- a/java/java-indexing-api/src/com/intellij/psi/search/searches/ClassInheritorsSearch.java +++ b/java/java-indexing-api/src/com/intellij/psi/search/searches/ClassInheritorsSearch.java @@ -10,6 +10,7 @@ import com.intellij.openapi.util.Conditions; import com.intellij.psi.PsiClass; import com.intellij.psi.PsiFile; import com.intellij.psi.SmartPointerManager; +import com.intellij.psi.search.PsiSearchHelper; import com.intellij.psi.search.SearchScope; import com.intellij.psi.util.PsiUtilCore; import com.intellij.util.*; @@ -159,7 +160,7 @@ public final class ClassInheritorsSearch extends ExtensibleQueryFactory> { - private static final Logger LOG = Logger.getInstance(JavaFunctionalExpressionIndex.class); public static final ID> INDEX_ID = ID.create("java.fun.expression"); private static final KeyDescriptor KEY_DESCRIPTOR = new KeyDescriptor<>() { @Override @@ -452,10 +451,13 @@ public class JavaFunctionalExpressionIndex extends FileBasedIndexExtension visited = new HashSet<>(); processSubInterfaces(aClass, visited); @@ -90,7 +91,7 @@ public final class JavaFunctionalExpressionSearcher extends QueryExecutorBase result = MultiMap.createLinkedSet(); descriptors.get(0).dumbService.runReadActionInSmartMode(() -> { for (SamDescriptor descriptor : descriptors) { - GlobalSearchScope scope = new JavaSourceFilterScope(descriptor.effectiveUseScope); + GlobalSearchScope scope = new JavaSourceFilterScope(descriptor.effectiveUseScope, false, true); for (FunctionalExpressionKey key : descriptor.keys) { FileBasedIndex.getInstance().processValues(JavaFunctionalExpressionIndex.INDEX_ID, key, null, (file, infos) -> { result.putValues(file, ContainerUtil.map(infos, entry -> entry.occurrence)); diff --git a/java/java-indexing-impl/src/com/intellij/psi/impl/search/JavaSourceFilterScope.java b/java/java-indexing-impl/src/com/intellij/psi/impl/search/JavaSourceFilterScope.java index 79a95856c12c..3262b68c46cb 100644 --- a/java/java-indexing-impl/src/com/intellij/psi/impl/search/JavaSourceFilterScope.java +++ b/java/java-indexing-impl/src/com/intellij/psi/impl/search/JavaSourceFilterScope.java @@ -17,21 +17,27 @@ import org.jetbrains.jps.model.java.JavaModuleSourceRootTypes; public class JavaSourceFilterScope extends DelegatingGlobalSearchScope { private final @Nullable ProjectFileIndex myIndex; private final boolean myIncludeVersions; + private final boolean myIncludeLibrarySources; public JavaSourceFilterScope(@NotNull GlobalSearchScope delegate) { this(delegate, false); } + public JavaSourceFilterScope(@NotNull GlobalSearchScope delegate, boolean includeVersions) { + this(delegate, includeVersions, false); + } + /** * By default, the scope excludes version-specific classes of multi-release .jar files * (i.e. *.class files located under META-INF/versions/ directory). * Setting {@code includeVersions} parameter to {@code true} allows such files to pass the filter. */ - public JavaSourceFilterScope(@NotNull GlobalSearchScope delegate, boolean includeVersions) { + public JavaSourceFilterScope(@NotNull GlobalSearchScope delegate, boolean includeVersions, boolean includeLibrarySources) { super(delegate); Project project = getProject(); myIndex = project == null ? null : ProjectRootManager.getInstance(project).getFileIndex(); myIncludeVersions = includeVersions; + myIncludeLibrarySources = includeLibrarySources; } @Override @@ -49,7 +55,7 @@ public class JavaSourceFilterScope extends DelegatingGlobalSearchScope { } return myIndex.isUnderSourceRootOfType(file, JavaModuleSourceRootTypes.SOURCES) || - myBaseScope.isForceSearchingInLibrarySources() && myIndex.isInLibrarySource(file); + (myIncludeLibrarySources || myBaseScope.isForceSearchingInLibrarySources()) && myIndex.isInLibrarySource(file); } private static boolean isVersioned(VirtualFile file, ProjectFileIndex index) { diff --git a/java/java-tests/testData/libraryScopeEnlarger/lib/classes/TestClass.class b/java/java-tests/testData/libraryScopeEnlarger/lib/classes/TestClass.class new file mode 100644 index 000000000000..bedb21f9361d Binary files /dev/null and b/java/java-tests/testData/libraryScopeEnlarger/lib/classes/TestClass.class differ diff --git a/java/java-tests/testData/libraryScopeEnlarger/lib/classes/TestInterfaceImpl.class b/java/java-tests/testData/libraryScopeEnlarger/lib/classes/TestInterfaceImpl.class new file mode 100644 index 000000000000..274bdef21295 Binary files /dev/null and b/java/java-tests/testData/libraryScopeEnlarger/lib/classes/TestInterfaceImpl.class differ diff --git a/java/java-tests/testData/libraryScopeEnlarger/lib/src/TestClass.java b/java/java-tests/testData/libraryScopeEnlarger/lib/src/TestClass.java new file mode 100644 index 000000000000..227c66811cba --- /dev/null +++ b/java/java-tests/testData/libraryScopeEnlarger/lib/src/TestClass.java @@ -0,0 +1,5 @@ +public class TestClass { + TestSrcInterface lambdaImpl = () -> { + return ""; + }; +} \ No newline at end of file diff --git a/java/java-tests/testData/libraryScopeEnlarger/lib/src/TestInterfaceImpl.java b/java/java-tests/testData/libraryScopeEnlarger/lib/src/TestInterfaceImpl.java new file mode 100644 index 000000000000..c5bdbd8f982d --- /dev/null +++ b/java/java-tests/testData/libraryScopeEnlarger/lib/src/TestInterfaceImpl.java @@ -0,0 +1,6 @@ +public class TestInterfaceImpl implements TestSrcInterface { + @Override + public String testMethod() { + return ""; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/libraryScopeEnlarger/src/TestSrcInterface.java b/java/java-tests/testData/libraryScopeEnlarger/src/TestSrcInterface.java new file mode 100644 index 000000000000..d79750e44855 --- /dev/null +++ b/java/java-tests/testData/libraryScopeEnlarger/src/TestSrcInterface.java @@ -0,0 +1,3 @@ +public interface TestSrcInterface { + public String testMethod(); +} diff --git a/java/java-tests/testSrc/com/intellij/scopes/LibraryUseSearchUsingScopeEnlargerTest.java b/java/java-tests/testSrc/com/intellij/scopes/LibraryUseSearchUsingScopeEnlargerTest.java new file mode 100644 index 000000000000..fa14f451b489 --- /dev/null +++ b/java/java-tests/testSrc/com/intellij/scopes/LibraryUseSearchUsingScopeEnlargerTest.java @@ -0,0 +1,112 @@ +// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package com.intellij.scopes; + +import com.intellij.JavaTestUtil; +import com.intellij.codeInsight.daemon.LineMarkerInfo; +import com.intellij.codeInsight.daemon.impl.DaemonCodeAnalyzerImpl; +import com.intellij.codeInsight.daemon.impl.MarkerType; +import com.intellij.ide.plugins.DynamicPluginsTestUtil; +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.roots.OrderRootType; +import com.intellij.openapi.roots.impl.LibraryScopeCache; +import com.intellij.openapi.util.Disposer; +import com.intellij.openapi.vfs.LocalFileSystem; +import com.intellij.pom.java.LanguageLevel; +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiMethod; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.psi.search.SearchScope; +import com.intellij.psi.search.UseScopeEnlarger; +import com.intellij.psi.search.searches.ClassInheritorsSearch; +import com.intellij.psi.search.searches.OverridingMethodsSearch; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.testFramework.builders.JavaModuleFixtureBuilder; +import com.intellij.testFramework.fixtures.JavaCodeInsightFixtureTestCase; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.junit.Test; + +import java.io.File; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +/** + * Test ensuring searchers use {@link com.intellij.psi.search.PsiSearchHelper} to enable enlarging use scopes in plugins. + * For this test, we will enlarge the use scope to include libraries. + */ +public class LibraryUseSearchUsingScopeEnlargerTest extends JavaCodeInsightFixtureTestCase { + + @Override + protected void setUp() throws Exception { + super.setUp(); + Disposer.register(getTestRootDisposable(), DynamicPluginsTestUtil.loadExtensionWithText( + "", + getClass().getClassLoader())); + //bug? Test seems to use stale data. + LocalFileSystem.getInstance().refreshIoFiles(Collections.singleton(new File(getTestDataPath())), false, true, null); + } + + @Override + protected void tuneFixture(JavaModuleFixtureBuilder moduleBuilder) throws Exception { + super.tuneFixture(moduleBuilder); + moduleBuilder.addLibrary("lib", Map.of(OrderRootType.CLASSES, new String[]{getTestDataPath() + "lib/classes"}, + OrderRootType.SOURCES, new String[]{getTestDataPath() + "lib/src"})); + moduleBuilder.addSourceContentRoot(getTestDataPath() + "src"); + moduleBuilder.setMockJdkLevel(JavaModuleFixtureBuilder.MockJdkLevel.jdk15); + moduleBuilder.setLanguageLevel(LanguageLevel.JDK_1_8); + } + + @Test + public void testSearchFromScourceLooksInLibrary() { + PsiClass sourceClass = myFixture.findClass("TestSrcInterface"); + PsiClass libImpl = myFixture.findClass("TestInterfaceImpl"); + Collection classInheritors = ClassInheritorsSearch.search(sourceClass).findAll(); + + assertContainsElements(classInheritors, libImpl); + } + + @Test + public void testLineMarkersUseEnlarger() { + PsiClass sourceClass = myFixture.findClass("TestSrcInterface"); + + myFixture.openFileInEditor(sourceClass.getContainingFile().getVirtualFile()); + myFixture.doHighlighting(); + Document document = myFixture.getDocument(sourceClass.getContainingFile()); + + List> lineMarkers = DaemonCodeAnalyzerImpl.getLineMarkers(document, getProject()); + assertSize(2, lineMarkers); + + assertEquals(MarkerType.SUBCLASSED_CLASS.getNavigationHandler(), lineMarkers.get(0).getNavigationHandler()); + assertEquals(MarkerType.OVERRIDDEN_METHOD.getNavigationHandler(), lineMarkers.get(1).getNavigationHandler()); + } + + @Test + public void testOverridingMethodSearcherUsesEnlarger() { + PsiClass sourceClass = myFixture.findClass("TestSrcInterface"); + PsiMethod testMethod = PsiTreeUtil.findChildOfType(sourceClass, PsiMethod.class); + + Collection methods = OverridingMethodsSearch.search(testMethod, GlobalSearchScope.allScope(getProject()), false).findAll(); + assertSize(1, methods); + } + + @Override + protected @NonNls String getBasePath() { + return JavaTestUtil.getRelativeJavaTestDataPath() + "/libraryScopeEnlarger/"; + } + + @Override + protected boolean toAddSourceRoot() { + return false; + } + + public static class LibraryUseScopeEnlarger extends UseScopeEnlarger { + @Override + public @Nullable SearchScope getAdditionalUseScope(@NotNull PsiElement element) { + return LibraryScopeCache.getInstance(element.getProject()).getLibrariesOnlyScope(); + } + } +} diff --git a/platform/indexing-api/src/com/intellij/util/indexing/DefaultFileTypeSpecificWithProjectInputFilter.java b/platform/indexing-api/src/com/intellij/util/indexing/DefaultFileTypeSpecificWithProjectInputFilter.java new file mode 100644 index 000000000000..272ca67f5047 --- /dev/null +++ b/platform/indexing-api/src/com/intellij/util/indexing/DefaultFileTypeSpecificWithProjectInputFilter.java @@ -0,0 +1,29 @@ +// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package com.intellij.util.indexing; + +import com.intellij.openapi.fileTypes.FileType; +import com.intellij.util.Consumer; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; + +@ApiStatus.Experimental +public class DefaultFileTypeSpecificWithProjectInputFilter implements FileBasedIndex.FileTypeSpecificInputFilter, + FileBasedIndex.ProjectSpecificInputFilter { + private final FileType[] myFileTypes; + + public DefaultFileTypeSpecificWithProjectInputFilter(FileType @NotNull ... fileTypes) { + myFileTypes = fileTypes; + } + + @Override + public boolean acceptInput(@NotNull IndexedFile file) { + return true; + } + + @Override + public void registerFileTypesUsedForIndexing(@NotNull Consumer fileTypeSink) { + for (FileType ft : myFileTypes) { + fileTypeSink.consume(ft); + } + } +}