take into account use scope enlarger while inheritors/functional expression searches

GitOrigin-RevId: 8b62d47f2aef69308f891e2e537c76ece7d5e5ca
This commit is contained in:
Brian McNamara
2021-02-04 12:49:15 +00:00
committed by intellij-monorepo-bot
parent 278f96df51
commit ca35b63d33
12 changed files with 178 additions and 12 deletions
@@ -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) {
@@ -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<PsiClass
throw new ProcessCanceledException();
}
PsiFile file = aClass.getContainingFile();
return (file != null ? file : aClass).getUseScope();
return PsiSearchHelper.getInstance(aClass.getProject()).getUseScope(file != null ? file : aClass);
}), checkDeep);
}
@@ -20,11 +20,11 @@ import com.intellij.ide.highlighter.JavaFileType;
import com.intellij.lang.LighterAST;
import com.intellij.lang.LighterASTNode;
import com.intellij.lang.LighterASTTokenNode;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.FileIndexFacade;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.io.DataInputOutputUtilRt;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.JavaTokenType;
import com.intellij.psi.PsiBinaryExpression;
import com.intellij.psi.impl.java.stubs.FunctionalExpressionKey;
@@ -58,7 +58,6 @@ import java.util.function.BiConsumer;
import static com.intellij.psi.impl.source.tree.JavaElementType.*;
public class JavaFunctionalExpressionIndex extends FileBasedIndexExtension<FunctionalExpressionKey, List<JavaFunctionalExpressionIndex.IndexEntry>> {
private static final Logger LOG = Logger.getInstance(JavaFunctionalExpressionIndex.class);
public static final ID<FunctionalExpressionKey, List<IndexEntry>> INDEX_ID = ID.create("java.fun.expression");
private static final KeyDescriptor<FunctionalExpressionKey> KEY_DESCRIPTOR = new KeyDescriptor<>() {
@Override
@@ -452,10 +451,13 @@ public class JavaFunctionalExpressionIndex extends FileBasedIndexExtension<Funct
@NotNull
@Override
public FileBasedIndex.InputFilter getInputFilter() {
return new DefaultFileTypeSpecificInputFilter(JavaFileType.INSTANCE) {
return new DefaultFileTypeSpecificWithProjectInputFilter(JavaFileType.INSTANCE) {
@Override
public boolean acceptInput(@NotNull VirtualFile file) {
return super.acceptInput(file) && JavaFileElementType.isInSourceContent(file);
public boolean acceptInput(@NotNull IndexedFile file) {
Project project = file.getProject();
return super.acceptInput(file) &&
(JavaFileElementType.isInSourceContent(file.getFile())) ||
(project != null && FileIndexFacade.getInstance(project).isInSource(file.getFile()));
}
};
}
@@ -81,6 +81,7 @@ public final class JavaFunctionalExpressionSearcher extends QueryExecutorBase<Ps
if (InjectedLanguageManager.getInstance(project).isInjectedFragment(aClass.getContainingFile()) || !hasJava8Modules(project)) {
return;
}
PsiSearchHelper psiSearchHelper = PsiSearchHelper.getInstance(project);
Set<PsiClass> visited = new HashSet<>();
processSubInterfaces(aClass, visited);
@@ -90,7 +91,7 @@ public final class JavaFunctionalExpressionSearcher extends QueryExecutorBase<Ps
PsiType samType = saMethod.getReturnType();
if (samType == null) continue;
SearchScope scope = samClass.getUseScope().intersectWith(session.scope);
SearchScope scope = psiSearchHelper.getUseScope(samClass).intersectWith(session.scope);
descriptors.add(new SamDescriptor(samClass, saMethod, samType, GlobalSearchScopeUtil.toGlobalSearchScope(scope, project)));
}
}
@@ -111,7 +112,7 @@ public final class JavaFunctionalExpressionSearcher extends QueryExecutorBase<Ps
MultiMap<VirtualFile, FunExprOccurrence> 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));
@@ -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) {
@@ -0,0 +1,5 @@
public class TestClass {
TestSrcInterface lambdaImpl = () -> {
return "";
};
}
@@ -0,0 +1,6 @@
public class TestInterfaceImpl implements TestSrcInterface {
@Override
public String testMethod() {
return "";
}
}
@@ -0,0 +1,3 @@
public interface TestSrcInterface {
public String testMethod();
}
@@ -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(
"<useScopeEnlarger implementation=\"com.intellij.scopes.LibraryUseSearchUsingScopeEnlargerTest$LibraryUseScopeEnlarger\"/>",
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<PsiClass> 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<LineMarkerInfo<?>> 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<PsiMethod> 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();
}
}
}
@@ -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<? super FileType> fileTypeSink) {
for (FileType ft : myFileTypes) {
fileTypeSink.consume(ft);
}
}
}