mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Java: Extend control flow analysis to support enhanced switch & switch expression. No break-with-value yet (IDEA-202131)
This commit is contained in:
+1
-1
@@ -91,7 +91,7 @@ public class HighlightControlFlowUtil {
|
||||
String description = JavaErrorMessages.message("unreachable.statement");
|
||||
PsiElement keyword = null;
|
||||
if (unreachableStatement instanceof PsiIfStatement ||
|
||||
unreachableStatement instanceof PsiSwitchStatement ||
|
||||
unreachableStatement instanceof PsiSwitchBlock ||
|
||||
unreachableStatement instanceof PsiLoopStatement) {
|
||||
keyword = unreachableStatement.getFirstChild();
|
||||
}
|
||||
|
||||
@@ -850,8 +850,48 @@ class ControlFlowAnalyzer extends JavaElementVisitor {
|
||||
finishElement(statement);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitSwitchLabeledRuleStatement(PsiSwitchLabeledRuleStatement statement) {
|
||||
startElement(statement);
|
||||
PsiExpressionList caseValues = statement.getCaseValues();
|
||||
|
||||
if (caseValues != null) {
|
||||
for (PsiExpression caseValue : caseValues.getExpressions()) {
|
||||
myStartStatementStack.pushStatement(caseValue, false);
|
||||
myEndStatementStack.pushStatement(caseValue, false);
|
||||
caseValue.accept(this);
|
||||
myStartStatementStack.popStatement();
|
||||
myEndStatementStack.popStatement();
|
||||
}
|
||||
}
|
||||
|
||||
PsiStatement body = statement.getBody();
|
||||
if (body != null) {
|
||||
body.accept(this);
|
||||
}
|
||||
|
||||
PsiSwitchBlock switchBlock = statement.getEnclosingSwitchBlock();
|
||||
if (switchBlock != null) {
|
||||
Instruction instruction =
|
||||
new GoToInstruction(0, BranchingInstruction.Role.END, PsiTreeUtil.isAncestor(switchBlock, myCodeFragment, true));
|
||||
myCurrentFlow.addInstruction(instruction);
|
||||
addElementOffsetLater(switchBlock, false);
|
||||
}
|
||||
|
||||
finishElement(statement);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitSwitchStatement(PsiSwitchStatement statement) {
|
||||
generateSwitchBlockInstructions(statement);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitSwitchExpression(PsiSwitchExpression expression) {
|
||||
generateSwitchBlockInstructions(expression);
|
||||
}
|
||||
|
||||
public void generateSwitchBlockInstructions(PsiSwitchBlock statement) {
|
||||
startElement(statement);
|
||||
|
||||
PsiExpression expr = statement.getExpression();
|
||||
@@ -862,12 +902,12 @@ class ControlFlowAnalyzer extends JavaElementVisitor {
|
||||
PsiCodeBlock body = statement.getBody();
|
||||
if (body != null) {
|
||||
PsiStatement[] statements = body.getStatements();
|
||||
PsiSwitchLabelStatement defaultLabel = null;
|
||||
PsiSwitchLabelStatementBase defaultLabel = null;
|
||||
for (PsiStatement aStatement : statements) {
|
||||
ProgressManager.checkCanceled();
|
||||
if (aStatement instanceof PsiSwitchLabelStatement) {
|
||||
if (((PsiSwitchLabelStatement)aStatement).isDefaultCase()) {
|
||||
defaultLabel = (PsiSwitchLabelStatement)aStatement;
|
||||
if (aStatement instanceof PsiSwitchLabelStatementBase) {
|
||||
if (((PsiSwitchLabelStatementBase)aStatement).isDefaultCase()) {
|
||||
defaultLabel = (PsiSwitchLabelStatementBase)aStatement;
|
||||
}
|
||||
Instruction instruction = new ConditionalGoToInstruction(0, expr);
|
||||
myCurrentFlow.addInstruction(instruction);
|
||||
|
||||
@@ -1157,7 +1157,7 @@ public class ControlFlowUtil {
|
||||
return getUnreachableStatementParent(parent);
|
||||
}
|
||||
if (parent instanceof PsiIfStatement && ((PsiIfStatement)parent).getCondition() == expression ||
|
||||
parent instanceof PsiSwitchStatement && ((PsiSwitchStatement)parent).getExpression() == expression ||
|
||||
parent instanceof PsiSwitchBlock && ((PsiSwitchBlock)parent).getExpression() == expression ||
|
||||
parent instanceof PsiWhileStatement && ((PsiWhileStatement)parent).getCondition() == expression ||
|
||||
parent instanceof PsiForeachStatement && ((PsiForeachStatement)parent).getIteratedValue() == expression) {
|
||||
return parent;
|
||||
|
||||
+108
@@ -0,0 +1,108 @@
|
||||
class C {
|
||||
void finalVariableAssignedInAllBranches(int k) {
|
||||
final String s;
|
||||
switch (k) {
|
||||
case 1 -> s = "a";
|
||||
case 2 -> s = "b";
|
||||
case 3, 4 -> s = "c";
|
||||
default -> s = "d";
|
||||
}
|
||||
System.out.println(s);
|
||||
}
|
||||
|
||||
enum EnumAB {A, B}
|
||||
void finalVariableAssignedInAllEnumConstantBranches(EnumAB ab) {
|
||||
final int n;
|
||||
switch (ab) {
|
||||
case A -> n = 1;
|
||||
case B -> n = 2;
|
||||
}
|
||||
System.out.println(<error descr="Variable 'n' might not have been initialized">n</error>);
|
||||
}
|
||||
|
||||
void assignedInSomeBranches(String s) {
|
||||
int n;
|
||||
switch ((int)Math.random()) {
|
||||
case 1 -> n = 1;
|
||||
default -> {}
|
||||
}
|
||||
System.out.println(<error descr="Variable 'n' might not have been initialized">n</error>);
|
||||
}
|
||||
|
||||
void finalVariableReassignedAfterSwitchStatement(int n) {
|
||||
final String s;
|
||||
switch (n) {
|
||||
case 1 -> s = "a";
|
||||
default -> {}
|
||||
}
|
||||
<error descr="Variable 's' might already have been assigned to">s</error> = "b";
|
||||
System.out.println(s);
|
||||
}
|
||||
|
||||
void finalVariableReassignedAfterSwitchExpression(int n) {
|
||||
final String s;
|
||||
String t = switch (n) {
|
||||
case 1 -> s = "a";
|
||||
default -> "";
|
||||
};
|
||||
<error descr="Variable 's' might already have been assigned to">s</error> = t;
|
||||
System.out.println(s);
|
||||
}
|
||||
|
||||
void finalVariableReassignedInSwitchStatement(int n) {
|
||||
final String s = "b";
|
||||
switch (n) {
|
||||
case 1 -> <error descr="Cannot assign a value to final variable 's'">s</error> = "a";
|
||||
default -> {}
|
||||
};
|
||||
System.out.println(s);
|
||||
}
|
||||
|
||||
void finalVariableReassignedInSwitchExpression(int n) {
|
||||
final String s = "b";
|
||||
String string = switch (n) {
|
||||
case 1 -> <error descr="Cannot assign a value to final variable 's'">s</error> = "a";
|
||||
default -> "";
|
||||
};
|
||||
System.out.println(s);
|
||||
}
|
||||
|
||||
|
||||
static class FinalFieldAssignedInSomeBranches {
|
||||
<error descr="Variable 'n' might not have been initialized">final int n</error>;
|
||||
{
|
||||
switch ((int)Math.random()) {
|
||||
case 1 -> n = 1;
|
||||
default -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class FinalFieldAssignedInSomeBranchesNoDefault {
|
||||
<error descr="Variable 'n' might not have been initialized">final int n</error>;
|
||||
{
|
||||
switch ((int)Math.random()) {
|
||||
case 1 -> n = 1;
|
||||
case 0 -> n = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class FinalFieldAssignedInAllBranches {
|
||||
final int n;
|
||||
{
|
||||
switch ((int)Math.random()) {
|
||||
case 1 -> n = 1;
|
||||
default -> n = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class FinalFieldInitializedWithswitchExpression {
|
||||
final int n =
|
||||
switch ((int)Math.random()) {
|
||||
case 1 -> 1;
|
||||
default -> 0;
|
||||
};
|
||||
}
|
||||
}
|
||||
+146
@@ -0,0 +1,146 @@
|
||||
class C {
|
||||
void alwaysThrow(String s) {
|
||||
switch (s) {
|
||||
case "a" -> throw new IllegalArgumentException();
|
||||
default -> throw new IllegalStateException();
|
||||
}
|
||||
<error descr="Unreachable statement">System.out.println();</error>
|
||||
}
|
||||
|
||||
void breakFromEndlessLoop() {
|
||||
EndlessLoop:
|
||||
for (;;) {
|
||||
switch ((int)Math.random()) {
|
||||
case 1 -> {break EndlessLoop;}
|
||||
default -> throw new RuntimeException();
|
||||
}
|
||||
}
|
||||
System.out.println();
|
||||
}
|
||||
|
||||
void continueEndlessLoop() {
|
||||
EndlessLoop:
|
||||
for (;;) {
|
||||
switch ((int)Math.random()) {
|
||||
case 1 -> {continue EndlessLoop;}
|
||||
default -> throw new RuntimeException();
|
||||
}
|
||||
}
|
||||
<error descr="Unreachable statement">System.out.println();</error>
|
||||
}
|
||||
|
||||
void endlessLoopsInAllBranches(String s) {
|
||||
switch (s) {
|
||||
case "a" -> { while(true); }
|
||||
default -> { for(;;); }
|
||||
}
|
||||
<error descr="Unreachable statement">System.out.println();</error>
|
||||
}
|
||||
|
||||
void endlessLoopInBranch(String s) {
|
||||
switch (s) {
|
||||
case "a" -> { while(true); }
|
||||
default -> {}
|
||||
};
|
||||
System.out.println();
|
||||
}
|
||||
|
||||
/* todo
|
||||
void endlessLoopInBranchWithValue(String arg) {
|
||||
int result = switch (arg) {
|
||||
case "one" -> { while(true); break 1;}
|
||||
default -> 0;
|
||||
};
|
||||
System.out.println(result);
|
||||
}
|
||||
*/
|
||||
|
||||
static class FinalFieldSwitchExpression {
|
||||
final String s = switch ((int)Math.random()) {
|
||||
case 1 -> "a";
|
||||
default -> "?";
|
||||
};
|
||||
{
|
||||
System.out.println(s);
|
||||
}
|
||||
}
|
||||
|
||||
void finalVariableSwitchExpression(String s) {
|
||||
final int n = switch (s) {
|
||||
case "a" -> 1;
|
||||
default -> 0;
|
||||
};
|
||||
System.out.println(n);
|
||||
}
|
||||
|
||||
void notDefinitelyAssigned(String s) {
|
||||
int n;
|
||||
switch (s) {
|
||||
case "a" -> n = 1;
|
||||
}
|
||||
System.out.println(<error descr="Variable 'n' might not have been initialized">n</error>);
|
||||
}
|
||||
|
||||
int returnBeforeEnhancedSwitchStatement(String s) {
|
||||
return 2;
|
||||
<error descr="Unreachable statement">switch</error>(s) {
|
||||
case "a" -> {return 1;}
|
||||
default -> {return 0;}
|
||||
}
|
||||
}
|
||||
|
||||
int returnBeforeSwitchExpressionInInitializer(String s) {
|
||||
return 2;
|
||||
int n = <error descr="Unreachable statement">switch</error>(s) {
|
||||
case "a" -> 1;
|
||||
default -> 0;
|
||||
};
|
||||
}
|
||||
|
||||
int returnBeforeSwitchExpressionInAssignment(String s) {
|
||||
int n;
|
||||
return 2;
|
||||
n = <error descr="Unreachable statement">switch</error>(s) {
|
||||
case "a": n= 1;break;
|
||||
default: n= 0;
|
||||
};
|
||||
}
|
||||
|
||||
int returnSwitchExpression(String s) {
|
||||
return switch(s) {
|
||||
case "a" -> 1;
|
||||
default -> 0;
|
||||
};
|
||||
<error descr="Unreachable statement">System.out.println();</error>
|
||||
}
|
||||
|
||||
static class SwitchExpressionReturnedFromTry {
|
||||
int foo(String s) throws Exception {
|
||||
try {
|
||||
return switch (s) {
|
||||
case "a" -> bar(1);
|
||||
default -> bar(0);
|
||||
};
|
||||
} finally {
|
||||
System.out.println("b");
|
||||
}
|
||||
<error descr="Unreachable statement">System.out.println("c");</error>
|
||||
}
|
||||
int bar(int i) throws Exception { return i; }
|
||||
}
|
||||
|
||||
static class SwitchStatementReturnsFromTry {
|
||||
int foo(String s) throws Exception {
|
||||
try {
|
||||
switch (s) {
|
||||
case "a" -> { return bar(1); }
|
||||
default -> { return bar(0); }
|
||||
}
|
||||
} finally {
|
||||
System.out.println("b");
|
||||
}
|
||||
<error descr="Unreachable statement">System.out.println("c");</error>
|
||||
}
|
||||
int bar(int i) throws Exception { return i; }
|
||||
}
|
||||
}
|
||||
+2
@@ -12,6 +12,8 @@ class LightJava12HighlightingTest : LightCodeInsightFixtureTestCase() {
|
||||
fun testSwitchExpressions() = doTest()
|
||||
fun testSwitchNumericPromotion() = doTest()
|
||||
fun testSimpleInferenceCases() = doTest()
|
||||
fun testEnhancedSwitchDefinitelyAssigned() = doTest()
|
||||
fun testEnhancedSwitchUnreachable() = doTest()
|
||||
|
||||
private fun doTest() {
|
||||
myFixture.configureByFile(getTestName(false) + ".java")
|
||||
|
||||
Reference in New Issue
Block a user