mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
cache repeated requests for class inheritors and method overriders to speedup highlighting of heavily subclassed library classes to fix IDEA-152346 Syntax highlighting takes 45 seconds
This commit is contained in:
@@ -24,10 +24,8 @@ import com.intellij.psi.util.MethodSignature;
|
||||
import com.intellij.psi.util.MethodSignatureUtil;
|
||||
import com.intellij.psi.util.PsiSuperMethodUtil;
|
||||
import com.intellij.psi.util.TypeConversionUtil;
|
||||
import com.intellij.util.containers.FactoryMap;
|
||||
import gnu.trove.THashSet;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@@ -66,12 +64,11 @@ public class FindSuperElementsHelper {
|
||||
}
|
||||
|
||||
public static PsiMethod getSiblingInheritedViaSubClass(@NotNull PsiMethod method) {
|
||||
return Pair.getFirst(getSiblingInheritedViaSubClass(method, createSubClassCache()));
|
||||
return Pair.getFirst(getSiblingInfoInheritedViaSubClass(method));
|
||||
}
|
||||
|
||||
// returns super method, sub class
|
||||
public static Pair<PsiMethod, PsiClass> getSiblingInheritedViaSubClass(@NotNull final PsiMethod method,
|
||||
@NotNull Map<PsiClass, PsiClass> subClassCache) {
|
||||
public static Pair<PsiMethod, PsiClass> getSiblingInfoInheritedViaSubClass(@NotNull final PsiMethod method) {
|
||||
if (!method.hasModifierProperty(PsiModifier.PUBLIC)) return null;
|
||||
if (method.hasModifierProperty(PsiModifier.STATIC)) return null;
|
||||
final PsiClass containingClass = method.getContainingClass();
|
||||
@@ -86,55 +83,44 @@ public class FindSuperElementsHelper {
|
||||
final Ref<Pair<PsiMethod, PsiClass>> result = Ref.create();
|
||||
ClassInheritorsSearch.search(containingClass, containingClass.getUseScope(), true, true, false).forEach(
|
||||
inheritor -> {
|
||||
ProgressManager.checkCanceled();
|
||||
for (PsiClassType interfaceType : inheritor.getImplementsListTypes()) {
|
||||
ProgressManager.checkCanceled();
|
||||
for (PsiClassType interfaceType : inheritor.getImplementsListTypes()) {
|
||||
PsiClassType.ClassResolveResult resolved = interfaceType.resolveGenerics();
|
||||
PsiClass anInterface = resolved.getElement();
|
||||
if (anInterface == null || !checkedInterfaces.add(PsiAnchor.create(anInterface))) continue;
|
||||
for (PsiMethod superMethod : anInterface.findMethodsByName(method.getName(), true)) {
|
||||
PsiElement navigationElement = superMethod.getNavigationElement();
|
||||
if (!(navigationElement instanceof PsiMethod)) continue; // Kotlin
|
||||
superMethod = (PsiMethod)navigationElement;
|
||||
ProgressManager.checkCanceled();
|
||||
PsiClassType.ClassResolveResult resolved = interfaceType.resolveGenerics();
|
||||
PsiClass anInterface = resolved.getElement();
|
||||
if (anInterface == null || !checkedInterfaces.add(PsiAnchor.create(anInterface))) continue;
|
||||
for (PsiMethod superMethod : anInterface.findMethodsByName(method.getName(), true)) {
|
||||
PsiElement navigationElement = superMethod.getNavigationElement();
|
||||
if (!(navigationElement instanceof PsiMethod)) continue; // Kotlin
|
||||
superMethod = (PsiMethod)navigationElement;
|
||||
ProgressManager.checkCanceled();
|
||||
PsiClass superInterface = superMethod.getContainingClass();
|
||||
if (superInterface == null) {
|
||||
continue;
|
||||
}
|
||||
if (containingClass.isInheritor(superInterface, true)) {
|
||||
// if containingClass implements the superInterface then it's not a sibling inheritance but a pretty boring the usual one
|
||||
continue;
|
||||
}
|
||||
|
||||
// calculate substitutor of containingClass --> inheritor
|
||||
PsiSubstitutor substitutor = TypeConversionUtil.getSuperClassSubstitutor(containingClass, inheritor, PsiSubstitutor.EMPTY);
|
||||
// calculate substitutor of inheritor --> superInterface
|
||||
substitutor = TypeConversionUtil.getSuperClassSubstitutor(superInterface, inheritor, substitutor);
|
||||
|
||||
final MethodSignature superSignature = superMethod.getSignature(substitutor);
|
||||
final MethodSignature derivedSignature = method.getSignature(PsiSubstitutor.EMPTY);
|
||||
boolean isOverridden = MethodSignatureUtil.isSubsignature(superSignature, derivedSignature);
|
||||
|
||||
if (!isOverridden) {
|
||||
continue;
|
||||
}
|
||||
result.set(Pair.create(superMethod, inheritor));
|
||||
return false;
|
||||
PsiClass superInterface = superMethod.getContainingClass();
|
||||
if (superInterface == null) {
|
||||
continue;
|
||||
}
|
||||
if (containingClass.isInheritor(superInterface, true)) {
|
||||
// if containingClass implements the superInterface then it's not a sibling inheritance but a pretty boring the usual one
|
||||
continue;
|
||||
}
|
||||
|
||||
// calculate substitutor of containingClass --> inheritor
|
||||
PsiSubstitutor substitutor = TypeConversionUtil.getSuperClassSubstitutor(containingClass, inheritor, PsiSubstitutor.EMPTY);
|
||||
// calculate substitutor of inheritor --> superInterface
|
||||
substitutor = TypeConversionUtil.getSuperClassSubstitutor(superInterface, inheritor, substitutor);
|
||||
|
||||
final MethodSignature superSignature = superMethod.getSignature(substitutor);
|
||||
final MethodSignature derivedSignature = method.getSignature(PsiSubstitutor.EMPTY);
|
||||
boolean isOverridden = MethodSignatureUtil.isSubsignature(superSignature, derivedSignature);
|
||||
|
||||
if (!isOverridden) {
|
||||
continue;
|
||||
}
|
||||
result.set(Pair.create(superMethod, inheritor));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
return result.get();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static Map<PsiClass, PsiClass> createSubClassCache() {
|
||||
return new FactoryMap<PsiClass, PsiClass>() {
|
||||
@Nullable
|
||||
@Override
|
||||
protected PsiClass create(PsiClass aClass) {
|
||||
return ClassInheritorsSearch.search(aClass, false).findFirst();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2011 JetBrains s.r.o.
|
||||
* 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.
|
||||
@@ -299,7 +299,7 @@ public class CodeInsightUtil {
|
||||
});
|
||||
} else {
|
||||
Query<PsiClass> baseQuery = ClassInheritorsSearch.search(
|
||||
new ClassInheritorsSearch.SearchParameters(baseClass, scope, true, false, false, matcher::prefixMatches));
|
||||
new ClassInheritorsSearch.SearchParameters(baseClass, scope, true, true, false, matcher::prefixMatches));
|
||||
Query<PsiClass> query = new FilteredQuery<>(baseQuery, psiClass -> !(psiClass instanceof PsiTypeParameter));
|
||||
query.forEach(inheritorsProcessor);
|
||||
}
|
||||
|
||||
+7
-11
@@ -27,12 +27,12 @@ import com.intellij.openapi.editor.colors.EditorColorsScheme;
|
||||
import com.intellij.openapi.editor.markup.GutterIconRenderer;
|
||||
import com.intellij.openapi.editor.markup.SeparatorPlacement;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.impl.FindSuperElementsHelper;
|
||||
import com.intellij.psi.search.searches.AllOverridingMethodsSearch;
|
||||
import com.intellij.psi.search.searches.DirectClassInheritorsSearch;
|
||||
import com.intellij.psi.search.searches.FunctionalExpressionSearch;
|
||||
import com.intellij.psi.search.searches.SuperMethodsSearch;
|
||||
import com.intellij.psi.util.MethodSignatureBackedByPsiMethod;
|
||||
@@ -48,7 +48,6 @@ import org.jetbrains.annotations.Nullable;
|
||||
import javax.swing.*;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public class JavaLineMarkerProvider extends LineMarkerProviderDescriptor {
|
||||
@@ -162,7 +161,6 @@ public class JavaLineMarkerProvider extends LineMarkerProviderDescriptor {
|
||||
@Override
|
||||
public void collectSlowLineMarkers(@NotNull final List<PsiElement> elements, @NotNull final Collection<LineMarkerInfo> result) {
|
||||
ApplicationManager.getApplication().assertReadAccessAllowed();
|
||||
Map<PsiClass, PsiClass> subClassCache = FindSuperElementsHelper.createSubClassCache();
|
||||
|
||||
Collection<PsiMethod> methods = new THashSet<>();
|
||||
//noinspection ForLoopReplaceableByForEach
|
||||
@@ -178,18 +176,17 @@ public class JavaLineMarkerProvider extends LineMarkerProviderDescriptor {
|
||||
}
|
||||
}
|
||||
else if (parent instanceof PsiClass && !(parent instanceof PsiTypeParameter)) {
|
||||
collectInheritingClasses((PsiClass)parent, result, subClassCache);
|
||||
collectInheritingClasses((PsiClass)parent, result);
|
||||
}
|
||||
}
|
||||
if (!methods.isEmpty()) {
|
||||
collectOverridingMethods(methods, result);
|
||||
collectSiblingInheritedMethods(methods, result, subClassCache);
|
||||
collectSiblingInheritedMethods(methods, result);
|
||||
}
|
||||
}
|
||||
|
||||
private static void collectSiblingInheritedMethods(@NotNull final Collection<PsiMethod> methods,
|
||||
@NotNull Collection<LineMarkerInfo> result,
|
||||
@NotNull Map<PsiClass, PsiClass> subClassCache) {
|
||||
@NotNull Collection<LineMarkerInfo> result) {
|
||||
for (PsiMethod method : methods) {
|
||||
ProgressManager.checkCanceled();
|
||||
PsiClass aClass = method.getContainingClass();
|
||||
@@ -198,7 +195,7 @@ public class JavaLineMarkerProvider extends LineMarkerProviderDescriptor {
|
||||
boolean canHaveSiblingSuper = !method.hasModifierProperty(PsiModifier.ABSTRACT) && !method.hasModifierProperty(PsiModifier.STATIC) && method.hasModifierProperty(PsiModifier.PUBLIC)&& !method.hasModifierProperty(PsiModifier.FINAL)&& !method.hasModifierProperty(PsiModifier.NATIVE);
|
||||
if (!canHaveSiblingSuper) continue;
|
||||
|
||||
PsiMethod siblingInheritedViaSubClass = Pair.getFirst(FindSuperElementsHelper.getSiblingInheritedViaSubClass(method, subClassCache));
|
||||
PsiMethod siblingInheritedViaSubClass = FindSuperElementsHelper.getSiblingInheritedViaSubClass(method);
|
||||
if (siblingInheritedViaSubClass == null) {
|
||||
continue;
|
||||
}
|
||||
@@ -229,14 +226,13 @@ public class JavaLineMarkerProvider extends LineMarkerProviderDescriptor {
|
||||
}
|
||||
|
||||
protected void collectInheritingClasses(@NotNull PsiClass aClass,
|
||||
@NotNull Collection<LineMarkerInfo> result,
|
||||
@NotNull Map<PsiClass, PsiClass> subClassCache) {
|
||||
@NotNull Collection<LineMarkerInfo> result) {
|
||||
if (aClass.hasModifierProperty(PsiModifier.FINAL)) {
|
||||
return;
|
||||
}
|
||||
if (CommonClassNames.JAVA_LANG_OBJECT.equals(aClass.getQualifiedName())) return; // It's useless to have overridden markers for object.
|
||||
|
||||
PsiClass subClass = subClassCache.get(aClass);
|
||||
PsiClass subClass = DirectClassInheritorsSearch.search(aClass).findFirst();
|
||||
if (subClass != null || FunctionalExpressionSearch.search(aClass).findFirst() != null) {
|
||||
final Icon icon;
|
||||
if (aClass.isInterface()) {
|
||||
|
||||
@@ -160,8 +160,7 @@ public class MarkerType {
|
||||
}
|
||||
@Nullable
|
||||
private static String calculateOverridingSiblingMethodTooltip(@NotNull PsiMethod method) {
|
||||
Pair<PsiMethod, PsiClass> pair =
|
||||
FindSuperElementsHelper.getSiblingInheritedViaSubClass(method, FindSuperElementsHelper.createSubClassCache());
|
||||
Pair<PsiMethod, PsiClass> pair = FindSuperElementsHelper.getSiblingInfoInheritedViaSubClass(method);
|
||||
if (pair == null) return null;
|
||||
PsiMethod superMethod = pair.getFirst();
|
||||
PsiClass subClass = pair.getSecond();
|
||||
@@ -247,7 +246,7 @@ public class MarkerType {
|
||||
|
||||
private static String getOverriddenMethodTooltip(@NotNull PsiMethod method) {
|
||||
PsiElementProcessor.CollectElementsWithLimit<PsiMethod> processor = new PsiElementProcessor.CollectElementsWithLimit<PsiMethod>(5);
|
||||
OverridingMethodsSearch.search(method, true).forEach(new PsiElementProcessorAdapter<PsiMethod>(processor));
|
||||
OverridingMethodsSearch.search(method).forEach(new PsiElementProcessorAdapter<PsiMethod>(processor));
|
||||
|
||||
boolean isAbstract = method.hasModifierProperty(PsiModifier.ABSTRACT);
|
||||
|
||||
@@ -287,7 +286,7 @@ public class MarkerType {
|
||||
if (!ProgressManager.getInstance().runProcessWithProgressSynchronously(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
OverridingMethodsSearch.search(method, true).forEach(new PsiElementProcessorAdapter<PsiMethod>(collectProcessor));
|
||||
OverridingMethodsSearch.search(method).forEach(new PsiElementProcessorAdapter<PsiMethod>(collectProcessor));
|
||||
if (isAbstract && collectProcessor.getCollection().size() < 2) {
|
||||
final PsiClass aClass = ApplicationManager.getApplication().runReadAction(new Computable<PsiClass>() {
|
||||
@Override
|
||||
@@ -468,7 +467,7 @@ public class MarkerType {
|
||||
@Override
|
||||
public void run(@NotNull final ProgressIndicator indicator) {
|
||||
super.run(indicator);
|
||||
OverridingMethodsSearch.search(myMethod, true).forEach(
|
||||
OverridingMethodsSearch.search(myMethod).forEach(
|
||||
new CommonProcessors.CollectProcessor<PsiMethod>() {
|
||||
@Override
|
||||
public boolean process(PsiMethod psiMethod) {
|
||||
|
||||
+27
@@ -56,6 +56,7 @@ public class ClassInheritorsSearch extends ExtensibleQueryFactory<PsiClass, Clas
|
||||
myScope = scope;
|
||||
myCheckDeep = checkDeep;
|
||||
myCheckInheritance = checkInheritance;
|
||||
assert checkInheritance;
|
||||
myIncludeAnonymous = includeAnonymous;
|
||||
myNameCondition = nameCondition;
|
||||
}
|
||||
@@ -96,6 +97,32 @@ public class ClassInheritorsSearch extends ExtensibleQueryFactory<PsiClass, Clas
|
||||
(myIncludeAnonymous ? " (anonymous)":"")+
|
||||
(myNameCondition == Conditions.<String>alwaysTrue() ? "" : " condition: "+myNameCondition);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
SearchParameters that = (SearchParameters)o;
|
||||
|
||||
if (myCheckDeep != that.myCheckDeep) return false;
|
||||
if (myCheckInheritance != that.myCheckInheritance) return false;
|
||||
if (myIncludeAnonymous != that.myIncludeAnonymous) return false;
|
||||
if (!myClass.equals(that.myClass)) return false;
|
||||
if (!myScope.equals(that.myScope)) return false;
|
||||
return myNameCondition.equals(that.myNameCondition);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = myClass.hashCode();
|
||||
result = 31 * result + myScope.hashCode();
|
||||
result = 31 * result + (myCheckDeep ? 1 : 0);
|
||||
result = 31 * result + (myCheckInheritance ? 1 : 0);
|
||||
result = 31 * result + (myIncludeAnonymous ? 1 : 0);
|
||||
result = 31 * result + myNameCondition.hashCode();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
private ClassInheritorsSearch() {}
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* 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.components.ServiceManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.psi.PsiClass;
|
||||
import com.intellij.psi.PsiMethod;
|
||||
import com.intellij.psi.impl.AnyPsiChangeListener;
|
||||
import com.intellij.psi.impl.PsiManagerImpl;
|
||||
import com.intellij.psi.search.searches.ClassInheritorsSearch;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.atomic.AtomicIntegerArray;
|
||||
|
||||
class HighlightingCaches {
|
||||
public static HighlightingCaches getInstance(Project project) {
|
||||
return ServiceManager.getService(project, HighlightingCaches.class);
|
||||
}
|
||||
|
||||
private final List<Map<?,?>> allCaches = ContainerUtil.createConcurrentList();
|
||||
|
||||
public HighlightingCaches(Project project) {
|
||||
project.getMessageBus().connect().subscribe(PsiManagerImpl.ANY_PSI_CHANGE_TOPIC, new AnyPsiChangeListener() {
|
||||
@Override
|
||||
public void beforePsiChanged(boolean isPhysical) {
|
||||
if (isPhysical) {
|
||||
allCaches.forEach(Map::clear);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPsiChanged(boolean isPhysical) {
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
final Map<PsiClass, Pair<List<PsiClass>, AtomicIntegerArray>> DIRECT_SUB_CLASSES = createCache();
|
||||
final Map<ClassInheritorsSearch.SearchParameters, Collection<PsiClass>> ALL_SUB_CLASSES = createCache();
|
||||
final Map<PsiMethod, Collection<PsiMethod>> OVERRIDING_METHODS = createCache();
|
||||
|
||||
@NotNull
|
||||
private <T,V> Map<T,V> createCache() {
|
||||
ConcurrentMap<T, V> map = ContainerUtil.createConcurrentSoftKeySoftValueMap(10, 0.7f, Runtime.getRuntime().availableProcessors(), ContainerUtil.canonicalStrategy());
|
||||
allCaches.add(map);
|
||||
return map;
|
||||
}
|
||||
}
|
||||
+36
-15
@@ -18,7 +18,6 @@ package com.intellij.psi.impl.search;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.application.QueryExecutorBase;
|
||||
import com.intellij.openapi.application.ReadActionProcessor;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.progress.ProgressIndicator;
|
||||
import com.intellij.openapi.progress.ProgressIndicatorProvider;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
@@ -33,22 +32,22 @@ import com.intellij.psi.search.searches.AllClassesSearch;
|
||||
import com.intellij.psi.search.searches.ClassInheritorsSearch;
|
||||
import com.intellij.psi.search.searches.DirectClassInheritorsSearch;
|
||||
import com.intellij.psi.util.PsiUtilCore;
|
||||
import com.intellij.util.CommonProcessors;
|
||||
import com.intellij.util.Processor;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.containers.Stack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
|
||||
public class JavaClassInheritorsSearcher extends QueryExecutorBase<PsiClass, ClassInheritorsSearch.SearchParameters> {
|
||||
private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.search.JavaClassInheritorsSearcher");
|
||||
|
||||
@Override
|
||||
public void processQuery(@NotNull ClassInheritorsSearch.SearchParameters parameters, @NotNull Processor<PsiClass> consumer) {
|
||||
final PsiClass baseClass = parameters.getClassToProcess();
|
||||
final SearchScope searchScope = parameters.getScope();
|
||||
|
||||
LOG.assertTrue(searchScope != null);
|
||||
assert parameters.isCheckDeep();
|
||||
assert parameters.isCheckInheritance();
|
||||
|
||||
ProgressIndicator progress = ProgressIndicatorProvider.getGlobalProgressIndicator();
|
||||
if (progress != null) {
|
||||
@@ -67,7 +66,6 @@ public class JavaClassInheritorsSearcher extends QueryExecutorBase<PsiClass, Cla
|
||||
progress.popState();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static boolean processInheritors(@NotNull final ClassInheritorsSearch.SearchParameters parameters,
|
||||
@@ -79,11 +77,34 @@ public class JavaClassInheritorsSearcher extends QueryExecutorBase<PsiClass, Cla
|
||||
Project project = PsiUtilCore.getProjectInReadAction(baseClass);
|
||||
if (isJavaLangObject(baseClass)) {
|
||||
return AllClassesSearch.search(searchScope, project, parameters.getNameCondition()).forEach(aClass -> {
|
||||
ProgressManager.checkCanceled();
|
||||
return isJavaLangObject(aClass) || consumer.process(aClass);
|
||||
ProgressManager.checkCanceled();
|
||||
return isJavaLangObject(aClass) || consumer.process(aClass);
|
||||
});
|
||||
}
|
||||
|
||||
Collection<PsiClass> cached = HighlightingCaches.getInstance(project).ALL_SUB_CLASSES.get(parameters);
|
||||
if (cached == null) {
|
||||
cached = new ArrayList<>();
|
||||
boolean success = getAllSubClasses(project, baseClass, parameters, new CommonProcessors.CollectProcessor<>(cached));
|
||||
assert success;
|
||||
HighlightingCaches.getInstance(project).ALL_SUB_CLASSES.put(parameters, cached);
|
||||
}
|
||||
|
||||
Processor<PsiClass> readActionedConsumer = ReadActionProcessor.wrapInReadAction(consumer);
|
||||
for (final PsiClass subClass : cached) {
|
||||
ProgressManager.checkCanceled();
|
||||
if (!readActionedConsumer.process(subClass)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean getAllSubClasses(@NotNull Project project,
|
||||
@NotNull PsiClass baseClass,
|
||||
@NotNull ClassInheritorsSearch.SearchParameters parameters,
|
||||
@NotNull Processor<PsiClass> consumer) {
|
||||
SearchScope searchScope = parameters.getScope();
|
||||
final Ref<PsiClass> currentBase = Ref.create(null);
|
||||
final Stack<PsiAnchor> stack = new Stack<>();
|
||||
final Set<PsiAnchor> processed = ContainerUtil.newTroveSet();
|
||||
@@ -93,7 +114,7 @@ public class JavaClassInheritorsSearcher extends QueryExecutorBase<PsiClass, Cla
|
||||
public boolean processInReadAction(PsiClass candidate) {
|
||||
ProgressManager.checkCanceled();
|
||||
|
||||
if (parameters.isCheckInheritance() || parameters.isCheckDeep() && !(candidate instanceof PsiAnonymousClass)) {
|
||||
if (parameters.isCheckInheritance() || !(candidate instanceof PsiAnonymousClass)) {
|
||||
if (!candidate.isInheritor(currentBase.get(), false)) {
|
||||
return true;
|
||||
}
|
||||
@@ -103,25 +124,25 @@ public class JavaClassInheritorsSearcher extends QueryExecutorBase<PsiClass, Cla
|
||||
if (candidate instanceof PsiAnonymousClass) {
|
||||
return consumer.process(candidate);
|
||||
}
|
||||
|
||||
|
||||
final String name = candidate.getName();
|
||||
if (name != null && parameters.getNameCondition().value(name) && !consumer.process(candidate)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (parameters.isCheckDeep() && !(candidate instanceof PsiAnonymousClass) && !isFinal(candidate)) {
|
||||
if (!(candidate instanceof PsiAnonymousClass) && !candidate.hasModifierProperty(PsiModifier.FINAL)) {
|
||||
stack.push(PsiAnchor.create(candidate));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
ApplicationManager.getApplication().runReadAction(() -> {
|
||||
stack.push(PsiAnchor.create(baseClass));
|
||||
stack.push(PsiAnchor.create(baseClass));
|
||||
});
|
||||
final GlobalSearchScope projectScope = GlobalSearchScope.allScope(project);
|
||||
|
||||
|
||||
while (!stack.isEmpty()) {
|
||||
ProgressManager.checkCanceled();
|
||||
|
||||
|
||||
+133
-73
@@ -20,6 +20,7 @@ import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Comparing;
|
||||
import com.intellij.openapi.util.Computable;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.*;
|
||||
@@ -27,6 +28,7 @@ import com.intellij.psi.impl.java.stubs.index.JavaAnonymousClassBaseRefOccurence
|
||||
import com.intellij.psi.impl.java.stubs.index.JavaSuperClassNameOccurenceIndex;
|
||||
import com.intellij.psi.search.EverythingGlobalScope;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
import com.intellij.psi.search.PsiSearchScopeUtil;
|
||||
import com.intellij.psi.search.SearchScope;
|
||||
import com.intellij.psi.search.searches.AllClassesSearch;
|
||||
import com.intellij.psi.search.searches.DirectClassInheritorsSearch;
|
||||
@@ -34,15 +36,12 @@ import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.psi.util.PsiUtilCore;
|
||||
import com.intellij.util.Processor;
|
||||
import com.intellij.util.QueryExecutor;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.SmartList;
|
||||
import com.intellij.util.containers.HashMap;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicIntegerArray;
|
||||
|
||||
/**
|
||||
* @author max
|
||||
@@ -67,94 +66,155 @@ public class JavaDirectInheritorsSearcher implements QueryExecutor<PsiClass, Dir
|
||||
}
|
||||
|
||||
final GlobalSearchScope scope = useScope instanceof GlobalSearchScope ? (GlobalSearchScope)useScope : new EverythingGlobalScope(project);
|
||||
final String searchKey = ApplicationManager.getApplication().runReadAction((Computable<String>)aClass::getName);
|
||||
if (StringUtil.isEmpty(searchKey)) {
|
||||
Pair<List<PsiClass>, AtomicIntegerArray> pair = calculateDirectSubClasses(project, aClass);
|
||||
List<PsiClass> result = pair.getFirst();
|
||||
AtomicIntegerArray isInheritorFlag = pair.getSecond();
|
||||
|
||||
if (result.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Collection<PsiReferenceList> candidates =
|
||||
MethodUsagesSearcher.resolveInReadAction(project, () -> JavaSuperClassNameOccurenceIndex.getInstance().get(searchKey, project, scope));
|
||||
|
||||
Map<String, List<PsiClass>> classes = new HashMap<>();
|
||||
|
||||
for (final PsiReferenceList referenceList : candidates) {
|
||||
final VirtualFile jarFile = getJarFile(aClass);
|
||||
// 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;
|
||||
boolean sameJarClassFound = false;
|
||||
for (int i = 0; i < result.size(); i++) {
|
||||
ProgressManager.checkCanceled();
|
||||
final PsiClass candidate = (PsiClass)ApplicationManager.getApplication().runReadAction((Computable<PsiElement>)referenceList::getParent);
|
||||
if (!checkInheritance(parameters, aClass, candidate, project)) continue;
|
||||
|
||||
String fqn = ApplicationManager.getApplication().runReadAction((Computable<String>)candidate::getQualifiedName);
|
||||
List<PsiClass> list = classes.get(fqn);
|
||||
if (list == null) {
|
||||
list = new ArrayList<>();
|
||||
classes.put(fqn, list);
|
||||
}
|
||||
list.add(candidate);
|
||||
}
|
||||
|
||||
if (!classes.isEmpty()) {
|
||||
final VirtualFile jarFile = getJarFile(aClass);
|
||||
for (List<PsiClass> sameNamedClasses : classes.values()) {
|
||||
ProgressManager.checkCanceled();
|
||||
if (!processSameNamedClasses(sameNamedClasses, jarFile, consumer)) return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (parameters.includeAnonymous()) {
|
||||
Collection<PsiAnonymousClass> anonymousCandidates =
|
||||
MethodUsagesSearcher.resolveInReadAction(project, () -> JavaAnonymousClassBaseRefOccurenceIndex.getInstance().get(searchKey, project, scope));
|
||||
|
||||
for (PsiAnonymousClass candidate : anonymousCandidates) {
|
||||
ProgressManager.checkCanceled();
|
||||
if (!checkInheritance(parameters, aClass, candidate, project)) continue;
|
||||
|
||||
if (!consumer.process(candidate)) return false;
|
||||
}
|
||||
|
||||
boolean isEnum = ApplicationManager.getApplication().runReadAction((Computable<Boolean>)aClass::isEnum);
|
||||
if (isEnum) {
|
||||
// abstract enum can be subclassed in the body
|
||||
PsiField[] fields = ApplicationManager.getApplication().runReadAction((Computable<PsiField[]>)aClass::getFields);
|
||||
for (final PsiField field : fields) {
|
||||
PsiClass subClass = result.get(i);
|
||||
if (subClass instanceof PsiAnonymousClass) {
|
||||
// we reached anonymous classes tail, process them all and exit
|
||||
if (!parameters.includeAnonymous()) {
|
||||
return true;
|
||||
}
|
||||
for (; i < result.size(); i++) {
|
||||
ProgressManager.checkCanceled();
|
||||
if (field instanceof PsiEnumConstant) {
|
||||
PsiEnumConstantInitializer initializingClass =
|
||||
ApplicationManager.getApplication().runReadAction((Computable<PsiEnumConstantInitializer>)((PsiEnumConstant)field)::getInitializingClass);
|
||||
if (initializingClass != null) {
|
||||
if (!consumer.process(initializingClass)) return false;
|
||||
}
|
||||
subClass = result.get(i);
|
||||
if (!checkInheritance(parameters.isCheckInheritance(), aClass, subClass, project, isInheritorFlag, i)) continue;
|
||||
if (!isInScope(scope, subClass)) continue;
|
||||
if (!consumer.process(subClass)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (subClass == PsiUtil.NULL_PSI_CLASS) {
|
||||
// the end of the same-FQN group. Process only same-jar classes in the group or the whole group if there were none.
|
||||
if (!sameJarClassFound) {
|
||||
for (int g=groupStart; g<i; g++) {
|
||||
ProgressManager.checkCanceled();
|
||||
subClass = result.get(g);
|
||||
if (!checkInheritance(parameters.isCheckInheritance(), aClass, subClass, project, isInheritorFlag, g)) continue;
|
||||
if (!consumer.process(subClass)) return false;
|
||||
}
|
||||
}
|
||||
groupStart = i+1;
|
||||
sameJarClassFound = false;
|
||||
}
|
||||
else {
|
||||
if (!checkInheritance(parameters.isCheckInheritance(), aClass, subClass, project, isInheritorFlag, i)) continue;
|
||||
if (!isInScope(scope, subClass)) continue;
|
||||
VirtualFile currentJarFile = getJarFile(subClass);
|
||||
boolean fromSameJar = Comparing.equal(currentJarFile, jarFile);
|
||||
if (fromSameJar) {
|
||||
sameJarClassFound = true;
|
||||
if (!consumer.process(subClass)) return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean checkInheritance(@NotNull DirectClassInheritorsSearch.SearchParameters p,
|
||||
@NotNull PsiClass aClass,
|
||||
@NotNull PsiClass candidate,
|
||||
@NotNull Project project) {
|
||||
return MethodUsagesSearcher.resolveInReadAction(project, () -> !p.isCheckInheritance() || candidate.isInheritor(aClass, false));
|
||||
private static boolean isInScope(GlobalSearchScope scope, PsiClass subClass) {
|
||||
return ApplicationManager.getApplication().runReadAction((Computable<Boolean>)() -> PsiSearchScopeUtil.isInScope(scope, subClass));
|
||||
}
|
||||
|
||||
private static boolean processSameNamedClasses(@NotNull List<PsiClass> sameNamedClasses,
|
||||
@Nullable VirtualFile jarFile,
|
||||
@NotNull Processor<PsiClass> consumer) {
|
||||
// if there is a class from the same jar, prefer it
|
||||
boolean sameJarClassFound = false;
|
||||
private static final int INHERITANCE_UNKNOWN = 0;
|
||||
private static final int INHERITANCE_YES = 1;
|
||||
private static final int INHERITANCE_NO = 2;
|
||||
|
||||
if (jarFile != null && sameNamedClasses.size() > 1) {
|
||||
for (PsiClass sameNamedClass : sameNamedClasses) {
|
||||
// Returns pair ( list of direct subclasses, array of corresponding isInheritor flags )
|
||||
// The array initially contains INHERITANCE_UNKNOWN values, then the isInheritor() method result is cached in the array as INHERITANCE_YES or INHERITANCE_NO.
|
||||
// The list starts with non-anonymous classes, ends with anonymous sub classes
|
||||
// Regular 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)
|
||||
// The groups are separated with NULL_PSI_CLASS
|
||||
@NotNull
|
||||
private static Pair<List<PsiClass>, AtomicIntegerArray> calculateDirectSubClasses(@NotNull Project project, @NotNull PsiClass baseClass) {
|
||||
Pair<List<PsiClass>, AtomicIntegerArray> cached = HighlightingCaches.getInstance(project).DIRECT_SUB_CLASSES.get(baseClass);
|
||||
if (cached != null) {
|
||||
return cached;
|
||||
}
|
||||
|
||||
final String className = ApplicationManager.getApplication().runReadAction((Computable<String>)baseClass::getName);
|
||||
if (StringUtil.isEmpty(className)) {
|
||||
return Pair.create(Collections.emptyList(), new AtomicIntegerArray(0));
|
||||
}
|
||||
GlobalSearchScope allScope = GlobalSearchScope.allScope(project);
|
||||
Collection<PsiReferenceList> candidates =
|
||||
MethodUsagesSearcher.resolveInReadAction(project, () -> JavaSuperClassNameOccurenceIndex.getInstance().get(className, project, allScope));
|
||||
|
||||
Map<String, List<PsiClass>> classes = new HashMap<>();
|
||||
int count = 0;
|
||||
|
||||
for (final PsiReferenceList referenceList : candidates) {
|
||||
ProgressManager.checkCanceled();
|
||||
final PsiClass candidate = (PsiClass)ApplicationManager.getApplication().runReadAction((Computable<PsiElement>)referenceList::getParent);
|
||||
|
||||
String fqn = ApplicationManager.getApplication().runReadAction((Computable<String>)candidate::getQualifiedName);
|
||||
List<PsiClass> list = classes.get(fqn);
|
||||
if (list == null) {
|
||||
list = new SmartList<>();
|
||||
classes.put(fqn, list);
|
||||
}
|
||||
list.add(candidate);
|
||||
count++;
|
||||
}
|
||||
|
||||
Collection<PsiAnonymousClass> anonymousCandidates =
|
||||
MethodUsagesSearcher.resolveInReadAction(project, () -> JavaAnonymousClassBaseRefOccurenceIndex.getInstance().get(className, project, allScope));
|
||||
|
||||
List<PsiClass> result = new ArrayList<>(count+classes.size()+anonymousCandidates.size()+1);
|
||||
for (Map.Entry<String, List<PsiClass>> entry : classes.entrySet()) {
|
||||
result.addAll(entry.getValue());
|
||||
result.add(PsiUtil.NULL_PSI_CLASS);
|
||||
}
|
||||
|
||||
result.addAll(anonymousCandidates);
|
||||
|
||||
boolean isEnum = ApplicationManager.getApplication().runReadAction((Computable<Boolean>)baseClass::isEnum);
|
||||
if (isEnum) {
|
||||
// abstract enum can be subclassed in the body
|
||||
PsiField[] fields = ApplicationManager.getApplication().runReadAction((Computable<PsiField[]>)baseClass::getFields);
|
||||
for (final PsiField field : fields) {
|
||||
ProgressManager.checkCanceled();
|
||||
boolean fromSameJar = Comparing.equal(getJarFile(sameNamedClass), jarFile);
|
||||
if (fromSameJar) {
|
||||
sameJarClassFound = true;
|
||||
if (!consumer.process(sameNamedClass)) return false;
|
||||
if (field instanceof PsiEnumConstant) {
|
||||
PsiEnumConstantInitializer initializingClass =
|
||||
ApplicationManager.getApplication().runReadAction((Computable<PsiEnumConstantInitializer>)((PsiEnumConstant)field)::getInitializingClass);
|
||||
if (initializingClass != null) {
|
||||
result.add(initializingClass);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return sameJarClassFound || ContainerUtil.process(sameNamedClasses, consumer);
|
||||
Pair<List<PsiClass>, AtomicIntegerArray> pair = Pair.create(result, new AtomicIntegerArray(result.size()));
|
||||
HighlightingCaches.getInstance(project).DIRECT_SUB_CLASSES.put(baseClass, pair);
|
||||
return pair;
|
||||
}
|
||||
|
||||
private static boolean checkInheritance(boolean checkInheritance,
|
||||
@NotNull PsiClass aClass,
|
||||
@NotNull PsiClass candidate,
|
||||
@NotNull Project project,
|
||||
@NotNull AtomicIntegerArray isInheritorFlags,
|
||||
int i) {
|
||||
if (!checkInheritance) return true;
|
||||
int cachedFlag = isInheritorFlags.get(i);
|
||||
if (cachedFlag == INHERITANCE_YES) return true;
|
||||
if (cachedFlag == INHERITANCE_NO) return false;
|
||||
assert cachedFlag == INHERITANCE_UNKNOWN;
|
||||
boolean isReallyInherited = MethodUsagesSearcher.resolveInReadAction(project, () -> candidate.isInheritor(aClass, false));
|
||||
isInheritorFlags.set(i, isReallyInherited ? INHERITANCE_YES : INHERITANCE_NO);
|
||||
return isReallyInherited;
|
||||
}
|
||||
|
||||
private static VirtualFile getJarFile(@NotNull PsiClass aClass) {
|
||||
|
||||
+63
-32
@@ -15,9 +15,14 @@
|
||||
*/
|
||||
package com.intellij.psi.impl.search;
|
||||
|
||||
import com.intellij.openapi.application.Application;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Computable;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
import com.intellij.psi.search.PsiSearchScopeUtil;
|
||||
import com.intellij.psi.search.SearchScope;
|
||||
import com.intellij.psi.search.searches.ClassInheritorsSearch;
|
||||
import com.intellij.psi.search.searches.OverridingMethodsSearch;
|
||||
@@ -29,56 +34,79 @@ import com.intellij.util.QueryExecutor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashSet;
|
||||
|
||||
/**
|
||||
* @author max
|
||||
*/
|
||||
public class JavaOverridingMethodsSearcher implements QueryExecutor<PsiMethod, OverridingMethodsSearch.SearchParameters> {
|
||||
@Override
|
||||
public boolean execute(@NotNull final OverridingMethodsSearch.SearchParameters p, @NotNull final Processor<PsiMethod> consumer) {
|
||||
final PsiMethod method = p.getMethod();
|
||||
final SearchScope scope = p.getScope();
|
||||
public boolean execute(@NotNull final OverridingMethodsSearch.SearchParameters parameters, @NotNull final Processor<PsiMethod> consumer) {
|
||||
final PsiMethod method = parameters.getMethod();
|
||||
|
||||
final PsiClass parentClass = ApplicationManager.getApplication().runReadAction(new Computable<PsiClass>() {
|
||||
@Nullable
|
||||
@Override
|
||||
public PsiClass compute() {
|
||||
return method.getContainingClass();
|
||||
Project project = ApplicationManager.getApplication().runReadAction((Computable<Project>)method::getProject);
|
||||
Collection<PsiMethod> cached = HighlightingCaches.getInstance(project).OVERRIDING_METHODS.get(method);
|
||||
if (cached == null) {
|
||||
cached = compute(method, project);
|
||||
HighlightingCaches.getInstance(project).OVERRIDING_METHODS.put(method, cached);
|
||||
}
|
||||
|
||||
final SearchScope scope = parameters.getScope();
|
||||
|
||||
for (final PsiMethod subMethod : cached) {
|
||||
ProgressManager.checkCanceled();
|
||||
if (!ApplicationManager.getApplication().runReadAction((Computable<Boolean>)() -> PsiSearchScopeUtil.isInScope(scope, subMethod))) {
|
||||
continue;
|
||||
}
|
||||
});
|
||||
assert parentClass != null;
|
||||
Processor<PsiClass> inheritorsProcessor = new Processor<PsiClass>() {
|
||||
@Override
|
||||
public boolean process(final PsiClass inheritor) {
|
||||
PsiMethod found = ApplicationManager.getApplication().runReadAction(new Computable<PsiMethod>() {
|
||||
@Override
|
||||
@Nullable
|
||||
public PsiMethod compute() {
|
||||
return findOverridingMethod(inheritor, parentClass, method);
|
||||
}
|
||||
});
|
||||
return found == null || consumer.process(found) && p.isCheckDeep();
|
||||
if (!consumer.process(subMethod) || !parameters.isCheckDeep()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static Collection<PsiMethod> compute(@NotNull PsiMethod method, @NotNull Project project) {
|
||||
Collection<PsiMethod> result = new LinkedHashSet<>();
|
||||
|
||||
Application application = ApplicationManager.getApplication();
|
||||
final PsiClass containingClass = application.runReadAction((Computable<PsiClass>)method::getContainingClass);
|
||||
assert containingClass != null;
|
||||
Processor<PsiClass> inheritorsProcessor = inheritor -> {
|
||||
PsiMethod found = application.runReadAction((Computable<PsiMethod>)() -> findOverridingMethod(project, inheritor, method, containingClass));
|
||||
if (found != null) {
|
||||
result.add(found);
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
return ClassInheritorsSearch.search(parentClass, scope, true).forEach(inheritorsProcessor);
|
||||
// use wider scope to handle public method defined in package-private class which is subclassed by public class in the same package which is subclassed by public class from another package with redefined method
|
||||
SearchScope allScope = GlobalSearchScope.allScope(project);
|
||||
boolean success = ClassInheritorsSearch.search(containingClass, allScope, true).forEach(inheritorsProcessor);
|
||||
assert success;
|
||||
return result;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiMethod findOverridingMethod(PsiClass inheritor, @NotNull PsiClass parentClass, PsiMethod method) {
|
||||
private static PsiMethod findOverridingMethod(@NotNull Project project,
|
||||
@NotNull PsiClass inheritor,
|
||||
@NotNull PsiMethod method,
|
||||
@NotNull PsiClass methodContainingClass) {
|
||||
String name = method.getName();
|
||||
if (inheritor.findMethodsByName(name, false).length > 0) {
|
||||
PsiMethod found = MethodSignatureUtil.findMethodBySuperSignature(inheritor, getSuperSignature(inheritor, parentClass, method), false);
|
||||
if (found != null && isAcceptable(found, method)) {
|
||||
PsiMethod found = MethodSignatureUtil.findMethodBySuperSignature(inheritor, getSuperSignature(inheritor, methodContainingClass, method), false);
|
||||
if (found != null && isAcceptable(project, found, inheritor, method, methodContainingClass)) {
|
||||
return found;
|
||||
}
|
||||
}
|
||||
|
||||
if (parentClass.isInterface() && !inheritor.isInterface()) { //check for sibling implementation
|
||||
if (methodContainingClass.isInterface() && !inheritor.isInterface()) { //check for sibling implementation
|
||||
final PsiClass superClass = inheritor.getSuperClass();
|
||||
if (superClass != null && !superClass.isInheritor(parentClass, true) && superClass.findMethodsByName(name, true).length > 0) {
|
||||
MethodSignature signature = getSuperSignature(inheritor, parentClass, method);
|
||||
if (superClass != null && !superClass.isInheritor(methodContainingClass, true) && superClass.findMethodsByName(name, true).length > 0) {
|
||||
MethodSignature signature = getSuperSignature(inheritor, methodContainingClass, method);
|
||||
PsiMethod derived = MethodSignatureUtil.findMethodInSuperClassBySignatureInDerived(inheritor, superClass, signature, true);
|
||||
if (derived != null && isAcceptable(derived, method)) {
|
||||
if (derived != null && isAcceptable(project, derived, inheritor, method, methodContainingClass)) {
|
||||
return derived;
|
||||
}
|
||||
}
|
||||
@@ -94,10 +122,13 @@ public class JavaOverridingMethodsSearcher implements QueryExecutor<PsiMethod, O
|
||||
}
|
||||
|
||||
|
||||
private static boolean isAcceptable(final PsiMethod found, final PsiMethod method) {
|
||||
private static boolean isAcceptable(@NotNull Project project,
|
||||
@NotNull PsiMethod found,
|
||||
@NotNull PsiClass foundContainingClass,
|
||||
@NotNull PsiMethod method,
|
||||
@NotNull PsiClass methodContainingClass) {
|
||||
return !found.hasModifierProperty(PsiModifier.STATIC) &&
|
||||
(!method.hasModifierProperty(PsiModifier.PACKAGE_LOCAL) ||
|
||||
JavaPsiFacade.getInstance(found.getProject())
|
||||
.arePackagesTheSame(method.getContainingClass(), found.getContainingClass()));
|
||||
JavaPsiFacade.getInstance(project).arePackagesTheSame(methodContainingClass, foundContainingClass));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
package com.intellij.psi.util;
|
||||
|
||||
import com.intellij.lang.java.JavaLanguage;
|
||||
import com.intellij.navigation.ItemPresentation;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.projectRoots.JavaSdkVersion;
|
||||
@@ -23,6 +24,7 @@ import com.intellij.openapi.projectRoots.JavaVersionService;
|
||||
import com.intellij.openapi.roots.LanguageLevelProjectExtension;
|
||||
import com.intellij.openapi.util.Comparing;
|
||||
import com.intellij.openapi.util.Key;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vfs.VfsUtilCore;
|
||||
@@ -1280,4 +1282,282 @@ public final class PsiUtil extends PsiUtilCore {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class NullPsiClass extends NullPsiElement implements PsiClass {
|
||||
@Nullable
|
||||
@Override
|
||||
public ItemPresentation getPresentation() {
|
||||
throw createException();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public PsiDocComment getDocComment() {
|
||||
throw createException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasTypeParameters() {
|
||||
throw createException();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public PsiModifierList getModifierList() {
|
||||
throw createException();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getName() {
|
||||
throw createException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDeprecated() {
|
||||
throw createException();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public PsiTypeParameterList getTypeParameterList() {
|
||||
throw createException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void navigate(boolean requestFocus) {
|
||||
throw createException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canNavigate() {
|
||||
throw createException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasModifierProperty(@PsiModifier.ModifierConstant @NonNls @NotNull String name) {
|
||||
throw createException();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public PsiTypeParameter[] getTypeParameters() {
|
||||
throw createException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canNavigateToSource() {
|
||||
throw createException();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getQualifiedName() {
|
||||
throw createException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInterface() {
|
||||
throw createException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAnnotationType() {
|
||||
throw createException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnum() {
|
||||
throw createException();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public PsiReferenceList getExtendsList() {
|
||||
throw createException();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public PsiReferenceList getImplementsList() {
|
||||
throw createException();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public PsiClassType[] getExtendsListTypes() {
|
||||
throw createException();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public PsiClassType[] getImplementsListTypes() {
|
||||
throw createException();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public PsiClass getSuperClass() {
|
||||
throw createException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PsiClass[] getInterfaces() {
|
||||
throw createException();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public PsiClass[] getSupers() {
|
||||
throw createException();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public PsiClassType[] getSuperTypes() {
|
||||
throw createException();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public PsiField[] getFields() {
|
||||
throw createException();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public PsiMethod[] getMethods() {
|
||||
throw createException();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public PsiMethod[] getConstructors() {
|
||||
throw createException();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public PsiClass[] getInnerClasses() {
|
||||
throw createException();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public PsiClassInitializer[] getInitializers() {
|
||||
throw createException();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public PsiField[] getAllFields() {
|
||||
throw createException();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public PsiMethod[] getAllMethods() {
|
||||
throw createException();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public PsiClass[] getAllInnerClasses() {
|
||||
throw createException();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public PsiField findFieldByName(@NonNls String name, boolean checkBases) {
|
||||
throw createException();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public PsiMethod findMethodBySignature(PsiMethod patternMethod, boolean checkBases) {
|
||||
throw createException();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public PsiMethod[] findMethodsBySignature(PsiMethod patternMethod, boolean checkBases) {
|
||||
throw createException();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public PsiMethod[] findMethodsByName(@NonNls String name, boolean checkBases) {
|
||||
throw createException();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public List<Pair<PsiMethod, PsiSubstitutor>> findMethodsAndTheirSubstitutorsByName(@NonNls String name, boolean checkBases) {
|
||||
throw createException();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public List<Pair<PsiMethod, PsiSubstitutor>> getAllMethodsAndTheirSubstitutors() {
|
||||
throw createException();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public PsiClass findInnerClassByName(@NonNls String name, boolean checkBases) {
|
||||
throw createException();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public PsiElement getLBrace() {
|
||||
throw createException();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public PsiElement getRBrace() {
|
||||
throw createException();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public PsiIdentifier getNameIdentifier() {
|
||||
throw createException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PsiElement getScope() {
|
||||
throw createException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInheritor(@NotNull PsiClass baseClass, boolean checkDeep) {
|
||||
throw createException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInheritorDeep(PsiClass baseClass, @Nullable PsiClass classToByPass) {
|
||||
throw createException();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public PsiClass getContainingClass() {
|
||||
throw createException();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Collection<HierarchicalMethodSignature> getVisibleSignatures() {
|
||||
throw createException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PsiElement setName(@NonNls @NotNull String name) {
|
||||
throw createException();
|
||||
}
|
||||
}
|
||||
|
||||
public static final PsiClass NULL_PSI_CLASS = new NullPsiClass();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2015 JetBrains s.r.o.
|
||||
* 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.
|
||||
@@ -59,7 +59,7 @@ import java.util.List;
|
||||
public class PsiUtilCore {
|
||||
private static final Logger LOG = Logger.getInstance("#com.intellij.psi.util.PsiUtilCore");
|
||||
public static final PsiElement NULL_PSI_ELEMENT = new NullPsiElement();
|
||||
private static class NullPsiElement implements PsiElement {
|
||||
protected static class NullPsiElement implements PsiElement {
|
||||
@Override
|
||||
@NotNull
|
||||
public Project getProject() {
|
||||
|
||||
+10
-9
@@ -31,7 +31,6 @@ import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.impl.FindSuperElementsHelper;
|
||||
import com.intellij.psi.impl.PsiImplUtil;
|
||||
import com.intellij.psi.search.searches.AllOverridingMethodsSearch;
|
||||
import com.intellij.psi.search.searches.SuperMethodsSearch;
|
||||
@@ -58,7 +57,10 @@ import org.jetbrains.plugins.groovy.lang.psi.util.GrTraitUtil;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.util.GroovyPropertyUtils;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.util.*;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author ilyas
|
||||
@@ -85,7 +87,7 @@ public class GroovyLineMarkerProvider extends JavaLineMarkerProvider {
|
||||
final MarkerType type = GroovyMarkerTypes.OVERRIDING_PROPERTY_TYPE;
|
||||
return new LineMarkerInfo<>(element, element.getTextRange(), icon, Pass.UPDATE_ALL, type.getTooltip(),
|
||||
type.getNavigationHandler(),
|
||||
GutterIconRenderer.Alignment.LEFT);
|
||||
GutterIconRenderer.Alignment.LEFT);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -95,7 +97,7 @@ public class GroovyLineMarkerProvider extends JavaLineMarkerProvider {
|
||||
final Icon icon = AllIcons.Gutter.OverridingMethod;
|
||||
final MarkerType type = GroovyMarkerTypes.GR_OVERRIDING_METHOD;
|
||||
return new LineMarkerInfo<>(element, element.getTextRange(), icon, Pass.UPDATE_ALL, type.getTooltip(),
|
||||
type.getNavigationHandler(), GutterIconRenderer.Alignment.LEFT);
|
||||
type.getNavigationHandler(), GutterIconRenderer.Alignment.LEFT);
|
||||
}
|
||||
}
|
||||
//need to draw method separator above docComment
|
||||
@@ -132,8 +134,8 @@ public class GroovyLineMarkerProvider extends JavaLineMarkerProvider {
|
||||
}
|
||||
LineMarkerInfo info =
|
||||
new LineMarkerInfo<>(element, comment != null ? comment.getTextRange() : element.getTextRange(), null,
|
||||
Pass.UPDATE_ALL, FunctionUtil.<Object, String>nullConstant(), null,
|
||||
GutterIconRenderer.Alignment.RIGHT);
|
||||
Pass.UPDATE_ALL, FunctionUtil.<Object, String>nullConstant(), null,
|
||||
GutterIconRenderer.Alignment.RIGHT);
|
||||
EditorColorsScheme scheme = myColorsManager.getGlobalScheme();
|
||||
info.separatorColor = scheme.getColor(CodeInsightColors.METHOD_SEPARATORS_COLOR);
|
||||
info.separatorPlacement = SeparatorPlacement.TOP;
|
||||
@@ -186,7 +188,6 @@ public class GroovyLineMarkerProvider extends JavaLineMarkerProvider {
|
||||
@Override
|
||||
public void collectSlowLineMarkers(@NotNull final List<PsiElement> elements, @NotNull final Collection<LineMarkerInfo> result) {
|
||||
Set<PsiMethod> methods = new HashSet<>();
|
||||
Map<PsiClass, PsiClass> subClassCache = FindSuperElementsHelper.createSubClassCache();
|
||||
for (PsiElement element : elements) {
|
||||
ProgressManager.checkCanceled();
|
||||
if (element instanceof GrField) {
|
||||
@@ -202,7 +203,7 @@ public class GroovyLineMarkerProvider extends JavaLineMarkerProvider {
|
||||
}
|
||||
}
|
||||
else if (element instanceof PsiClass && !(element instanceof PsiTypeParameter)) {
|
||||
collectInheritingClasses((PsiClass)element, result, subClassCache);
|
||||
collectInheritingClasses((PsiClass)element, result);
|
||||
}
|
||||
}
|
||||
collectOverridingMethods(methods, result);
|
||||
@@ -244,7 +245,7 @@ public class GroovyLineMarkerProvider extends JavaLineMarkerProvider {
|
||||
final MarkerType type = element instanceof GrField ? GroovyMarkerTypes.OVERRIDEN_PROPERTY_TYPE
|
||||
: GroovyMarkerTypes.GR_OVERRIDEN_METHOD;
|
||||
LineMarkerInfo info = new LineMarkerInfo<>(range, range.getTextRange(), icon, Pass.UPDATE_OVERRIDDEN_MARKERS, type.getTooltip(),
|
||||
type.getNavigationHandler(), GutterIconRenderer.Alignment.RIGHT);
|
||||
type.getNavigationHandler(), GutterIconRenderer.Alignment.RIGHT);
|
||||
result.add(info);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -446,6 +446,8 @@
|
||||
serviceImplementation="com.intellij.psi.impl.PsiNameHelperImpl"/>
|
||||
<projectService serviceInterface="com.intellij.psi.impl.file.impl.JavaFileManager"
|
||||
serviceImplementation="com.intellij.psi.impl.file.impl.JavaFileManagerImpl"/>
|
||||
<projectService
|
||||
serviceImplementation="com.intellij.psi.impl.search.HighlightingCaches"/>
|
||||
<projectService serviceInterface="com.intellij.codeInsight.guess.GuessManager"
|
||||
serviceImplementation="com.intellij.codeInsight.guess.impl.GuessManagerImpl"/>
|
||||
<projectService serviceInterface="com.intellij.psi.impl.source.resolve.JavaResolveCache"
|
||||
|
||||
Reference in New Issue
Block a user