diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/SwitchBlockHighlightingModel.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/SwitchBlockHighlightingModel.java index 78f50165774e..1a269e891ee6 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/SwitchBlockHighlightingModel.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/SwitchBlockHighlightingModel.java @@ -783,11 +783,12 @@ public class SwitchBlockHighlightingModel { @NotNull List elements, @NotNull List results) { Set missingClasses; + Map patternClasses = null; if (elements.isEmpty()) { missingClasses = Collections.emptySet(); } else { - Map patternClasses = new HashMap<>(); + patternClasses = new HashMap<>(); for (PsiCaseLabelElement element : elements) { PsiPattern patternLabelElement = ObjectUtils.tryCast(element, PsiPattern.class); if (patternLabelElement == null) continue; @@ -821,15 +822,42 @@ public class SwitchBlockHighlightingModel { } HighlightInfo info = createCompletenessInfoForSwitch(!elements.isEmpty()); if (!missingClasses.isEmpty()) { + List allNames = findAllNames(elements, missingClasses, patternClasses); Set missingCases = new SmartHashSet<>(); missingClasses.forEach(aClass -> missingCases.add(aClass.getQualifiedName())); - IntentionAction fix = - getFixFactory().createAddMissingSealedClassBranchesFix(myBlock, missingCases, StreamEx.of(missingCases).toList()); + IntentionAction fix = getFixFactory().createAddMissingSealedClassBranchesFix(myBlock, missingCases, allNames); QuickFixAction.registerQuickFixAction(info, fix); } results.add(info); } + @NotNull + private static List findAllNames(@NotNull List elements, + @NotNull Set missingClasses, + @NotNull Map patternClasses) { + List result = new ArrayList<>(); + elements.forEach(element -> result.add(element.getText())); + for (PsiClass aClass : missingClasses) { + String className = aClass.getQualifiedName(); + PsiPattern pattern = patternClasses.get(aClass); + if (pattern != null) { + result.add(result.lastIndexOf(pattern.getText()) + 1, className); + } + else { + pattern = + StreamEx.of(elements).select(PsiPattern.class).findFirst(who -> JavaPsiPatternUtil.dominates(who, TypeUtils.getType(aClass))) + .orElse(null); + if (pattern != null) { + result.add(result.indexOf(pattern.getText()), aClass.getQualifiedName()); + } + else { + result.add(aClass.getQualifiedName()); + } + } + } + return result; + } + @NotNull private static Collection getPermittedClasses(@NotNull PsiClass psiClass) { PsiReferenceList permitsList = psiClass.getPermitsList(); diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/createMissingSealedClassSwitchBranches/after1.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/createMissingSealedClassSwitchBranches/after1.java deleted file mode 100644 index de5fd87d7911..000000000000 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/createMissingSealedClassSwitchBranches/after1.java +++ /dev/null @@ -1,21 +0,0 @@ -// "Create missing switch branch 'Sub2'" "true" -sealed abstract class I { -} - -final class Sub1 extends I { -} - -final class Sub2 extends I { -} - -class Test { - void testI(I i) { - switch (i) { - case Sub1 s1: - System.out.println("ok"); - break; - case Sub2 sub2: - break; - } - } -} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/createMissingSealedClassSwitchBranches/after3.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/createMissingSealedClassSwitchBranches/after3.java deleted file mode 100644 index ea319f6ff3fe..000000000000 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/createMissingSealedClassSwitchBranches/after3.java +++ /dev/null @@ -1,18 +0,0 @@ -// "Create missing branches: 'Scratch.X', and 'Scratch.Parent.X'" "true" -class Scratch { - sealed interface Parent { - record X() implements Parent {} - } - record X() implements Parent {} - record Y() implements Parent {} - - void test(Parent parent) { - switch (parent) { - case Y y -> {} - case X x -> { - } - case Parent.X x -> { - } - } - } -} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/createMissingSealedClassSwitchBranches/afterDeepSealedHierarchy.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/createMissingSealedClassSwitchBranches/afterDeepSealedHierarchy.java new file mode 100644 index 000000000000..d3e61928d537 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/createMissingSealedClassSwitchBranches/afterDeepSealedHierarchy.java @@ -0,0 +1,19 @@ +// "Create missing switch branch 'Ab'" "true" +sealed interface A {} +sealed interface Aa extends A {} +final class Aaa implements Aa {} +final class Aab implements Aa {} +final class Ab implements A {} + +public class Test { + void test(A a) { + switch (a) { + case Aaa x -> { + } + case Aab x -> { + } + case Ab ab -> { + } + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/createMissingSealedClassSwitchBranches/afterDominatingPatternPresent.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/createMissingSealedClassSwitchBranches/afterDominatingPatternPresent.java new file mode 100644 index 000000000000..5677fb88ecfb --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/createMissingSealedClassSwitchBranches/afterDominatingPatternPresent.java @@ -0,0 +1,18 @@ +// "Create missing switch branch 'Sub1'" "true" +sealed interface I {} +sealed interface J extends I {} +final class Sub1 implements I, J {} +final class Sub2 implements I {} + +class Test { + void test(I i) { + switch (i) { + case Sub2 sub2: + break; + case Sub1 sub1: + break; + case J j: + break; + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/createMissingSealedClassSwitchBranches/afterGuardedAndDominatingPatternsPresent.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/createMissingSealedClassSwitchBranches/afterGuardedAndDominatingPatternsPresent.java new file mode 100644 index 000000000000..e5119a11819c --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/createMissingSealedClassSwitchBranches/afterGuardedAndDominatingPatternsPresent.java @@ -0,0 +1,20 @@ +// "Create missing switch branch 'Sub1'" "true" +sealed interface I {} +sealed interface J extends I {} +final class Sub1 implements I, J {} +final class Sub2 implements I {} + +class Test { + void test(I i) { + switch (i) { + case Sub1 sub1 && Math.random() > 0.5: + break; + case Sub1 sub1: + break; + case Sub2 sub2: + break; + case J j: + break; + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/createMissingSealedClassSwitchBranches/after2.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/createMissingSealedClassSwitchBranches/afterGuardedPatternPresent.java similarity index 79% rename from java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/createMissingSealedClassSwitchBranches/after2.java rename to java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/createMissingSealedClassSwitchBranches/afterGuardedPatternPresent.java index e28f5f63c84b..74608fa69ffa 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/createMissingSealedClassSwitchBranches/after2.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/createMissingSealedClassSwitchBranches/afterGuardedPatternPresent.java @@ -6,12 +6,12 @@ final class Sub2 implements I {} class Test { void test(I i) { switch (i) { - case Sub1 sub1 && sub1 != null: - break; - case Sub2 sub2: + case Sub1 sub1 && Math.random() > 0.5: break; case Sub1 sub1: break; + case Sub2 sub2: + break; } } } \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/createMissingSealedClassSwitchBranches/afterIdenticalGuardedPatterns.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/createMissingSealedClassSwitchBranches/afterIdenticalGuardedPatterns.java new file mode 100644 index 000000000000..cff0a89936cd --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/createMissingSealedClassSwitchBranches/afterIdenticalGuardedPatterns.java @@ -0,0 +1,17 @@ +// "Create missing switch branch 'Sub1'" "true" +sealed interface I {} +final class Sub1 implements I {} +final class Sub2 implements I {} + +class Test { + void test(I i) { + switch (i) { + case Sub1 sub1 && Math.random() > 0.5 -> {} + case Sub1 sub1 && Math.random() > 0.1 -> {} + case Sub1 sub1 && Math.random() > 0.5 -> {} + case Sub1 sub1 -> { + } + case Sub2 sub2 -> {} + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/createMissingSealedClassSwitchBranches/before1.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/createMissingSealedClassSwitchBranches/before1.java deleted file mode 100644 index 7f2d6832cc59..000000000000 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/createMissingSealedClassSwitchBranches/before1.java +++ /dev/null @@ -1,19 +0,0 @@ -// "Create missing switch branch 'Sub2'" "true" -sealed abstract class I { -} - -final class Sub1 extends I { -} - -final class Sub2 extends I { -} - -class Test { - void testI(I i) { - switch (i) { - case Sub1 s1: - System.out.println("ok"); - break; - } - } -} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/createMissingSealedClassSwitchBranches/before3.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/createMissingSealedClassSwitchBranches/before3.java deleted file mode 100644 index ea7de7e5e9c0..000000000000 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/createMissingSealedClassSwitchBranches/before3.java +++ /dev/null @@ -1,14 +0,0 @@ -// "Create missing branches: 'Scratch.X', and 'Scratch.Parent.X'" "true" -class Scratch { - sealed interface Parent { - record X() implements Parent {} - } - record X() implements Parent {} - record Y() implements Parent {} - - void test(Parent parent) { - switch (parent) { - case Y y -> {} - } - } -} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/createMissingSealedClassSwitchBranches/beforeDeepSealedHierarchy.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/createMissingSealedClassSwitchBranches/beforeDeepSealedHierarchy.java new file mode 100644 index 000000000000..8723ce50b586 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/createMissingSealedClassSwitchBranches/beforeDeepSealedHierarchy.java @@ -0,0 +1,17 @@ +// "Create missing switch branch 'Ab'" "true" +sealed interface A {} +sealed interface Aa extends A {} +final class Aaa implements Aa {} +final class Aab implements Aa {} +final class Ab implements A {} + +public class Test { + void test(A a) { + switch (a) { + case Aaa x -> { + } + case Aab x -> { + } + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/createMissingSealedClassSwitchBranches/beforeDominatingPatternPresent.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/createMissingSealedClassSwitchBranches/beforeDominatingPatternPresent.java new file mode 100644 index 000000000000..4bc6ec26cf8e --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/createMissingSealedClassSwitchBranches/beforeDominatingPatternPresent.java @@ -0,0 +1,16 @@ +// "Create missing switch branch 'Sub1'" "true" +sealed interface I {} +sealed interface J extends I {} +final class Sub1 implements I, J {} +final class Sub2 implements I {} + +class Test { + void test(I i) { + switch (i) { + case Sub2 sub2: + break; + case J j: + break; + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/createMissingSealedClassSwitchBranches/beforeGuardedAndDominatingPatternsPresent.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/createMissingSealedClassSwitchBranches/beforeGuardedAndDominatingPatternsPresent.java new file mode 100644 index 000000000000..7847bedb9809 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/createMissingSealedClassSwitchBranches/beforeGuardedAndDominatingPatternsPresent.java @@ -0,0 +1,18 @@ +// "Create missing switch branch 'Sub1'" "true" +sealed interface I {} +sealed interface J extends I {} +final class Sub1 implements I, J {} +final class Sub2 implements I {} + +class Test { + void test(I i) { + switch (i) { + case Sub1 sub1 && Math.random() > 0.5: + break; + case Sub2 sub2: + break; + case J j: + break; + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/createMissingSealedClassSwitchBranches/before2.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/createMissingSealedClassSwitchBranches/beforeGuardedPatternPresent.java similarity index 85% rename from java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/createMissingSealedClassSwitchBranches/before2.java rename to java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/createMissingSealedClassSwitchBranches/beforeGuardedPatternPresent.java index 9e4a9cfe18cd..10296cdd9864 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/createMissingSealedClassSwitchBranches/before2.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/createMissingSealedClassSwitchBranches/beforeGuardedPatternPresent.java @@ -6,7 +6,7 @@ final class Sub2 implements I {} class Test { void test(I i) { switch (i) { - case Sub1 sub1 && sub1 != null: + case Sub1 sub1 && Math.random() > 0.5: break; case Sub2 sub2: break; diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/createMissingSealedClassSwitchBranches/beforeIdenticalGuardedPatterns.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/createMissingSealedClassSwitchBranches/beforeIdenticalGuardedPatterns.java new file mode 100644 index 000000000000..1137d9729eb7 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/createMissingSealedClassSwitchBranches/beforeIdenticalGuardedPatterns.java @@ -0,0 +1,15 @@ +// "Create missing switch branch 'Sub1'" "true" +sealed interface I {} +final class Sub1 implements I {} +final class Sub2 implements I {} + +class Test { + void test(I i) { + switch (i) { + case Sub1 sub1 && Math.random() > 0.5 -> {} + case Sub1 sub1 && Math.random() > 0.1 -> {} + case Sub1 sub1 && Math.random() > 0.5 -> {} + case Sub2 sub2 -> {} + } + } +} \ No newline at end of file diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/fixes/CreateSealedClassMissingSwitchBranchesFix.java b/plugins/InspectionGadgets/src/com/siyeh/ig/fixes/CreateSealedClassMissingSwitchBranchesFix.java index f33cbe3f2a7a..5510a0a59a23 100644 --- a/plugins/InspectionGadgets/src/com/siyeh/ig/fixes/CreateSealedClassMissingSwitchBranchesFix.java +++ b/plugins/InspectionGadgets/src/com/siyeh/ig/fixes/CreateSealedClassMissingSwitchBranchesFix.java @@ -2,11 +2,9 @@ package com.siyeh.ig.fixes; import com.intellij.codeInsight.intention.FileModifier; -import com.intellij.psi.PsiClass; -import com.intellij.psi.PsiFile; -import com.intellij.psi.PsiSwitchBlock; -import com.intellij.psi.PsiSwitchLabelStatementBase; +import com.intellij.psi.*; import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.util.containers.ContainerUtil; import com.siyeh.InspectionGadgetsBundle; import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NotNull; @@ -46,6 +44,10 @@ public class CreateSealedClassMissingSwitchBranchesFix extends CreateMissingSwit @Override protected @NotNull Function> getCaseExtractor() { - return label -> Collections.emptyList(); + return label -> { + PsiCaseLabelElementList list = label.getCaseLabelElementList(); + if (list == null) return Collections.emptyList(); + return ContainerUtil.map(list.getElements(), PsiCaseLabelElement::getText); + }; } } diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/psiutils/CreateSwitchBranchesUtil.java b/plugins/InspectionGadgets/src/com/siyeh/ig/psiutils/CreateSwitchBranchesUtil.java index 8a6ea9ae8c3f..b1b1824943b6 100644 --- a/plugins/InspectionGadgets/src/com/siyeh/ig/psiutils/CreateSwitchBranchesUtil.java +++ b/plugins/InspectionGadgets/src/com/siyeh/ig/psiutils/CreateSwitchBranchesUtil.java @@ -82,7 +82,7 @@ public final class CreateSwitchBranchesUtil { return PsiTreeUtil.getChildrenOfTypeAsList(block.getBody(), PsiSwitchLabelStatementBase.class); } Map prevToNext = - StreamEx.of(allNames).pairMap(Couple::of).toMap(c -> c.getFirst(), c -> c.getSecond()); + StreamEx.of(allNames).distinct().pairMap(Couple::of).toMap(c -> c.getFirst(), c -> c.getSecond()); List missingLabels = StreamEx.of(allNames).filter(missingNames::contains).toList(); String nextLabel = getNextLabel(prevToNext, missingLabels); PsiElement bodyElement = body.getFirstBodyElement(); @@ -90,8 +90,8 @@ public final class CreateSwitchBranchesUtil { while (bodyElement != null) { PsiSwitchLabelStatementBase label = ObjectUtils.tryCast(bodyElement, PsiSwitchLabelStatementBase.class); if (label != null) { - List constants = caseExtractor.apply(label); - while (nextLabel != null && constants.contains(nextLabel)) { + List caseLabelNames = caseExtractor.apply(label); + while (nextLabel != null && caseLabelNames.contains(nextLabel)) { addedLabels.add(addSwitchLabelStatementBefore(missingLabels.get(0), bodyElement, switchBlock, isRuleBasedFormat, isPatternsGenerated)); missingLabels.remove(0); if (missingLabels.isEmpty()) {