diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/TryWithIdenticalCatchesInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/TryWithIdenticalCatchesInspection.java index c92cc4adb2e9..89e2a02b86c4 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/TryWithIdenticalCatchesInspection.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/migration/TryWithIdenticalCatchesInspection.java @@ -19,22 +19,26 @@ import com.intellij.codeInspection.ProblemDescriptor; import com.intellij.openapi.project.Project; import com.intellij.pom.java.JavaFeature; import com.intellij.psi.*; +import com.intellij.psi.codeStyle.CodeStyleManager; +import com.intellij.psi.codeStyle.JavaCodeStyleManager; import com.intellij.psi.search.LocalSearchScope; -import com.intellij.psi.util.TypeConversionUtil; import com.intellij.refactoring.extractMethod.InputVariables; import com.intellij.refactoring.util.duplicates.DuplicatesFinder; import com.intellij.refactoring.util.duplicates.Match; import com.intellij.refactoring.util.duplicates.ReturnValue; +import com.intellij.util.ArrayUtil; +import com.intellij.util.ObjectUtils; +import com.intellij.util.containers.ContainerUtil; import com.siyeh.InspectionGadgetsBundle; import com.siyeh.ig.BaseInspection; import com.siyeh.ig.BaseInspectionVisitor; import com.siyeh.ig.InspectionGadgetsFix; 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.Iterator; import java.util.List; /** @@ -88,106 +92,242 @@ public class TryWithIdenticalCatchesInspection extends BaseInspection { @Override public void visitTryStatement(PsiTryStatement statement) { super.visitTryStatement(statement); - final PsiCatchSection[] catchSections = statement.getCatchSections(); - if (catchSections.length < 2) { - return; - } - final PsiParameter[] parameters = statement.getCatchBlockParameters(); - if (catchSections.length != parameters.length) { - return; - } - final boolean[] duplicates = new boolean[catchSections.length]; - for (int i = 0; i < catchSections.length - 1; i++) { - final PsiCatchSection catchSection = catchSections[i]; - final PsiCodeBlock catchBlock = catchSection.getCatchBlock(); - if (catchBlock == null) { - continue; - } - final PsiParameter parameter = catchSection.getParameter(); - if (parameter == null) { - continue; - } - final DuplicatesFinder finder = buildDuplicatesFinder(catchBlock, parameter); - for (int j = i + 1; j < catchSections.length; j++) { - if (duplicates[j]) { - continue; - } - final PsiCatchSection otherSection = catchSections[j]; - final PsiCodeBlock otherCatchBlock = otherSection.getCatchBlock(); - if (otherCatchBlock == null) { - continue; - } - final PsiParameter otherParameter = otherSection.getParameter(); - if (otherParameter == null) { - continue; - } - final Match match = finder.isDuplicate(otherCatchBlock, true); - if (match == null) { - continue; - } - final DuplicatesFinder reverseFinder = buildDuplicatesFinder(otherCatchBlock, otherParameter); - final Match otherMatch = reverseFinder.isDuplicate(catchBlock, true); - if (otherMatch == null) { - continue; - } - final ReturnValue returnValue = match.getReturnValue(); - final ReturnValue otherReturnValue = otherMatch.getReturnValue(); - if (returnValue == null) { - if (otherReturnValue != null) { - continue; - } - } - else if (!returnValue.isEquivalent(otherReturnValue)) { - continue; - } - if (j > i ? !canCollapse(parameters, i, j) : !canCollapse(parameters, j, i)) { - continue; - } - final PsiJavaToken rParenth = otherSection.getRParenth(); - if (rParenth != null) { - registerErrorAtOffset(otherSection, 0, rParenth.getStartOffsetInParent() + 1, catchSection.getParameter().getType(), - Integer.valueOf(i), Integer.valueOf(j)); - } - duplicates[i] = true; - duplicates[j] = true; + + final CatchSectionWrapper[] sections = CatchSectionWrapper.createWrappers(statement); + if (sections == null) return; + + final CatchSectionIndices[] catchSectionIndices = getCatchSectionIndices(sections); + if (catchSectionIndices == null) return; + + for (int index = 0; index < catchSectionIndices.length; index++) { + int collapseIntoIndex = catchSectionIndices[index].myCollapseIntoIndex; + if (collapseIntoIndex >= 0) { + registerProblem(sections, index, collapseIntoIndex); } } } + private void registerProblem(@NotNull CatchSectionWrapper[] sections, int at, int collapseIntoIndex) { + final PsiCatchSection section = sections[at].myCatchSection; + final PsiJavaToken rParenth = section.getRParenth(); + if (rParenth != null) { + registerErrorAtOffset(section, 0, rParenth.getStartOffsetInParent() + 1, sections[collapseIntoIndex].myParameter.getType()); + } + } + } + + @Nullable + static CatchSectionIndices[] getCatchSectionIndices(@NotNull CatchSectionWrapper[] sections) { + final CatchSectionIndices[] indices = new CatchSectionIndices[sections.length]; + for (int index = 0; index < sections.length; index++) { + indices[index] = new CatchSectionIndices(index); + } + + boolean duplicateFound = false; + for (int from = 0; from < sections.length - 1; from++) { + if (indices[from].myHasDuplicate) continue; + final CatchSectionWrapper section = sections[from]; + if (section == null) continue; + + for (int to = from + 1; to < sections.length; to++) { + if (indices[to].myHasDuplicate) continue; + final CatchSectionWrapper otherSection = sections[to]; + if (otherSection == null || !section.isDuplicate(otherSection)) continue; + + indices[from].addDuplicate(indices[to]); + duplicateFound = true; + } + } + if (!duplicateFound) return null; + + final boolean[][] canSwap = new boolean[sections.length][sections.length]; + for (int from = 0; from < sections.length; from++) { + for (int to = from + 1; to < sections.length; to++) { + canSwap[from][to] = canSwap[to][from] = sections[from] != null && sections[from].canSwapWith(sections[to]); + } + } + + for (int index = 0; index < sections.length; index++) { + indices[index].computeInsertionRange(canSwap); + } + + for (CatchSectionIndices idx : indices) { + final int[] duplicates = idx.myDuplicates; + if (duplicates == null) continue; + + for (int from : duplicates) { + for (int to : duplicates) { + indices[to].tryCollapseInto(indices[from]); + } + } + } + return indices; + } + + private static class CatchSectionIndices { + final int myIndex; + int myCanInsertBefore = -1; + int myCanInsertAfter = -1; + + boolean myHasDuplicate; + int[] myDuplicates; + int myCollapseIntoIndex = -1; + + CatchSectionIndices(int index) { + myIndex = index; + } + + void addDuplicate(CatchSectionIndices duplicate) { + if (myDuplicates == null) { + myDuplicates = new int[]{myIndex, duplicate.myIndex}; + myHasDuplicate = true; + } + else { + myDuplicates = ArrayUtil.append(myDuplicates, duplicate.myIndex); + } + duplicate.myHasDuplicate = true; + } + + void computeInsertionRange(@NotNull boolean[][] canSwap) { + boolean[] canSwapWith = canSwap[myIndex]; + + for (int before = myIndex; ; before--) { + if (before - 1 < 0 || !canSwapWith[before - 1]) { + myCanInsertBefore = before; + break; + } + } + + for (int after = myIndex; ; after++) { + if (after + 1 >= canSwapWith.length || !canSwapWith[after + 1]) { + myCanInsertAfter = after; + break; + } + } + } + + public void tryCollapseInto(CatchSectionIndices collapseInto) { + if (myCollapseIntoIndex < 0 && myIndex > collapseInto.myIndex && myCanInsertBefore <= collapseInto.myCanInsertAfter + 1) { + myCollapseIntoIndex = collapseInto.myIndex; + } + } + } + + private static class CatchSectionWrapper { + @NotNull final PsiCatchSection myCatchSection; + @NotNull final PsiCodeBlock myCodeBlock; + @NotNull final PsiParameter myParameter; + @NotNull final List myTypes; + @NotNull final DuplicatesFinder myFinder; + + private CatchSectionWrapper(@NotNull PsiCatchSection catchSection, + @NotNull PsiCodeBlock codeBlock, + @NotNull PsiParameter parameter, + @NotNull List types, + @NotNull DuplicatesFinder finder) { + myCatchSection = catchSection; + myCodeBlock = codeBlock; + myParameter = parameter; + myTypes = types; + myFinder = finder; + } + + boolean isDuplicate(@NotNull CatchSectionWrapper section) { + final Match match = findDuplicate(section); + if (match == null) { + return false; + } + final Match otherMatch = section.findDuplicate(this); + if (otherMatch == null) { + return false; + } + final ReturnValue returnValue = match.getReturnValue(); + final ReturnValue otherReturnValue = otherMatch.getReturnValue(); + if (returnValue == null) { + return otherReturnValue == null; + } + return returnValue.isEquivalent(otherReturnValue); + } + + private Match findDuplicate(@NotNull CatchSectionWrapper section) { + return myFinder.isDuplicate(section.myCodeBlock, true); + } + + boolean canSwapWith(@Nullable CatchSectionWrapper section) { + if (section == null) return false; + for (PsiClassType type : myTypes) { + for (PsiClassType otherType : section.myTypes) { + if (type.isAssignableFrom(otherType) || otherType.isAssignableFrom(type)) { + return false; + } + } + } + return true; + } + + @Nullable + static CatchSectionWrapper[] createWrappers(@NotNull PsiTryStatement statement) { + final PsiCatchSection[] catchSections = statement.getCatchSections(); + if (catchSections.length < 2) { + return null; + } + final PsiParameter[] parameters = statement.getCatchBlockParameters(); + if (catchSections.length != parameters.length) { + return null; + } + final CatchSectionWrapper[] sections = new CatchSectionWrapper[catchSections.length]; + for (int i = 0; i < sections.length; i++) { + sections[i] = createWrapper(catchSections[i]); + } + return sections; + } + + @Nullable + private static CatchSectionWrapper createWrapper(@NotNull PsiCatchSection catchSection) { + final PsiParameter parameter = catchSection.getParameter(); + final PsiCodeBlock codeBlock = catchSection.getCatchBlock(); + if (parameter != null && codeBlock != null) { + final List types = getClassTypes(parameter.getType()); + if (types != null) { + final DuplicatesFinder finder = buildDuplicatesFinder(codeBlock, parameter); + return new CatchSectionWrapper(catchSection, codeBlock, parameter, types, finder); + } + } + return null; + } + + @Nullable + private static List getClassTypes(@Nullable PsiType type) { + if (type instanceof PsiClassType) { + return Collections.singletonList((PsiClassType)type); + } + if (type instanceof PsiDisjunctionType) { + final List disjunctions = ((PsiDisjunctionType)type).getDisjunctions(); + if (!disjunctions.isEmpty()) { + final List classTypes = ContainerUtil.mapNotNull(disjunctions, t -> ObjectUtils.tryCast(t, PsiClassType.class)); + if (classTypes.size() == disjunctions.size()) { + return classTypes; + } + } + } + return null; + } + + @NotNull private static DuplicatesFinder buildDuplicatesFinder(@NotNull PsiCodeBlock catchBlock, @NotNull PsiParameter parameter) { final InputVariables inputVariables = new InputVariables(Collections.singletonList(parameter), parameter.getProject(), new LocalSearchScope(catchBlock), false); return new DuplicatesFinder(new PsiElement[]{catchBlock}, inputVariables, null, Collections.emptyList()); } - - private static boolean canCollapse(PsiParameter[] parameters, int index1, int index2) { - if (index2 <= index1) throw new IllegalArgumentException(); - final PsiType type = parameters[index2].getType(); - for (int i = index1 + 1; i < index2; i++) { - final PsiType otherType = parameters[i].getType(); - if (TypeConversionUtil.isAssignable(type, otherType)) { - return false; - } - } - return true; - } } @Override protected InspectionGadgetsFix buildFix(Object... infos) { - return new CollapseCatchSectionsFix(((Integer)infos[1]).intValue(), ((Integer)infos[2]).intValue()); + return new CollapseCatchSectionsFix(); } private static class CollapseCatchSectionsFix extends InspectionGadgetsFix { - private final int myCollapseIntoIndex; - private final int mySectionIndex; - - public CollapseCatchSectionsFix(int collapseIntoIndex, int sectionIndex) { - myCollapseIntoIndex = collapseIntoIndex; - mySectionIndex = sectionIndex; - } - @Override @NotNull public String getFamilyName() { @@ -197,68 +337,60 @@ public class TryWithIdenticalCatchesInspection extends BaseInspection { @Override protected void doFix(Project project, ProblemDescriptor descriptor) { // smart psi pointer lost correct catch section when multiple catch sections were collapsed in batch mode - // so use index of catch section to retrieve it instead. - final PsiTryStatement tryStatement = (PsiTryStatement)descriptor.getPsiElement().getParent(); - final PsiCatchSection[] catchSections = tryStatement.getCatchSections(); - if (myCollapseIntoIndex >= catchSections.length || mySectionIndex >= catchSections.length) { - return; // something has gone stale - } - final PsiCatchSection collapseInto = catchSections[myCollapseIntoIndex]; - final PsiCatchSection section = catchSections[mySectionIndex]; - final PsiParameter parameter1 = collapseInto.getParameter(); - final PsiParameter parameter2 = section.getParameter(); - if (parameter1 == null || parameter2 == null) { - return; - } - final PsiType type1 = parameter1.getType(); - final PsiType type2 = parameter2.getType(); - if (TypeConversionUtil.isAssignable(type1, type2)) { - section.delete(); - return; - } - else if (TypeConversionUtil.isAssignable(type2, type1)) { - collapseInto.delete(); - return; - } - final List types = new ArrayList<>(); - collectDisjunctTypes(type1, types); - collectDisjunctTypes(type2, types); - final StringBuilder typeText = new StringBuilder(); - for (PsiType type : types) { - if (typeText.length() > 0) { - typeText.append(" | "); + // so we need to re-calculate everything based on what exists at this point + final PsiCatchSection catchSection = (PsiCatchSection)descriptor.getPsiElement(); + final PsiTryStatement tryStatement = (PsiTryStatement)catchSection.getParent(); + + final CatchSectionWrapper[] sections = CatchSectionWrapper.createWrappers(tryStatement); + if (sections == null) return; + + int sectionIndex = getSectionIndex(sections, catchSection); + if (sectionIndex < 0) return; + + CatchSectionWrapper duplicateSection = sections[sectionIndex]; + if (duplicateSection == null) return; + + final CatchSectionIndices[] duplicatesIndices = getCatchSectionIndices(sections); + if (duplicatesIndices == null) return; + + final int collapseIntoIndex = duplicatesIndices[sectionIndex].myCollapseIntoIndex; + if (collapseIntoIndex < 0) return; + + final CatchSectionWrapper collapseIntoSection = sections[collapseIntoIndex]; + if (collapseIntoSection == null) return; + + final PsiTypeElement collapseIntoTypeElement = collapseIntoSection.myParameter.getTypeElement(); + if (collapseIntoTypeElement == null) return; + + final List parameterTypes = new ArrayList<>(collapseIntoSection.myTypes); + parameterTypes.addAll(duplicateSection.myTypes); + + final List filteredTypes = PsiDisjunctionType.flattenAndRemoveDuplicates(parameterTypes); + final PsiType disjunction = PsiDisjunctionType.createDisjunction(filteredTypes, tryStatement.getManager()); + final PsiTypeElement newTypeElement = JavaPsiFacade.getElementFactory(project).createTypeElement(disjunction); + + JavaCodeStyleManager.getInstance(project).shortenClassReferences(collapseIntoTypeElement.replace(newTypeElement)); + + int insertBeforeIndex = duplicatesIndices[sectionIndex].myCanInsertBefore; + if (collapseIntoIndex < insertBeforeIndex) { + // We can't leave the merged 'catch' section at collapseIntoIndex because it conflicts with other caught exceptions + final PsiCatchSection[] catchSections = tryStatement.getCatchSections(); + if (insertBeforeIndex < catchSections.length && catchSections[insertBeforeIndex] != null) { + tryStatement.addBefore(collapseIntoSection.myCatchSection, catchSections[insertBeforeIndex]); + collapseIntoSection.myCatchSection.delete(); } - typeText.append(type.getCanonicalText()); } - final PsiTypeElement newTypeElement = - JavaPsiFacade.getElementFactory(project).createTypeElementFromText(typeText.toString(), tryStatement); - final PsiTypeElement typeElement = parameter1.getTypeElement(); - if (typeElement == null) { - return; - } - typeElement.replace(newTypeElement); - section.delete(); + + duplicateSection.myCatchSection.delete(); } - private static void collectDisjunctTypes(PsiType type, List out) { - if (type instanceof PsiDisjunctionType) { - final PsiDisjunctionType disjunctionType = (PsiDisjunctionType)type; - final List disjunctions = disjunctionType.getDisjunctions(); - for (PsiType disjunction : disjunctions) { - collectDisjunctTypes(disjunction, out); - } - return; - } - for (Iterator iterator = out.iterator(); iterator.hasNext(); ) { - final PsiType collectedType = iterator.next(); - if (TypeConversionUtil.isAssignable(type, collectedType)) { - iterator.remove(); - } - else if (TypeConversionUtil.isAssignable(collectedType, type)) { - return; + private static int getSectionIndex(@NotNull CatchSectionWrapper[] sections, @NotNull PsiElement catchSection) { + for (int i = 0; i < sections.length; i++) { + if (sections[i].myCatchSection == catchSection) { + return i; } } - out.add(type); + return -1; } } } diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/errorhandling/try_identical_catches/IdenticalCatchThreeOutOfFour.after.java b/plugins/InspectionGadgets/test/com/siyeh/igtest/errorhandling/try_identical_catches/IdenticalCatchThreeOutOfFour.after.java new file mode 100644 index 000000000000..cd6a343232a8 --- /dev/null +++ b/plugins/InspectionGadgets/test/com/siyeh/igtest/errorhandling/try_identical_catches/IdenticalCatchThreeOutOfFour.after.java @@ -0,0 +1,24 @@ +class Main { + static class Ex1 extends Exception {} + static class Ex2 extends Exception {} + static class Ex3 extends Ex1 {} + + public void test() { + try { + if(Math.random() > 0.5) { + throw new Ex1(); + } + if(Math.random() > 0.5) { + throw new Ex2(); + } + if(Math.random() > 0.5) { + throw new Ex3(); + } + } catch(Ex3 ignored) { + } catch(RuntimeException | Ex2 | Ex1 ex) { + ex.printStackTrace(); + } catch(Error e) { + return; + } + } +} \ No newline at end of file diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/errorhandling/try_identical_catches/IdenticalCatchThreeOutOfFour.java b/plugins/InspectionGadgets/test/com/siyeh/igtest/errorhandling/try_identical_catches/IdenticalCatchThreeOutOfFour.java new file mode 100644 index 000000000000..f04b2aaa49af --- /dev/null +++ b/plugins/InspectionGadgets/test/com/siyeh/igtest/errorhandling/try_identical_catches/IdenticalCatchThreeOutOfFour.java @@ -0,0 +1,33 @@ +class Main { + static class Ex1 extends Exception {} + static class Ex2 extends Exception {} + static class Ex3 extends Ex1 {} + + public void test() { + try { + if(Math.random() > 0.5) { + throw new Ex1(); + } + if(Math.random() > 0.5) { + throw new Ex2(); + } + if(Math.random() > 0.5) { + throw new Ex3(); + } + } + catch(RuntimeException ex) { + ex.printStackTrace(); + } + catch(Ex3 ignored) { + } + catch(Error e) { + return; + } + catch(Ex2 ex) { + ex.printStackTrace(); + } + catch(Ex1 ex) { + ex.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/errorhandling/try_identical_catches/IdenticalCatchUnrelatedExceptions.after.java b/plugins/InspectionGadgets/test/com/siyeh/igtest/errorhandling/try_identical_catches/IdenticalCatchUnrelatedExceptions.after.java new file mode 100644 index 000000000000..928fade4c122 --- /dev/null +++ b/plugins/InspectionGadgets/test/com/siyeh/igtest/errorhandling/try_identical_catches/IdenticalCatchUnrelatedExceptions.after.java @@ -0,0 +1,17 @@ +import java.io.IOException; + +class C { + private static void throwStuff() throws IOException {} + + public static void main(String[] args) { + try { + throwStuff(); + } catch (NumberFormatException nfe) { + System.out.println("ERROR 2:" + nfe.getMessage()); + System.exit(1); + } catch (IOException | IllegalArgumentException ioe) { + System.out.println("ERROR:" + ioe.getMessage()); + System.exit(1); + } + } +} \ No newline at end of file diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/errorhandling/try_identical_catches/IdenticalCatchUnrelatedExceptions.java b/plugins/InspectionGadgets/test/com/siyeh/igtest/errorhandling/try_identical_catches/IdenticalCatchUnrelatedExceptions.java new file mode 100644 index 000000000000..2c6c83e28072 --- /dev/null +++ b/plugins/InspectionGadgets/test/com/siyeh/igtest/errorhandling/try_identical_catches/IdenticalCatchUnrelatedExceptions.java @@ -0,0 +1,20 @@ +import java.io.IOException; + +class C { + private static void throwStuff() throws IOException {} + + public static void main(String[] args) { + try { + throwStuff(); + } catch (IOException ioe) { + System.out.println("ERROR:" + ioe.getMessage()); + System.exit(1); + } catch (NumberFormatException nfe) { + System.out.println("ERROR 2:" + nfe.getMessage()); + System.exit(1); + } catch (IllegalArgumentException iae) { + System.out.println("ERROR:" + iae.getMessage()); + System.exit(1); + } + } +} \ No newline at end of file diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/errorhandling/try_identical_catches/NonDisjunctTypes.after.java b/plugins/InspectionGadgets/test/com/siyeh/igtest/errorhandling/try_identical_catches/NonDisjunctTypes.after.java index 496224ac20ee..c94077a07f1d 100644 --- a/plugins/InspectionGadgets/test/com/siyeh/igtest/errorhandling/try_identical_catches/NonDisjunctTypes.after.java +++ b/plugins/InspectionGadgets/test/com/siyeh/igtest/errorhandling/try_identical_catches/NonDisjunctTypes.after.java @@ -14,8 +14,7 @@ public class NonDisjunctTypes { if(Math.random() > 0.5) { throw new Ex3(); } - } - catch(RuntimeException | Ex1 ex) { + } catch(RuntimeException | Ex1 ex) { ex.printStackTrace(); } } diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/errorhandling/try_identical_catches/TryIdenticalCatches.java b/plugins/InspectionGadgets/test/com/siyeh/igtest/errorhandling/try_identical_catches/TryIdenticalCatches.java index ff838abeae7c..3e8409ebae45 100644 --- a/plugins/InspectionGadgets/test/com/siyeh/igtest/errorhandling/try_identical_catches/TryIdenticalCatches.java +++ b/plugins/InspectionGadgets/test/com/siyeh/igtest/errorhandling/try_identical_catches/TryIdenticalCatches.java @@ -107,7 +107,7 @@ class TryIdenticalCatches { } catch (E4 e) { } catch (E2 e) { } catch (E3 e) { - } catch (E1 e) { + } catch (E1 e) { } } diff --git a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/migration/TryWithIdenticalCatchesTest.java b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/migration/TryWithIdenticalCatchesTest.java index e71da2a94483..4fa3e9b1f4da 100644 --- a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/migration/TryWithIdenticalCatchesTest.java +++ b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/migration/TryWithIdenticalCatchesTest.java @@ -17,8 +17,14 @@ package com.siyeh.ig.migration; import com.intellij.codeInsight.intention.IntentionAction; import com.intellij.openapi.application.PluginPathManager; +import com.intellij.psi.PsiCatchSection; +import com.intellij.psi.PsiTryStatement; +import com.intellij.psi.util.PsiTreeUtil; import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase; +import java.util.ArrayList; +import java.util.List; + /** * @author yole */ @@ -35,12 +41,40 @@ public class TryWithIdenticalCatchesTest extends LightCodeInsightFixtureTestCase highlightTest(); } + public void testIdenticalCatchUnrelatedExceptions() { + doTest(); + } + + public void testIdenticalCatchThreeOutOfFour() { + doTest(true); + } + public void doTest() { + doTest(false); + } + + public void doTest(boolean processAll) { highlightTest(); String name = getTestName(false); - IntentionAction intention = myFixture.findSingleIntention("Collapse 'catch' blocks"); - assertNotNull(intention); - myFixture.launchAction(intention); + if (processAll) { + PsiTryStatement tryStatement = PsiTreeUtil.getParentOfType(myFixture.getElementAtCaret(), PsiTryStatement.class); + assertNotNull("tryStatement", tryStatement); + PsiCatchSection[] catchSections = tryStatement.getCatchSections(); + List intentions = new ArrayList<>(); + for (PsiCatchSection section : catchSections) { + getEditor().getCaretModel().moveToOffset(section.getTextOffset()); + intentions.addAll(myFixture.filterAvailableIntentions("Collapse 'catch' blocks")); + } + assertFalse("intentions.isEmpty", intentions.isEmpty()); + for (IntentionAction intention : intentions) { + myFixture.launchAction(intention); + } + } + else { + IntentionAction intention = myFixture.findSingleIntention("Collapse 'catch' blocks"); + assertNotNull(intention); + myFixture.launchAction(intention); + } myFixture.checkResultByFile("com/siyeh/igtest/errorhandling/try_identical_catches/" + name + ".after.java"); }