mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
search for all sibling super methods in a single pass over class inheritors (IDEA-152346)
This commit is contained in:
@@ -16,16 +16,15 @@
|
||||
package com.intellij.psi.impl;
|
||||
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.search.searches.ClassInheritorsSearch;
|
||||
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.psi.util.*;
|
||||
import com.intellij.util.Processor;
|
||||
import com.intellij.util.containers.MultiMap;
|
||||
import com.intellij.util.containers.hash.HashMap;
|
||||
import gnu.trove.THashSet;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@@ -64,67 +63,124 @@ public class FindSuperElementsHelper {
|
||||
}
|
||||
|
||||
public static PsiMethod getSiblingInheritedViaSubClass(@NotNull PsiMethod method) {
|
||||
return Pair.getFirst(getSiblingInfoInheritedViaSubClass(method));
|
||||
SiblingInfo info = getSiblingInfoInheritedViaSubClass(method);
|
||||
return info == null ? null : info.superMethod;
|
||||
}
|
||||
|
||||
// returns (super method, sub class) or null if can't find any siblings
|
||||
public static Pair<PsiMethod, PsiClass> getSiblingInfoInheritedViaSubClass(@NotNull final PsiMethod method) {
|
||||
boolean canHaveSiblingSuper = !method.hasModifierProperty(PsiModifier.ABSTRACT) &&
|
||||
!method.hasModifierProperty(PsiModifier.STATIC) &&
|
||||
method.hasModifierProperty(PsiModifier.PUBLIC) &&
|
||||
!method.hasModifierProperty(PsiModifier.FINAL) &&
|
||||
!method.hasModifierProperty(PsiModifier.NATIVE);
|
||||
if (!canHaveSiblingSuper) return null;
|
||||
final PsiClass containingClass = method.getContainingClass();
|
||||
if (containingClass == null || containingClass.isInterface() || containingClass.hasModifierProperty(PsiModifier.FINAL)) {
|
||||
return null;
|
||||
/**
|
||||
* @return (super method, sub class) or null if can't find any siblings
|
||||
*/
|
||||
@Nullable
|
||||
public static SiblingInfo getSiblingInfoInheritedViaSubClass(@NotNull final PsiMethod method) {
|
||||
return getSiblingInheritanceInfos(Collections.singletonList(method)).get(method);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static Map<PsiMethod, SiblingInfo> getSiblingInheritanceInfos(@NotNull final Collection<PsiMethod> methods) {
|
||||
MultiMap<PsiClass, PsiMethod> byClass = MultiMap.create();
|
||||
for (PsiMethod method : methods) {
|
||||
PsiClass containingClass = method.getContainingClass();
|
||||
if (canHaveSiblingSuper(method, containingClass)) {
|
||||
byClass.putValue(containingClass, method);
|
||||
}
|
||||
}
|
||||
if (CommonClassNames.JAVA_LANG_OBJECT.equals(containingClass.getQualifiedName())) {
|
||||
return null;
|
||||
|
||||
Map<PsiMethod, SiblingInfo> result = new HashMap<>();
|
||||
for (PsiClass psiClass : byClass.keySet()) {
|
||||
SiblingInheritorSearcher searcher = new SiblingInheritorSearcher(byClass.get(psiClass), psiClass);
|
||||
ClassInheritorsSearch.search(psiClass, psiClass.getUseScope(), true, true, false).forEach(searcher);
|
||||
result.putAll(searcher.getResult());
|
||||
}
|
||||
final Collection<PsiAnchor> checkedInterfaces = new THashSet<>();
|
||||
checkedInterfaces.add(PsiAnchor.create(containingClass));
|
||||
final Ref<Pair<PsiMethod, PsiClass>> result = Ref.create();
|
||||
ClassInheritorsSearch.search(containingClass, containingClass.getUseScope(), true, true, false).forEach(
|
||||
inheritor -> {
|
||||
return result;
|
||||
}
|
||||
|
||||
private static boolean canHaveSiblingSuper(PsiMethod method, PsiClass containingClass) {
|
||||
return containingClass != null &&
|
||||
PsiUtil.canBeOverriden(method) &&
|
||||
!method.hasModifierProperty(PsiModifier.ABSTRACT) &&
|
||||
!method.hasModifierProperty(PsiModifier.NATIVE) &&
|
||||
method.hasModifierProperty(PsiModifier.PUBLIC) &&
|
||||
!containingClass.isInterface() &&
|
||||
!CommonClassNames.JAVA_LANG_OBJECT.equals(containingClass.getQualifiedName());
|
||||
}
|
||||
|
||||
public static class SiblingInfo {
|
||||
@NotNull public final PsiMethod superMethod;
|
||||
@NotNull public final PsiClass subClass;
|
||||
|
||||
private SiblingInfo(@NotNull PsiMethod superMethod, @NotNull PsiClass subClass) {
|
||||
this.superMethod = superMethod;
|
||||
this.subClass = subClass;
|
||||
}
|
||||
}
|
||||
|
||||
private static class SiblingInheritorSearcher implements Processor<PsiClass> {
|
||||
private final PsiClass myContainingClass;
|
||||
private final Set<PsiMethod> myRemainingMethods;
|
||||
private final Map<PsiMethod, SiblingInfo> myResult = new HashMap<>();
|
||||
private final Collection<PsiAnchor> myCheckedInterfaces = new THashSet<>();
|
||||
|
||||
SiblingInheritorSearcher(Collection<PsiMethod> methods, PsiClass containingClass) {
|
||||
myContainingClass = containingClass;
|
||||
myRemainingMethods = new HashSet<>(methods);
|
||||
myCheckedInterfaces.add(PsiAnchor.create(containingClass));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean process(PsiClass inheritor) {
|
||||
ProgressManager.checkCanceled();
|
||||
for (PsiClassType interfaceType : inheritor.getImplementsListTypes()) {
|
||||
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 anInterface = interfaceType.resolveGenerics().getElement();
|
||||
if (anInterface != null && myCheckedInterfaces.add(PsiAnchor.create(anInterface))) {
|
||||
processInterface(inheritor, anInterface);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
return result.get();
|
||||
return !myRemainingMethods.isEmpty();
|
||||
}
|
||||
|
||||
private void processInterface(PsiClass inheritor, PsiClass anInterface) {
|
||||
for (Iterator<PsiMethod> methodIterator = myRemainingMethods.iterator(); methodIterator.hasNext(); ) {
|
||||
PsiMethod method = methodIterator.next();
|
||||
SiblingInfo info = findSibling(inheritor, anInterface, method);
|
||||
if (info != null) {
|
||||
myResult.put(method, info);
|
||||
methodIterator.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private SiblingInfo findSibling(PsiClass inheritor, PsiClass anInterface, PsiMethod method) {
|
||||
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 || myContainingClass.isInheritor(superInterface, true)) {
|
||||
// if containingClass implements the superInterface then it's not a sibling inheritance but a pretty boring the usual one
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isOverridden(inheritor, method, superMethod, superInterface)) {
|
||||
return new SiblingInfo(superMethod, inheritor);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean isOverridden(PsiClass inheritor, PsiMethod method, PsiMethod superMethod, PsiClass superInterface) {
|
||||
// calculate substitutor of containingClass --> inheritor
|
||||
PsiSubstitutor substitutor = TypeConversionUtil.getSuperClassSubstitutor(myContainingClass, inheritor, PsiSubstitutor.EMPTY);
|
||||
// calculate substitutor of inheritor --> superInterface
|
||||
substitutor = TypeConversionUtil.getSuperClassSubstitutor(superInterface, inheritor, substitutor);
|
||||
|
||||
return MethodSignatureUtil.isSubsignature(superMethod.getSignature(substitutor), method.getSignature(PsiSubstitutor.EMPTY));
|
||||
}
|
||||
|
||||
Map<PsiMethod, SiblingInfo> getResult() {
|
||||
return myResult;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,6 +48,7 @@ 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 {
|
||||
@@ -187,13 +188,8 @@ public class JavaLineMarkerProvider extends LineMarkerProviderDescriptor {
|
||||
|
||||
private static void collectSiblingInheritedMethods(@NotNull final Collection<PsiMethod> methods,
|
||||
@NotNull Collection<LineMarkerInfo> result) {
|
||||
for (PsiMethod method : methods) {
|
||||
ProgressManager.checkCanceled();
|
||||
|
||||
PsiMethod siblingInheritedViaSubClass = FindSuperElementsHelper.getSiblingInheritedViaSubClass(method);
|
||||
if (siblingInheritedViaSubClass == null) {
|
||||
continue;
|
||||
}
|
||||
Map<PsiMethod, FindSuperElementsHelper.SiblingInfo> map = FindSuperElementsHelper.getSiblingInheritanceInfos(methods);
|
||||
for (PsiMethod method : map.keySet()) {
|
||||
PsiElement range = getMethodRange(method);
|
||||
ArrowUpLineMarkerInfo upInfo = new ArrowUpLineMarkerInfo(range, AllIcons.Gutter.ImplementingMethod, MarkerType.SIBLING_OVERRIDING_METHOD,
|
||||
Pass.UPDATE_OVERRIDDEN_MARKERS);
|
||||
|
||||
@@ -33,7 +33,6 @@ import com.intellij.openapi.progress.ProgressIndicator;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.openapi.project.DumbService;
|
||||
import com.intellij.openapi.util.Computable;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.impl.FindSuperElementsHelper;
|
||||
import com.intellij.psi.presentation.java.ClassPresentationUtil;
|
||||
@@ -150,10 +149,10 @@ public class MarkerType {
|
||||
}
|
||||
@Nullable
|
||||
private static String calculateOverridingSiblingMethodTooltip(@NotNull PsiMethod method) {
|
||||
Pair<PsiMethod, PsiClass> pair = FindSuperElementsHelper.getSiblingInfoInheritedViaSubClass(method);
|
||||
FindSuperElementsHelper.SiblingInfo pair = FindSuperElementsHelper.getSiblingInfoInheritedViaSubClass(method);
|
||||
if (pair == null) return null;
|
||||
PsiMethod superMethod = pair.getFirst();
|
||||
PsiClass subClass = pair.getSecond();
|
||||
PsiMethod superMethod = pair.superMethod;
|
||||
PsiClass subClass = pair.subClass;
|
||||
boolean isAbstract = method.hasModifierProperty(PsiModifier.ABSTRACT);
|
||||
boolean isSuperAbstract = superMethod.hasModifierProperty(PsiModifier.ABSTRACT);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user