J2K: cast right operand of equals for primitive types to left operand type
#KT-9997 Fixed
This commit is contained in:
@@ -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
|
||||
|
||||
+128
-1
@@ -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) {
|
||||
}
|
||||
}
|
||||
+128
-1
@@ -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) {
|
||||
}
|
||||
}
|
||||
@@ -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) {}
|
||||
}
|
||||
@@ -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) {
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
|
||||
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user