InlineSuperClass: do not inline class with sealed parent as anonymous, fix permits list in case when sealed parent and child are both sealed (IDEA-243902)

GitOrigin-RevId: 52ac1c93c8d9091b18172ec27a95c0eccc672915
This commit is contained in:
Artemiy Sartakov
2020-08-04 05:26:46 +00:00
committed by intellij-monorepo-bot
parent ae1f9c7d4a
commit 3ea4e3a5b5
10 changed files with 105 additions and 10 deletions
@@ -39,6 +39,7 @@ import com.intellij.refactoring.util.InlineUtil;
import com.intellij.refactoring.util.RefactoringUtil;
import com.intellij.util.ArrayUtil;
import com.intellij.util.Processor;
import com.siyeh.ig.psiutils.ClassUtils;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -73,7 +74,9 @@ public class InlineToAnonymousClassHandler extends JavaInlineActionHandler {
}
if (!(element instanceof PsiClass)) return false;
if (element instanceof PsiAnonymousClass) return false;
return findClassInheritors((PsiClass)element);
PsiClass psiClass = (PsiClass)element;
if (!findClassInheritors(psiClass)) return false;
return !isParentSealed(psiClass);
}
private static boolean findClassInheritors(final PsiClass element) {
@@ -92,6 +95,11 @@ public class InlineToAnonymousClassHandler extends JavaInlineActionHandler {
return inheritors.isEmpty();
}
private static boolean isParentSealed(@NotNull PsiClass psiClass) {
if (PsiTreeUtil.findChildOfType(psiClass, PsiMember.class) == null) return false;
return ClassUtils.hasSealedParent(psiClass);
}
@Override
public boolean canInlineElementInEditor(PsiElement element, Editor editor) {
if (canInlineElement(element)) {
@@ -20,11 +20,14 @@ import com.intellij.usageView.UsageInfo;
import com.intellij.usageView.UsageViewDescriptor;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.containers.MultiMap;
import com.siyeh.ig.psiutils.ClassUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import static com.intellij.util.ObjectUtils.tryCast;
/**
* @author yole
*/
@@ -216,6 +219,13 @@ public class InlineToAnonymousClassProcessor extends BaseRefactoringProcessor {
else if (element != null && element.getParent() instanceof PsiNewExpression) {
newExpressions.add((PsiNewExpression) element.getParent());
}
else if (element instanceof PsiJavaCodeReferenceElement && element.getParent() instanceof PsiReferenceList) {
PsiReferenceList refList = (PsiReferenceList) element.getParent();
PsiClass parentClass = tryCast(refList.getParent(), PsiClass.class);
if (parentClass != null && refList == parentClass.getPermitsList()) {
ClassUtils.removeFromPermitsList(parentClass, myClass);
}
}
else {
PsiImportStatement statement = PsiTreeUtil.getParentOfType(element, PsiImportStatement.class);
if (statement != null && !myInlineThisOnly) {
@@ -119,9 +119,13 @@ public class InlineSuperClassRefactoringProcessor extends FixableUsagesRefactori
if (parent instanceof PsiReferenceList) {
final PsiElement pparent = parent.getParent();
if (pparent instanceof PsiClass) {
final PsiJavaCodeReferenceElement classRef = (PsiJavaCodeReferenceElement)element;
final PsiClass inheritor = (PsiClass)pparent;
if (parent.equals(inheritor.getExtendsList()) || parent.equals(inheritor.getImplementsList())) {
usages.add(new ReplaceExtendsListUsageInfo((PsiJavaCodeReferenceElement)element, mySuperClass, inheritor));
usages.add(new ReplaceExtendsListUsageInfo(classRef, mySuperClass, inheritor));
}
else if (parent.equals(inheritor.getPermitsList())) {
usages.add(new RemovePermitsListUsageInfo(classRef, mySuperClass, inheritor));
}
}
} else {
@@ -0,0 +1,24 @@
// 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.inlineSuperClass.usageInfo;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiJavaCodeReferenceElement;
import com.intellij.refactoring.safeDelete.usageInfo.SafeDeletePermitsClassUsageInfo;
import com.intellij.refactoring.util.FixableUsageInfo;
import com.intellij.util.IncorrectOperationException;
public class RemovePermitsListUsageInfo extends FixableUsageInfo {
private final SafeDeletePermitsClassUsageInfo mySafeDeletePermitsClassUsageInfo;
public RemovePermitsListUsageInfo(PsiJavaCodeReferenceElement reference, PsiClass refClass, PsiClass parentClass) {
super(reference);
mySafeDeletePermitsClassUsageInfo = new SafeDeletePermitsClassUsageInfo(reference, refClass, parentClass, false);
}
@Override
public void fixUsage() throws IncorrectOperationException {
if (mySafeDeletePermitsClassUsageInfo.isSafeDelete()) {
mySafeDeletePermitsClassUsageInfo.deleteElement();
}
}
}
@@ -575,7 +575,7 @@ public class JavaSafeDeleteProcessor extends SafeDeleteProcessorDelegateBase {
PsiJavaCodeReferenceElement classRef = (PsiJavaCodeReferenceElement) element;
final PsiClass inheritor = (PsiClass) pparent;
if (parent.equals(inheritor.getPermitsList())) {
usages.add(new SafeDeletePermitsClassUsageInfo(classRef, psiClass, inheritor));
usages.add(new SafeDeletePermitsClassUsageInfo(classRef, psiClass, inheritor, true));
return true;
}
//If psiClass contains only private members, then it is safe to remove it and change inheritor's extends/implements accordingly
@@ -2,21 +2,24 @@
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 org.jetbrains.annotations.Nullable;
import java.util.Objects;
public class SafeDeletePermitsClassUsageInfo extends SafeDeleteReferenceUsageInfo {
private final PsiClass myParentClass;
private final boolean myChangeParentModifier;
public SafeDeletePermitsClassUsageInfo(final PsiJavaCodeReferenceElement reference, PsiClass refClass, PsiClass parentClass) {
public SafeDeletePermitsClassUsageInfo(PsiJavaCodeReferenceElement reference, PsiClass refClass,
PsiClass parentClass, boolean changeParentModifier) {
super(reference, refClass, true);
myParentClass = parentClass;
myChangeParentModifier = changeParentModifier;
}
@Override
@@ -27,17 +30,26 @@ public class SafeDeletePermitsClassUsageInfo extends SafeDeleteReferenceUsageInf
@Override
public void deleteElement() throws IncorrectOperationException {
final PsiClass refClass = getReferencedElement();
ClassUtils.removeFromPermitsList(myParentClass, refClass);
if (myChangeParentModifier) {
ClassUtils.removeFromPermitsList(myParentClass, refClass);
}
else {
PsiJavaCodeReferenceElement exChildRef = findReference();
if (exChildRef != null) exChildRef.delete();
}
}
@Override
public boolean isSafeDelete() {
if (getElement() != null) return true;
return getElement() != null && findReference() != null;
}
private @Nullable PsiJavaCodeReferenceElement findReference() {
PsiReferenceList permitsList = myParentClass.getPermitsList();
if (permitsList == null) return false;
if (permitsList == null) return null;
PsiJavaCodeReferenceElement[] childRefs = permitsList.getReferenceElements();
if (childRefs.length < 1) return false;
return ContainerUtil.exists(childRefs, ref -> ref.resolve() == getReferencedElement());
if (childRefs.length < 1) return null;
return ContainerUtil.find(childRefs, ref -> ref.resolve() == getReferencedElement());
}
@Override
@@ -0,0 +1,9 @@
sealed class Parent permits Parent.A {
void test() {
Object obj = new A<caret>();
}
static final class A extends Parent {}
}
@@ -0,0 +1,7 @@
final class Parent {
void test() {
Object obj = new Parent();
}
}
@@ -0,0 +1,11 @@
sealed class Parent {
void test() {
Object obj = new A<caret>();
}
static final class A extends Parent {
void foo() {}
}
}
@@ -271,6 +271,16 @@ public class InlineToAnonymousClassTest extends LightRefactoringTestCase {
doTest(true, false);
}
public void testSealed() {
setLanguageLevel(LanguageLevel.JDK_15_PREVIEW);
doTest(false, false);
}
public void testSealedParentChildWithMembers() {
setLanguageLevel(LanguageLevel.JDK_15_PREVIEW);
doTestCanBeInvokedOnReference(false);
}
public void testNoInlineAbstract() {
doTestNoInline("Abstract classes cannot be inlined");
}