mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Java: Highlight switch expressions that don't produce the result on some execution path (IDEA-202621)
This commit is contained in:
+31
@@ -51,6 +51,7 @@ import com.intellij.util.containers.MultiMap;
|
||||
import com.intellij.util.containers.hash.HashSet;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
import com.intellij.xml.util.XmlStringUtil;
|
||||
import com.siyeh.ig.psiutils.ControlFlowUtils;
|
||||
import gnu.trove.THashMap;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -2017,6 +2018,36 @@ public class HighlightUtil extends HighlightUtilBase {
|
||||
return results;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
static Collection<HighlightInfo> checkSwitchExpressionHasResult(@NotNull PsiSwitchExpression switchExpression) {
|
||||
PsiCodeBlock switchBody = switchExpression.getBody();
|
||||
if (switchBody != null) {
|
||||
PsiStatement lastStatement = PsiTreeUtil.getPrevSiblingOfType(switchBody.getRBrace(), PsiStatement.class);
|
||||
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();
|
||||
// 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);
|
||||
results.add(HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(target)
|
||||
.descriptionAndTooltip(JavaErrorMessages.message("switch.expr.rule.should.produce.result")).create());
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
// 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);
|
||||
return Collections.singletonList(HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(target)
|
||||
.descriptionAndTooltip(JavaErrorMessages.message("switch.expr.should.produce.result")).create());
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* see JLS 8.3.2.3
|
||||
|
||||
+1
@@ -1608,6 +1608,7 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
|
||||
if (!myHolder.hasErrorResults()) myHolder.add(checkFeature(expression, Feature.SWITCH_EXPRESSION));
|
||||
checkSwitchBlock(expression);
|
||||
if (!myHolder.hasErrorResults()) myHolder.addAll(HighlightUtil.checkSwitchExpressionReturnTypeCompatible(expression));
|
||||
if (!myHolder.hasErrorResults()) myHolder.addAll(HighlightUtil.checkSwitchExpressionHasResult(expression));
|
||||
}
|
||||
|
||||
private void checkSwitchBlock(PsiSwitchBlock switchBlock) {
|
||||
|
||||
@@ -269,6 +269,8 @@ 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.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
|
||||
|
||||
illegal.forward.reference=Illegal forward reference
|
||||
illegal.self.reference=Illegal self reference
|
||||
|
||||
+39
@@ -177,4 +177,43 @@ class C {
|
||||
};
|
||||
System.out.println(<error descr="Variable 'n' might not have been initialized">n</error>);
|
||||
}
|
||||
|
||||
void switchExpressionAssignedInFinally(int n) {
|
||||
String s;
|
||||
try {
|
||||
} finally {
|
||||
s = switch (n) {
|
||||
case -1 -> throw new RuntimeException();
|
||||
case 0 -> "a";
|
||||
default -> "b";
|
||||
};
|
||||
}
|
||||
System.out.println(s);
|
||||
}
|
||||
|
||||
void allSwitchRulesAssignInFinally(int n) {
|
||||
String s;
|
||||
try {
|
||||
} finally {
|
||||
String string = switch (n) {
|
||||
case -1 -> throw new RuntimeException();
|
||||
case 0 -> s = "a";
|
||||
default -> { break s = "b"; }
|
||||
};
|
||||
}
|
||||
System.out.println(s);
|
||||
}
|
||||
|
||||
void notAllSwitchRulesAssignInFinally(int n) {
|
||||
String s;
|
||||
try {
|
||||
} finally {
|
||||
String t = switch (n) {
|
||||
case -1 -> throw new RuntimeException();
|
||||
case 0 -> s = "a";
|
||||
default -> "b";
|
||||
};
|
||||
}
|
||||
System.out.println(<error descr="Variable 's' might not have been initialized">s</error>);
|
||||
}
|
||||
}
|
||||
+24
@@ -86,6 +86,30 @@ class C {
|
||||
<error descr="Unreachable statement">System.out.println();</error>
|
||||
}
|
||||
|
||||
void switchExpressionUnreachableInFinally(int n) {
|
||||
String s;
|
||||
try {
|
||||
} finally {
|
||||
return;
|
||||
s = <error descr="Unreachable statement">switch</error> (n) {
|
||||
case 0 -> "a";
|
||||
default -> "b";
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
void switchExpressionReachableInFinally(int n) {
|
||||
String s;
|
||||
try {
|
||||
return;
|
||||
} finally {
|
||||
s = switch (n) {
|
||||
case 0 -> "a";
|
||||
default -> "b";
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
static class SwitchExpressionReturnedFromTry {
|
||||
int foo(String s) throws Exception {
|
||||
try {
|
||||
|
||||
+149
@@ -0,0 +1,149 @@
|
||||
class C {
|
||||
void defaultBranchHasNoResult(int n) {
|
||||
String s = <error descr="Switch expression should produce result in all execution paths">switch</error> (n) {
|
||||
default:
|
||||
};
|
||||
}
|
||||
|
||||
void defaultRuleHasNoResult(int n) {
|
||||
String s = switch (n) {
|
||||
<error descr="Switch expression rule should produce result in all execution paths">default</error> -> {}
|
||||
};
|
||||
}
|
||||
|
||||
void defaultBranchSometimesHasNoResult(int n, boolean b) {
|
||||
String s = <error descr="Switch expression should produce result in all execution paths">switch</error> (n) {
|
||||
default: {
|
||||
if (b) break "";
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
void defaultRuleSometimesHasNoResult(int n, boolean b) {
|
||||
String s = switch (n) {
|
||||
<error descr="Switch expression rule should produce result in all execution paths">default</error> -> {
|
||||
if (b) break "";
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
void defaultBranchAlwaysThrows(int n) {
|
||||
String s = switch (n) {
|
||||
default: throw new RuntimeException();
|
||||
};
|
||||
}
|
||||
|
||||
void defaultRuleAlwaysThrows(int n) {
|
||||
String s = switch (n) {
|
||||
default -> throw new RuntimeException();
|
||||
};
|
||||
}
|
||||
|
||||
void defaultBranchSometimesThrows(int n, boolean b) {
|
||||
String s = switch (n) {
|
||||
default:
|
||||
if (b) throw new RuntimeException();
|
||||
break "";
|
||||
};
|
||||
}
|
||||
|
||||
void defaultRuleSometimesThrows(int n, boolean b) {
|
||||
String s = switch (n) {
|
||||
default -> {
|
||||
if (b) throw new RuntimeException();
|
||||
break "";
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
void defaultBranchHasManyResults(int n, int k) {
|
||||
String s = switch (n) {
|
||||
default: {
|
||||
if (k < n) break "a";
|
||||
if (k > n) break "b";
|
||||
break "c";
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
void defaultRuleHasManyResults(int n, int k) {
|
||||
String s = switch (n) {
|
||||
default -> {
|
||||
if (k < n) break "a";
|
||||
if (k > n) break "b";
|
||||
break "c";
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
void oneOfBranchesHasNoResult(int n) {
|
||||
String s = <error descr="Switch expression should produce result in all execution paths">switch</error> (n) {
|
||||
case 0: break "";
|
||||
default:
|
||||
};
|
||||
}
|
||||
|
||||
void oneOfRulesHasNoResult(int n) {
|
||||
String s = switch (n) {
|
||||
case 0 -> "";
|
||||
<error descr="Switch expression rule should produce result in all execution paths">default</error> -> {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
void allBranchesHaveNoResult(int n) {
|
||||
String s = <error descr="Switch expression should produce result in all execution paths">switch</error> (n) {
|
||||
case 0:
|
||||
default:
|
||||
};
|
||||
}
|
||||
|
||||
void allRulesHaveNoResult(int n) {
|
||||
String s = switch (n) {
|
||||
<error descr="Switch expression rule should produce result in all execution paths">case</error> 0 -> {
|
||||
}
|
||||
<error descr="Switch expression rule should produce result in all execution paths">default</error> -> {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
void allBranchesDoHaveResult(int n) {
|
||||
String s = switch (n) {
|
||||
case -1: throw new RuntimeException();
|
||||
case 0: break "a";
|
||||
default: break "b";
|
||||
};
|
||||
}
|
||||
|
||||
void allRulesDoHaveResult(int n) {
|
||||
String s = switch (n) {
|
||||
case -1 -> throw new RuntimeException();
|
||||
case 0 -> "a";
|
||||
default -> "b";
|
||||
};
|
||||
}
|
||||
|
||||
void allBranchesDoHaveResultInFinally(int n) {
|
||||
String s;
|
||||
try {
|
||||
} finally {
|
||||
s = switch (n) {
|
||||
case -1: throw new RuntimeException();
|
||||
case 0: break "a";
|
||||
default: break "b";
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
void allRulesDoHaveResultInFinally(int n) {
|
||||
String s;
|
||||
try {
|
||||
} finally {
|
||||
s = switch (n) {
|
||||
case -1 -> throw new RuntimeException();
|
||||
case 0 -> "a";
|
||||
default -> "b";
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
@@ -15,6 +15,7 @@ class LightJava12HighlightingTest : LightCodeInsightFixtureTestCase() {
|
||||
fun testSimpleInferenceCases() = doTest()
|
||||
fun testEnhancedSwitchDefinitelyAssigned() = doTest()
|
||||
fun testEnhancedSwitchUnreachable() = doTest()
|
||||
fun testSwitchExpressionHasResult() = doTest()
|
||||
|
||||
private fun doTest() {
|
||||
myFixture.configureByFile(getTestName(false) + ".java")
|
||||
|
||||
Reference in New Issue
Block a user