diff --git a/java/java-impl-inspections/src/com/intellij/codeInspection/RedundantExplicitCloseInspection.java b/java/java-impl-inspections/src/com/intellij/codeInspection/RedundantExplicitCloseInspection.java index fae81166b5e6..6d96e4232bec 100644 --- a/java/java-impl-inspections/src/com/intellij/codeInspection/RedundantExplicitCloseInspection.java +++ b/java/java-impl-inspections/src/com/intellij/codeInspection/RedundantExplicitCloseInspection.java @@ -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 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 getTerminatingStatements(@Nullable PsiStatement last) { + if (last == null) return Collections.emptyList(); + List 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; } } diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantExplicitClose/afterLoneIf.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantExplicitClose/afterLoneIf.java new file mode 100644 index 000000000000..c2181cb39b0a --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantExplicitClose/afterLoneIf.java @@ -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) { + } + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantExplicitClose/afterSubStatement.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantExplicitClose/afterSubStatement.java new file mode 100644 index 000000000000..216754d76e07 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantExplicitClose/afterSubStatement.java @@ -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) { + } + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantExplicitClose/beforeLoneIf.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantExplicitClose/beforeLoneIf.java new file mode 100644 index 000000000000..9a407432f44e --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantExplicitClose/beforeLoneIf.java @@ -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(); + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantExplicitClose/beforeSubStatement.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantExplicitClose/beforeSubStatement.java new file mode 100644 index 000000000000..4e4e657faeff --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantExplicitClose/beforeSubStatement.java @@ -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(); + } else if (args.length == 1) { + System.out.println("One parameter: " + args[0]); + } else if (args.length > 1) { + } + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/redundantExplicitClose/RedundantExplicitClose.java b/java/java-tests/testData/inspection/redundantExplicitClose/RedundantExplicitClose.java new file mode 100644 index 000000000000..68cf7736779b --- /dev/null +++ b/java/java-tests/testData/inspection/redundantExplicitClose/RedundantExplicitClose.java @@ -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"); + ac.close(); + } else if (args.length == 1) { + System.out.println("One parameter: " + args[0]); + } else if (args.length > 1) { + } + } + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/impl/quickfix/RedundantExplicitCloseInspectionTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/impl/quickfix/RedundantExplicitCloseFixTest.java similarity index 88% rename from java/java-tests/testSrc/com/intellij/codeInsight/daemon/impl/quickfix/RedundantExplicitCloseInspectionTest.java rename to java/java-tests/testSrc/com/intellij/codeInsight/daemon/impl/quickfix/RedundantExplicitCloseFixTest.java index 19185d229f52..1e7593b464f0 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/impl/quickfix/RedundantExplicitCloseInspectionTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/impl/quickfix/RedundantExplicitCloseFixTest.java @@ -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[]{ diff --git a/java/java-tests/testSrc/com/intellij/java/codeInspection/RedundantExplicitCloseInspectionTest.java b/java/java-tests/testSrc/com/intellij/java/codeInspection/RedundantExplicitCloseInspectionTest.java new file mode 100644 index 000000000000..edffe5a25fb4 --- /dev/null +++ b/java/java-tests/testSrc/com/intellij/java/codeInspection/RedundantExplicitCloseInspectionTest.java @@ -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(); } +} \ No newline at end of file