[java-inspections] IDEA-300159 Redundant close(): Search closing statements in if statements

PR#2106
Reviewed-by: Tagir Valeev <tagir.valeev@jetbrains.com>

GitOrigin-RevId: 501e4fee7441bf38e1d0ad19f826d3a7b565920b
This commit is contained in:
Tagir Valeev
2022-08-19 10:55:36 +00:00
committed by intellij-monorepo-bot
parent 76800e0899
commit 4efbdf1fb5
8 changed files with 165 additions and 35 deletions
@@ -1,17 +1,21 @@
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInspection;
import com.intellij.codeInsight.daemon.impl.quickfix.DeleteElementFix;
import com.intellij.java.JavaBundle;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.ArrayUtil;
import com.siyeh.ig.callMatcher.CallMatcher;
import com.siyeh.ig.psiutils.CommentTracker;
import com.siyeh.ig.psiutils.ControlFlowUtils;
import com.siyeh.ig.psiutils.EquivalenceChecker;
import one.util.streamex.StreamEx;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import static com.intellij.util.ObjectUtils.tryCast;
@@ -29,40 +33,45 @@ public class RedundantExplicitCloseInspection extends AbstractBaseJavaLocalInspe
PsiCodeBlock tryBlock = statement.getTryBlock();
if (tryBlock == null) return;
PsiStatement last = ArrayUtil.getLastElement(tryBlock.getStatements());
PsiExpressionStatement expressionStatement = tryCast(last, PsiExpressionStatement.class);
if(expressionStatement == null) return;
PsiMethodCallExpression call = tryCast(expressionStatement.getExpression(), PsiMethodCallExpression.class);
if (!CLOSE.test(call)) return;
PsiExpression qualifier = call.getMethodExpression().getQualifierExpression();
PsiReferenceExpression reference = tryCast(PsiUtil.skipParenthesizedExprDown(qualifier), PsiReferenceExpression.class);
if(reference == null) return;
PsiVariable variable = tryCast(reference.resolve(), PsiVariable.class);
if(variable == null) return;
boolean isReferenceToResourceVariable = StreamEx.of(resourceList.iterator()).anyMatch(
element -> variable == element ||
element instanceof PsiResourceExpression &&
EquivalenceChecker.getCanonicalPsiEquivalence()
.expressionsAreEquivalent(reference, ((PsiResourceExpression)element).getExpression()));
if(!isReferenceToResourceVariable) return;
holder.registerProblem(last, JavaBundle.message("inspection.redundant.explicit.close"), new DeleteRedundantCloseFix());
List<PsiStatement> terminatingStatements = getTerminatingStatements(ArrayUtil.getLastElement(tryBlock.getStatements()));
for (PsiStatement last : terminatingStatements) {
PsiExpressionStatement expressionStatement = tryCast(last, PsiExpressionStatement.class);
if(expressionStatement == null) return;
PsiMethodCallExpression call = tryCast(expressionStatement.getExpression(), PsiMethodCallExpression.class);
if (!CLOSE.test(call)) return;
PsiExpression qualifier = call.getMethodExpression().getQualifierExpression();
PsiReferenceExpression reference = tryCast(PsiUtil.skipParenthesizedExprDown(qualifier), PsiReferenceExpression.class);
if(reference == null) return;
PsiVariable variable = tryCast(reference.resolve(), PsiVariable.class);
if(variable == null) return;
boolean isReferenceToResourceVariable = StreamEx.of(resourceList.iterator()).anyMatch(
element -> variable == element ||
element instanceof PsiResourceExpression &&
EquivalenceChecker.getCanonicalPsiEquivalence()
.expressionsAreEquivalent(reference, ((PsiResourceExpression)element).getExpression()));
if(!isReferenceToResourceVariable) return;
holder.registerProblem(last, JavaBundle.message("inspection.redundant.explicit.close"), new DeleteElementFix(last, CommonQuickFixBundle.message("fix.remove.redundant", "close()")));
}
}
};
}
private static class DeleteRedundantCloseFix implements LocalQuickFix {
@Nls
@NotNull
@Override
public String getFamilyName() {
return CommonQuickFixBundle.message("fix.remove.redundant", "close()");
}
@Override
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
PsiElement element = descriptor.getStartElement();
new CommentTracker().deleteAndRestoreComments(element);
@NotNull
private static List<PsiStatement> getTerminatingStatements(@Nullable PsiStatement last) {
if (last == null) return Collections.emptyList();
List<PsiStatement> terminatingStatements = new ArrayList<>();
PsiIfStatement ifStatement = tryCast(last, PsiIfStatement.class);
if (ifStatement != null) {
PsiStatement[] thenStatements = ControlFlowUtils.unwrapBlock(ifStatement.getThenBranch());
terminatingStatements.addAll(getTerminatingStatements(ArrayUtil.getLastElement(thenStatements)));
PsiStatement elseBranch = ifStatement.getElseBranch();
if (elseBranch != null) {
PsiStatement[] elseStatements = ControlFlowUtils.unwrapBlock(elseBranch);
terminatingStatements.addAll(getTerminatingStatements(ArrayUtil.getLastElement(elseStatements)));
}
} else {
terminatingStatements.add(last);
}
return terminatingStatements;
}
}
@@ -0,0 +1,18 @@
// "Remove redundant 'close()'" "true-preview"
class MyAutoCloseable implements AutoCloseable {
@Override
public void close() {
}
}
class RemoveTry {
public static void main(String[] args) {
try(MyAutoCloseable ac = new MyAutoCloseable()) {
System.out.println("Number of parameters?");
if (args.length == 0) {
}
}
}
}
@@ -0,0 +1,21 @@
// "Remove redundant 'close()'" "true-preview"
class MyAutoCloseable implements AutoCloseable {
@Override
public void close() {
}
}
class RemoveTry {
public static void main(String[] args) {
try(MyAutoCloseable ac = new MyAutoCloseable()) {
if (args.length == 0) {
System.out.println("No parameters");
} else if (args.length == 1) {
System.out.println("One parameter: " + args[0]);
} else if (args.length > 1) {
}
}
}
}
@@ -0,0 +1,17 @@
// "Remove redundant 'close()'" "true-preview"
class MyAutoCloseable implements AutoCloseable {
@Override
public void close() {
}
}
class RemoveTry {
public static void main(String[] args) {
try(MyAutoCloseable ac = new MyAutoCloseable()) {
System.out.println("Number of parameters?");
if (args.length == 0) ac.close<caret>();
}
}
}
@@ -0,0 +1,22 @@
// "Remove redundant 'close()'" "true-preview"
class MyAutoCloseable implements AutoCloseable {
@Override
public void close() {
}
}
class RemoveTry {
public static void main(String[] args) {
try(MyAutoCloseable ac = new MyAutoCloseable()) {
if (args.length == 0) {
System.out.println("No parameters");
ac.close<caret>();
} else if (args.length == 1) {
System.out.println("One parameter: " + args[0]);
} else if (args.length > 1) {
}
}
}
}
@@ -0,0 +1,21 @@
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
class MyAutoCloseable implements AutoCloseable {
@Override
public void close() {
}
}
class C {
public static void main(String[] args) {
try(MyAutoCloseable ac = new MyAutoCloseable()) {
if (args.length == 0) {
System.out.println("No parameters");
<warning descr="Redundant 'close()'">ac.close();</warning>
} else if (args.length == 1) {
System.out.println("One parameter: " + args[0]);
} else if (args.length > 1) {
}
}
}
}
@@ -6,8 +6,7 @@ import com.intellij.codeInspection.LocalInspectionTool;
import com.intellij.codeInspection.RedundantExplicitCloseInspection;
import org.jetbrains.annotations.NotNull;
public class RedundantExplicitCloseInspectionTest extends LightQuickFixParameterizedTestCase {
public class RedundantExplicitCloseFixTest extends LightQuickFixParameterizedTestCase {
@Override
protected LocalInspectionTool @NotNull [] configureLocalInspectionTools() {
return new LocalInspectionTool[]{
@@ -0,0 +1,23 @@
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.java.codeInspection;
import com.intellij.JavaTestUtil;
import com.intellij.codeInspection.InspectionProfileEntry;
import com.intellij.codeInspection.RedundantExplicitCloseInspection;
import com.siyeh.ig.LightJavaInspectionTestCase;
import org.jetbrains.annotations.Nullable;
public class RedundantExplicitCloseInspectionTest extends LightJavaInspectionTestCase {
@Override
protected String getTestDataPath() {
return JavaTestUtil.getJavaTestDataPath() + "/inspection/redundantExplicitClose/";
}
@Nullable
@Override
protected InspectionProfileEntry getInspection() {
return new RedundantExplicitCloseInspection();
}
public void testRedundantExplicitClose() { doTest(); }
}