KT-19390: Fix char + String in Java is converted to code with multiple type errors in Kotlin (#1235)

#KT-19390 Fixed
This commit is contained in:
Toshiaki Kameyama
2017-11-13 20:13:34 +09:00
committed by Simon Ogorodnik
parent 1e940bde01
commit 2be27d5380
8 changed files with 96 additions and 21 deletions
@@ -17,7 +17,6 @@
package org.jetbrains.kotlin.j2k
import com.intellij.psi.*
import com.intellij.psi.CommonClassNames.*
import org.jetbrains.kotlin.j2k.ast.*
class CodeConverter(
@@ -112,10 +111,15 @@ class CodeConverter(
convertedExpression = BangBangExpression.surroundIfNullable(convertedExpression)
}
if (needConversion(actualType, expectedType)) {
if (actualType.needTypeConversion(expectedType)) {
val expectedTypeStr = expectedType.canonicalText
if (expression is PsiLiteralExpression) {
if (expectedTypeStr == "float" || expectedTypeStr == "double") {
if (actualType.canonicalText == "char" && expression.parent !is PsiExpressionList) {
expression.getTypeConversionMethod(expectedType)?.also {
convertedExpression = MethodCallExpression.buildNonNull(convertedExpression, it)
}
}
else if (expectedTypeStr == "float" || expectedTypeStr == "double") {
var text = convertedExpression.canonicalCode()
if (text.last() in setOf('f', 'L')) {
text = text.substring(0, text.length - 1)
@@ -165,7 +169,7 @@ class CodeConverter(
resultType = resultType.toNotNullType()
}
if (needConversion(actualType, expectedType)) {
if (actualType.needTypeConversion(expectedType)) {
val expectedTypeStr = expectedType.canonicalText
val willConvert = if (convertedExpression is LiteralExpression
@@ -186,21 +190,4 @@ class CodeConverter(
private fun PsiPrefixExpression.isLiteralWithSign()
= operand is PsiLiteralExpression && operationTokenType in setOf(JavaTokenType.PLUS, JavaTokenType.MINUS)
private fun needConversion(actual: PsiType, expected: PsiType): Boolean {
val expectedStr = expected.canonicalText
val actualStr = actual.canonicalText
return expectedStr != actualStr &&
expectedStr != typeConversionMap[actualStr] &&
actualStr != typeConversionMap[expectedStr]
}
private val typeConversionMap: Map<String, String> = mapOf(
JAVA_LANG_BYTE to "byte",
JAVA_LANG_SHORT to "short",
JAVA_LANG_INTEGER to "int",
JAVA_LANG_LONG to "long",
JAVA_LANG_FLOAT to "float",
JAVA_LANG_DOUBLE to "double",
JAVA_LANG_CHARACTER to "char"
)
}
@@ -486,3 +486,37 @@ class TypeConverter(val converter: Converter) {
private val mutableKotlinClasses = toKotlinMutableTypesMap.values.toSet()
}
}
fun PsiExpression.getTypeConversionMethod(expectedType: PsiType): String? {
val actualType = this.type ?: return null
if (actualType == expectedType) return null
if (expectedType.canonicalText == CommonClassNames.JAVA_LANG_STRING) return "toString"
return when (expectedType) {
PsiType.BYTE -> "toByte"
PsiType.SHORT -> "toShort"
PsiType.INT -> "toInt"
PsiType.LONG -> "toLong"
PsiType.FLOAT -> "toFloat"
PsiType.DOUBLE -> "toDouble"
PsiType.CHAR -> "toChar"
else -> null
}
}
fun PsiType.needTypeConversion(expected: PsiType): Boolean {
val expectedStr = expected.canonicalText
val actualStr = canonicalText
return expectedStr != actualStr &&
expectedStr != typeConversionMap[actualStr] &&
actualStr != typeConversionMap[expectedStr]
}
private val typeConversionMap: Map<String, String> = mapOf(
CommonClassNames.JAVA_LANG_BYTE to "byte",
CommonClassNames.JAVA_LANG_SHORT to "short",
CommonClassNames.JAVA_LANG_INTEGER to "int",
CommonClassNames.JAVA_LANG_LONG to "long",
CommonClassNames.JAVA_LANG_FLOAT to "float",
CommonClassNames.JAVA_LANG_DOUBLE to "double",
CommonClassNames.JAVA_LANG_CHARACTER to "char"
)
@@ -0,0 +1,8 @@
public class CharToNumber {
private byte b = 'c';
private short s = 'c';
private int i = 'c';
private long l = 'c';
private float f = 'c';
private double d = 'c';
}
@@ -0,0 +1,8 @@
class CharToNumber {
private val b: Byte = 'c'.toByte()
private val s: Short = 'c'.toShort()
private val i: Int = 'c'.toInt()
private val l: Long = 'c'.toLong()
private val f = 'c'.toFloat()
private val d = 'c'.toDouble()
}
@@ -0,0 +1,7 @@
public class CharToString {
private String value;
public String test() {
return '"' + value + '"';
}
}
@@ -0,0 +1,7 @@
class CharToString {
private val value: String? = null
fun test(): String {
return '"'.toString() + value + '"'.toString()
}
}
@@ -3359,6 +3359,18 @@ public class JavaToKotlinConverterForWebDemoTestGenerated extends AbstractJavaTo
doTest(fileName);
}
@TestMetadata("charToNumber.java")
public void testCharToNumber() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/literalExpression/charToNumber.java");
doTest(fileName);
}
@TestMetadata("charToString.java")
public void testCharToString() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/literalExpression/charToString.java");
doTest(fileName);
}
@TestMetadata("dollarInsideString.java")
public void testDollarInsideString() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/literalExpression/dollarInsideString.java");
@@ -3359,6 +3359,18 @@ public class JavaToKotlinConverterSingleFileTestGenerated extends AbstractJavaTo
doTest(fileName);
}
@TestMetadata("charToNumber.java")
public void testCharToNumber() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/literalExpression/charToNumber.java");
doTest(fileName);
}
@TestMetadata("charToString.java")
public void testCharToString() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/literalExpression/charToString.java");
doTest(fileName);
}
@TestMetadata("dollarInsideString.java")
public void testDollarInsideString() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("j2k/testData/fileOrElement/literalExpression/dollarInsideString.java");