mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-13 21:55:01 +07:00
Java: Fixed false positive "unreachable statement" in the control flow analysis (IDEA-164442)
This commit is contained in:
@@ -44,7 +44,7 @@ class ControlFlowAnalyzer extends JavaElementVisitor {
|
||||
private final Stack<PsiParameter> myCatchParameters = new Stack<PsiParameter>();// stack of PsiParameter for catch
|
||||
private final Stack<PsiElement> myCatchBlocks = new Stack<PsiElement>();
|
||||
|
||||
private final Stack<PsiElement> myFinallyBlocks = new Stack<PsiElement>();
|
||||
private final Stack<FinallyBlockSubroutine> myFinallyBlocks = new Stack<FinallyBlockSubroutine>();
|
||||
private final Stack<PsiElement> myUnhandledExceptionCatchBlocks = new Stack<PsiElement>();
|
||||
|
||||
// element to jump to from inner (sub)expression in "jump to begin" situation.
|
||||
@@ -271,7 +271,7 @@ class ControlFlowAnalyzer extends JavaElementVisitor {
|
||||
|
||||
// generate jump to the top finally block
|
||||
if (!myFinallyBlocks.isEmpty()) {
|
||||
final PsiElement finallyBlock = myFinallyBlocks.peek();
|
||||
final PsiElement finallyBlock = myFinallyBlocks.peek().getElement();
|
||||
ConditionalThrowToInstruction throwToInstruction = new ConditionalThrowToInstruction(-2);
|
||||
myCurrentFlow.addInstruction(throwToInstruction);
|
||||
if (!patchUncheckedThrowInstructionIfInsideFinally(throwToInstruction, element, finallyBlock)) {
|
||||
@@ -431,13 +431,16 @@ class ControlFlowAnalyzer extends JavaElementVisitor {
|
||||
}
|
||||
|
||||
private void callFinallyBlocksOnExit(PsiStatement exitedStatement) {
|
||||
for (final ListIterator<PsiElement> it = myFinallyBlocks.listIterator(myFinallyBlocks.size()); it.hasPrevious(); ) {
|
||||
final PsiElement finallyBlock = it.previous();
|
||||
for (final ListIterator<FinallyBlockSubroutine> it = myFinallyBlocks.listIterator(myFinallyBlocks.size()); it.hasPrevious(); ) {
|
||||
final FinallyBlockSubroutine finallyBlockSubroutine = it.previous();
|
||||
PsiElement finallyBlock = finallyBlockSubroutine.getElement();
|
||||
final PsiElement enclosingTryStatement = finallyBlock.getParent();
|
||||
if (enclosingTryStatement == null || !PsiTreeUtil.isAncestor(exitedStatement, enclosingTryStatement, false)) {
|
||||
break;
|
||||
}
|
||||
myCurrentFlow.addInstruction(new CallInstruction(0, 0, myStack));
|
||||
CallInstruction instruction = new CallInstruction(0, 0, myStack);
|
||||
finallyBlockSubroutine.addCall(instruction);
|
||||
myCurrentFlow.addInstruction(instruction);
|
||||
addElementOffsetLater(finallyBlock, true);
|
||||
}
|
||||
}
|
||||
@@ -842,7 +845,7 @@ class ControlFlowAnalyzer extends JavaElementVisitor {
|
||||
}
|
||||
else {
|
||||
instruction.offset = -4; // -4 for return
|
||||
addElementOffsetLater(myFinallyBlocks.peek(), true);
|
||||
addElementOffsetLater(myFinallyBlocks.peek().getElement(), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -935,7 +938,7 @@ class ControlFlowAnalyzer extends JavaElementVisitor {
|
||||
}
|
||||
else {
|
||||
instruction.offset = -2; // -2 to rethrow exception
|
||||
element = myFinallyBlocks.peek();
|
||||
element = myFinallyBlocks.peek().getElement();
|
||||
addElementOffsetLater(element, true);
|
||||
}
|
||||
}
|
||||
@@ -1069,8 +1072,10 @@ class ControlFlowAnalyzer extends JavaElementVisitor {
|
||||
|
||||
PsiCodeBlock finallyBlock = statement.getFinallyBlock();
|
||||
|
||||
FinallyBlockSubroutine finallyBlockSubroutine = null;
|
||||
if (finallyBlock != null) {
|
||||
myFinallyBlocks.push(finallyBlock);
|
||||
finallyBlockSubroutine = new FinallyBlockSubroutine(finallyBlock);
|
||||
myFinallyBlocks.push(finallyBlockSubroutine);
|
||||
}
|
||||
|
||||
PsiResourceList resourceList = statement.getResourceList();
|
||||
@@ -1129,16 +1134,22 @@ class ControlFlowAnalyzer extends JavaElementVisitor {
|
||||
|
||||
if (finallyBlock != null) {
|
||||
// normal completion, call finally block and proceed
|
||||
myCurrentFlow.addInstruction(new CallInstruction(0, 0, myStack));
|
||||
CallInstruction normalCompletion = new CallInstruction(0, 0, myStack);
|
||||
finallyBlockSubroutine.addCall(normalCompletion);
|
||||
myCurrentFlow.addInstruction(normalCompletion);
|
||||
addElementOffsetLater(finallyBlock, true);
|
||||
myCurrentFlow.addInstruction(new GoToInstruction(0));
|
||||
addElementOffsetLater(statement, false);
|
||||
// return completion, call finally block and return
|
||||
myCurrentFlow.addInstruction(new CallInstruction(0, 0, myStack));
|
||||
CallInstruction returnCompletion = new CallInstruction(0, 0, myStack);
|
||||
finallyBlockSubroutine.addCall(returnCompletion);
|
||||
myCurrentFlow.addInstruction(returnCompletion);
|
||||
addElementOffsetLater(finallyBlock, true);
|
||||
addReturnInstruction(statement);
|
||||
// throw exception completion, call finally block and rethrow
|
||||
myCurrentFlow.addInstruction(new CallInstruction(0, 0, myStack));
|
||||
CallInstruction throwExceptionCompletion = new CallInstruction(0, 0, myStack);
|
||||
finallyBlockSubroutine.addCall(throwExceptionCompletion);
|
||||
myCurrentFlow.addInstruction(throwExceptionCompletion);
|
||||
addElementOffsetLater(finallyBlock, true);
|
||||
final GoToInstruction gotoUncheckedRethrow = new GoToInstruction(0);
|
||||
myCurrentFlow.addInstruction(gotoUncheckedRethrow);
|
||||
@@ -1147,31 +1158,22 @@ class ControlFlowAnalyzer extends JavaElementVisitor {
|
||||
finallyBlock.accept(this);
|
||||
final int procStart = myCurrentFlow.getStartOffset(finallyBlock);
|
||||
final int procEnd = myCurrentFlow.getEndOffset(finallyBlock);
|
||||
int offset = procStart - 6;
|
||||
final List<Instruction> instructions = myCurrentFlow.getInstructions();
|
||||
CallInstruction callInstruction = (CallInstruction)instructions.get(offset);
|
||||
callInstruction.procBegin = procStart;
|
||||
callInstruction.procEnd = procEnd;
|
||||
offset += 2;
|
||||
callInstruction = (CallInstruction)instructions.get(offset);
|
||||
callInstruction.procBegin = procStart;
|
||||
callInstruction.procEnd = procEnd;
|
||||
offset += 2;
|
||||
callInstruction = (CallInstruction)instructions.get(offset);
|
||||
callInstruction.procBegin = procStart;
|
||||
callInstruction.procEnd = procEnd;
|
||||
for (CallInstruction callInstruction : finallyBlockSubroutine.getCalls()) {
|
||||
callInstruction.procBegin = procStart;
|
||||
callInstruction.procEnd = procEnd;
|
||||
}
|
||||
|
||||
// generate return instructions
|
||||
// first three return instructions are for normal completion, return statement call completion and unchecked exception throwing completion resp.
|
||||
|
||||
// normal completion
|
||||
myCurrentFlow.addInstruction(new ReturnInstruction(0, myStack, callInstruction));
|
||||
myCurrentFlow.addInstruction(new ReturnInstruction(0, myStack, normalCompletion));
|
||||
|
||||
// return statement call completion
|
||||
myCurrentFlow.addInstruction(new ReturnInstruction(procStart - 3, myStack, callInstruction));
|
||||
myCurrentFlow.addInstruction(new ReturnInstruction(procStart - 3, myStack, returnCompletion));
|
||||
|
||||
// unchecked exception throwing completion
|
||||
myCurrentFlow.addInstruction(new ReturnInstruction(procStart - 1, myStack, callInstruction));
|
||||
myCurrentFlow.addInstruction(new ReturnInstruction(procStart - 1, myStack, throwExceptionCompletion));
|
||||
|
||||
// checked exception throwing completion. need to dispatch to the correct catch clause
|
||||
final List<PsiElement> unhandledExceptionCatchBlocks = finallyBlockToUnhandledExceptions.remove(finallyBlock);
|
||||
@@ -1179,7 +1181,7 @@ class ControlFlowAnalyzer extends JavaElementVisitor {
|
||||
ProgressManager.checkCanceled();
|
||||
PsiElement catchBlock = unhandledExceptionCatchBlocks.get(i);
|
||||
|
||||
final ReturnInstruction returnInstruction = new ReturnInstruction(0, myStack, callInstruction);
|
||||
final ReturnInstruction returnInstruction = new ReturnInstruction(0, myStack, throwExceptionCompletion);
|
||||
returnInstruction.setRethrowFromFinally();
|
||||
myCurrentFlow.addInstruction(returnInstruction);
|
||||
if (catchBlock == null) {
|
||||
@@ -1725,4 +1727,28 @@ class ControlFlowAnalyzer extends JavaElementVisitor {
|
||||
if (refExpr.getParent() instanceof PsiMethodCallExpression) return null;
|
||||
return myPolicy.getUsedVariable(refExpr);
|
||||
}
|
||||
|
||||
private static class FinallyBlockSubroutine {
|
||||
private final PsiElement myElement;
|
||||
private final List<CallInstruction> myCalls;
|
||||
|
||||
public FinallyBlockSubroutine(@NotNull PsiElement element) {
|
||||
myElement = element;
|
||||
myCalls = new ArrayList<CallInstruction>();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public PsiElement getElement() {
|
||||
return myElement;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public List<CallInstruction> getCalls() {
|
||||
return myCalls;
|
||||
}
|
||||
|
||||
private void addCall(@NotNull CallInstruction callInstruction) {
|
||||
myCalls.add(callInstruction);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+15
@@ -488,4 +488,19 @@ class ManyExitsFromTry {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class BreakFromCatchInPresenceOfFinally {
|
||||
String f() {
|
||||
while (true) {
|
||||
try {
|
||||
return "try";
|
||||
}
|
||||
catch (Exception e) {
|
||||
break;
|
||||
}
|
||||
finally {}
|
||||
}
|
||||
return "end"; // reachable
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user