[java-dfa] Report unreachable switch branches after 100% reachable branch

Fixes IDEA-294215 Inspection "Constant conditions & exceptions" does not report all unreachable switch labels

GitOrigin-RevId: 6f1e493b6a665221c1978a5c918028638ec23c09
This commit is contained in:
Tagir Valeev
2022-05-18 10:20:33 +00:00
committed by intellij-monorepo-bot
parent 6bd7e15e13
commit d09640c53d
4 changed files with 33 additions and 2 deletions
@@ -325,6 +325,7 @@ public abstract class DataFlowInspectionBase extends AbstractBaseJavaLocalInspec
private void reportUnreachableSwitchBranches(Map<PsiCaseLabelElement, ThreeState> labelReachability, ProblemsHolder holder) {
if (labelReachability.isEmpty()) return;
Set<PsiSwitchBlock> coveredSwitches = new HashSet<>();
Map<PsiCaseLabelElement, PsiSwitchBlock> unreachableLabels = new HashMap<>();
for (Map.Entry<PsiCaseLabelElement, ThreeState> entry : labelReachability.entrySet()) {
if (entry.getValue() != ThreeState.YES) continue;
@@ -338,6 +339,14 @@ public abstract class DataFlowInspectionBase extends AbstractBaseJavaLocalInspec
.nonNull().flatArray(PsiCaseLabelElementList::getElements)
.append(StreamEx.iterate(label, Objects::nonNull, l -> PsiTreeUtil.getPrevSiblingOfType(l, PsiCaseLabelElement.class)).skip(1))
.allMatch(l -> labelReachability.get(l) == ThreeState.NO)) {
// Add all labels after always-reachable one as unreachable
StreamEx.iterate(labelStatement, Objects::nonNull, l -> PsiTreeUtil.getNextSiblingOfType(l, PsiSwitchLabelStatementBase.class))
.remove(SwitchUtils::isDefaultLabel)
.skip(1).map(PsiSwitchLabelStatementBase::getCaseLabelElementList)
.nonNull().flatArray(PsiCaseLabelElementList::getElements)
.append(StreamEx.iterate(label, Objects::nonNull, l -> PsiTreeUtil.getNextSiblingOfType(l, PsiCaseLabelElement.class)).skip(1))
.forEach(l -> unreachableLabels.put(l, switchBlock));
continue;
}
coveredSwitches.add(switchBlock);
@@ -357,13 +366,16 @@ public abstract class DataFlowInspectionBase extends AbstractBaseJavaLocalInspec
PsiSwitchLabelStatementBase labelStatement = Objects.requireNonNull(PsiImplUtil.getSwitchLabel(label));
PsiSwitchBlock switchBlock = labelStatement.getEnclosingSwitchBlock();
if (switchBlock == null || coveredSwitches.contains(switchBlock)) continue;
unreachableLabels.put(label, switchBlock);
}
unreachableLabels.forEach((label, switchBlock) -> {
// duplicate case label is a compilation error so no need to highlight by the inspection
Set<PsiElement> suspiciousElements = SwitchBlockHighlightingModel.findSuspiciousLabelElements(switchBlock);
if (!suspiciousElements.contains(label)) {
holder.registerProblem(label, JavaAnalysisBundle.message("dataflow.message.unreachable.switch.label"),
new DeleteSwitchLabelFix(label));
}
}
});
}
private static boolean canRemoveUnreachableBranches(PsiSwitchLabelStatementBase labelStatement, PsiSwitchBlock statement) {
@@ -30,7 +30,7 @@ public class SwitchExpressionsJava12 {
int i1 = switch(x) {
case A -> 1;
case B -> 2;
case C -> 3;
case <warning descr="Switch label 'C' is unreachable">C</warning> -> 3;
};
if (<warning descr="Condition 'i1 == 0' is always 'false'">i1 == 0</warning>) {} // exhaustive
@@ -0,0 +1,18 @@
class Test {
enum Colors {
RED,
GREEN,
BLUE,
YELLOW,
}
public Test(Colors color) {
if (color == Colors.RED || color == Colors.GREEN) {
switch (color) {
case RED -> System.out.println("Red");
case <warning descr="Switch label 'BLUE' is unreachable">BLUE</warning> -> System.out.println("Blue");
case GREEN -> System.out.println("Green");
case <warning descr="Switch label 'YELLOW' is unreachable">YELLOW</warning> -> System.out.println("Yellow");
}
}
}
}
@@ -19,6 +19,7 @@ public class DataFlowInspection16Test extends DataFlowInspectionTestCase {
public void testInstanceOfPattern() { doTest(); }
public void testSwitchStatements() { doTest(); }
public void testSwitchStatementUnreachableBranches() { doTest(); }
public void testSwitchExpressions() { doTest(); }
public void testSwitchExpressionsNullability() { doTest(); }
public void testConstantDescAsWrapperSupertype() {