IDEA-239116 Switch expression without any results is not highlighted as erroneous

GitOrigin-RevId: 66580d3aa4a95e1fdb32b1a3131544560c9c432a
This commit is contained in:
Tagir Valeev
2020-04-29 10:51:14 +00:00
committed by intellij-monorepo-bot
parent 5d753d02bd
commit b3ba7b6783
5 changed files with 74 additions and 11 deletions
@@ -2108,31 +2108,74 @@ public class HighlightUtil {
PsiCodeBlock switchBody = switchExpression.getBody();
if (switchBody != null) {
PsiStatement lastStatement = PsiTreeUtil.getPrevSiblingOfType(switchBody.getRBrace(), PsiStatement.class);
boolean hasResult = false;
if (lastStatement instanceof PsiSwitchLabeledRuleStatement) {
Collection<HighlightInfo> results = new ArrayList<>();
for (PsiSwitchLabeledRuleStatement rule = (PsiSwitchLabeledRuleStatement)lastStatement;
rule != null;
rule = PsiTreeUtil.getPrevSiblingOfType(rule, PsiSwitchLabeledRuleStatement.class)) {
PsiStatement ruleBody = rule.getBody();
if (ruleBody instanceof PsiExpressionStatement) {
hasResult = true;
}
// the expression and throw statements are fine, only the block statement could be an issue
if (ruleBody instanceof PsiBlockStatement && ControlFlowUtils.statementMayCompleteNormally(ruleBody)) {
PsiElement target = ObjectUtils.notNull(ObjectUtils.tryCast(rule.getFirstChild(), PsiKeyword.class), rule);
String message = JavaErrorBundle.message("switch.expr.rule.should.produce.result");
results.add(HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(target).descriptionAndTooltip(message).create());
if (ruleBody instanceof PsiBlockStatement) {
if (ControlFlowUtils.statementMayCompleteNormally(ruleBody)) {
PsiElement target = ObjectUtils.notNull(ObjectUtils.tryCast(rule.getFirstChild(), PsiKeyword.class), rule);
String message = JavaErrorBundle.message("switch.expr.rule.should.produce.result");
results.add(HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(target).descriptionAndTooltip(message).create());
}
else if (!hasResult && hasYield(switchExpression, ruleBody)) {
hasResult = true;
}
}
}
return results;
if (!results.isEmpty()) {
return results;
}
} else {
// previous statements may have no result as well, but in that case they fall through to the last one, which needs to be checked anyway
if (lastStatement != null && ControlFlowUtils.statementMayCompleteNormally(lastStatement)) {
PsiElement target = ObjectUtils.notNull(ObjectUtils.tryCast(switchExpression.getFirstChild(), PsiKeyword.class), switchExpression);
String message = JavaErrorBundle.message("switch.expr.should.produce.result");
return Collections.singletonList(HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(target).descriptionAndTooltip(message).create());
}
hasResult = hasYield(switchExpression, switchBody);
}
// previous statements may have no result as well, but in that case they fall through to the last one, which needs to be checked anyway
if (lastStatement != null && ControlFlowUtils.statementMayCompleteNormally(lastStatement)) {
if (!hasResult) {
PsiElement target = ObjectUtils.notNull(ObjectUtils.tryCast(switchExpression.getFirstChild(), PsiKeyword.class), switchExpression);
String message = JavaErrorBundle.message("switch.expr.should.produce.result");
return Collections.singletonList(HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(target).descriptionAndTooltip(message).create());
return Collections.singletonList(HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(target)
.descriptionAndTooltip(JavaErrorBundle.message("switch.expr.no.result")).create());
}
}
return Collections.emptyList();
}
private static boolean hasYield(@NotNull PsiSwitchExpression switchExpression, PsiElement scope) {
class YieldFinder extends JavaRecursiveElementWalkingVisitor {
boolean hasYield = false;
@Override
public void visitYieldStatement(PsiYieldStatement statement) {
if (statement.findEnclosingExpression() == switchExpression) {
hasYield = true;
stopWalking();
}
}
// do not go inside to save time: declarations cannot contain yield that points to outer switch expression
@Override
public void visitDeclarationStatement(PsiDeclarationStatement statement) {}
// do not go inside to save time: expressions cannot contain yield that points to outer switch expression
@Override
public void visitExpression(PsiExpression expression) {}
}
YieldFinder finder = new YieldFinder();
scope.accept(finder);
return finder.hasYield;
}
/**
* See JLS 8.3.2.3.
*/
@@ -241,6 +241,7 @@ duplicate.switch.label=Duplicate label ''{0}''
switch.colon.expected.after.case.label=':' expected
switch.expr.empty='switch' expression does not have any case clauses
switch.expr.incomplete='switch' expression does not cover all possible input values
switch.expr.no.result=Switch expression does not have any result expressions
switch.expr.should.produce.result=Switch expression should produce result in all execution paths
switch.expr.rule.should.produce.result=Switch expression rule should produce result in all execution paths
@@ -28,13 +28,13 @@ class C {
}
void defaultBranchAlwaysThrows(int n) {
String s = switch (n) {
String s = <error descr="Switch expression does not have any result expressions">switch</error> (n) {
default: throw new RuntimeException();
};
}
void defaultRuleAlwaysThrows(int n) {
String s = switch (n) {
String s = <error descr="Switch expression does not have any result expressions">switch</error> (n) {
default -> throw new RuntimeException();
};
}
@@ -0,0 +1,18 @@
class Test {
void test() {
int i = <error descr="Switch expression does not have any result expressions">switch</error>(0) {
default -> throw new NullPointerException();
};
}
void test2() {
int i = <error descr="Switch expression does not have any result expressions">switch</error>(0) {
case 0 -> {while(true);}
case 1 -> {
throw new RuntimeException();
}
default -> throw new NullPointerException();
};
}
}
@@ -11,6 +11,7 @@ class JavaSwitchExpressionsHighlightingTest : LightJavaCodeInsightFixtureTestCas
fun testEnhancedSwitchStatements() = doTest()
fun testSwitchExpressions() = doTest()
fun testSwitchExpressionsNoResult() = doTest()
fun testSwitchExpressionsEnumResolve() = doTest()
fun testSwitchNumericPromotion() = doTest()
fun testSimpleInferenceCases() = doTest()