use stub hierarchy in DirectClassInheritorsSearch

This commit is contained in:
peter
2016-06-09 18:34:58 +02:00
parent 0ac464856f
commit 8f51b87597
8 changed files with 158 additions and 35 deletions
@@ -15,14 +15,17 @@
*/
package com.intellij.psi.stubsHierarchy.impl;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileWithId;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import com.intellij.psi.search.DelegatingGlobalSearchScope;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.stubsHierarchy.ClassHierarchy;
import com.intellij.psi.stubsHierarchy.SmartClassAnchor;
import com.intellij.psi.stubsHierarchy.impl.Symbol.ClassSymbol;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.indexing.FileBasedIndex;
import gnu.trove.TIntHashSet;
import gnu.trove.TIntStack;
import org.jetbrains.annotations.NotNull;
import java.util.*;
@@ -32,6 +35,7 @@ import java.util.*;
*/
public class SingleClassHierarchy extends ClassHierarchy {
private final BitSet myCoveredFiles;
private final BitSet myAmbiguousSupers;
private final List<StubClassAnchor> myCoveredClasses;
private final StubClassAnchor[] myClassAnchors;
private final StubClassAnchor[] myClassAnchorsByFileIds;
@@ -42,10 +46,23 @@ public class SingleClassHierarchy extends ClassHierarchy {
myCoveredFiles = calcCoveredFiles(classSymbols);
myClassAnchors = ContainerUtil.map2Array(classSymbols, StubClassAnchor.class, symbol -> symbol.myClassAnchor);
myClassAnchorsByFileIds = mkByFileId(myClassAnchors);
excludeUncoveredFiles(classSymbols);
connectSubTypes(classSymbols);
myAmbiguousSupers = calcAmbiguousSupers(classSymbols);
myCoveredClasses = Collections.unmodifiableList(ContainerUtil.filter(myClassAnchors, this::isCovered));
}
@NotNull
private static BitSet calcAmbiguousSupers(ClassSymbol[] classSymbols) {
BitSet ambiguousSupers = new BitSet();
for (ClassSymbol symbol : classSymbols) {
if (!symbol.isHierarchyIncomplete() && symbol.hasAmbiguousSupers()) {
ambiguousSupers.set(symbol.myClassAnchor.myId);
}
}
return ambiguousSupers;
}
@NotNull
private static BitSet calcCoveredFiles(ClassSymbol[] classSymbols) {
BitSet problematicFiles = new BitSet();
@@ -79,6 +96,10 @@ public class SingleClassHierarchy extends ClassHierarchy {
@NotNull
@Override
public SmartClassAnchor[] getDirectSubtypeCandidates(@NotNull PsiClass psiClass) {
PsiElement original = psiClass.getOriginalElement();
if (original instanceof PsiClass) {
psiClass = (PsiClass)original;
}
int fileId = Math.abs(FileBasedIndex.getFileId(psiClass.getContainingFile().getVirtualFile()));
SmartClassAnchor anchor = forPsiClass(fileId, psiClass);
return anchor == null ? StubClassAnchor.EMPTY_ARRAY : getDirectSubtypeCandidates(anchor);
@@ -101,39 +122,28 @@ public class SingleClassHierarchy extends ClassHierarchy {
return result;
}
public StubClassAnchor[] getAllSubtypes(PsiClass base) {
int fileId = Math.abs(FileBasedIndex.getFileId(base.getContainingFile().getVirtualFile()));
@Override
public boolean hasAmbiguousSupers(@NotNull SmartClassAnchor anchor) {
return myAmbiguousSupers.get(((StubClassAnchor)anchor).myId);
}
TIntHashSet resultIds = new TIntHashSet();
TIntHashSet processed = new TIntHashSet();
TIntStack queue = new TIntStack();
StubClassAnchor baseAnchor = forPsiClass(fileId, base);
if (baseAnchor == null) {
return StubClassAnchor.EMPTY_ARRAY;
@NotNull
@Override
public GlobalSearchScope restrictToUncovered(@NotNull GlobalSearchScope scope) {
if (myCoveredClasses.isEmpty()) {
return scope;
}
queue.push(baseAnchor.myId);
while (queue.size() > 0) {
int id = queue.pop();
if (processed.add(id)) {
int start = subtypeStart(id);
int end = subtypeEnd(id);
for (int i = start; i < end; i++) {
int subtypeId = mySubtypes[i];
resultIds.add(subtypeId);
if (!processed.contains(subtypeId)) {
queue.push(subtypeId);
}
return new DelegatingGlobalSearchScope(scope, this) {
@Override
public boolean contains(@NotNull VirtualFile file) {
if (file instanceof VirtualFileWithId && myCoveredFiles.get(((VirtualFileWithId)file).getId())) {
return false;
}
return super.contains(file);
}
}
int[] allIds = resultIds.toArray();
StubClassAnchor[] result = new StubClassAnchor[allIds.length];
for (int i = 0; i < result.length; i++) {
result[i] = myClassAnchors[allIds[i]];
}
return result;
};
}
private static StubClassAnchor[] mkByFileId(final StubClassAnchor[] classAnchors) {
@@ -154,8 +164,6 @@ public class SingleClassHierarchy extends ClassHierarchy {
}
private void connectSubTypes(ClassSymbol[] classSymbols) {
excludeUncoveredFiles(classSymbols);
int[] sizes = calculateSizes(classSymbols);
int[] starts = new int[classSymbols.length];
@@ -18,6 +18,7 @@ package com.intellij.psi.stubsHierarchy.impl;
import com.intellij.psi.impl.java.stubs.hierarchy.IndexTree;
import com.intellij.psi.stubsHierarchy.stubs.UnitInfo;
import com.intellij.util.BitUtil;
import gnu.trove.TIntHashSet;
import org.jetbrains.annotations.NotNull;
import java.util.Comparator;
@@ -159,6 +160,20 @@ public abstract class Symbol {
boolean isHierarchyIncomplete() {
return myHierarchyIncomplete;
}
boolean hasAmbiguousSupers() {
ClassSymbol[] superClasses = rawSuperClasses();
if (superClasses.length < 2) return false;
TIntHashSet superNames = new TIntHashSet();
for (ClassSymbol symbol : superClasses) {
if (!superNames.add(symbol.myShortName)) {
return true;
}
}
return false;
}
}
/**
@@ -17,6 +17,7 @@ package com.intellij.psi.impl.search;
import com.intellij.concurrency.JobLauncher;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ReadAction;
import com.intellij.openapi.progress.ProgressIndicatorProvider;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.Project;
@@ -175,7 +176,7 @@ public class JavaDirectInheritorsSearcher implements QueryExecutor<PsiClass, Dir
@NotNull PsiClass baseClass,
@NotNull String baseClassName,
@NotNull SearchScope useScope) {
GlobalSearchScope globalUseScope = ApplicationManager.getApplication().runReadAction((Computable<GlobalSearchScope>)()-> toGlobal(useScope, project));
GlobalSearchScope globalUseScope = ReadAction.compute(() -> StubHierarchyInheritorSearcher.restrictScope(toGlobal(useScope, project)));
Collection<PsiReferenceList> candidates =
MethodUsagesSearcher.resolveInReadAction(project, () -> JavaSuperClassNameOccurenceIndex.getInstance().get(baseClassName, project, globalUseScope));
@@ -0,0 +1,80 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.psi.impl.search;
import com.intellij.openapi.application.QueryExecutorBase;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiClass;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.PsiSearchScopeUtil;
import com.intellij.psi.search.searches.DirectClassInheritorsSearch;
import com.intellij.psi.stubsHierarchy.ClassHierarchy;
import com.intellij.psi.stubsHierarchy.HierarchyService;
import com.intellij.psi.stubsHierarchy.SmartClassAnchor;
import com.intellij.util.Processor;
import org.jetbrains.annotations.NotNull;
/**
* @author peter
*/
public class StubHierarchyInheritorSearcher extends QueryExecutorBase<PsiClass, DirectClassInheritorsSearch.SearchParameters> {
public StubHierarchyInheritorSearcher() {
super(true);
}
private static boolean isSearching() {
return Registry.is("java.use.stub.hierarchy.in.inheritor.search");
}
@NotNull
public static GlobalSearchScope restrictScope(@NotNull GlobalSearchScope scope) {
if (!isSearching()) return scope;
Project project = scope.getProject();
return project == null ? scope : HierarchyService.getHierarchy(project).restrictToUncovered(scope);
}
@Override
public void processQuery(@NotNull DirectClassInheritorsSearch.SearchParameters queryParameters, @NotNull Processor<PsiClass> consumer) {
if (!(queryParameters.getScope() instanceof GlobalSearchScope) || !isSearching()) return;
PsiClass base = queryParameters.getClassToProcess();
GlobalSearchScope scope = (GlobalSearchScope)queryParameters.getScope();
ClassHierarchy hierarchy = HierarchyService.getHierarchy(base.getProject());
for (SmartClassAnchor anchor : hierarchy.getDirectSubtypeCandidates(base)) {
if (!processCandidate(consumer, base, scope, hierarchy, anchor)) {
return;
}
}
}
private static boolean processCandidate(Processor<PsiClass> consumer,
PsiClass base,
GlobalSearchScope scope,
ClassHierarchy hierarchy, SmartClassAnchor anchor) {
VirtualFile file = anchor.retrieveFile();
if (!scope.contains(file)) return true;
PsiClass candidate = anchor.retrieveClass(base.getProject());
if (!PsiSearchScopeUtil.isInScope(candidate.getResolveScope(), base)) return true;
if (hierarchy.hasAmbiguousSupers(anchor) && !candidate.isInheritor(base, false)) return true;
return consumer.process(candidate);
}
}
@@ -16,6 +16,7 @@
package com.intellij.psi.stubsHierarchy;
import com.intellij.psi.PsiClass;
import com.intellij.psi.search.GlobalSearchScope;
import org.jetbrains.annotations.NotNull;
import java.util.List;
@@ -41,4 +42,15 @@ public abstract class ClassHierarchy {
@NotNull
public abstract SmartClassAnchor[] getDirectSubtypeCandidates(@NotNull SmartClassAnchor anchor);
/**
* @return whether stub hierarchy resolver couldn't determine the super class exactly because there were several possible candidates
*/
public abstract boolean hasAmbiguousSupers(@NotNull SmartClassAnchor anchor);
/**
* @return the given scope restricted to the files not covered by this hierarchy, to use usual PSI/resolve-based inheritor search in it
*/
@NotNull
public abstract GlobalSearchScope restrictToUncovered(@NotNull GlobalSearchScope scope);
}
@@ -312,7 +312,11 @@ java.annotations.inference.nullable.method.transitivity=true
java.annotations.inference.nullable.method.transitivity.description=Restart is required; if a method result is a call to a @Nullable method, reports the caller as @Nullable as well
java.hierarchy.service=false
java.hierarchy.service.description=Restart is required; calculate class hierarchy in background
java.hierarchy.service.description=Enable special indexing for quick JVM class hierarchy calculation
java.hierarchy.service.restartRequired=true
java.use.stub.hierarchy.in.inheritor.search=false
java.use.stub.hierarchy.in.inheritor.search.description=Use stub hierarchy (enabled by java.hierarchy.service key) in inheritor searches
java.max.package.name.length=1000
java.max.package.name.length.description=An upper length limit on string that the IDE considers worth checking for being package names
@@ -20,6 +20,7 @@ import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.util.Computable;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import com.intellij.psi.impl.search.StubHierarchyInheritorSearcher;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.SearchScope;
import com.intellij.psi.search.searches.DirectClassInheritorsSearch;
@@ -74,7 +75,8 @@ public class GroovyDirectInheritorsSearcher implements QueryExecutor<PsiClass, D
@Override
public List<PsiClass> compute() {
if (!clazz.isValid()) return Collections.emptyList();
return getDerivingClassCandidates(clazz, (GlobalSearchScope)scope, queryParameters.includeAnonymous());
GlobalSearchScope restrictedScope = StubHierarchyInheritorSearcher.restrictScope((GlobalSearchScope)scope);
return getDerivingClassCandidates(clazz, restrictedScope, queryParameters.includeAnonymous());
}
});
for (final PsiClass candidate : candidates) {
+1
View File
@@ -353,6 +353,7 @@
<annotatedElementsSearch implementation="com.intellij.psi.impl.search.AnnotatedElementsSearcher"/>
<annotatedPackagesSearch implementation="com.intellij.psi.impl.search.AnnotatedPackagesSearcher"/>
<directClassInheritorsSearch implementation="com.intellij.psi.impl.search.JavaDirectInheritorsSearcher"/>
<directClassInheritorsSearch implementation="com.intellij.psi.impl.search.StubHierarchyInheritorSearcher"/>
<classInheritorsSearch implementation="com.intellij.psi.impl.search.JavaClassInheritorsSearcher"/>
<overridingMethodsSearch implementation="com.intellij.psi.impl.search.JavaOverridingMethodsSearcher"/>
<allOverridingMethodsSearch implementation="com.intellij.psi.impl.search.JavaAllOverridingMethodsSearcher"/>