mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-01-07 22:09:38 +07:00
direct inheritors search: ensure cached value contains results both from compiler refs and source search
if compiler references first produce results, then these results must be included in cache, otherwise, if next time compiler references won't be available, cached value would contain partial (thus invalid) result
This commit is contained in:
@@ -33,7 +33,6 @@ import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* @author max
|
||||
@@ -57,25 +56,13 @@ public class JavaDirectInheritorsSearcher implements QueryExecutor<PsiClass, Dir
|
||||
});
|
||||
}
|
||||
|
||||
SearchScope scope;
|
||||
SearchScope useScope;
|
||||
CompilerDirectHierarchyInfo info = performSearchUsingCompilerIndices(parameters, project);
|
||||
if (info == null) {
|
||||
scope = parameters.getScope();
|
||||
useScope = ReadAction.compute(baseClass::getUseScope);
|
||||
}
|
||||
else {
|
||||
if (!processInheritorCandidates(info.getHierarchyChildren(), consumer, parameters.includeAnonymous())) return false;
|
||||
scope = ReadAction.compute(() -> parameters.getScope().intersectWith(info.getDirtyScope()));
|
||||
useScope = ReadAction.compute(() -> baseClass.getUseScope().intersectWith(info.getDirtyScope()));
|
||||
}
|
||||
|
||||
PsiClass[] cache = getOrCalculateDirectSubClasses(project, baseClass, useScope);
|
||||
PsiClass[] cache = getOrCalculateDirectSubClasses(project, baseClass, parameters);
|
||||
|
||||
if (cache.length == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
SearchScope scope = parameters.getScope();
|
||||
VirtualFile baseClassJarFile = null;
|
||||
// iterate by same-FQN groups. For each group process only same-jar subclasses, or all of them if they are all outside the jarFile.
|
||||
int groupStart = 0;
|
||||
@@ -140,7 +127,7 @@ public class JavaDirectInheritorsSearcher implements QueryExecutor<PsiClass, Dir
|
||||
// The list starts with non-anonymous classes, ends with anonymous sub classes
|
||||
// Classes grouped by their FQN. (Because among the same-named subclasses we should return only the same-jar ones, or all of them if there were none)
|
||||
@NotNull
|
||||
private static PsiClass[] getOrCalculateDirectSubClasses(@NotNull Project project, @NotNull PsiClass baseClass, @NotNull SearchScope useScope) {
|
||||
private static PsiClass[] getOrCalculateDirectSubClasses(@NotNull Project project, @NotNull PsiClass baseClass, @NotNull DirectClassInheritorsSearch.SearchParameters parameters) {
|
||||
ConcurrentMap<PsiClass, PsiClass[]> map = HighlightingCaches.getInstance(project).DIRECT_SUB_CLASSES;
|
||||
PsiClass[] cache = map.get(baseClass);
|
||||
if (cache != null) {
|
||||
@@ -151,7 +138,7 @@ public class JavaDirectInheritorsSearcher implements QueryExecutor<PsiClass, Dir
|
||||
if (StringUtil.isEmpty(baseClassName)) {
|
||||
return PsiClass.EMPTY_ARRAY;
|
||||
}
|
||||
cache = calculateDirectSubClasses(project, baseClass, baseClassName, useScope);
|
||||
cache = calculateDirectSubClasses(project, baseClass, baseClassName, parameters);
|
||||
// for non-physical elements ignore the cache completely because non-physical elements created so often/unpredictably so I can't figure out when to clear caches in this case
|
||||
if (ReadAction.compute(baseClass::isPhysical)) {
|
||||
cache = ConcurrencyUtil.cacheOrGet(map, baseClass, cache);
|
||||
@@ -174,7 +161,16 @@ public class JavaDirectInheritorsSearcher implements QueryExecutor<PsiClass, Dir
|
||||
private static PsiClass[] calculateDirectSubClasses(@NotNull Project project,
|
||||
@NotNull PsiClass baseClass,
|
||||
@NotNull String baseClassName,
|
||||
@NotNull SearchScope useScope) {
|
||||
@NotNull DirectClassInheritorsSearch.SearchParameters parameters) {
|
||||
SearchScope useScope;
|
||||
CompilerDirectHierarchyInfo info = performSearchUsingCompilerIndices(parameters, project);
|
||||
if (info == null) {
|
||||
useScope = ReadAction.compute(baseClass::getUseScope);
|
||||
}
|
||||
else {
|
||||
useScope = ReadAction.compute(() -> baseClass.getUseScope().intersectWith(info.getDirtyScope()));
|
||||
}
|
||||
|
||||
DumbService dumbService = DumbService.getInstance(project);
|
||||
GlobalSearchScope globalUseScope = dumbService.runReadActionInSmartMode(
|
||||
() -> StubHierarchyInheritorSearcher.restrictScope(new JavaSourceFilterScope(GlobalSearchScopeUtil.toGlobalSearchScope(useScope, project))));
|
||||
@@ -255,6 +251,10 @@ public class JavaDirectInheritorsSearcher implements QueryExecutor<PsiClass, Dir
|
||||
}
|
||||
}
|
||||
|
||||
if (info != null) {
|
||||
info.getHierarchyChildren().filter(element -> element instanceof PsiClass).forEach(aClass -> result.add((PsiClass)aClass));
|
||||
}
|
||||
|
||||
return result.isEmpty() ? PsiClass.EMPTY_ARRAY : result.toArray(PsiClass.EMPTY_ARRAY);
|
||||
}
|
||||
|
||||
@@ -275,17 +275,4 @@ public class JavaDirectInheritorsSearcher implements QueryExecutor<PsiClass, Dir
|
||||
(GlobalSearchScope)scope,
|
||||
JavaFileType.INSTANCE);
|
||||
}
|
||||
|
||||
private static boolean processInheritorCandidates(@NotNull Stream<PsiElement> classStream,
|
||||
@NotNull Processor<? super PsiClass> consumer,
|
||||
boolean acceptAnonymous) {
|
||||
if (!acceptAnonymous) {
|
||||
classStream = classStream.filter(c -> !(c instanceof PsiAnonymousClass));
|
||||
}
|
||||
return ContainerUtil.process(classStream.iterator(), e -> {
|
||||
ProgressManager.checkCanceled();
|
||||
PsiClass c = (PsiClass) e;
|
||||
return consumer.process(c);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,9 @@ import com.intellij.openapi.fileEditor.FileDocumentManager
|
||||
import com.intellij.openapi.module.JavaModuleType
|
||||
import com.intellij.openapi.module.Module
|
||||
import com.intellij.openapi.roots.ModuleRootModificationUtil
|
||||
import com.intellij.psi.PsiClassOwner
|
||||
import com.intellij.psi.PsiManager
|
||||
import com.intellij.psi.search.searches.ClassInheritorsSearch
|
||||
import com.intellij.testFramework.PsiTestUtil
|
||||
|
||||
class CompilerReferencesMultiModuleTest : CompilerReferencesTestBase() {
|
||||
@@ -48,6 +50,26 @@ class CompilerReferencesMultiModuleTest : CompilerReferencesTestBase() {
|
||||
assertEmpty(dirtyModules())
|
||||
}
|
||||
|
||||
@Throws(Exception::class)
|
||||
fun testDirtyScopeCachedResults() {
|
||||
val file1 = myFixture.addFileToProject("A/Foo.java", "public class Foo {" +
|
||||
"static class Bar extends Foo {} " +
|
||||
"}")
|
||||
myFixture.addFileToProject("B/Unrelated.java", "public class Unrelated {}")
|
||||
rebuildProject()
|
||||
val foo = (file1 as PsiClassOwner).classes[0]
|
||||
assertOneElement(ClassInheritorsSearch.search(foo, foo.useScope, false).findAll())
|
||||
|
||||
try {
|
||||
kotlin.test.assertTrue { CompilerReferenceService.IS_ENABLED_KEY.asBoolean() }
|
||||
CompilerReferenceService.IS_ENABLED_KEY.setValue(false)
|
||||
assertOneElement(ClassInheritorsSearch.search(foo, foo.useScope, false).findAll())
|
||||
}
|
||||
finally {
|
||||
CompilerReferenceService.IS_ENABLED_KEY.setValue(true)
|
||||
}
|
||||
}
|
||||
|
||||
fun testLeafModuleTyping() {
|
||||
myFixture.addFileToProject("BaseClass.java", "public interface BaseClass{}")
|
||||
val classA = myFixture.addFileToProject("A/ClassA.java", "public class ClassA implements BaseClass{}")
|
||||
|
||||
Reference in New Issue
Block a user