J2k: for through range with != comparison is also recognized

This commit is contained in:
Valentin Kipyatkov
2015-03-31 13:16:39 +03:00
parent 11b4e66fd0
commit d5326031c4
9 changed files with 120 additions and 47 deletions
@@ -152,81 +152,78 @@ class ForConverter(
&& !loopVar.hasWriteAccesses(referenceSearcher, condition) && !loopVar.hasWriteAccesses(referenceSearcher, condition)
&& condition is PsiBinaryExpression) { && condition is PsiBinaryExpression) {
val operationTokenType = condition.getOperationTokenType()
val reversed = when (operationTokenType) {
JavaTokenType.LT, JavaTokenType.LE -> false
JavaTokenType.GT, JavaTokenType.GE -> true
else -> return null
}
val left = condition.getLOperand() as? PsiReferenceExpression ?: return null val left = condition.getLOperand() as? PsiReferenceExpression ?: return null
val right = condition.getROperand() ?: return null val right = condition.getROperand() ?: return null
if (left.resolve() == loopVar) { if (left.resolve() == loopVar) {
val start = loopVar.getInitializer() ?: return null val start = loopVar.getInitializer() ?: return null
val operationType = if (reversed) JavaTokenType.MINUSMINUS else JavaTokenType.PLUSPLUS val operationType = (update as? PsiExpressionStatement)?.getExpression()?.isVariableIncrementOrDecrement(loopVar)
if ((update as? PsiExpressionStatement)?.getExpression()?.isVariableIncrementOrDecrement(loopVar, operationType) ?: false) { val reversed = when (operationType) {
val range = forIterationRange(start, right, operationTokenType).assignNoPrototype() JavaTokenType.PLUSPLUS -> false
val explicitType = if (settings.specifyLocalVariableTypeByDefault) JavaTokenType.MINUSMINUS -> true
PrimitiveType(Identifier("Int").assignNoPrototype()).assignNoPrototype() else -> return null
else
null
return ForeachStatement(loopVar.declarationIdentifier(), explicitType, range, codeConverter.convertStatementOrBlock(body), statement.isInSingleLine())
} }
val inclusive = when (condition.getOperationTokenType()) {
JavaTokenType.LT -> if (reversed) return null else false
JavaTokenType.LE -> if (reversed) return null else true
JavaTokenType.GT -> if (reversed) false else return null
JavaTokenType.GE -> if (reversed) true else return null
JavaTokenType.NE -> false
else -> return null
}
val range = forIterationRange(start, right, reversed, inclusive).assignNoPrototype()
val explicitType = if (settings.specifyLocalVariableTypeByDefault)
PrimitiveType(Identifier("Int").assignNoPrototype()).assignNoPrototype()
else
null
return ForeachStatement(loopVar.declarationIdentifier(), explicitType, range, codeConverter.convertStatementOrBlock(body), statement.isInSingleLine())
} }
} }
} }
return null return null
} }
private fun PsiElement.isVariableIncrementOrDecrement(variable: PsiVariable, operationTokenType: IElementType): Boolean { private fun PsiElement.isVariableIncrementOrDecrement(variable: PsiVariable): IElementType? {
//TODO: simplify code when KT-5453 fixed //TODO: simplify code when KT-5453 fixed
val pair = when (this) { val pair = when (this) {
is PsiPostfixExpression -> getOperationTokenType() to getOperand() is PsiPostfixExpression -> getOperationTokenType() to getOperand()
is PsiPrefixExpression -> getOperationTokenType() to getOperand() is PsiPrefixExpression -> getOperationTokenType() to getOperand()
else -> return false else -> return null
} }
return pair.first == operationTokenType && (pair.second as? PsiReferenceExpression)?.resolve() == variable if ((pair.second as? PsiReferenceExpression)?.resolve() != variable) return null
return pair.first
} }
private fun forIterationRange(start: PsiExpression, bound: PsiExpression, comparisonTokenType: IElementType): Expression { private fun forIterationRange(start: PsiExpression, bound: PsiExpression, reversed: Boolean, inclusiveComparison: Boolean): Expression {
val indicesRange = indicesIterationRange(start, bound, comparisonTokenType) val indicesRange = indicesIterationRange(start, bound, reversed, inclusiveComparison)
if (indicesRange != null) return indicesRange if (indicesRange != null) return indicesRange
val startConverted = codeConverter.convertExpression(start) val startConverted = codeConverter.convertExpression(start)
return when (comparisonTokenType) { return if (reversed)
JavaTokenType.LT, JavaTokenType.LE -> DownToExpression(startConverted, convertBound(bound, if (inclusiveComparison) 0 else +1))
RangeExpression(startConverted, convertBound(bound, if (comparisonTokenType == JavaTokenType.LT) -1 else 0)) else
RangeExpression(startConverted, convertBound(bound, if (inclusiveComparison) 0 else -1))
JavaTokenType.GT, JavaTokenType.GE ->
DownToExpression(startConverted, convertBound(bound, if (comparisonTokenType == JavaTokenType.GT) +1 else 0))
else ->
throw IllegalAccessException()
}
} }
private fun indicesIterationRange(start: PsiExpression, bound: PsiExpression, comparisonTokenType: IElementType): Expression? { private fun indicesIterationRange(start: PsiExpression, bound: PsiExpression, reversed: Boolean, inclusiveComparison: Boolean): Expression? {
val reversed = when (comparisonTokenType) {
JavaTokenType.LT -> false
JavaTokenType.GE -> true
else -> return null
}
val lower = if (reversed) bound else start
val upper = if (reversed) start else bound
if ((lower as? PsiLiteralExpression)?.getValue() != 0) return null
val collectionSize = if (reversed) { val collectionSize = if (reversed) {
if (upper !is PsiBinaryExpression) return null if (!inclusiveComparison) return null
if (upper.getOperationTokenType() != JavaTokenType.MINUS) return null
if ((upper.getROperand() as? PsiLiteralExpression)?.getValue() != 1) return null if ((bound as? PsiLiteralExpression)?.getValue() != 0) return null
upper.getLOperand()
if (start !is PsiBinaryExpression) return null
if (start.getOperationTokenType() != JavaTokenType.MINUS) return null
if ((start.getROperand() as? PsiLiteralExpression)?.getValue() != 1) return null
start.getLOperand()
} }
else { else {
upper if (inclusiveComparison) return null
if ((start as? PsiLiteralExpression)?.getValue() != 0) return null
bound
} }
var indices: Expression? = null var indices: Expression? = null
// check if it's iteration through list indices // check if it's iteration through list indices
@@ -0,0 +1,7 @@
public class A {
void foo(int min) {
for(int i = 10; i != min; i--) {
System.out.println(i);
}
}
}
@@ -0,0 +1,7 @@
public class A {
fun foo(min: Int) {
for (i in 10 downTo min + 1) {
println(i)
}
}
}
@@ -0,0 +1,3 @@
//statement
int[] array = new int[10];
for (int i = 0; i != 10; i++) {array[i] = i;}
@@ -0,0 +1,4 @@
val array = IntArray(10)
for (i in 0..9) {
array[i] = i
}
@@ -0,0 +1,10 @@
import java.util.List;
import java.util.ArrayList;
class C{
void foo(List<String> list) {
for (int i = 0; i != list.size(); i++) {
list.set(i, "a");
}
}
}
@@ -0,0 +1,9 @@
import java.util.ArrayList
class C {
fun foo(list: MutableList<String>) {
for (i in list.indices) {
list.set(i, "a")
}
}
}
@@ -1892,6 +1892,12 @@ public class JavaToKotlinConverterForWebDemoTestGenerated extends AbstractJavaTo
doTest(fileName); doTest(fileName);
} }
@TestMetadata("downTo4.java")
public void testDownTo4() throws Exception {
String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/for/downTo4.java");
doTest(fileName);
}
@TestMetadata("falseArrayIndicesReversed.java") @TestMetadata("falseArrayIndicesReversed.java")
public void testFalseArrayIndicesReversed() throws Exception { public void testFalseArrayIndicesReversed() throws Exception {
String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/for/falseArrayIndicesReversed.java"); String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/for/falseArrayIndicesReversed.java");
@@ -1940,6 +1946,12 @@ public class JavaToKotlinConverterForWebDemoTestGenerated extends AbstractJavaTo
doTest(fileName); doTest(fileName);
} }
@TestMetadata("forRangeWithNE.java")
public void testForRangeWithNE() throws Exception {
String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/for/forRangeWithNE.java");
doTest(fileName);
}
@TestMetadata("forThroughArrayIndices.java") @TestMetadata("forThroughArrayIndices.java")
public void testForThroughArrayIndices() throws Exception { public void testForThroughArrayIndices() throws Exception {
String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/for/forThroughArrayIndices.java"); String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/for/forThroughArrayIndices.java");
@@ -1958,6 +1970,12 @@ public class JavaToKotlinConverterForWebDemoTestGenerated extends AbstractJavaTo
doTest(fileName); doTest(fileName);
} }
@TestMetadata("forThroughListIndicesNE.java")
public void testForThroughListIndicesNE() throws Exception {
String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/for/forThroughListIndicesNE.java");
doTest(fileName);
}
@TestMetadata("forThroughNonArrayIndices.java") @TestMetadata("forThroughNonArrayIndices.java")
public void testForThroughNonArrayIndices() throws Exception { public void testForThroughNonArrayIndices() throws Exception {
String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/for/forThroughNonArrayIndices.java"); String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/for/forThroughNonArrayIndices.java");
@@ -1892,6 +1892,12 @@ public class JavaToKotlinConverterSingleFileTestGenerated extends AbstractJavaTo
doTest(fileName); doTest(fileName);
} }
@TestMetadata("downTo4.java")
public void testDownTo4() throws Exception {
String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/for/downTo4.java");
doTest(fileName);
}
@TestMetadata("falseArrayIndicesReversed.java") @TestMetadata("falseArrayIndicesReversed.java")
public void testFalseArrayIndicesReversed() throws Exception { public void testFalseArrayIndicesReversed() throws Exception {
String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/for/falseArrayIndicesReversed.java"); String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/for/falseArrayIndicesReversed.java");
@@ -1940,6 +1946,12 @@ public class JavaToKotlinConverterSingleFileTestGenerated extends AbstractJavaTo
doTest(fileName); doTest(fileName);
} }
@TestMetadata("forRangeWithNE.java")
public void testForRangeWithNE() throws Exception {
String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/for/forRangeWithNE.java");
doTest(fileName);
}
@TestMetadata("forThroughArrayIndices.java") @TestMetadata("forThroughArrayIndices.java")
public void testForThroughArrayIndices() throws Exception { public void testForThroughArrayIndices() throws Exception {
String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/for/forThroughArrayIndices.java"); String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/for/forThroughArrayIndices.java");
@@ -1958,6 +1970,12 @@ public class JavaToKotlinConverterSingleFileTestGenerated extends AbstractJavaTo
doTest(fileName); doTest(fileName);
} }
@TestMetadata("forThroughListIndicesNE.java")
public void testForThroughListIndicesNE() throws Exception {
String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/for/forThroughListIndicesNE.java");
doTest(fileName);
}
@TestMetadata("forThroughNonArrayIndices.java") @TestMetadata("forThroughNonArrayIndices.java")
public void testForThroughNonArrayIndices() throws Exception { public void testForThroughNonArrayIndices() throws Exception {
String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/for/forThroughNonArrayIndices.java"); String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/for/forThroughNonArrayIndices.java");