mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-01-07 22:09:38 +07:00
optimizations: do not calculate containing file too often, do not traverse list unnecessarily, to alleviate some of IDEA-275798 IDE causes high CPU load (after some time)
GitOrigin-RevId: cd891346ca7fb3de8e6af0817f9c5ff74a6091ad
This commit is contained in:
committed by
intellij-monorepo-bot
parent
93da6311fc
commit
b03472d8ca
@@ -4,11 +4,8 @@ package com.intellij.psi.impl.search;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.roots.ProjectFileIndex;
|
||||
import com.intellij.openapi.util.NotNullLazyValue;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.psi.CommonClassNames;
|
||||
import com.intellij.psi.PsiClass;
|
||||
import com.intellij.psi.PsiCompiledElement;
|
||||
import com.intellij.psi.PsiModifierList;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.impl.ResolveScopeManager;
|
||||
import com.intellij.psi.impl.source.resolve.JavaResolveUtil;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
import com.intellij.psi.search.PsiSearchScopeUtil;
|
||||
@@ -18,12 +15,9 @@ import com.intellij.psi.util.CachedValuesManager;
|
||||
import com.intellij.psi.util.PsiModificationTracker;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.reference.SoftReference;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.lang.ref.Reference;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
@@ -34,7 +28,7 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
public final class RelaxedDirectInheritorChecker {
|
||||
private final String myBaseClassName;
|
||||
private final PsiClass myBaseClass;
|
||||
private final NotNullLazyValue<Pair<PsiClass[], Boolean>> myClasses;
|
||||
private final NotNullLazyValue<ClassesAndAmbiguities> myClasses;
|
||||
private final ProjectFileIndex myFileIndex;
|
||||
|
||||
public RelaxedDirectInheritorChecker(@NotNull PsiClass baseClass) {
|
||||
@@ -44,58 +38,82 @@ public final class RelaxedDirectInheritorChecker {
|
||||
myFileIndex = ProjectFileIndex.getInstance(myBaseClass.getProject());
|
||||
}
|
||||
|
||||
private static @NotNull Pair<PsiClass[], Boolean> getClassesAndTheirAmbiguities(@NotNull Project project, @NotNull String classShortName) {
|
||||
Map<String, Reference<Pair<PsiClass[],Boolean>>> cache = CachedValuesManager.getManager(project).getCachedValue(project, () ->
|
||||
private static class ClassesAndAmbiguities {
|
||||
@NotNull final PsiClass @NotNull[] classes;
|
||||
@NotNull final PsiFile @NotNull[] containingFiles;
|
||||
final boolean isAmbiguous;
|
||||
|
||||
private ClassesAndAmbiguities(@NotNull PsiClass @NotNull[] classes, @NotNull PsiFile @NotNull[] containingFiles, boolean isAmbiguous) {
|
||||
this.classes = classes;
|
||||
this.containingFiles = containingFiles;
|
||||
this.isAmbiguous = isAmbiguous;
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static ClassesAndAmbiguities getClassesAndTheirAmbiguities(@NotNull Project project, @NotNull String classShortName) {
|
||||
Map<String, Reference<ClassesAndAmbiguities>> cache = CachedValuesManager.getManager(project).getCachedValue(project, () ->
|
||||
CachedValueProvider.Result.create(new ConcurrentHashMap<>(), PsiModificationTracker.MODIFICATION_COUNT));
|
||||
Pair<PsiClass[], Boolean> result = SoftReference.dereference(cache.get(classShortName));
|
||||
ClassesAndAmbiguities result = SoftReference.dereference(cache.get(classShortName));
|
||||
if (result == null) {
|
||||
PsiClass[] classes = PsiShortNamesCache.getInstance(project).getClassesByName(classShortName, GlobalSearchScope.allScope(project));
|
||||
boolean ambiguities = hasAmbiguities(Arrays.asList(classes));
|
||||
result = Pair.create(classes, ambiguities);
|
||||
String ambiguity = null;
|
||||
PsiFile[] files = new PsiFile[classes.length];
|
||||
for (int i = 0; i < classes.length; i++) {
|
||||
PsiClass psiClass = classes[i];
|
||||
ambiguity = hasAmbiguitiesSoFar(psiClass, ambiguity);
|
||||
files[i] = psiClass.getContainingFile();
|
||||
}
|
||||
boolean ambiguities = ambiguity == AMBIGUITY_FOUND;
|
||||
result = new ClassesAndAmbiguities(classes, files, ambiguities);
|
||||
|
||||
cache.put(classShortName, new SoftReference<>(result));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// false if all classes in the list have the same FQN
|
||||
private static boolean hasAmbiguities(@NotNull List<? extends PsiClass> classes) {
|
||||
int locals = 0;
|
||||
String theFQN = null;
|
||||
for (PsiClass psiClass : classes) {
|
||||
String qName = psiClass.getQualifiedName();
|
||||
if (qName == null) {
|
||||
locals++;
|
||||
if (locals > 1) return true;
|
||||
}
|
||||
else if (theFQN == null) {
|
||||
theFQN = qName;
|
||||
}
|
||||
else if (!theFQN.equals(qName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return locals == 1 && theFQN != null;
|
||||
// because of lack of multiple-value return, we have this monstrosity
|
||||
// in the end (after the whole list is traversed with 'a=hasAmbiguitiesSoFar(c,a)' called on each element), one of these cases is returned:
|
||||
// - AMBIGUITY_FOUND: if there exist two classes with different FQNs (or at least one local class and at least one with not-null FQN)
|
||||
// - LOCAL_CLASS_FOUND: one local class found, all others have same FQN
|
||||
// - other: if all classes in the list have the same FQN
|
||||
private static String hasAmbiguitiesSoFar(@NotNull PsiClass psiClass, String oldFqn) {
|
||||
if (oldFqn == AMBIGUITY_FOUND) return AMBIGUITY_FOUND;
|
||||
String qName = Objects.requireNonNullElse(psiClass.getQualifiedName(), LOCAL_CLASS_FOUND);
|
||||
return oldFqn == null || oldFqn.equals(qName) && !oldFqn.equals(LOCAL_CLASS_FOUND) ? qName : AMBIGUITY_FOUND;
|
||||
}
|
||||
private static final String LOCAL_CLASS_FOUND = "?LOCAL_CLASS_FOUND";
|
||||
private static final String AMBIGUITY_FOUND = "?AMBIGUITY_FOUND";
|
||||
|
||||
/**
|
||||
* This assumes that {@code inheritorCandidate} is in the use scope of {@link #myBaseClass}
|
||||
*/
|
||||
public boolean checkInheritance(@NotNull PsiClass inheritorCandidate) {
|
||||
if (!inheritorCandidate.isValid() || !myBaseClass.isValid()) return false;
|
||||
if (myFileIndex.isInSourceContent(inheritorCandidate.getContainingFile().getVirtualFile())) {
|
||||
Pair<PsiClass[], Boolean> value = myClasses.getValue();
|
||||
boolean hasGlobalAmbiguities = value.getSecond();
|
||||
PsiFile inheritorCandidateContainingFile = inheritorCandidate.getContainingFile();
|
||||
if (myFileIndex.isInSourceContent(inheritorCandidateContainingFile.getVirtualFile())) {
|
||||
ClassesAndAmbiguities value = myClasses.getValue();
|
||||
boolean hasGlobalAmbiguities = value.isAmbiguous;
|
||||
if (!hasGlobalAmbiguities) {
|
||||
return true;
|
||||
}
|
||||
|
||||
PsiClass[] classes = value.getFirst();
|
||||
GlobalSearchScope scope = inheritorCandidate.getResolveScope();
|
||||
List<PsiClass> accessible = ContainerUtil.findAll(classes, base ->
|
||||
PsiSearchScopeUtil.isInScope(scope, base) && isAccessibleLight(inheritorCandidate, base));
|
||||
if (!hasAmbiguities(accessible)) {
|
||||
return accessible.contains(myBaseClass);
|
||||
PsiClass[] classes = value.classes;
|
||||
PsiFile[] files = value.containingFiles;
|
||||
GlobalSearchScope scope = ResolveScopeManager.getInstance(inheritorCandidateContainingFile.getProject()).getResolveScope(inheritorCandidateContainingFile);
|
||||
String ambiguity = null;
|
||||
boolean hasBaseClass = false;
|
||||
for (int i = 0; i < classes.length; i++) {
|
||||
PsiClass base = classes[i];
|
||||
PsiFile file = files[i];
|
||||
if (PsiSearchScopeUtil.isInScope(scope, file) && isAccessibleLight(inheritorCandidate, inheritorCandidateContainingFile, base)) {
|
||||
hasBaseClass |= base.equals(myBaseClass);
|
||||
ambiguity = hasAmbiguitiesSoFar(base, ambiguity);
|
||||
}
|
||||
}
|
||||
if (ambiguity != AMBIGUITY_FOUND) {
|
||||
return hasBaseClass;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,12 +129,14 @@ public final class RelaxedDirectInheritorChecker {
|
||||
inheritorCandidate.isAnnotationType() && CommonClassNames.JAVA_LANG_ANNOTATION_ANNOTATION.equals(myBaseClass.getQualifiedName());
|
||||
}
|
||||
|
||||
private static boolean isAccessibleLight(@NotNull PsiClass inheritorCandidate, @NotNull PsiClass base) {
|
||||
private static boolean isAccessibleLight(@NotNull PsiClass inheritorCandidate,
|
||||
@NotNull PsiFile inheritorCandidateContainingFile,
|
||||
@NotNull PsiClass base) {
|
||||
PsiModifierList modifierList = base.getModifierList();
|
||||
if (modifierList != null && PsiUtil.getAccessLevel(modifierList) == PsiUtil.ACCESS_LEVEL_PROTECTED) {
|
||||
return true; // requires hierarchy checks => resolve
|
||||
}
|
||||
|
||||
return JavaResolveUtil.isAccessible(base, base.getContainingClass(), modifierList, inheritorCandidate, null, null);
|
||||
return JavaResolveUtil.isAccessible(base, base.getContainingClass(), modifierList, inheritorCandidate, null, null, inheritorCandidateContainingFile);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user