[java-intention] IDEA-357879 "Replace catch sections with throw" intention doesn't check ancestors

GitOrigin-RevId: ab1f6d8c13b76850481b38c3fe0137f22fc11071
This commit is contained in:
Mikhail Pyltsin
2024-08-19 19:50:20 +02:00
committed by intellij-monorepo-bot
parent b08201f587
commit 6d84f1e4df
6 changed files with 117 additions and 45 deletions

View File

@@ -68,7 +68,10 @@ replace.on.demand.import.intention.name=Replace with single class imports
replace.on.demand.import.intention.family.name=Replace on demand import with single class imports
replace.operator.assignment.with.assignment.intention.family.name=Replace operator assignment with assignment
convert.catch.to.throws.intention.name=Replace 'catch' section with 'throws' declaration
convert.catch.to.throws.intention.name.capitalized=Replace 'catch' Section With 'throws' Declaration
convert.catch.to.throws.intention.family.name=Replace catch section with throws declaration
convert.catch.to.throws.only.current.method=Add throws declaration only to the current method
convert.catch.to.throws.super.and.current.methods=Add throws declaration to the current method and super methods
wrap.vararg.arguments.with.explicit.array.intention.name=Wrap vararg arguments with explicit array creation
wrap.vararg.arguments.with.explicit.array.intention.family.name=Wrap vararg arguments with explicit array creation
extract.while.loop.condition.to.if.statement.intention.name=Extract condition to internal 'if' statement

View File

