search for all sibling super methods in a single pass over class inheritors (IDEA-152346)

This commit is contained in:
peter
2016-06-02 16:38:30 +02:00
parent b4c0e648c0
commit 1ddcaf21c1
3 changed files with 121 additions and 70 deletions
@@ -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);