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)
&& 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 right = condition.getROperand() ?: return null
if (left.resolve() == loopVar) {
val start = loopVar.getInitializer() ?: return null
val operationType = if (reversed) JavaTokenType.MINUSMINUS else JavaTokenType.PLUSPLUS
if ((update as? PsiExpressionStatement)?.getExpression()?.isVariableIncrementOrDecrement(loopVar, operationType) ?: false) {
val range = forIterationRange(start, right, operationTokenType).assignNoPrototype()
val explicitType = if (settings.specifyLocalVariableTypeByDefault)
PrimitiveType(Identifier("Int").assignNoPrototype()).assignNoPrototype()
else
null
return ForeachStatement(loopVar.declarationIdentifier(), explicitType, range, codeConverter.convertStatementOrBlock(body), statement.isInSingleLine())
val operationType = (update as? PsiExpressionStatement)?.getExpression()?.isVariableIncrementOrDecrement(loopVar)
val reversed = when (operationType) {
JavaTokenType.PLUSPLUS -> false
JavaTokenType.MINUSMINUS -> true
else -> return null
}
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
}
private fun PsiElement.isVariableIncrementOrDecrement(variable: PsiVariable, operationTokenType: IElementType): Boolean {
private fun PsiElement.isVariableIncrementOrDecrement(variable: PsiVariable): IElementType? {
//TODO: simplify code when KT-5453 fixed
val pair = when (this) {
is PsiPostfixExpression -> 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 {
val indicesRange = indicesIterationRange(start, bound, comparisonTokenType)
private fun forIterationRange(start: PsiExpression, bound: PsiExpression, reversed: Boolean, inclusiveComparison: Boolean): Expression {
val indicesRange = indicesIterationRange(start, bound, reversed, inclusiveComparison)
if (indicesRange != null) return indicesRange
val startConverted = codeConverter.convertExpression(start)
return when (comparisonTokenType) {
JavaTokenType.LT, JavaTokenType.LE ->
RangeExpression(startConverted, convertBound(bound, if (comparisonTokenType == JavaTokenType.LT) -1 else 0))
JavaTokenType.GT, JavaTokenType.GE ->
DownToExpression(startConverted, convertBound(bound, if (comparisonTokenType == JavaTokenType.GT) +1 else 0))
else ->
throw IllegalAccessException()
}
return if (reversed)
DownToExpression(startConverted, convertBound(bound, if (inclusiveComparison) 0 else +1))
else
RangeExpression(startConverted, convertBound(bound, if (inclusiveComparison) 0 else -1))
}
private fun indicesIterationRange(start: PsiExpression, bound: PsiExpression, comparisonTokenType: IElementType): 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
private fun indicesIterationRange(start: PsiExpression, bound: PsiExpression, reversed: Boolean, inclusiveComparison: Boolean): Expression? {
val collectionSize = if (reversed) {
if (upper !is PsiBinaryExpression) return null
if (upper.getOperationTokenType() != JavaTokenType.MINUS) return null
if ((upper.getROperand() as? PsiLiteralExpression)?.getValue() != 1) return null
upper.getLOperand()
if (!inclusiveComparison) return null
if ((bound as? PsiLiteralExpression)?.getValue() != 0) return null
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 {
upper
if (inclusiveComparison) return null
if ((start as? PsiLiteralExpression)?.getValue() != 0) return null
bound
}
var indices: Expression? = null
// 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);
}
@TestMetadata("downTo4.java")
public void testDownTo4() throws Exception {
String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/for/downTo4.java");
doTest(fileName);
}
@TestMetadata("falseArrayIndicesReversed.java")
public void testFalseArrayIndicesReversed() throws Exception {
String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/for/falseArrayIndicesReversed.java");
@@ -1940,6 +1946,12 @@ public class JavaToKotlinConverterForWebDemoTestGenerated extends AbstractJavaTo
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")
public void testForThroughArrayIndices() throws Exception {
String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/for/forThroughArrayIndices.java");
@@ -1958,6 +1970,12 @@ public class JavaToKotlinConverterForWebDemoTestGenerated extends AbstractJavaTo
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")
public void testForThroughNonArrayIndices() throws Exception {
String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/for/forThroughNonArrayIndices.java");
@@ -1892,6 +1892,12 @@ public class JavaToKotlinConverterSingleFileTestGenerated extends AbstractJavaTo
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")
public void testFalseArrayIndicesReversed() throws Exception {
String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/for/falseArrayIndicesReversed.java");
@@ -1940,6 +1946,12 @@ public class JavaToKotlinConverterSingleFileTestGenerated extends AbstractJavaTo
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")
public void testForThroughArrayIndices() throws Exception {
String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/for/forThroughArrayIndices.java");
@@ -1958,6 +1970,12 @@ public class JavaToKotlinConverterSingleFileTestGenerated extends AbstractJavaTo
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")
public void testForThroughNonArrayIndices() throws Exception {
String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/for/forThroughNonArrayIndices.java");