Java control flow: Detect unreachable code when a break or continue in the try block is overridden in the finally block of the same try statement (IDEA-35597)

This commit is contained in:
Pavel Dolgov
2016-07-29 14:00:42 +03:00
parent 3e3b40df65
commit 7b97664b85
3 changed files with 98 additions and 11 deletions

View File

@@ -412,6 +412,8 @@ class ControlFlowAnalyzer extends JavaElementVisitor {
startElement(statement);
PsiStatement exitedStatement = statement.findExitedStatement();
if (exitedStatement != null) {
callFinallyBlocksOnExit(exitedStatement);
final Instruction instruction;
final PsiElement finallyBlock = findEnclosingFinallyBlockElement(statement, exitedStatement);
final int finallyStartOffset = finallyBlock == null ? -1 : myCurrentFlow.getStartOffset(finallyBlock);
@@ -430,6 +432,18 @@ class ControlFlowAnalyzer extends JavaElementVisitor {
finishElement(statement);
}
private void callFinallyBlocksOnExit(PsiStatement exitedStatement) {
for (final ListIterator<PsiElement> it = myFinallyBlocks.listIterator(myFinallyBlocks.size()); it.hasPrevious(); ) {
final PsiElement finallyBlock = it.previous();
final PsiElement enclosingTryStatement = finallyBlock.getParent();
if (enclosingTryStatement == null || !PsiTreeUtil.isAncestor(exitedStatement, enclosingTryStatement, false)) {
break;
}
myCurrentFlow.addInstruction(new CallInstruction(0, 0, myStack));
addElementOffsetLater(finallyBlock, true);
}
}
private PsiElement findEnclosingFinallyBlockElement(@NotNull PsiElement sourceElement, @Nullable PsiElement jumpElement) {
PsiElement element = sourceElement;
while (element != null && !(element instanceof PsiFile)) {
@@ -451,21 +465,14 @@ class ControlFlowAnalyzer extends JavaElementVisitor {
PsiStatement continuedStatement = statement.findContinuedStatement();
if (continuedStatement != null) {
PsiElement body = null;
if (continuedStatement instanceof PsiForStatement) {
body = ((PsiForStatement)continuedStatement).getBody();
}
else if (continuedStatement instanceof PsiWhileStatement) {
body = ((PsiWhileStatement)continuedStatement).getBody();
}
else if (continuedStatement instanceof PsiDoWhileStatement) {
body = ((PsiDoWhileStatement)continuedStatement).getBody();
}
else if (continuedStatement instanceof PsiForeachStatement) {
body = ((PsiForeachStatement)continuedStatement).getBody();
if (continuedStatement instanceof PsiLoopStatement) {
body = ((PsiLoopStatement)continuedStatement).getBody();
}
if (body == null) {
body = myCodeFragment;
}
callFinallyBlocksOnExit(continuedStatement);
final Instruction instruction;
final PsiElement finallyBlock = findEnclosingFinallyBlockElement(statement, continuedStatement);
final int finallyStartOffset = finallyBlock == null ? -1 : myCurrentFlow.getStartOffset(finallyBlock);

View File

@@ -452,4 +452,18 @@ class CompoundAssign {
<error descr="Variable 'i' might not have been initialized">i</error> += i = 2;
System.out.println(i);
}
}
class BreakAndFinally {
void f() {
final int i;
t:
try {
break t;
}
finally {
i = 1;
}
System.out.println(i);
}
}

View File

@@ -402,3 +402,69 @@ class Good3 {
return false;
}
}
class ContinueFromFinally {
void foo() {
while (true) {
try {
break;
} finally {
continue;
}
}
<error descr="Unreachable statement">System.out.println();</error>
}
}
class BreakFromNestedTry {
void foo() {
outer:
{
inner:
try {
try {
break inner;
} finally {
System.out.println();
}
} finally {
break outer;
}
<error descr="Unreachable statement">System.out.println();</error>
}
}
}
class BreakFromNestedFinally {
void foo() {
outer:
{
inner:
try {
try {
} finally {
break inner;
}
} finally {
break outer;
}
<error descr="Unreachable statement">System.out.println();</error>
}
}
}
class ContinueFromTry {
void foo() {
outer:
{
do {
try {
continue;
} finally {
break outer;
}
} while (false);
<error descr="Unreachable statement">System.out.println();</error>
}
}
}