mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
CountingLoop: support decreasing loops
Fixes IDEA-190412 Unroll loop action: support decreasing loops
This commit is contained in:
+1
-1
@@ -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());
|
||||
|
||||
+7
@@ -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
|
||||
|
||||
+1
-1
@@ -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()),
|
||||
|
||||
+6
-1
@@ -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);
|
||||
|
||||
+1
-1
@@ -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;
|
||||
|
||||
+1
-1
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
+16
@@ -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
-1
@@ -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);
|
||||
}
|
||||
|
||||
+8
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
+8
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
+37
-21
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package com.siyeh.ig.psiutils;
|
||||
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaRelationValue.RelationType;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
@@ -25,7 +26,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
import static com.intellij.util.ObjectUtils.tryCast;
|
||||
|
||||
/**
|
||||
* Represents a loop of form {@code for(int/long counter = initializer; counter </<= bound; counter++)}
|
||||
* Represents a loop of form {@code for(int/long counter = initializer; counter </<= bound; counter++/--)}
|
||||
*
|
||||
* @author Tagir Valeev
|
||||
*/
|
||||
@@ -35,17 +36,20 @@ public class CountingLoop {
|
||||
final @NotNull PsiExpression myInitializer;
|
||||
final @NotNull PsiExpression myBound;
|
||||
final boolean myIncluding;
|
||||
final boolean myDescending;
|
||||
|
||||
private CountingLoop(@NotNull PsiLoopStatement loop,
|
||||
@NotNull PsiLocalVariable counter,
|
||||
@NotNull PsiExpression initializer,
|
||||
@NotNull PsiExpression bound,
|
||||
boolean including) {
|
||||
boolean including,
|
||||
boolean descending) {
|
||||
myInitializer = initializer;
|
||||
myCounter = counter;
|
||||
myLoop = loop;
|
||||
myBound = bound;
|
||||
myIncluding = including;
|
||||
myDescending = descending;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -87,6 +91,13 @@ public class CountingLoop {
|
||||
return myIncluding;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if the loop is descending
|
||||
*/
|
||||
public boolean isDescending() {
|
||||
return myDescending;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static CountingLoop from(PsiForStatement forStatement) {
|
||||
// check that initialization is for(int/long i = <initial_value>;...;...)
|
||||
@@ -100,33 +111,38 @@ public class CountingLoop {
|
||||
if(initializer == null) return null;
|
||||
|
||||
// check that increment is like for(...;...;i++)
|
||||
if(!VariableAccessUtils.variableIsIncremented(counter, forStatement.getUpdate())) return null;
|
||||
boolean descending;
|
||||
if(VariableAccessUtils.variableIsIncremented(counter, forStatement.getUpdate())) {
|
||||
descending = false;
|
||||
} else if (VariableAccessUtils.variableIsDecremented(counter, forStatement.getUpdate())) {
|
||||
descending = true;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
// check that condition is like for(...;i<bound;...) or for(...;i<=bound;...)
|
||||
PsiBinaryExpression condition = tryCast(forStatement.getCondition(), PsiBinaryExpression.class);
|
||||
if(condition == null) return null;
|
||||
IElementType type = condition.getOperationTokenType();
|
||||
boolean closed = false;
|
||||
PsiExpression bound;
|
||||
PsiExpression ref;
|
||||
if(type.equals(JavaTokenType.LE)) {
|
||||
bound = condition.getROperand();
|
||||
ref = condition.getLOperand();
|
||||
RelationType relationType = RelationType.fromElementType(type);
|
||||
if (relationType == null || !relationType.isInequality()) return null;
|
||||
if (relationType.isSubRelation(RelationType.EQ)) {
|
||||
closed = true;
|
||||
} else if(type.equals(JavaTokenType.LT)) {
|
||||
bound = condition.getROperand();
|
||||
ref = condition.getLOperand();
|
||||
} else if(type.equals(JavaTokenType.GE)) {
|
||||
bound = condition.getLOperand();
|
||||
ref = condition.getROperand();
|
||||
closed = true;
|
||||
} else if(type.equals(JavaTokenType.GT)) {
|
||||
bound = condition.getLOperand();
|
||||
ref = condition.getROperand();
|
||||
} else return null;
|
||||
if(bound == null || !ExpressionUtils.isReferenceTo(ref, counter)) return null;
|
||||
}
|
||||
if (descending) {
|
||||
relationType = relationType.getFlipped();
|
||||
assert relationType != null;
|
||||
}
|
||||
PsiExpression bound = ExpressionUtils.getOtherOperand(condition, counter);
|
||||
if (bound == null) return null;
|
||||
if (bound == condition.getLOperand()) {
|
||||
relationType = relationType.getFlipped();
|
||||
assert relationType != null;
|
||||
}
|
||||
if (!relationType.isSubRelation(RelationType.LT)) return null;
|
||||
if(!TypeConversionUtil.areTypesAssignmentCompatible(counter.getType(), bound)) return null;
|
||||
if(VariableAccessUtils.variableIsAssigned(counter, forStatement.getBody())) return null;
|
||||
return new CountingLoop(forStatement, counter, initializer, bound, closed);
|
||||
return new CountingLoop(forStatement, counter, initializer, bound, closed, descending);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user