diff --git a/j2k/src/org/jetbrains/kotlin/j2k/ExpressionConverter.kt b/j2k/src/org/jetbrains/kotlin/j2k/ExpressionConverter.kt index 09bdfabdc22..f21bc3340ef 100644 --- a/j2k/src/org/jetbrains/kotlin/j2k/ExpressionConverter.kt +++ b/j2k/src/org/jetbrains/kotlin/j2k/ExpressionConverter.kt @@ -20,6 +20,7 @@ package org.jetbrains.kotlin.j2k import com.intellij.openapi.project.Project import com.intellij.psi.* import com.intellij.psi.search.GlobalSearchScope +import com.intellij.psi.tree.IElementType import com.intellij.psi.util.MethodSignature import com.intellij.psi.util.MethodSignatureUtil import com.intellij.psi.util.PsiTreeUtil @@ -109,10 +110,19 @@ class DefaultExpressionConverter : JavaElementVisitor(), ExpressionConverter { override fun visitBinaryExpression(expression: PsiBinaryExpression) { val left = expression.lOperand val right = expression.rOperand - var leftConverted = codeConverter.convertExpression(left, null) - var rightConverted = codeConverter.convertExpression(right, null) val operationTokenType = expression.operationTokenType + + val leftOperandExpectedType = getOperandExpectedType(left, right, operationTokenType) + var leftConverted = codeConverter.convertExpression(left, leftOperandExpectedType) + var rightConverted = codeConverter.convertExpression( + right, + if (leftOperandExpectedType == null) + getOperandExpectedType(right, left, operationTokenType) + else + null + ) + if (operationTokenType in NON_NULL_OPERAND_OPS) { leftConverted = BangBangExpression.surroundIfNullable(leftConverted) rightConverted = BangBangExpression.surroundIfNullable(rightConverted) @@ -132,6 +142,54 @@ class DefaultExpressionConverter : JavaElementVisitor(), ExpressionConverter { } } + private fun getOperandExpectedType(current: PsiExpression?, other: PsiExpression?, operationTokenType: IElementType): PsiType? { + val currentType = current?.type + val otherType = other?.type + if (currentType !is PsiPrimitiveType || otherType !is PsiPrimitiveType) return null + if (currentType == PsiType.BOOLEAN || otherType == PsiType.BOOLEAN) return null + + if (operationTokenType == JavaTokenType.EQEQ + || operationTokenType == JavaTokenType.NE + || currentType == PsiType.CHAR) { + if (currentType < otherType) return otherType + } + + return null + } + + infix operator fun PsiPrimitiveType.compareTo(other: PsiPrimitiveType): Int { + return when(this) { + other -> 0 + PsiType.BYTE -> when(other) { + PsiType.CHAR -> 1 + else -> -1 + } + PsiType.SHORT -> when(other) { + PsiType.CHAR, + PsiType.BYTE -> 1 + else -> -1 + } + PsiType.INT -> when(other) { + PsiType.BYTE, + PsiType.SHORT, + PsiType.CHAR -> 1 + else -> -1 + } + PsiType.LONG -> when(other) { + PsiType.DOUBLE, + PsiType.FLOAT -> -1 + else -> 1 + } + PsiType.FLOAT -> when(other) { + PsiType.DOUBLE -> -1 + else -> 1 + } + PsiType.DOUBLE -> 1 + PsiType.CHAR -> -1 + else -> throw AssertionError("Unknown primitive type $this") + } + } + private fun canKeepEqEq(left: PsiExpression, right: PsiExpression?): Boolean { if (left.isNullLiteral() || (right?.isNullLiteral() ?: false)) return true val type = left.type diff --git a/j2k/testData/fileOrElement/issues/kt-9997.java b/j2k/testData/fileOrElement/issues/kt-9997.java index ab2d2808051..13a49837010 100644 --- a/j2k/testData/fileOrElement/issues/kt-9997.java +++ b/j2k/testData/fileOrElement/issues/kt-9997.java @@ -1,5 +1,5 @@ class A { - public void foo() { + public void equals() { int i = 1; byte b = 1; short s = 1; @@ -7,5 +7,132 @@ class A { double d = 1.0; float f = 1.0f; char c = 1; + + t(i == i); + t(i == b); + t(i == s); + t(i == l); + t(i == d); + t(i == f); + t(i == c); + + t(b == i); + t(b == b); + t(b == s); + t(b == l); + t(b == d); + t(b == f); + t(b == c); + + t(s == i); + t(s == b); + t(s == s); + t(s == l); + t(s == d); + t(s == f); + t(s == c); + + t(l == i); + t(l == b); + t(l == s); + t(l == l); + t(l == d); + t(l == f); + t(l == c); + + t(d == i); + t(d == b); + t(d == s); + t(d == l); + t(d == d); + t(d == f); + t(d == c); + + t(f == i); + t(f == b); + t(f == s); + t(f == l); + t(f == d); + t(f == f); + t(f == c); + + t(c == i); + t(c == b); + t(c == s); + t(c == l); + t(c == d); + t(c == f); + t(c == c); + + t(i != d); + } + + public void compare() { + int i = 1; + byte b = 1; + short s = 1; + long l = 1; + double d = 1.0; + float f = 1.0f; + char c = 1; + + t(i > i); + t(i > b); + t(i > s); + t(i > l); + t(i > d); + t(i > f); + t(i > c); + + t(b > i); + t(b > b); + t(b > s); + t(b > l); + t(b > d); + t(b > f); + t(b > c); + + t(s > i); + t(s > b); + t(s > s); + t(s > l); + t(s > d); + t(s > f); + t(s > c); + + t(l > i); + t(l > b); + t(l > s); + t(l > l); + t(l > d); + t(l > f); + t(l > c); + + t(d > i); + t(d > b); + t(d > s); + t(d > l); + t(d > d); + t(d > f); + t(d > c); + + t(f > i); + t(f > b); + t(f > s); + t(f > l); + t(f > d); + t(f > f); + t(f > c); + + t(c > i); + t(c > b); + t(c > s); + t(c > l); + t(c > d); + t(c > f); + t(c > c); + } + + private void t(boolean b) { } } \ No newline at end of file diff --git a/j2k/testData/fileOrElement/issues/kt-9997.kt b/j2k/testData/fileOrElement/issues/kt-9997.kt index 9c38834a0a0..e2987b1265b 100644 --- a/j2k/testData/fileOrElement/issues/kt-9997.kt +++ b/j2k/testData/fileOrElement/issues/kt-9997.kt @@ -1,5 +1,5 @@ internal class A { - fun foo() { + fun equals() { val i = 1 val b: Byte = 1 val s: Short = 1 @@ -7,5 +7,132 @@ internal class A { val d = 1.0 val f = 1.0f val c: Char = 1.toChar() + + t(i == i) + t(i == b.toInt()) + t(i == s.toInt()) + t(i.toLong() == l) + t(i.toDouble() == d) + t(i.toFloat() == f) + t(i == c.toInt()) + + t(b.toInt() == i) + t(b == b) + t(b.toShort() == s) + t(b.toLong() == l) + t(b.toDouble() == d) + t(b.toFloat() == f) + t(b == c.toByte()) + + t(s.toInt() == i) + t(s == b.toShort()) + t(s == s) + t(s.toLong() == l) + t(s.toDouble() == d) + t(s.toFloat() == f) + t(s == c.toShort()) + + t(l == i.toLong()) + t(l == b.toLong()) + t(l == s.toLong()) + t(l == l) + t(l.toDouble() == d) + t(l.toFloat() == f) + t(l == c.toLong()) + + t(d == i.toDouble()) + t(d == b.toDouble()) + t(d == s.toDouble()) + t(d == l.toDouble()) + t(d == d) + t(d == f.toDouble()) + t(d == c.toDouble()) + + t(f == i.toFloat()) + t(f == b.toFloat()) + t(f == s.toFloat()) + t(f == l.toFloat()) + t(f.toDouble() == d) + t(f == f) + t(f == c.toFloat()) + + t(c.toInt() == i) + t(c.toByte() == b) + t(c.toShort() == s) + t(c.toLong() == l) + t(c.toDouble() == d) + t(c.toFloat() == f) + t(c == c) + + t(i.toDouble() != d) + } + + fun compare() { + val i = 1 + val b: Byte = 1 + val s: Short = 1 + val l: Long = 1 + val d = 1.0 + val f = 1.0f + val c: Char = 1.toChar() + + t(i > i) + t(i > b) + t(i > s) + t(i > l) + t(i > d) + t(i > f) + t(i > c.toInt()) + + t(b > i) + t(b > b) + t(b > s) + t(b > l) + t(b > d) + t(b > f) + t(b > c.toByte()) + + t(s > i) + t(s > b) + t(s > s) + t(s > l) + t(s > d) + t(s > f) + t(s > c.toShort()) + + t(l > i) + t(l > b) + t(l > s) + t(l > l) + t(l > d) + t(l > f) + t(l > c.toLong()) + + t(d > i) + t(d > b) + t(d > s) + t(d > l) + t(d > d) + t(d > f) + t(d > c.toDouble()) + + t(f > i) + t(f > b) + t(f > s) + t(f > l) + t(f > d) + t(f > f) + t(f > c.toFloat()) + + t(c.toInt() > i) + t(c.toByte() > b) + t(c.toShort() > s) + t(c.toLong() > l) + t(c.toDouble() > d) + t(c.toFloat() > f) + t(c > c) + } + + private fun t(b: Boolean) { } } \ No newline at end of file diff --git a/j2k/testData/fileOrElement/issues/operationsWithChar.java b/j2k/testData/fileOrElement/issues/operationsWithChar.java new file mode 100644 index 00000000000..71413469660 --- /dev/null +++ b/j2k/testData/fileOrElement/issues/operationsWithChar.java @@ -0,0 +1,45 @@ +//file +class Test { + public void operationsWithChar() { + char c = 1; + int i = 1; + + b(i > c); + b(i >= c); + b(i < c); + b(i <= c); + + b(c > i); + b(c >= i); + b(c < i); + b(c <= i); + + b(c == i); + b(c != i); + b(i == c); + b(i != c); + + i(i + c); + i(i - c); + i(i / c); + i(i * c); + i(i % c); + i(i | c); + i(i & c); + i(i << c); + i(i >> c); + + i(c + i); + i(c - i); + i(c / i); + i(c * i); + i(c % i); + i(c | i); + i(c & i); + i(c << i); + i(c >> i); + } + + public void b(boolean b) {} + public void i(int i) {} +} \ No newline at end of file diff --git a/j2k/testData/fileOrElement/issues/operationsWithChar.kt b/j2k/testData/fileOrElement/issues/operationsWithChar.kt new file mode 100644 index 00000000000..2a2a8ddbf32 --- /dev/null +++ b/j2k/testData/fileOrElement/issues/operationsWithChar.kt @@ -0,0 +1,47 @@ +internal class Test { + fun operationsWithChar() { + val c: Char = 1.toChar() + val i = 1 + + b(i > c.toInt()) + b(i >= c.toInt()) + b(i < c.toInt()) + b(i <= c.toInt()) + + b(c.toInt() > i) + b(c.toInt() >= i) + b(c.toInt() < i) + b(c.toInt() <= i) + + b(c.toInt() == i) + b(c.toInt() != i) + b(i == c.toInt()) + b(i != c.toInt()) + + i(i + c.toInt()) + i(i - c.toInt()) + i(i / c.toInt()) + i(i * c.toInt()) + i(i % c.toInt()) + i(i or c.toInt()) + i(i and c.toInt()) + i(i shl c.toInt()) + i(i shr c.toInt()) + + i(c.toInt() + i) + i(c.toInt() - i) + i(c.toInt() / i) + i(c.toInt() * i) + i(c.toInt() % i) + i(c.toInt() or i) + i(c.toInt() and i) + i(c.toInt() shl i) + i(c.toInt() shr i) + } + + fun b(b: Boolean) { + } + + fun i(i: Int) { + } +} \ No newline at end of file diff --git a/j2k/tests/org/jetbrains/kotlin/j2k/JavaToKotlinConverterForWebDemoTestGenerated.java b/j2k/tests/org/jetbrains/kotlin/j2k/JavaToKotlinConverterForWebDemoTestGenerated.java index 636519bde0e..0fb57b0fbb8 100644 --- a/j2k/tests/org/jetbrains/kotlin/j2k/JavaToKotlinConverterForWebDemoTestGenerated.java +++ b/j2k/tests/org/jetbrains/kotlin/j2k/JavaToKotlinConverterForWebDemoTestGenerated.java @@ -2791,6 +2791,12 @@ public class JavaToKotlinConverterForWebDemoTestGenerated extends AbstractJavaTo doTest(fileName); } + @TestMetadata("operationsWithChar.java") + public void testOperationsWithChar() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/issues/operationsWithChar.java"); + doTest(fileName); + } + @TestMetadata("qualifyStatic.java") public void testQualifyStatic() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/issues/qualifyStatic.java"); diff --git a/j2k/tests/org/jetbrains/kotlin/j2k/JavaToKotlinConverterSingleFileTestGenerated.java b/j2k/tests/org/jetbrains/kotlin/j2k/JavaToKotlinConverterSingleFileTestGenerated.java index 7c11381f4ad..dd27c6a8583 100644 --- a/j2k/tests/org/jetbrains/kotlin/j2k/JavaToKotlinConverterSingleFileTestGenerated.java +++ b/j2k/tests/org/jetbrains/kotlin/j2k/JavaToKotlinConverterSingleFileTestGenerated.java @@ -2791,6 +2791,12 @@ public class JavaToKotlinConverterSingleFileTestGenerated extends AbstractJavaTo doTest(fileName); } + @TestMetadata("operationsWithChar.java") + public void testOperationsWithChar() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/issues/operationsWithChar.java"); + doTest(fileName); + } + @TestMetadata("qualifyStatic.java") public void testQualifyStatic() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/issues/qualifyStatic.java");