mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
InlineSuperClass: support sealed classes (IDEA-243902)
GitOrigin-RevId: 9e9d53ec7fa1ade7e0d1890fb5682c41da7c4f97
This commit is contained in:
committed by
intellij-monorepo-bot
parent
341bbc7bc1
commit
fccea59571
@@ -85,7 +85,7 @@ public class FillPermitsListFix extends LocalQuickFixAndIntentionActionOnPsiElem
|
||||
return missingInheritors;
|
||||
}
|
||||
|
||||
static void fillPermitsList(@NotNull PsiClass parent, @NotNull Collection<String> missingInheritors) {
|
||||
public static void fillPermitsList(@NotNull PsiClass parent, @NotNull Collection<String> missingInheritors) {
|
||||
PsiReferenceList permitsList = parent.getPermitsList();
|
||||
PsiFileFactory factory = PsiFileFactory.getInstance(parent.getProject());
|
||||
if (permitsList == null) {
|
||||
|
||||
+5
-17
@@ -24,6 +24,7 @@ import com.intellij.psi.*;
|
||||
import com.intellij.psi.util.MethodSignatureUtil;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import com.siyeh.ig.psiutils.ClassUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -120,17 +121,10 @@ public class UnimplementInterfaceAction implements IntentionAction {
|
||||
if (target == psiClass) return;
|
||||
|
||||
if (targetClass.hasModifierProperty(PsiModifier.SEALED)) {
|
||||
PsiReferenceList permitsList = targetClass.getPermitsList();
|
||||
if (permitsList != null) {
|
||||
Arrays.stream(permitsList.getReferenceElements())
|
||||
.filter(r -> r.resolve() == psiClass).findFirst()
|
||||
.ifPresent(r -> r.delete());
|
||||
PsiModifierList modifiers = psiClass.getModifierList();
|
||||
if (modifiers != null && modifiers.hasExplicitModifier(PsiModifier.NON_SEALED)) {
|
||||
boolean hasAnotherSealedParent = hasSealedParent(psiClass.getExtendsListTypes(), targetClass);
|
||||
if (!hasAnotherSealedParent) hasAnotherSealedParent = hasSealedParent(psiClass.getImplementsListTypes(), targetClass);
|
||||
if (!hasAnotherSealedParent) modifiers.setModifierProperty(PsiModifier.NON_SEALED, false);
|
||||
}
|
||||
ClassUtils.removeFromPermitsList(targetClass, psiClass);
|
||||
final PsiModifierList modifiers = psiClass.getModifierList();
|
||||
if (modifiers != null && modifiers.hasExplicitModifier(PsiModifier.NON_SEALED) && !ClassUtils.hasSealedParent(psiClass)) {
|
||||
modifiers.setModifierProperty(PsiModifier.NON_SEALED, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,12 +140,6 @@ public class UnimplementInterfaceAction implements IntentionAction {
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean hasSealedParent(PsiClassType[] types, PsiClass toExclude) {
|
||||
return Arrays.stream(types)
|
||||
.map(t -> t.resolve())
|
||||
.anyMatch(parent -> parent != null && parent != toExclude && parent.hasModifierProperty(PsiModifier.SEALED));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean startInWriteAction() {
|
||||
return true;
|
||||
|
||||
+16
@@ -15,14 +15,18 @@
|
||||
*/
|
||||
package com.intellij.refactoring.safeDelete.usageInfo;
|
||||
|
||||
import com.intellij.codeInsight.intention.impl.FillPermitsListFix;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.codeStyle.CodeStyleManager;
|
||||
import com.intellij.psi.util.TypeConversionUtil;
|
||||
import com.intellij.util.ArrayUtilRt;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import com.siyeh.ig.psiutils.ClassUtils;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
/**
|
||||
* @author ven
|
||||
*/
|
||||
@@ -55,6 +59,14 @@ public class SafeDeleteExtendsClassUsageInfo extends SafeDeleteReferenceUsageInf
|
||||
copyExtendsList(refClass.getImplementsList(), targetTypeParameter, elementFactory);
|
||||
|
||||
getElement().delete();
|
||||
|
||||
if (!refClass.hasModifierProperty(PsiModifier.SEALED)) return;
|
||||
ClassUtils.removeFromPermitsList(refClass, myExtendingClass);
|
||||
final PsiModifierList modifiers = myExtendingClass.getModifierList();
|
||||
if (modifiers == null || !modifiers.hasModifierProperty(PsiModifier.NON_SEALED)) return;
|
||||
if (!ClassUtils.hasSealedParent(myExtendingClass)) {
|
||||
modifiers.setModifierProperty(PsiModifier.NON_SEALED, false);
|
||||
}
|
||||
}
|
||||
|
||||
private void copyExtendsList(@Nullable PsiReferenceList sourceExtendsList,
|
||||
@@ -68,6 +80,10 @@ public class SafeDeleteExtendsClassUsageInfo extends SafeDeleteReferenceUsageInf
|
||||
if (ArrayUtilRt.find(existingRefTypes, referenceType) > -1) continue;
|
||||
PsiClassType classType = (PsiClassType)mySubstitutor.substitute(referenceType);
|
||||
PsiElement extendsRef = targetExtendsList.add(elementFactory.createReferenceElementByType(classType));
|
||||
PsiClass classToExtend = classType.resolve();
|
||||
if (classToExtend != null && classToExtend.hasModifierProperty(PsiModifier.SEALED)) {
|
||||
FillPermitsListFix.fillPermitsList(classToExtend, Collections.singleton(myExtendingClass.getQualifiedName()));
|
||||
}
|
||||
CodeStyleManager.getInstance(myExtendingClass.getProject()).reformat(extendsRef);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
// "Unimplement Class" "true"
|
||||
public sealed interface A permits B {}
|
||||
|
||||
sealed class C {}
|
||||
final class C {}
|
||||
|
||||
non-sealed class B implements A {}
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
// "Unimplement Class" "true"
|
||||
sealed class A {
|
||||
final class A {
|
||||
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// "Unimplement Class" "true"
|
||||
sealed class A {}
|
||||
final class A {}
|
||||
|
||||
final class B {}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// "Unimplement Class" "true"
|
||||
sealed class A {}
|
||||
final class A {}
|
||||
|
||||
class B {}
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
// "Unimplement Class" "true"
|
||||
sealed class A {}
|
||||
final class A {}
|
||||
|
||||
sealed class B permits C {}
|
||||
|
||||
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
sealed interface SuperSuper permits Super, Test {}
|
||||
|
||||
final class Super implements SuperSuper {}
|
||||
|
||||
non-sealed class Test implements SuperSuper {}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
sealed interface SuperSuper permits Super, Test {}
|
||||
|
||||
sealed class Super implements SuperSuper permits Test {}
|
||||
|
||||
non-sealed class Test extends Super implements SuperSuper {}
|
||||
+1
@@ -1,3 +1,4 @@
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
abstract class Super {
|
||||
abstract void doSmth();
|
||||
}
|
||||
|
||||
final class Test {
|
||||
void doSmth() {
|
||||
}
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
abstract sealed class Super permits Test {
|
||||
abstract void doSmth();
|
||||
}
|
||||
|
||||
final class Test extends Super {
|
||||
@Override
|
||||
void doSmth() {
|
||||
}
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
sealed interface SuperSuper permits Super, Test {}
|
||||
|
||||
final class Super implements SuperSuper {}
|
||||
|
||||
non-sealed class Test implements SuperSuper {}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
sealed interface SuperSuper permits Super {}
|
||||
|
||||
sealed class Super implements SuperSuper permits Test {}
|
||||
|
||||
non-sealed class Test extends Super {}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
final class Test {
|
||||
void doSmth() {
|
||||
System.out.println("hello");
|
||||
}
|
||||
}
|
||||
|
||||
class Test1 {
|
||||
void doSmth() {
|
||||
System.out.println("hello");
|
||||
}
|
||||
}
|
||||
|
||||
sealed class Test2 permits Test3 {
|
||||
void doSmth() {
|
||||
System.out.println("hello");
|
||||
}
|
||||
}
|
||||
|
||||
final class Test3 extends Test2 {}
|
||||
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
sealed class Super permits Test, Test1, Test2 {
|
||||
void doSmth() {
|
||||
System.out.println("hello");
|
||||
}
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
final class Test extends Super {}
|
||||
|
||||
non-sealed class Test1 extends Super {}
|
||||
|
||||
sealed class Test2 extends Super permits Test3 {}
|
||||
|
||||
final class Test3 extends Test2 {}
|
||||
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
sealed class Super permits Test1 {}
|
||||
|
||||
final class Test {}
|
||||
|
||||
final class Test1 extends Super {}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
sealed class Super permits Test, Test1 {}
|
||||
|
||||
final class Test extends Super {}
|
||||
|
||||
final class Test1 extends Super {}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
final class Super {}
|
||||
|
||||
class Test {}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
sealed class Super permits Test {}
|
||||
|
||||
non-sealed class Test extends Super {}
|
||||
@@ -22,6 +22,8 @@ import com.intellij.refactoring.BaseRefactoringProcessor;
|
||||
import com.intellij.refactoring.LightMultiFileTestCase;
|
||||
import com.intellij.refactoring.inlineSuperClass.InlineSuperClassRefactoringProcessor;
|
||||
import com.intellij.refactoring.util.DocCommentPolicy;
|
||||
import com.intellij.testFramework.LightProjectDescriptor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* @author anna
|
||||
@@ -32,6 +34,11 @@ public class InlineSuperClassTest extends LightMultiFileTestCase {
|
||||
return JavaTestUtil.getJavaTestDataPath() + "/refactoring/inlineSuperClass/";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NotNull LightProjectDescriptor getProjectDescriptor() {
|
||||
return JAVA_15;
|
||||
}
|
||||
|
||||
public void testInlineOneClass() { doTest(false, true); }
|
||||
public void testInlineOneClassWithConflicts() { doTest(true, true); }
|
||||
public void testAbstractOverrides() { doTest(); }
|
||||
@@ -77,6 +84,12 @@ public class InlineSuperClassTest extends LightMultiFileTestCase {
|
||||
public void testThisQualificationInsideAnonymous() { doTest(); }
|
||||
public void testOrderOfInnerClasses() { doTest(); }
|
||||
public void testSuperMethodWithoutBody() { doTest(); }
|
||||
public void testSealedAbstractParentOneInheritor() { doTest(false, true); }
|
||||
public void testSealedParentManyInheritors() { doTest(false, true); }
|
||||
public void testSealedParentNonSealedInheritor() { doTest(false, true); }
|
||||
public void testSealedGrandParentNonSealedInheritor() { doTest(false, true); }
|
||||
public void testSealedParentInlineAll() { doTest(); }
|
||||
public void testMultipleSealedParents() { doTest(false, true); }
|
||||
|
||||
private void doTest() {
|
||||
doTest(false, false);
|
||||
|
||||
+26
@@ -24,6 +24,7 @@ import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.psi.util.TypeConversionUtil;
|
||||
import com.intellij.util.Processor;
|
||||
import com.intellij.util.Query;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import one.util.streamex.StreamEx;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
@@ -338,6 +339,31 @@ public final class ClassUtils {
|
||||
return selfInstance != null && newOnlyAssignsToStaticSelfInstance(getIfOnlyInvisibleConstructors(aClass)[0], selfInstance);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes exChild class reference from permits list of a parent.
|
||||
* If this was the last element in permits list then parent class is converted to final if possible (sealed modifier is removed).
|
||||
*/
|
||||
public static void removeFromPermitsList(@NotNull PsiClass parent, @NotNull PsiClass exChild) {
|
||||
PsiReferenceList permitsList = parent.getPermitsList();
|
||||
if (permitsList == null) return;
|
||||
PsiJavaCodeReferenceElement[] childRefs = permitsList.getReferenceElements();
|
||||
PsiJavaCodeReferenceElement exChildRef = ContainerUtil.find(childRefs, ref -> ref.resolve() == exChild);
|
||||
if (exChildRef == null) return;
|
||||
exChildRef.delete();
|
||||
if (childRefs.length != 1) return;
|
||||
PsiModifierList modifiers = parent.getModifierList();
|
||||
if (modifiers == null) return;
|
||||
modifiers.setModifierProperty(PsiModifier.SEALED, false);
|
||||
if (!parent.hasModifierProperty(PsiModifier.ABSTRACT)) modifiers.setModifierProperty(PsiModifier.FINAL, true);
|
||||
}
|
||||
|
||||
public static boolean hasSealedParent(@NotNull PsiClass psiClass) {
|
||||
return StreamEx.of(psiClass.getExtendsListTypes())
|
||||
.append(psiClass.getImplementsListTypes())
|
||||
.map(r -> r.resolve())
|
||||
.anyMatch(parent -> parent != null && parent.hasModifierProperty(PsiModifier.SEALED));
|
||||
}
|
||||
|
||||
private static PsiField getIfOneStaticSelfInstance(PsiClass aClass) {
|
||||
Stream<PsiField> fieldStream = Arrays.stream(aClass.getFields());
|
||||
|
||||
|
||||
Reference in New Issue
Block a user