CountingLoop: support decreasing loops

Fixes IDEA-190412 Unroll loop action: support decreasing loops
This commit is contained in:
Tagir Valeev
2018-04-17 17:48:00 +07:00
parent 4417321362
commit 91b269f9fb
12 changed files with 92 additions and 29 deletions
@@ -643,7 +643,7 @@ public class ControlFlowAnalyzer extends JavaElementVisitor {
*/
private boolean addCountingLoopBound(PsiForStatement statement) {
CountingLoop loop = CountingLoop.from(statement);
if (loop == null) return false;
if (loop == null || loop.isDescending()) return false;
PsiLocalVariable counter = loop.getCounter();
Long start = asLong(loop.getInitializer());
Long end = asLong(loop.getBound());
@@ -113,6 +113,13 @@ public class DfaRelationValue extends DfaValue {
}
}
/**
* @return true if this relation is >, >=, <, != or <=
*/
public boolean isInequality() {
return this == LE || this == GE || this == LT || this == GT || this == NE;
}
@Override
public String toString() {
return myName;
@@ -112,13 +112,15 @@ public class UnrollLoopAction extends PsiElementBaseIntentionAction {
if (loop instanceof PsiForStatement) {
CountingLoop countingLoop = CountingLoop.from((PsiForStatement)loop);
if (countingLoop != null) {
boolean descending = countingLoop.isDescending();
long multiplier = descending ? -1 : 1;
Object from = ExpressionUtils.computeConstantExpression(countingLoop.getInitializer());
if (!(from instanceof Integer) && !(from instanceof Long)) return Collections.emptyList();
long fromValue = ((Number)from).longValue();
Object to = ExpressionUtils.computeConstantExpression(countingLoop.getBound());
if (!(to instanceof Integer) && !(to instanceof Long)) return Collections.emptyList();
long toValue = ((Number)to).longValue();
long diff = toValue - fromValue;
long diff = multiplier * (toValue - fromValue);
String suffix = PsiType.LONG.equals(countingLoop.getCounter().getType()) ? "L" : "";
if (countingLoop.isIncluding()) {
diff++; // overflow is ok: diff will become negative and we will exit
@@ -129,7 +131,8 @@ public class UnrollLoopAction extends PsiElementBaseIntentionAction {
return new AbstractList<PsiExpression>() {
@Override
public PsiExpression get(int index) {
return factory.createExpressionFromText(String.valueOf(fromValue + index) + suffix, loop);
long value = fromValue + multiplier * index;
return factory.createExpressionFromText(value + suffix, loop);
}
@Override
@@ -38,7 +38,7 @@ public class SuspiciousListRemoveInLoopInspection extends AbstractBaseJavaLocalI
if (!(parent instanceof PsiForStatement)) return;
CountingLoop loop = CountingLoop.from((PsiForStatement)parent);
if (loop == null) return;
if (loop == null || loop.isDescending()) return;
if (!arg.isReferenceTo(loop.getCounter())) return;
if (ControlFlowUtils.isExecutedOnceInLoop(parentStatement, (PsiLoopStatement)parent)) return;
holder.registerProblem(Objects.requireNonNull(call.getMethodExpression().getReferenceNameElement()),
@@ -124,7 +124,12 @@ public class UseBulkOperationInspection extends AbstractBaseJavaLocalInspectionT
@Nullable
private static PsiExpression findIterableForIndexedLoop(PsiForStatement loop, PsiExpression getElementExpression) {
CountingLoop countingLoop = CountingLoop.from(loop);
if (countingLoop == null || countingLoop.isIncluding() || !ExpressionUtils.isZero(countingLoop.getInitializer())) return null;
if (countingLoop == null ||
countingLoop.isIncluding() ||
countingLoop.isDescending() ||
!ExpressionUtils.isZero(countingLoop.getInitializer())) {
return null;
}
IndexedContainer container = IndexedContainer.fromLengthExpression(countingLoop.getBound());
if (container == null) return null;
PsiExpression index = container.extractIndexFromGetExpression(getElementExpression);
@@ -35,7 +35,7 @@ public class Java8ArraySetAllInspection extends AbstractBaseJavaLocalInspectionT
public void visitForStatement(PsiForStatement statement) {
super.visitForStatement(statement);
CountingLoop loop = CountingLoop.from(statement);
if (loop == null || loop.isIncluding()) return;
if (loop == null || loop.isIncluding() || loop.isDescending()) return;
IndexedContainer container = IndexedContainer.fromLengthExpression(loop.getBound());
if (container == null || !(container.getQualifier().getType() instanceof PsiArrayType)) return;
if (!StreamApiUtil.isSupportedStreamElement(container.getElementType())) return;
@@ -1209,7 +1209,7 @@ public class StreamApiMigrationInspection extends AbstractBaseJavaLocalInspectio
@Nullable
public static CountingLoopSource from(PsiForStatement forStatement) {
CountingLoop loop = CountingLoop.from(forStatement);
if (loop == null) return null;
if (loop == null || loop.isDescending()) return null;
return new CountingLoopSource(forStatement, loop.getCounter(), loop.getInitializer(), loop.getBound(), loop.isIncluding());
}
}
@@ -0,0 +1,16 @@
// "Unroll loop" "true"
class Test {
void test() {
System.out.println("Hi!" + 10);
System.out.println("Hi!" + 9);
System.out.println("Hi!" + 8);
System.out.println("Hi!" + 7);
System.out.println("Hi!" + 6);
System.out.println("Hi!" + 5);
System.out.println("Hi!" + 4);
System.out.println("Hi!" + 3);
System.out.println("Hi!" + 2);
System.out.println("Hi!" + 1);
System.out.println("Hi!" + 0);
}
}
@@ -1,7 +1,7 @@
// "Unroll loop" "true"
class Test {
void test() {
fo<caret>r (int i = 0; i < 10; i++ // line comment
fo<caret>r (int i = 0; i != 10; i++ // line comment
) {
System.out.println("Hi!" + i);
}
@@ -0,0 +1,8 @@
// "Unroll loop" "true"
class Test {
void test() {
fo<caret>r (int i = 10; 0 <= i; --i) {
System.out.println("Hi!" + i);
}
}
}
@@ -0,0 +1,8 @@
// "Unroll loop" "false"
class Test {
void test() {
fo<caret>r (int i = 10; i <= 0; --i) {
System.out.println("Hi!" + i);
}
}
}