diff --git a/java/java-analysis-impl/src/com/intellij/psi/impl/FindSuperElementsHelper.java b/java/java-analysis-impl/src/com/intellij/psi/impl/FindSuperElementsHelper.java index d3a4ce31faf1..aabcce64630c 100644 --- a/java/java-analysis-impl/src/com/intellij/psi/impl/FindSuperElementsHelper.java +++ b/java/java-analysis-impl/src/com/intellij/psi/impl/FindSuperElementsHelper.java @@ -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 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 getSiblingInheritanceInfos(@NotNull final Collection methods) { + MultiMap 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 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 checkedInterfaces = new THashSet<>(); - checkedInterfaces.add(PsiAnchor.create(containingClass)); - final Ref> 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 { + private final PsiClass myContainingClass; + private final Set myRemainingMethods; + private final Map myResult = new HashMap<>(); + private final Collection myCheckedInterfaces = new THashSet<>(); + + SiblingInheritorSearcher(Collection 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 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 getResult() { + return myResult; + } } } diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/JavaLineMarkerProvider.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/JavaLineMarkerProvider.java index fc8b106478e3..ce9c34649985 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/JavaLineMarkerProvider.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/JavaLineMarkerProvider.java @@ -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 methods, @NotNull Collection result) { - for (PsiMethod method : methods) { - ProgressManager.checkCanceled(); - - PsiMethod siblingInheritedViaSubClass = FindSuperElementsHelper.getSiblingInheritedViaSubClass(method); - if (siblingInheritedViaSubClass == null) { - continue; - } + Map 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); diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/MarkerType.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/MarkerType.java index f1d440c11b2a..fc74ee4cd790 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/MarkerType.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/MarkerType.java @@ -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 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);