mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IDEA-184196 No "Implements via subclass" icon when a method is final
This commit is contained in:
@@ -20,8 +20,8 @@ import com.intellij.psi.*;
|
||||
import com.intellij.psi.search.searches.ClassInheritorsSearch;
|
||||
import com.intellij.psi.util.MethodSignatureUtil;
|
||||
import com.intellij.psi.util.PsiSuperMethodUtil;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.psi.util.TypeConversionUtil;
|
||||
import com.intellij.util.ObjectUtils;
|
||||
import com.intellij.util.Processor;
|
||||
import com.intellij.util.containers.MultiMap;
|
||||
import com.intellij.util.containers.hash.HashMap;
|
||||
@@ -37,10 +37,7 @@ public class FindSuperElementsHelper {
|
||||
if (element instanceof PsiClass) {
|
||||
PsiClass aClass = (PsiClass) element;
|
||||
List<PsiClass> allSupers = new ArrayList<>(Arrays.asList(aClass.getSupers()));
|
||||
for (Iterator<PsiClass> iterator = allSupers.iterator(); iterator.hasNext();) {
|
||||
PsiClass superClass = iterator.next();
|
||||
if (CommonClassNames.JAVA_LANG_OBJECT.equals(superClass.getQualifiedName())) iterator.remove();
|
||||
}
|
||||
allSupers.removeIf(superClass -> CommonClassNames.JAVA_LANG_OBJECT.equals(superClass.getQualifiedName()));
|
||||
return allSupers.toArray(new PsiClass[allSupers.size()]);
|
||||
}
|
||||
if (element instanceof PsiMethod) {
|
||||
@@ -88,22 +85,31 @@ public class FindSuperElementsHelper {
|
||||
}
|
||||
}
|
||||
|
||||
Map<PsiMethod, SiblingInfo> result = new HashMap<>();
|
||||
Map<PsiMethod, SiblingInfo> result = null;
|
||||
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());
|
||||
Map<PsiMethod, SiblingInfo> searcherResult = searcher.getResult();
|
||||
if (!searcherResult.isEmpty()) {
|
||||
if (result == null) result = new HashMap<>();
|
||||
result.putAll(searcherResult);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
return result == null ? Collections.emptyMap() : result;
|
||||
}
|
||||
|
||||
private static boolean canHaveSiblingSuper(PsiMethod method, PsiClass containingClass) {
|
||||
public static boolean canHaveSiblingSuper(@NotNull PsiMethod method, PsiClass containingClass) {
|
||||
return containingClass != null &&
|
||||
PsiUtil.canBeOverridden(method) &&
|
||||
!method.isConstructor() &&
|
||||
// NB: method CAN be final
|
||||
!method.hasModifierProperty(PsiModifier.STATIC) &&
|
||||
!method.hasModifierProperty(PsiModifier.PRIVATE) &&
|
||||
!method.hasModifierProperty(PsiModifier.ABSTRACT) &&
|
||||
!method.hasModifierProperty(PsiModifier.NATIVE) &&
|
||||
method.hasModifierProperty(PsiModifier.PUBLIC) &&
|
||||
!containingClass.isInterface() &&
|
||||
!(containingClass instanceof PsiAnonymousClass) &&
|
||||
!containingClass.hasModifierProperty(PsiModifier.FINAL) &&
|
||||
!CommonClassNames.JAVA_LANG_OBJECT.equals(containingClass.getQualifiedName());
|
||||
}
|
||||
|
||||
@@ -120,10 +126,10 @@ public class FindSuperElementsHelper {
|
||||
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 Map<PsiMethod, SiblingInfo> myResult;
|
||||
private final Collection<PsiAnchor> myCheckedInterfaces = new THashSet<>();
|
||||
|
||||
SiblingInheritorSearcher(Collection<PsiMethod> methods, PsiClass containingClass) {
|
||||
SiblingInheritorSearcher(@NotNull Collection<PsiMethod> methods, @NotNull PsiClass containingClass) {
|
||||
myContainingClass = containingClass;
|
||||
myRemainingMethods = new HashSet<>(methods);
|
||||
myCheckedInterfaces.add(PsiAnchor.create(containingClass));
|
||||
@@ -142,19 +148,25 @@ public class FindSuperElementsHelper {
|
||||
return !myRemainingMethods.isEmpty();
|
||||
}
|
||||
|
||||
private void processInterface(PsiClass inheritor, PsiClass anInterface) {
|
||||
private void processInterface(@NotNull PsiClass inheritor, @NotNull PsiClass anInterface) {
|
||||
for (Iterator<PsiMethod> methodIterator = myRemainingMethods.iterator(); methodIterator.hasNext(); ) {
|
||||
ProgressManager.checkCanceled();
|
||||
PsiMethod method = methodIterator.next();
|
||||
SiblingInfo info = findSibling(inheritor, anInterface, method);
|
||||
if (info != null) {
|
||||
myResult.put(method, info);
|
||||
Map<PsiMethod, SiblingInfo> result;
|
||||
if ((result = myResult) == null) {
|
||||
myResult = result = new HashMap<>();
|
||||
}
|
||||
|
||||
result.put(method, info);
|
||||
methodIterator.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private SiblingInfo findSibling(PsiClass inheritor, PsiClass anInterface, PsiMethod method) {
|
||||
private SiblingInfo findSibling(@NotNull PsiClass inheritor, @NotNull PsiClass anInterface, @NotNull PsiMethod method) {
|
||||
for (PsiMethod superMethod : anInterface.findMethodsByName(method.getName(), true)) {
|
||||
PsiElement navigationElement = superMethod.getNavigationElement();
|
||||
if (!(navigationElement instanceof PsiMethod)) continue; // Kotlin
|
||||
@@ -173,7 +185,7 @@ public class FindSuperElementsHelper {
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean isOverridden(PsiClass inheritor, PsiMethod method, PsiMethod superMethod, PsiClass superInterface) {
|
||||
private boolean isOverridden(@NotNull PsiClass inheritor, @NotNull PsiMethod method, @NotNull PsiMethod superMethod, @NotNull PsiClass superInterface) {
|
||||
// calculate substitutor of containingClass --> inheritor
|
||||
PsiSubstitutor substitutor = TypeConversionUtil.getSuperClassSubstitutor(myContainingClass, inheritor, PsiSubstitutor.EMPTY);
|
||||
// calculate substitutor of inheritor --> superInterface
|
||||
@@ -182,8 +194,9 @@ public class FindSuperElementsHelper {
|
||||
return MethodSignatureUtil.isSubsignature(superMethod.getSignature(superInterfaceSubstitutor), method.getSignature(substitutor));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
Map<PsiMethod, SiblingInfo> getResult() {
|
||||
return myResult;
|
||||
return ObjectUtils.notNull(myResult, Collections.emptyMap());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+23
-13
@@ -185,7 +185,8 @@ public class JavaLineMarkerProvider extends LineMarkerProviderDescriptor {
|
||||
|
||||
List<Computable<List<LineMarkerInfo>>> tasks = new ArrayList<>();
|
||||
|
||||
MultiMap<PsiClass, PsiMethod> byClass = MultiMap.create();
|
||||
MultiMap<PsiClass, PsiMethod> canbeOverridden = MultiMap.create();
|
||||
MultiMap<PsiClass, PsiMethod> canHaveSiblings = MultiMap.create();
|
||||
//noinspection ForLoopReplaceableByForEach
|
||||
for (int i = 0; i < elements.size(); i++) {
|
||||
PsiElement element = elements.get(i);
|
||||
@@ -194,9 +195,12 @@ public class JavaLineMarkerProvider extends LineMarkerProviderDescriptor {
|
||||
PsiElement parent = element.getParent();
|
||||
if (parent instanceof PsiMethod) {
|
||||
final PsiMethod method = (PsiMethod)parent;
|
||||
PsiClass psiClass = method.getContainingClass();
|
||||
if (PsiUtil.canBeOverridden(method) && psiClass != null) {
|
||||
byClass.putValue(psiClass, method);
|
||||
PsiClass containingClass = method.getContainingClass();
|
||||
if (containingClass != null && PsiUtil.canBeOverridden(method)) {
|
||||
canbeOverridden.putValue(containingClass, method);
|
||||
}
|
||||
if (FindSuperElementsHelper.canHaveSiblingSuper(method, containingClass)) {
|
||||
canHaveSiblings.putValue(containingClass, method);
|
||||
}
|
||||
if (isServiceProviderMethod(method)) {
|
||||
tasks.add(() -> collectServiceProviderMethod(method));
|
||||
@@ -213,11 +217,14 @@ public class JavaLineMarkerProvider extends LineMarkerProviderDescriptor {
|
||||
}
|
||||
}
|
||||
}
|
||||
for (PsiClass psiClass : byClass.keySet()) {
|
||||
Collection<PsiMethod> methods = byClass.get(psiClass);
|
||||
tasks.add(() -> collectSiblingInheritedMethods(methods));
|
||||
for (PsiClass psiClass : canbeOverridden.keySet()) {
|
||||
Collection<PsiMethod> methods = canbeOverridden.get(psiClass);
|
||||
tasks.add(() -> collectOverridingMethods(methods, psiClass));
|
||||
}
|
||||
for (PsiClass psiClass : canHaveSiblings.keySet()) {
|
||||
Collection<PsiMethod> methods = canHaveSiblings.get(psiClass);
|
||||
tasks.add(() -> collectSiblingInheritedMethods(methods));
|
||||
}
|
||||
|
||||
Object lock = new Object();
|
||||
ProgressIndicator indicator = ProgressIndicatorProvider.getGlobalProgressIndicator();
|
||||
@@ -484,10 +491,10 @@ public class JavaLineMarkerProvider extends LineMarkerProviderDescriptor {
|
||||
}
|
||||
|
||||
|
||||
public static abstract class ServiceNavigationHandler implements GutterIconNavigationHandler<PsiElement> {
|
||||
protected final String myInterfaceClassName;
|
||||
public abstract static class ServiceNavigationHandler implements GutterIconNavigationHandler<PsiElement> {
|
||||
final String myInterfaceClassName;
|
||||
|
||||
protected ServiceNavigationHandler(@NotNull String interfaceClassName) {myInterfaceClassName = interfaceClassName;}
|
||||
ServiceNavigationHandler(@NotNull String interfaceClassName) {myInterfaceClassName = interfaceClassName;}
|
||||
|
||||
@Override
|
||||
public void navigate(MouseEvent e, PsiElement element) {
|
||||
@@ -505,16 +512,17 @@ public class JavaLineMarkerProvider extends LineMarkerProviderDescriptor {
|
||||
return myInterfaceClassName;
|
||||
}
|
||||
|
||||
protected boolean isTargetReference(PsiJavaCodeReferenceElement reference) {
|
||||
boolean isTargetReference(PsiJavaCodeReferenceElement reference) {
|
||||
return reference != null && getTargetFQN().equals(reference.getQualifiedName());
|
||||
}
|
||||
}
|
||||
|
||||
private static class ServiceUsesNavigationHandler extends ServiceNavigationHandler {
|
||||
public ServiceUsesNavigationHandler(String interfaceClassName) {
|
||||
ServiceUsesNavigationHandler(String interfaceClassName) {
|
||||
super(interfaceClassName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PsiJavaCodeReferenceElement findTargetReference(@NotNull PsiJavaModule module) {
|
||||
return StreamEx.of(module.getUses().iterator())
|
||||
.map(PsiUsesStatement::getClassReference)
|
||||
@@ -526,11 +534,12 @@ public class JavaLineMarkerProvider extends LineMarkerProviderDescriptor {
|
||||
private static class ServiceProvidesNavigationHandler extends ServiceNavigationHandler {
|
||||
private final String myImplementerClassName;
|
||||
|
||||
public ServiceProvidesNavigationHandler(@NotNull String interfaceClassName, @NotNull String implementerClassName) {
|
||||
ServiceProvidesNavigationHandler(@NotNull String interfaceClassName, @NotNull String implementerClassName) {
|
||||
super(interfaceClassName);
|
||||
myImplementerClassName = implementerClassName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PsiJavaCodeReferenceElement findTargetReference(@NotNull PsiJavaModule module) {
|
||||
PsiJavaCodeReferenceElement[] references =
|
||||
StreamEx.of(module.getProvides().iterator())
|
||||
@@ -542,6 +551,7 @@ public class JavaLineMarkerProvider extends LineMarkerProviderDescriptor {
|
||||
return ContainerUtil.find(references, this::isTargetReference);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
protected String getTargetFQN() {
|
||||
return myImplementerClassName;
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
package z;
|
||||
|
||||
interface I {
|
||||
void run();
|
||||
}
|
||||
abstract class A {
|
||||
public final void <caret>run() {}
|
||||
}
|
||||
|
||||
class Foo extends A implements I {
|
||||
}
|
||||
+19
@@ -97,6 +97,25 @@ public class JavaGotoSuperTest extends LightDaemonAnalyzerTestCase {
|
||||
LineMarkerInfo aMarker = findMarkerWithElement(markers, aRun.getNameIdentifier());
|
||||
assertSame(MarkerType.SIBLING_OVERRIDING_METHOD.getNavigationHandler(), aMarker.getNavigationHandler());
|
||||
}
|
||||
public void testSiblingInheritanceLineMarkersEvenIfMethodIsFinal() {
|
||||
configureByFile(getBasePath() + "SiblingInheritanceFinal.java");
|
||||
PsiJavaFile file = (PsiJavaFile)getFile();
|
||||
PsiClass i = JavaPsiFacade.getInstance(getProject()).findClass("z.I", GlobalSearchScope.fileScope(file));
|
||||
PsiClass a = JavaPsiFacade.getInstance(getProject()).findClass("z.A", GlobalSearchScope.fileScope(file));
|
||||
PsiMethod iRun = i.getMethods()[0];
|
||||
assertEquals("run", iRun.getName());
|
||||
PsiMethod aRun = a.getMethods()[0];
|
||||
assertEquals("run", aRun.getName());
|
||||
doHighlighting();
|
||||
Document document = getEditor().getDocument();
|
||||
List<LineMarkerInfo> markers = DaemonCodeAnalyzerImpl.getLineMarkers(document, getProject());
|
||||
assertTrue(markers.size() >= 2);
|
||||
LineMarkerInfo iMarker = findMarkerWithElement(markers, iRun.getNameIdentifier());
|
||||
assertSame(MarkerType.OVERRIDDEN_METHOD.getNavigationHandler(), iMarker.getNavigationHandler());
|
||||
|
||||
LineMarkerInfo aMarker = findMarkerWithElement(markers, aRun.getNameIdentifier());
|
||||
assertSame(MarkerType.SIBLING_OVERRIDING_METHOD.getNavigationHandler(), aMarker.getNavigationHandler());
|
||||
}
|
||||
|
||||
private static LineMarkerInfo findMarkerWithElement(List<LineMarkerInfo> markers, PsiElement psiMethod) {
|
||||
LineMarkerInfo marker = ContainerUtil.find(markers, info -> info.getElement().equals(psiMethod));
|
||||
|
||||
Reference in New Issue
Block a user