From 2be27d53800c56e2555d75420336904fe02161a8 Mon Sep 17 00:00:00 2001 From: Toshiaki Kameyama Date: Mon, 13 Nov 2017 20:13:34 +0900 Subject: [PATCH] KT-19390: Fix char + String in Java is converted to code with multiple type errors in Kotlin (#1235) #KT-19390 Fixed --- .../org/jetbrains/kotlin/j2k/CodeConverter.kt | 29 +++++----------- .../org/jetbrains/kotlin/j2k/TypeConverter.kt | 34 +++++++++++++++++++ .../literalExpression/charToNumber.java | 8 +++++ .../literalExpression/charToNumber.kt | 8 +++++ .../literalExpression/charToString.java | 7 ++++ .../literalExpression/charToString.kt | 7 ++++ ...otlinConverterForWebDemoTestGenerated.java | 12 +++++++ ...otlinConverterSingleFileTestGenerated.java | 12 +++++++ 8 files changed, 96 insertions(+), 21 deletions(-) create mode 100644 j2k/testData/fileOrElement/literalExpression/charToNumber.java create mode 100644 j2k/testData/fileOrElement/literalExpression/charToNumber.kt create mode 100644 j2k/testData/fileOrElement/literalExpression/charToString.java create mode 100644 j2k/testData/fileOrElement/literalExpression/charToString.kt diff --git a/j2k/src/org/jetbrains/kotlin/j2k/CodeConverter.kt b/j2k/src/org/jetbrains/kotlin/j2k/CodeConverter.kt index 25aaf632abd..f4209beeafb 100644 --- a/j2k/src/org/jetbrains/kotlin/j2k/CodeConverter.kt +++ b/j2k/src/org/jetbrains/kotlin/j2k/CodeConverter.kt @@ -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 = 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" - ) } diff --git a/j2k/src/org/jetbrains/kotlin/j2k/TypeConverter.kt b/j2k/src/org/jetbrains/kotlin/j2k/TypeConverter.kt index 2d457d1f826..f2b30d66cfb 100644 --- a/j2k/src/org/jetbrains/kotlin/j2k/TypeConverter.kt +++ b/j2k/src/org/jetbrains/kotlin/j2k/TypeConverter.kt @@ -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 = 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" +) diff --git a/j2k/testData/fileOrElement/literalExpression/charToNumber.java b/j2k/testData/fileOrElement/literalExpression/charToNumber.java new file mode 100644 index 00000000000..8c6dbe246da --- /dev/null +++ b/j2k/testData/fileOrElement/literalExpression/charToNumber.java @@ -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'; +} \ No newline at end of file diff --git a/j2k/testData/fileOrElement/literalExpression/charToNumber.kt b/j2k/testData/fileOrElement/literalExpression/charToNumber.kt new file mode 100644 index 00000000000..e51a8b81dd4 --- /dev/null +++ b/j2k/testData/fileOrElement/literalExpression/charToNumber.kt @@ -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() +} \ No newline at end of file diff --git a/j2k/testData/fileOrElement/literalExpression/charToString.java b/j2k/testData/fileOrElement/literalExpression/charToString.java new file mode 100644 index 00000000000..c8fa9fb6fc3 --- /dev/null +++ b/j2k/testData/fileOrElement/literalExpression/charToString.java @@ -0,0 +1,7 @@ +public class CharToString { + private String value; + + public String test() { + return '"' + value + '"'; + } +} \ No newline at end of file diff --git a/j2k/testData/fileOrElement/literalExpression/charToString.kt b/j2k/testData/fileOrElement/literalExpression/charToString.kt new file mode 100644 index 00000000000..396026bd5fe --- /dev/null +++ b/j2k/testData/fileOrElement/literalExpression/charToString.kt @@ -0,0 +1,7 @@ +class CharToString { + private val value: String? = null + + fun test(): String { + return '"'.toString() + value + '"'.toString() + } +} \ 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 578d2edb230..31e476d0189 100644 --- a/j2k/tests/org/jetbrains/kotlin/j2k/JavaToKotlinConverterForWebDemoTestGenerated.java +++ b/j2k/tests/org/jetbrains/kotlin/j2k/JavaToKotlinConverterForWebDemoTestGenerated.java @@ -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"); diff --git a/j2k/tests/org/jetbrains/kotlin/j2k/JavaToKotlinConverterSingleFileTestGenerated.java b/j2k/tests/org/jetbrains/kotlin/j2k/JavaToKotlinConverterSingleFileTestGenerated.java index 2047a071c6e..08c93a838cc 100644 --- a/j2k/tests/org/jetbrains/kotlin/j2k/JavaToKotlinConverterSingleFileTestGenerated.java +++ b/j2k/tests/org/jetbrains/kotlin/j2k/JavaToKotlinConverterSingleFileTestGenerated.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");