mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
flowBreaksLoop moved to ControlFlowUtils; fixed handling of nested BlockStatements (IDEA-CR-16590)
This commit is contained in:
+2
-30
@@ -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);
|
||||
|
||||
+47
@@ -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;
|
||||
|
||||
|
||||
+41
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user