flowBreaksLoop moved to ControlFlowUtils; fixed handling of nested BlockStatements (IDEA-CR-16590)

This commit is contained in:
Tagir Valeev
2016-12-28 13:11:32 +07:00
parent 7f97e7741e
commit 5513e21fdc
3 changed files with 90 additions and 30 deletions
@@ -126,41 +126,13 @@ public class StringConcatenationInLoopsInspection extends BaseInspection {
if (variable != null) {
PsiLoopStatement commonLoop = getOutermostCommonLoop(expression, variable);
return commonLoop != null && !flowBreaksLoop(PsiTreeUtil.getParentOfType(expression, PsiStatement.class), commonLoop);
return commonLoop != null && !ControlFlowUtils
.flowBreaksLoop(PsiTreeUtil.getParentOfType(expression, PsiStatement.class), commonLoop);
}
}
return !containingStatementExits(expression);
}
@Contract("null, _ -> false")
private static boolean flowBreaksLoop(PsiStatement statement, PsiLoopStatement loop) {
if(statement == null || statement == loop) return false;
for(PsiStatement sibling = statement; sibling != null; sibling = PsiTreeUtil.getNextSiblingOfType(sibling, PsiStatement.class)) {
if(sibling instanceof PsiContinueStatement) return false;
if(sibling instanceof PsiThrowStatement || sibling instanceof PsiReturnStatement) return true;
if(sibling instanceof PsiBreakStatement) {
PsiBreakStatement breakStatement = (PsiBreakStatement)sibling;
PsiStatement exitedStatement = breakStatement.findExitedStatement();
if(exitedStatement == loop) return true;
return flowBreaksLoop(exitedStatement, loop);
}
}
PsiElement parent = statement.getParent();
if(parent == loop) return false;
if(parent instanceof PsiCodeBlock) {
PsiElement gParent = parent.getParent();
if(gParent instanceof PsiBlockStatement || gParent instanceof PsiSwitchStatement) {
return flowBreaksLoop((PsiStatement)gParent, loop);
}
return false;
}
if(parent instanceof PsiLabeledStatement || parent instanceof PsiIfStatement || parent instanceof PsiSwitchLabelStatement
|| parent instanceof PsiSwitchStatement) {
return flowBreaksLoop((PsiStatement)parent, loop);
}
return false;
}
private PsiLoopStatement getOutermostCommonLoop(PsiExpression expression, PsiVariable variable) {
PsiElement stopAt = null;
PsiCodeBlock block = getSurroundingBlock(expression);
@@ -600,6 +600,53 @@ public class ControlFlowUtils {
return false;
}
/**
* Checks whether control flow after executing given statement will definitely not go into the next iteration of given loop.
*
* @param statement executed statement. It's not checked whether this statement itself breaks the loop.
* @param loop a surrounding loop. Must be parent of statement
* @return true if it can be statically defined that next loop iteration will not be executed.
*/
@Contract("null, _ -> false")
public static boolean flowBreaksLoop(PsiStatement statement, PsiLoopStatement loop) {
if(statement == null || statement == loop) return false;
for (PsiStatement sibling = nextExecutedStatement(statement); sibling != null; sibling = nextExecutedStatement(sibling)) {
if(sibling instanceof PsiContinueStatement) return false;
if(sibling instanceof PsiThrowStatement || sibling instanceof PsiReturnStatement) return true;
if(sibling instanceof PsiBreakStatement) {
PsiBreakStatement breakStatement = (PsiBreakStatement)sibling;
PsiStatement exitedStatement = breakStatement.findExitedStatement();
if(exitedStatement == loop) return true;
return flowBreaksLoop(exitedStatement, loop);
}
}
return false;
}
@Nullable
private static PsiStatement nextExecutedStatement(PsiStatement statement) {
PsiStatement next = PsiTreeUtil.getNextSiblingOfType(statement, PsiStatement.class);
while (next instanceof PsiBlockStatement) {
PsiStatement[] statements = ((PsiBlockStatement)next).getCodeBlock().getStatements();
if (statements.length == 0) break;
next = statements[0];
}
if (next == null) {
PsiElement parent = statement.getParent();
if (parent instanceof PsiCodeBlock) {
PsiElement gParent = parent.getParent();
if (gParent instanceof PsiBlockStatement || gParent instanceof PsiSwitchStatement) {
return nextExecutedStatement((PsiStatement)gParent);
}
}
else if (parent instanceof PsiLabeledStatement || parent instanceof PsiIfStatement || parent instanceof PsiSwitchLabelStatement
|| parent instanceof PsiSwitchStatement) {
return nextExecutedStatement((PsiStatement)parent);
}
}
return next;
}
private static class NakedBreakFinder extends JavaRecursiveElementWalkingVisitor {
private boolean m_found;
@@ -53,6 +53,47 @@ public class StringConcatenationInLoop
throw new Error("foo" + i);
}
}
String s = "";
for(int i = 0; i < 5; i++) {
if(i > 2) {
s += i;
{
System.out.println(s);
break;
}
}
}
for(int i = 0; i < 5; i++) {
if(i > 2) {
s += i;
{
System.out.println(s);
{
break;
}
}
}
}
for(int i = 0; i < 5; i++) {
if(i > 2) {
s += i;
{
{
System.out.println(s);
}
break;
}
}
}
for(int i = 0; i < 5; i++) {
if(i > 2) {
s <warning descr="String concatenation '+=' in loop">+=</warning> i;
{
System.out.println(s);
}
System.out.println(s);
}
}
System.out.println(foo);
return foo;
}