search java direct inheritors using javac indices (initial): do not process lots of non-direct inheritors for library classes (IDEA-CR-14364)

This commit is contained in:
Dmitry Batkovich
2016-10-12 02:01:14 +03:00
parent 53c37a01a7
commit bc7bbf4866
8 changed files with 95 additions and 96 deletions
@@ -88,17 +88,11 @@ class CompilerReferenceReader {
@NotNull GlobalSearchScope searchScope,
@NotNull GlobalSearchScope dirtyScope,
@NotNull Project project,
FileType fileType) {
@NotNull FileType fileType) {
if (classSearchElementInfo == null) return null;
LOG.assertTrue(classSearchElementInfo.searchElements.length == 1);
CompilerBackwardReferenceIndex.LightDefinition[] candidates =
Stream.of(classSearchElementInfo.searchElements)
.map(this::asLightUsage)
.map(myIndex.getBackwardHierarchyMap()::get)
.filter(Objects::nonNull)
.flatMap(Collection::stream)
.toArray(CompilerBackwardReferenceIndex.LightDefinition[]::new);
Collection<CompilerBackwardReferenceIndex.LightDefinition> candidates = myIndex.getBackwardHierarchyMap().get(asLightUsage(classSearchElementInfo.searchElements[0]));
if (candidates == null) return Couple.of(Collections.emptyMap(), Collections.emptyMap());
Set<Class<? extends LightUsage>> suitableClasses = new THashSet<>();
@@ -111,7 +105,8 @@ class CompilerReferenceReader {
final GlobalSearchScope effectiveSearchScope = GlobalSearchScope.notScope(dirtyScope).intersectWith(searchScope);
Map<VirtualFile, SmartList<String>> candidatesPerFile = Stream.of(candidates)
Map<VirtualFile, SmartList<String>> candidatesPerFile = candidates
.stream()
.filter(def -> suitableClasses.contains(def.getUsage().getClass()))
.map(definition -> {
final VirtualFile file = findFile(definition.getFileId());
@@ -25,17 +25,15 @@ import com.intellij.openapi.module.ModuleManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ProjectFileIndex;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.roots.impl.LibraryScopeCache;
import com.intellij.openapi.util.Couple;
import com.intellij.openapi.util.ModificationTracker;
import com.intellij.openapi.util.UserDataHolderBase;
import com.intellij.openapi.vfs.*;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiNamedElement;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.util.CachedValueProvider;
import com.intellij.psi.util.CachedValuesManager;
import com.intellij.psi.util.PsiModificationTracker;
import com.intellij.psi.util.*;
import com.intellij.util.containers.ConcurrentFactoryMap;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.indexing.FileBasedIndex;
@@ -204,9 +202,7 @@ public class CompilerReferenceServiceImpl extends CompilerReferenceService imple
@NotNull GlobalSearchScope searchScope,
@NotNull ClassResolvingCompilerSearchAdapter<T> inheritorSearchAdapter,
@NotNull FileType searchFileType) {
if (!isServiceEnabled() ||
InjectedLanguageManager.getInstance(myProject).isInjectedFragment(aClass.getContainingFile()) ||
!myProjectFileIndex.isInSourceContent(aClass.getContainingFile().getVirtualFile())) return null;
if (!isServiceEnabled() || InjectedLanguageManager.getInstance(myProject).isInjectedFragment(aClass.getContainingFile())) return null;
Couple<Map<VirtualFile, T[]>> directInheritorsAndCandidates =
CachedValuesManager.getCachedValue(aClass, () -> CachedValueProvider.Result.create(calculateDirectInheritors(aClass,
@@ -217,7 +213,11 @@ public class CompilerReferenceServiceImpl extends CompilerReferenceService imple
this));
if (directInheritorsAndCandidates == null) return null;
return new CompilerDirectInheritorInfoImpl<>(directInheritorsAndCandidates, myDirtyModulesHolder.getDirtyScope(), searchScope);
GlobalSearchScope dirtyScope = myDirtyModulesHolder.getDirtyScope();
if (ElementPlace.LIB == ElementPlace.get(aClass.getContainingFile().getVirtualFile(), myProjectFileIndex)) {
dirtyScope = dirtyScope.union(LibraryScopeCache.getInstance(myProject).getLibrariesOnlyScope());
}
return new CompilerDirectInheritorInfoImpl<>(directInheritorsAndCandidates, dirtyScope, searchScope);
}
private boolean isServiceEnabled() {
@@ -227,8 +227,8 @@ public class CompilerReferenceServiceImpl extends CompilerReferenceService imple
private <T extends PsiNamedElement> Couple<Map<VirtualFile, T[]>> calculateDirectInheritors(@NotNull PsiNamedElement aClass,
@NotNull ClassResolvingCompilerSearchAdapter<T> searchAdapter,
@NotNull GlobalSearchScope useScope,
FileType searchFileType) {
final CompilerElementInfo searchElementInfo = asCompilerElements(aClass, searchAdapter);
@NotNull FileType searchFileType) {
final CompilerElementInfo searchElementInfo = asCompilerElements(aClass, searchAdapter, false);
synchronized (myLock) {
if (myReader == null) return null;
return myReader.getDirectInheritors(aClass,
@@ -252,7 +252,7 @@ public class CompilerReferenceServiceImpl extends CompilerReferenceService imple
@Nullable
private TIntHashSet getReferentFileIds(@NotNull PsiElement element, @NotNull CompilerSearchAdapter adapter) {
final CompilerElementInfo compilerElementInfo = asCompilerElements(element, adapter);
final CompilerElementInfo compilerElementInfo = asCompilerElements(element, adapter, true);
if (compilerElementInfo == null) return null;
synchronized (myLock) {
@@ -268,27 +268,24 @@ public class CompilerReferenceServiceImpl extends CompilerReferenceService imple
}
@Nullable
private CompilerElementInfo asCompilerElements(@NotNull PsiElement element, @NotNull CompilerSearchAdapter adapter) {
final PsiFile file = element.getContainingFile();
if (file == null) return null;
final VirtualFile vFile = file.getVirtualFile();
if (vFile == null) return null;
ElementPlace place = ElementPlace.get(vFile, myProjectFileIndex);
if (place == null) {
private CompilerElementInfo asCompilerElements(@NotNull PsiElement psiElement, @NotNull CompilerSearchAdapter adapter, boolean buildHierarchyForLibraryElements) {
VirtualFile file = PsiUtilCore.getVirtualFile(psiElement);
ElementPlace place = ElementPlace.get(file, myProjectFileIndex);
if (place == null || (place == ElementPlace.SRC && myDirtyModulesHolder.contains(file))) {
return null;
}
if (myDirtyModulesHolder.contains(vFile)) {
return null;
}
if (place == ElementPlace.SRC) {
final CompilerElement compilerElement = adapter.asCompilerElement(element);
return compilerElement == null ? null : new CompilerElementInfo(place, compilerElement);
final CompilerElement compilerElement = adapter.asCompilerElement(psiElement);
if (compilerElement == null) return null;
if (place == ElementPlace.LIB && buildHierarchyForLibraryElements) {
final CompilerElement[] elements = adapter.getHierarchyRestrictedToLibrariesScope(compilerElement, psiElement);
final CompilerElement[] fullHierarchy = new CompilerElement[elements.length + 1];
fullHierarchy[0] = compilerElement;
System.arraycopy(elements, 0, fullHierarchy, 1, elements.length);
return new CompilerElementInfo(place, fullHierarchy);
}
else {
final CompilerElement[] elements = adapter.libraryElementAsCompilerElements(element);
return elements.length == 0 ? null : new CompilerElementInfo(place, elements);
return new CompilerElementInfo(place, compilerElement);
}
}
@@ -329,8 +326,8 @@ public class CompilerReferenceServiceImpl extends CompilerReferenceService imple
SRC, LIB;
private static ElementPlace get(VirtualFile file, ProjectFileIndex index) {
return index.isInSourceContent(file) ? SRC :
((index.isInLibrarySource(file) || index.isInLibraryClasses(file)) ? LIB : null);
if (file == null) return null;
return index.isInSourceContent(file) ? SRC : ((index.isInLibrarySource(file) || index.isInLibraryClasses(file)) ? LIB : null);
}
}
@@ -18,6 +18,8 @@ package com.intellij.compiler;
public abstract class CompilerElement {
public static final CompilerElement[] EMPTY_ARRAY = new CompilerElement[0];
public abstract CompilerElement override(String overriderName);
public static class CompilerMethod extends CompilerElement {
private final String myJavacClassName;
private final String myJavacMethodName;
@@ -40,6 +42,11 @@ public abstract class CompilerElement {
public int getJavacParameterCount() {
return myJavacParameterCount;
}
@Override
public CompilerElement override(String overriderName) {
return new CompilerMethod(overriderName, myJavacMethodName, myJavacParameterCount);
}
}
public static class CompilerClass extends CompilerElement {
@@ -52,6 +59,11 @@ public abstract class CompilerElement {
public String getJavacName() {
return myJavacName;
}
@Override
public CompilerElement override(String overriderName) {
return new CompilerClass(myJavacName);
}
}
public static class CompilerField extends CompilerElement {
@@ -70,6 +82,11 @@ public abstract class CompilerElement {
public String getJavacName() {
return myJavacName;
}
@Override
public CompilerElement override(String overriderName) {
return new CompilerField(overriderName, myJavacName);
}
}
public static class CompilerFunExpr extends CompilerElement {
@@ -82,5 +99,10 @@ public abstract class CompilerElement {
public String getJavacClassName() {
return myJavacClassName;
}
@Override
public CompilerElement override(String overriderName) {
throw new UnsupportedOperationException();
}
}
}
@@ -27,5 +27,5 @@ public interface CompilerSearchAdapter {
CompilerElement asCompilerElement(@NotNull PsiElement psi);
@NotNull
CompilerElement[] libraryElementAsCompilerElements(@NotNull PsiElement psi);
CompilerElement[] getHierarchyRestrictedToLibrariesScope(@NotNull CompilerElement baseLibraryElement, @NotNull PsiElement baseLibraryPsi);
}
@@ -26,18 +26,15 @@ import com.intellij.psi.impl.java.stubs.impl.PsiClassStubImpl;
import com.intellij.psi.impl.source.PsiFileImpl;
import com.intellij.psi.impl.source.PsiFileWithStubSupport;
import com.intellij.psi.search.searches.ClassInheritorsSearch;
import com.intellij.psi.stubs.StubBase;
import com.intellij.psi.stubs.StubElement;
import com.intellij.psi.stubs.StubTree;
import com.intellij.psi.util.ClassUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.ObjectUtils;
import com.intellij.util.Processor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.function.Function;
public class JavaBaseCompilerSearchAdapter implements ClassResolvingCompilerSearchAdapter<PsiClass> {
public static final JavaBaseCompilerSearchAdapter INSTANCE = new JavaBaseCompilerSearchAdapter();
@@ -82,43 +79,18 @@ public class JavaBaseCompilerSearchAdapter implements ClassResolvingCompilerSear
@NotNull
@Override
public CompilerElement[] libraryElementAsCompilerElements(@NotNull PsiElement element) {
if (mayBeVisibleOutsideOwnerFile(element)) {
if (element instanceof PsiField || element instanceof PsiMethod) {
final String name = ((PsiMember)element).getName();
final Function<String, CompilerElement> builder;
if (element instanceof PsiField) {
builder = (ownerJvmName) -> new CompilerElement.CompilerField(ownerJvmName, name);
}
else {
final int parametersCount = ((PsiMethod)element).getParameterList().getParametersCount();
builder = (ownerJvmName) -> new CompilerElement.CompilerMethod(ownerJvmName, name, parametersCount);
}
final List<CompilerElement> result = new ArrayList<>();
inLibrariesHierarchy(((PsiMember)element).getContainingClass(), aClass -> {
final String jvmClassName = ClassUtil.getJVMClassName(aClass);
if (jvmClassName != null) {
result.add(builder.apply(jvmClassName));
}
return true;
});
return result.toArray(new CompilerElement[result.size()]);
}
else if (element instanceof PsiClass) {
final List<CompilerElement> result = new ArrayList<>();
inLibrariesHierarchy((PsiClass)element, aClass -> {
final String jvmClassName = ClassUtil.getJVMClassName(aClass);
if (jvmClassName != null) {
result.add(new CompilerElement.CompilerClass(jvmClassName));
}
return true;
});
return result.toArray(new CompilerElement[result.size()]);
}
}
return CompilerElement.EMPTY_ARRAY;
public CompilerElement[] getHierarchyRestrictedToLibrariesScope(@NotNull CompilerElement baseLibraryElement, @NotNull PsiElement baseLibraryPsi) {
final PsiClass baseClass = ObjectUtils.notNull(baseLibraryPsi instanceof PsiClass ? (PsiClass)baseLibraryPsi : ((PsiMember)baseLibraryPsi).getContainingClass());
final List<CompilerElement> overridden = new ArrayList<>();
Processor<PsiClass> processor = c -> {
if (c.hasModifierProperty(PsiModifier.PRIVATE)) return true;
String qName = c.getQualifiedName();
if (qName == null) return true;
overridden.add(baseLibraryElement.override(qName));
return true;
};
ClassInheritorsSearch.search(baseClass, LibraryScopeCache.getInstance(baseClass.getProject()).getLibrariesOnlyScope(), true).forEach(processor);
return overridden.toArray(new CompilerElement[overridden.size()]);
}
@NotNull
@@ -137,14 +109,6 @@ public class JavaBaseCompilerSearchAdapter implements ClassResolvingCompilerSear
return true;
}
private static void inLibrariesHierarchy(PsiClass aClass, Processor<PsiClass> processor) {
if (aClass != null) {
processor.process(aClass);
ClassInheritorsSearch.search(aClass, LibraryScopeCache.getInstance(aClass.getProject()).getLibrariesOnlyScope(), true)
.forEach(processor);
}
}
private static List<PsiClass> retrieveMatchedClasses(VirtualFile file, Project project, Collection<InternalClassMatcher> matchers) {
final List<PsiClass> result = new ArrayList<>(matchers.size());
PsiFileWithStubSupport psiFile = ObjectUtils.notNull((PsiFileWithStubSupport)PsiManager.getInstance(project).findFile(file));
@@ -39,8 +39,7 @@ public class JavaFunctionalExpressionCompilerSearchAdapter implements CompilerSe
@NotNull
@Override
public CompilerElement[] libraryElementAsCompilerElements(@NotNull PsiElement psi) {
final CompilerElement element = asCompilerElement(psi);
return element == null ? CompilerElement.EMPTY_ARRAY : new CompilerElement[] {element};
public CompilerElement[] getHierarchyRestrictedToLibrariesScope(@NotNull CompilerElement baseLibraryElement, @NotNull PsiElement baseLibraryPsi) {
return CompilerElement.EMPTY_ARRAY;
}
}
@@ -0,0 +1,14 @@
import java.util.List;
import java.util.AbstractList;
class Foo {
interface ListImpl extends List {
}
abstract static class AbstractListImpl extends AbstractList {
}
}
@@ -20,10 +20,7 @@ import com.intellij.codeInsight.completion.AbstractCompilerAwareTest;
import com.intellij.openapi.fileTypes.StdFileTypes;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.PsiAnonymousClass;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiMember;
import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.testFramework.SkipSlowTestLocally;
@@ -101,17 +98,28 @@ public class CompilerReferencesTest extends AbstractCompilerAwareTest {
assertEmpty(candidates);
}
public void testHierarchyOfLibClass() {
myFixture.configureByFiles(getName() + "/Foo.java");
rebuildProject();
CompilerReferenceService.CompilerDirectInheritorInfo<PsiClass> directInheritorInfo = getHierarchyFor(myFixture.getJavaFacade().findClass(CommonClassNames.JAVA_UTIL_LIST));
PsiClass inheritor = assertOneElement(directInheritorInfo.getDirectInheritors().collect(Collectors.toList()));
assertEquals("Foo.ListImpl", inheritor.getQualifiedName());
}
private CompilerReferenceService.CompilerDirectInheritorInfo<PsiClass> getHierarchyUnderForElementCaret() {
final PsiElement atCaret = myFixture.getElementAtCaret();
assertNotNull(atCaret);
final PsiClass classAtCaret = PsiTreeUtil.getParentOfType(atCaret, PsiClass.class, false);
assertNotNull(classAtCaret);
return getHierarchyFor(classAtCaret);
}
private CompilerReferenceService.CompilerDirectInheritorInfo<PsiClass> getHierarchyFor(PsiClass classAtCaret) {
return CompilerReferenceService.getInstance(myFixture.getProject()).getDirectInheritors(classAtCaret,
assertInstanceOf(classAtCaret.getUseScope(), GlobalSearchScope.class),
assertInstanceOf(classAtCaret.getUseScope(), GlobalSearchScope.class),
JavaBaseCompilerSearchAdapter.INSTANCE,
StdFileTypes.JAVA);
}
private Set<VirtualFile> getReferentFilesForElementUnderCaret(CompilerSearchAdapter adapter) {