diff --git a/java/java-impl/src/com/intellij/refactoring/safeDelete/JavaSafeDeleteProcessor.java b/java/java-impl/src/com/intellij/refactoring/safeDelete/JavaSafeDeleteProcessor.java index f5770491f7d8..673bdba3c1d0 100644 --- a/java/java-impl/src/com/intellij/refactoring/safeDelete/JavaSafeDeleteProcessor.java +++ b/java/java-impl/src/com/intellij/refactoring/safeDelete/JavaSafeDeleteProcessor.java @@ -572,11 +572,16 @@ public class JavaSafeDeleteProcessor extends SafeDeleteProcessorDelegateBase { if (parent instanceof PsiReferenceList) { final PsiElement pparent = parent.getParent(); if (pparent instanceof PsiClass && element instanceof PsiJavaCodeReferenceElement) { + PsiJavaCodeReferenceElement classRef = (PsiJavaCodeReferenceElement) element; final PsiClass inheritor = (PsiClass) pparent; + if (parent.equals(inheritor.getPermitsList())) { + usages.add(new SafeDeletePermitsClassUsageInfo(classRef, psiClass, inheritor)); + return true; + } //If psiClass contains only private members, then it is safe to remove it and change inheritor's extends/implements accordingly if (justPrivates) { if (parent.equals(inheritor.getExtendsList()) || parent.equals(inheritor.getImplementsList())) { - usages.add(new SafeDeleteExtendsClassUsageInfo((PsiJavaCodeReferenceElement)element, psiClass, inheritor)); + usages.add(new SafeDeleteExtendsClassUsageInfo(classRef, psiClass, inheritor)); return true; } } diff --git a/java/java-impl/src/com/intellij/refactoring/safeDelete/usageInfo/SafeDeletePermitsClassUsageInfo.java b/java/java-impl/src/com/intellij/refactoring/safeDelete/usageInfo/SafeDeletePermitsClassUsageInfo.java new file mode 100644 index 000000000000..17fa2aa277a4 --- /dev/null +++ b/java/java-impl/src/com/intellij/refactoring/safeDelete/usageInfo/SafeDeletePermitsClassUsageInfo.java @@ -0,0 +1,56 @@ +// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package com.intellij.refactoring.safeDelete.usageInfo; + +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiClassType; +import com.intellij.psi.PsiJavaCodeReferenceElement; +import com.intellij.psi.PsiReferenceList; +import com.intellij.util.IncorrectOperationException; +import com.intellij.util.containers.ContainerUtil; +import com.siyeh.ig.psiutils.ClassUtils; + +import java.util.Objects; + +public class SafeDeletePermitsClassUsageInfo extends SafeDeleteReferenceUsageInfo { + private final PsiClass myParentClass; + + public SafeDeletePermitsClassUsageInfo(final PsiJavaCodeReferenceElement reference, PsiClass refClass, PsiClass parentClass) { + super(reference, refClass, true); + myParentClass = parentClass; + } + + @Override + public PsiClass getReferencedElement() { + return (PsiClass)super.getReferencedElement(); + } + + @Override + public void deleteElement() throws IncorrectOperationException { + final PsiClass refClass = getReferencedElement(); + ClassUtils.removeFromPermitsList(myParentClass, refClass); + } + + @Override + public boolean isSafeDelete() { + if (getElement() != null) return true; + PsiReferenceList permitsList = myParentClass.getPermitsList(); + if (permitsList == null) return false; + PsiJavaCodeReferenceElement[] childRefs = permitsList.getReferenceElements(); + if (childRefs.length < 1) return false; + return ContainerUtil.exists(childRefs, ref -> ref.resolve() == getReferencedElement()); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + if (!super.equals(o)) return false; + SafeDeletePermitsClassUsageInfo info = (SafeDeletePermitsClassUsageInfo)o; + return Objects.equals(myParentClass, info.myParentClass); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), myParentClass); + } +} diff --git a/java/java-tests/testData/refactoring/safeDelete/SealedParent.java b/java/java-tests/testData/refactoring/safeDelete/SealedParent.java new file mode 100644 index 000000000000..86fe79ac991e --- /dev/null +++ b/java/java-tests/testData/refactoring/safeDelete/SealedParent.java @@ -0,0 +1,9 @@ +sealed interface GrandParent1 permits Parent, A {} + +sealed class GrandParent2 permits Parent {} + +sealed class Parent extends GrandParent2 implements GrandParent1 permits A, B {} + +non-sealed class A extends Parent implements GrandParent1 {} + +final class B extends Parent {} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/safeDelete/SealedParent_after.java b/java/java-tests/testData/refactoring/safeDelete/SealedParent_after.java new file mode 100644 index 000000000000..fd3026d8e7af --- /dev/null +++ b/java/java-tests/testData/refactoring/safeDelete/SealedParent_after.java @@ -0,0 +1,7 @@ +sealed interface GrandParent1 permits A, B {} + +sealed class GrandParent2 permits A, B {} + +non-sealed class A extends GrandParent2 implements GrandParent1 {} + +final class B extends GrandParent2 implements GrandParent1 {} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/java/refactoring/SafeDeleteTest.java b/java/java-tests/testSrc/com/intellij/java/refactoring/SafeDeleteTest.java index 37c5871b398c..26c1febe91e0 100644 --- a/java/java-tests/testSrc/com/intellij/java/refactoring/SafeDeleteTest.java +++ b/java/java-tests/testSrc/com/intellij/java/refactoring/SafeDeleteTest.java @@ -276,7 +276,7 @@ public class SafeDeleteTest extends MultiFileTestCase { LanguageLevelProjectExtension.getInstance(getProject()).setLanguageLevel(LanguageLevel.JDK_1_7); doSingleFileTest(); } - + public void testLastResourceVariableConflictingVar() throws Exception { LanguageLevelProjectExtension.getInstance(getProject()).setLanguageLevel(LanguageLevel.JDK_1_7); doSingleFileTest(); @@ -388,6 +388,11 @@ public class SafeDeleteTest extends MultiFileTestCase { doSingleFileTest(); } + public void testSealedParent() throws Exception { + LanguageLevelProjectExtension.getInstance(getProject()).setLanguageLevel(LanguageLevel.JDK_15_PREVIEW); + doSingleFileTest(); + } + private void doTest(@NonNls final String qClassName) { doTest((rootDir, rootAfter) -> this.performAction(qClassName)); }