Java: Extend control flow analysis to support enhanced switch & switch expression, including break-with-value (IDEA-202131)

This commit is contained in:
Pavel Dolgov
2018-11-21 18:49:01 +03:00
parent 84aa9b4bf5
commit 9fc8a64cd6
3 changed files with 105 additions and 51 deletions
@@ -397,7 +397,9 @@ class ControlFlowAnalyzer extends JavaElementVisitor {
@Override
public void visitBreakStatement(PsiBreakStatement statement) {
startElement(statement);
PsiStatement exitedStatement = statement.findExitedStatement();
generateExpressionInstructions(statement.getValueExpression());
PsiElement exitedStatement = statement.findExitedElement();
if (exitedStatement != null) {
callFinallyBlocksOnExit(exitedStatement);
@@ -419,7 +421,7 @@ class ControlFlowAnalyzer extends JavaElementVisitor {
finishElement(statement);
}
private void callFinallyBlocksOnExit(PsiStatement exitedStatement) {
private void callFinallyBlocksOnExit(PsiElement exitedStatement) {
for (final ListIterator<FinallyBlockSubroutine> it = myFinallyBlocks.listIterator(myFinallyBlocks.size()); it.hasPrevious(); ) {
final FinallyBlockSubroutine finallyBlockSubroutine = it.previous();
PsiElement finallyBlock = finallyBlockSubroutine.getElement();
@@ -503,13 +505,8 @@ class ControlFlowAnalyzer extends JavaElementVisitor {
private void processVariable(@NotNull PsiVariable element) {
final PsiExpression initializer = element.getInitializer();
if (initializer != null) {
myStartStatementStack.pushStatement(initializer, false);
myEndStatementStack.pushStatement(initializer, false);
initializer.accept(this);
myStartStatementStack.popStatement();
myEndStatementStack.popStatement();
}
generateExpressionInstructions(initializer);
if (element instanceof PsiLocalVariable && initializer != null ||
element instanceof PsiField) {
if (element instanceof PsiLocalVariable && !myPolicy.isLocalVariableAccepted((PsiLocalVariable)element)) return;
@@ -839,13 +836,7 @@ class ControlFlowAnalyzer extends JavaElementVisitor {
startElement(statement);
PsiExpression caseValue = statement.getCaseValue();
if (caseValue != null) {
myStartStatementStack.pushStatement(caseValue, false);
myEndStatementStack.pushStatement(caseValue, false);
caseValue.accept(this);
myStartStatementStack.popStatement();
myEndStatementStack.popStatement();
}
generateExpressionInstructions(caseValue);
finishElement(statement);
}
@@ -857,11 +848,8 @@ class ControlFlowAnalyzer extends JavaElementVisitor {
if (caseValues != null) {
for (PsiExpression caseValue : caseValues.getExpressions()) {
myStartStatementStack.pushStatement(caseValue, false);
myEndStatementStack.pushStatement(caseValue, false);
caseValue.accept(this);
myStartStatementStack.popStatement();
myEndStatementStack.popStatement();
ProgressManager.checkCanceled();
generateExpressionInstructions(caseValue);
}
}
@@ -1321,6 +1309,13 @@ class ControlFlowAnalyzer extends JavaElementVisitor {
PsiExpression[] expressions = list.getExpressions();
for (final PsiExpression expression : expressions) {
ProgressManager.checkCanceled();
generateExpressionInstructions(expression);
}
}
private void generateExpressionInstructions(@Nullable PsiExpression expression) {
if (expression != null) {
// handle short circuit
myStartStatementStack.pushStatement(expression, false);
myEndStatementStack.pushStatement(expression, false);
@@ -105,4 +105,76 @@ class C {
default -> 0;
};
}
static class FinalFieldSwitchExpression {
final String s = switch ((int)Math.random()) {
case 1 -> "a";
default -> "?";
};
{
System.out.println(s);
}
}
static class FinalFieldValueBreakSwitchExpression {
final String s = switch ((int)Math.random()) {
case 1: break "a";
default: break "?";
};
{
System.out.println(s);
}
}
void finalVariableSwitchExpression(String s) {
final int n = switch (s) {
case "a" -> 1;
default -> 0;
};
System.out.println(n);
}
void finalVariableValueBreakSwitchExpression(String s) {
final int n = switch (s) {
case "a": break 1;
default: break 0;
};
System.out.println(n);
}
void definitelyAssignedInSwitchExpression(String s) {
int n;
int x = switch (s) {
case "a" -> n = 1;
default -> n = 0;
};
System.out.println(n);
}
void notDefinitelyAssignedInSwitchExpression(String s) {
int n;
int x = switch (s) {
case "a" -> n = 1;
default -> 0;
};
System.out.println(<error descr="Variable 'n' might not have been initialized">n</error>);
}
void definitelyAssignedInSwitchExpressionValueBreak(String s) {
int n;
int x = switch (s) {
case "a": break n = 1;
default: break n = 0;
};
System.out.println(n);
}
void notDefinitelyAssignedInSwitchExpressionValueBreak(String s) {
int n;
int x = switch (s) {
case "a": break n = 1;
default: break 0;
};
System.out.println(<error descr="Variable 'n' might not have been initialized">n</error>);
}
}
@@ -45,41 +45,13 @@ class C {
System.out.println();
}
/* todo
void endlessLoopInBranchWithValue(String arg) {
void endlessLoopInBranchWithValueBreak(String arg) {
int result = switch (arg) {
case "one" -> { while(true); break 1;}
case "one" -> { while(true); <error descr="Unreachable statement">break 1;</error>}
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;
@@ -129,6 +101,21 @@ class C {
int bar(int i) throws Exception { return i; }
}
static class ValueBreakSwitchExpressionReturnedFromTry {
int foo(String s) throws Exception {
try {
return switch (s) {
case "a": break bar(1);
default: break 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 {