@@ -2,20 +2,28 @@
package com.siyeh.ipp.exceptions;
import com.intellij.codeInsight.BlockUtils;
import com.intellij.modcommand.ActionContext;
import com.intellij.modcommand.ModCommand;
import com.intellij.modcommand.Presentation;
import com.intellij.modcommand.PsiBasedModCommandAction;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import com.siyeh.IntentionPowerPackBundle;
import com.siyeh.ipp.base.MCIntention;
import com.siyeh.ipp.base.PsiElementPredicate;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
/**
* @author Bas Leijdekkers
*/
public final class ConvertCatchToThrowsIntention extends MCIntention {
public final class ConvertCatchToThrowsIntention extends PsiBasedModCommandAction<PsiElement> {
public ConvertCatchToThrowsIntention() {
super(PsiElement.class);
}
@Override
public @NotNull String getFamilyName() {
@@ -23,18 +31,73 @@ public final class ConvertCatchToThrowsIntention extends MCIntention {
}
@Override
protected @NotNull String getTextForElement(@NotNull PsiElement element) {
return IntentionPowerPackBundle.message("convert.catch.to.throws.intention.name");
protected boolean isElementApplicable(@NotNull PsiElement element, @NotNull ActionContext context) {
final PsiElement parent = element.getParent();
if (!(parent instanceof PsiCatchSection)) {
return false;
}
if (element instanceof PsiCodeBlock) {
return false;
}
final PsiElement owner = PsiTreeUtil.getParentOfType(parent, PsiMethod.class, PsiClass.class, PsiLambdaExpression.class);
if (owner instanceof PsiLambdaExpression) {
final PsiMethod method = LambdaUtil.getFunctionalInterfaceMethod(owner);
return !(method instanceof PsiCompiledElement);
}
return owner instanceof PsiMethod;
}
@Override
protected @NotNull PsiElementPredicate getElementPredicate() {
return new ConvertCatchToThrowsPredicate();
}
@Override
protected void invoke(@NotNull PsiElement element) {
protected @NotNull ModCommand perform(@NotNull ActionContext context, @NotNull PsiElement element) {
final PsiCatchSection catchSection = (PsiCatchSection)element.getParent();
final PsiMethod method = getMethod(catchSection);
if (method == null) return ModCommand.nop();
PsiType catchType = catchSection.getCatchType();
if (catchType == null) return ModCommand.nop();
PsiMethod[] superMethods = method.findSuperMethods(true);
if (superMethods.length == 0) {
return ModCommand.psiUpdate(catchSection, (copyCatchSection, upd) -> {
deleteCatchAndAddToCurrentMethod(copyCatchSection, catchType);
});
}
return ModCommand.chooseAction(
IntentionPowerPackBundle.message("convert.catch.to.throws.intention.name.capitalized"),
ModCommand.psiUpdateStep(catchSection,
IntentionPowerPackBundle.message("convert.catch.to.throws.super.and.current.methods"),
(copyCatchSection, upd) -> {
List<PsiMethod> superMethodsToModify = new ArrayList<>();
PsiFile containingFile = catchSection.getContainingFile();
PsiFile copyCatchSectionContainingFile = copyCatchSection.getContainingFile();
for (PsiMethod superMethod : superMethods) {
if (!superMethod.isWritable() || !superMethod.isPhysical()) continue;
if (superMethod.getContainingFile() == containingFile) {
superMethodsToModify.add(
PsiTreeUtil.findSameElementInCopy(superMethod, copyCatchSectionContainingFile));
}
else {
PsiMethod copySuperMethod = upd.getWritable(superMethod);
superMethodsToModify.add(copySuperMethod);
}
}
deleteCatchAndAddToCurrentMethod(copyCatchSection, catchType);
for (PsiMethod superMethod : superMethodsToModify) {
addToThrowsList(superMethod.getThrowsList(), catchType);
}
}),
ModCommand.psiUpdateStep(catchSection,
IntentionPowerPackBundle.message("convert.catch.to.throws.only.current.method"),
(copyCatchSection, upd) -> deleteCatchAndAddToCurrentMethod(copyCatchSection, catchType))
);
}
private static void deleteCatchAndAddToCurrentMethod(@NotNull PsiCatchSection copyCatchSection, @NotNull PsiType catchType) {
PsiMethod copyMethod = getMethod(copyCatchSection);
deleteCatchSection(copyCatchSection);
if (copyMethod == null) return;
addToThrowsList(copyMethod.getThrowsList(), catchType);
}
private static @Nullable PsiMethod getMethod(@NotNull PsiCatchSection catchSection) {
final NavigatablePsiElement owner = PsiTreeUtil.getParentOfType(catchSection, PsiMethod.class, PsiLambdaExpression.class);
final PsiMethod method;
if (owner instanceof PsiMethod) {
@@ -44,17 +107,15 @@ public final class ConvertCatchToThrowsIntention extends MCIntention {
method = LambdaUtil.getFunctionalInterfaceMethod(owner);
}
else {
return;
return null;
}
if (method == null) {
return;
return null;
}
// todo warn if method implements or overrides some base method
// Warning
// "Method xx() of class XX implements/overrides method of class
// YY. Do you want to modify the base method?"
// [Yes][No][Cancel]
addToThrowsList(method.getThrowsList(), catchSection.getCatchType());
return method;
}
private static void deleteCatchSection(PsiCatchSection catchSection) {
final PsiTryStatement tryStatement = catchSection.getTryStatement();
if (tryStatement.getCatchSections().length > 1 || tryStatement.getResourceList() != null || tryStatement.getFinallyBlock() != null) {
catchSection.delete();
@@ -64,6 +125,11 @@ public final class ConvertCatchToThrowsIntention extends MCIntention {
}
}
@Override
protected Presentation getPresentation(@NotNull ActionContext context, @NotNull PsiElement element) {
return Presentation.of(IntentionPowerPackBundle.message("convert.catch.to.throws.intention.name"));
}
private static void addToThrowsList(PsiReferenceList throwsList, PsiType catchType) {
if (catchType instanceof PsiClassType classType) {
final PsiClassType[] types = throwsList.getReferencedTypes();

View File

@@ -1,26 +0,0 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.siyeh.ipp.exceptions;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import com.siyeh.ipp.base.PsiElementPredicate;
class ConvertCatchToThrowsPredicate implements PsiElementPredicate {
@Override
public boolean satisfiedBy(PsiElement element) {
final PsiElement parent = element.getParent();
if (!(parent instanceof PsiCatchSection)) {
return false;
}
if (element instanceof PsiCodeBlock) {
return false;
}
final PsiElement owner = PsiTreeUtil.getParentOfType(parent, PsiMethod.class, PsiClass.class, PsiLambdaExpression.class);
if (owner instanceof PsiLambdaExpression) {
final PsiMethod method = LambdaUtil.getFunctionalInterfaceMethod(owner);
return !(method instanceof PsiCompiledElement);
}
return owner instanceof PsiMethod;
}
}