diff --git a/idea/src/org/jetbrains/kotlin/idea/j2k/J2kPostProcessings.kt b/idea/src/org/jetbrains/kotlin/idea/j2k/J2kPostProcessings.kt index a29f89f57c8..83b7d4e0802 100644 --- a/idea/src/org/jetbrains/kotlin/idea/j2k/J2kPostProcessings.kt +++ b/idea/src/org/jetbrains/kotlin/idea/j2k/J2kPostProcessings.kt @@ -26,6 +26,7 @@ import org.jetbrains.kotlin.idea.inspections.RedundantSamConstructorInspection import org.jetbrains.kotlin.idea.intentions.* import org.jetbrains.kotlin.idea.intentions.branchedTransformations.intentions.IfThenToElvisIntention import org.jetbrains.kotlin.idea.intentions.branchedTransformations.intentions.IfThenToSafeAccessIntention +import org.jetbrains.kotlin.idea.intentions.conventionNameCalls.ReplaceGetIntention import org.jetbrains.kotlin.idea.quickfix.RemoveModifierFix import org.jetbrains.kotlin.idea.quickfix.RemoveRightPartOfBinaryExpressionFix import org.jetbrains.kotlin.idea.references.mainReference @@ -58,6 +59,7 @@ object J2KPostProcessingRegistrar { registerIntentionBasedProcessing(IfThenToElvisIntention()) { applyTo(it) } registerIntentionBasedProcessing(IfNullToElvisIntention()) { applyTo(it) } registerIntentionBasedProcessing(SimplifyNegatedBinaryExpressionIntention()) { applyTo(it) } + registerIntentionBasedProcessing(ReplaceGetIntention()) { applyTo(it) } registerDiagnosticBasedProcessing(Errors.USELESS_CAST) { element, diagnostic -> val expression = RemoveRightPartOfBinaryExpressionFix(element, "").invoke() diff --git a/idea/testData/copyPaste/conversion/RedundantTypeCast.expected.kt b/idea/testData/copyPaste/conversion/RedundantTypeCast.expected.kt index edcd7dcf298..48cf438eebf 100644 --- a/idea/testData/copyPaste/conversion/RedundantTypeCast.expected.kt +++ b/idea/testData/copyPaste/conversion/RedundantTypeCast.expected.kt @@ -1,6 +1,6 @@ fun foo(o: Any) { if (o is String) { - val l = o.length() + val l = o.length } somethingElse() } \ No newline at end of file diff --git a/idea/testData/copyPaste/conversion/RedundantTypeCast2.expected.kt b/idea/testData/copyPaste/conversion/RedundantTypeCast2.expected.kt index f5ec2526675..b71610200cc 100644 --- a/idea/testData/copyPaste/conversion/RedundantTypeCast2.expected.kt +++ b/idea/testData/copyPaste/conversion/RedundantTypeCast2.expected.kt @@ -1,5 +1,5 @@ fun foo(o: Any) { if (o !is String) return - val l = o.length() + val l = o.length somethingElse(o as String/* we should not remove this cast because it's not in pasted range*/) } \ No newline at end of file diff --git a/j2k/src/org/jetbrains/kotlin/j2k/ExpressionConverter.kt b/j2k/src/org/jetbrains/kotlin/j2k/ExpressionConverter.kt index 52bd443e8ab..cc1387dfa1a 100644 --- a/j2k/src/org/jetbrains/kotlin/j2k/ExpressionConverter.kt +++ b/j2k/src/org/jetbrains/kotlin/j2k/ExpressionConverter.kt @@ -312,8 +312,8 @@ class DefaultExpressionConverter : JavaElementVisitor(), ExpressionConverter { } if (target is PsiMethod) { - val specialMethod = SpecialMethod.values().firstOrNull { it.matches(target) } - if (specialMethod != null && (specialMethod.parameterCount == null || specialMethod.parameterCount == arguments.size())) { + val specialMethod = SpecialMethod.match(target, arguments.size(), converter.services) + if (specialMethod != null) { val converted = specialMethod.convertCall(methodExpr.getQualifierExpression(), arguments, typeArguments, codeConverter) if (converted != null) { result = converted @@ -350,23 +350,31 @@ class DefaultExpressionConverter : JavaElementVisitor(), ExpressionConverter { } override fun visitNewExpression(expression: PsiNewExpression) { + val type = expression.type if (expression.getArrayInitializer() != null) { result = codeConverter.convertExpression(expression.getArrayInitializer()) } else if (expression.getArrayDimensions().size() > 0 && expression.getType() is PsiArrayType) { result = ArrayWithoutInitializationExpression( - typeConverter.convertType(expression.getType(), Nullability.NotNull) as ArrayType, + typeConverter.convertType(expression.type, Nullability.NotNull) as ArrayType, codeConverter.convertExpressions(expression.getArrayDimensions())) } else { - val anonymousClass = expression.getAnonymousClass() + if (type?.canonicalText in PsiPrimitiveType.getAllBoxedTypeNames()) { + val argument = expression.argumentList?.expressions?.singleOrNull() + if (argument != null && argument.type is PsiPrimitiveType) { + result = codeConverter.convertExpression(argument) + return + } + } + val qualifier = expression.getQualifier() val classRef = expression.getClassOrAnonymousClassReference() val classRefConverted = if (classRef != null) converter.convertCodeReferenceElement(classRef, hasExternalQualifier = qualifier != null) else null result = NewClassExpression(classRefConverted, - convertArguments(expression), - codeConverter.convertExpression(qualifier), - if (anonymousClass != null) converter.convertAnonymousClassBody(anonymousClass) else null) + convertArguments(expression), + codeConverter.convertExpression(qualifier), + expression.anonymousClass?.let { converter.convertAnonymousClassBody(it) }) } } diff --git a/j2k/src/org/jetbrains/kotlin/j2k/SpecialMethod.kt b/j2k/src/org/jetbrains/kotlin/j2k/SpecialMethod.kt index 7618764c596..b16d6656435 100644 --- a/j2k/src/org/jetbrains/kotlin/j2k/SpecialMethod.kt +++ b/j2k/src/org/jetbrains/kotlin/j2k/SpecialMethod.kt @@ -21,11 +21,97 @@ import com.intellij.psi.CommonClassNames.JAVA_LANG_OBJECT import com.intellij.psi.CommonClassNames.JAVA_LANG_STRING import com.intellij.psi.impl.PsiExpressionEvaluator import org.jetbrains.kotlin.j2k.ast.* +import org.jetbrains.kotlin.utils.addToStdlib.check +import java.io.PrintStream +import java.util.* enum class SpecialMethod(val qualifiedClassName: String?, val methodName: String, val parameterCount: Int?) { + CHAR_SEQUENCE_LENGTH(CharSequence::class.java.name, "length", 0) { + override fun convertCall(qualifier: PsiExpression?, arguments: Array, typeArgumentsConverted: List, codeConverter: CodeConverter) + = convertMethodCallToPropertyUse(codeConverter, qualifier) + }, + + COLLECTION_SIZE(Collection::class.java.name, "size", 0) { + override fun convertCall(qualifier: PsiExpression?, arguments: Array, typeArgumentsConverted: List, codeConverter: CodeConverter) + = convertMethodCallToPropertyUse(codeConverter, qualifier) + }, + + MAP_SIZE(Map::class.java.name, "size", 0) { + override fun convertCall(qualifier: PsiExpression?, arguments: Array, typeArgumentsConverted: List, codeConverter: CodeConverter) + = convertMethodCallToPropertyUse(codeConverter, qualifier) + }, + + MAP_KEY_SET(Map::class.java.name, "keySet", 0) { + override fun convertCall(qualifier: PsiExpression?, arguments: Array, typeArgumentsConverted: List, codeConverter: CodeConverter) + = convertMethodCallToPropertyUse(codeConverter, qualifier, "keys") + }, + + MAP_VALUES(Map::class.java.name, "values", 0) { + override fun convertCall(qualifier: PsiExpression?, arguments: Array, typeArgumentsConverted: List, codeConverter: CodeConverter) + = convertMethodCallToPropertyUse(codeConverter, qualifier) + }, + + MAP_ENTRY_SET(Map::class.java.name, "entrySet", 0) { + override fun convertCall(qualifier: PsiExpression?, arguments: Array, typeArgumentsConverted: List, codeConverter: CodeConverter) + = convertMethodCallToPropertyUse(codeConverter, qualifier, "entries") + }, + + ENUM_NAME(Enum::class.java.name, "name", 0) { + override fun convertCall(qualifier: PsiExpression?, arguments: Array, typeArgumentsConverted: List, codeConverter: CodeConverter) + = convertMethodCallToPropertyUse(codeConverter, qualifier, "name") + }, + + ENUM_ORDINAL(Enum::class.java.name, "ordinal", 0) { + override fun convertCall(qualifier: PsiExpression?, arguments: Array, typeArgumentsConverted: List, codeConverter: CodeConverter) + = convertMethodCallToPropertyUse(codeConverter, qualifier) + }, + + CHAR_AT(CharSequence::class.java.name, "charAt", 1) { + override fun convertCall(qualifier: PsiExpression?, arguments: Array, typeArgumentsConverted: List, codeConverter: CodeConverter) + = convertWithChangedName("get", qualifier, arguments, typeArgumentsConverted, codeConverter) + }, + + NUMBER_BYTE_VALUE(Number::class.java.name, "byteValue", 0) { + override fun convertCall(qualifier: PsiExpression?, arguments: Array, typeArgumentsConverted: List, codeConverter: CodeConverter) + = convertWithChangedName("toByte", qualifier, arguments, typeArgumentsConverted, codeConverter) + }, + + NUMBER_SHORT_VALUE(Number::class.java.name, "shortValue", 0) { + override fun convertCall(qualifier: PsiExpression?, arguments: Array, typeArgumentsConverted: List, codeConverter: CodeConverter) + = convertWithChangedName("toShort", qualifier, arguments, typeArgumentsConverted, codeConverter) + }, + + NUMBER_INT_VALUE(Number::class.java.name, "intValue", 0) { + override fun convertCall(qualifier: PsiExpression?, arguments: Array, typeArgumentsConverted: List, codeConverter: CodeConverter) + = convertWithChangedName("toInt", qualifier, arguments, typeArgumentsConverted, codeConverter) + }, + + NUMBER_LONG_VALUE(Number::class.java.name, "longValue", 0) { + override fun convertCall(qualifier: PsiExpression?, arguments: Array, typeArgumentsConverted: List, codeConverter: CodeConverter) + = convertWithChangedName("toLong", qualifier, arguments, typeArgumentsConverted, codeConverter) + }, + + NUMBER_FLOAT_VALUE(Number::class.java.name, "floatValue", 0) { + override fun convertCall(qualifier: PsiExpression?, arguments: Array, typeArgumentsConverted: List, codeConverter: CodeConverter) + = convertWithChangedName("toFloat", qualifier, arguments, typeArgumentsConverted, codeConverter) + }, + + NUMBER_DOUBLE_VALUE(Number::class.java.name, "doubleValue", 0) { + override fun convertCall(qualifier: PsiExpression?, arguments: Array, typeArgumentsConverted: List, codeConverter: CodeConverter) + = convertWithChangedName("toDouble", qualifier, arguments, typeArgumentsConverted, codeConverter) + }, + + LIST_REMOVE(List::class.java.name, "remove", 1) { + override fun matches(method: PsiMethod, superMethodsSearcher: SuperMethodsSearcher) + = super.matches(method, superMethodsSearcher) && method.parameterList.parameters.single().type.canonicalText == "int" + + override fun convertCall(qualifier: PsiExpression?, arguments: Array, typeArgumentsConverted: List, codeConverter: CodeConverter) + = convertWithChangedName("removeAt", qualifier, arguments, typeArgumentsConverted, codeConverter) + }, + OBJECT_EQUALS(null, "equals", 1) { - override fun matches(method: PsiMethod) - = super.matches(method) && method.getParameterList().getParameters().single().getType().getCanonicalText() == JAVA_LANG_OBJECT + override fun matches(method: PsiMethod, superMethodsSearcher: SuperMethodsSearcher): Boolean + = super.matches(method, superMethodsSearcher) && method.getParameterList().getParameters().single().getType().getCanonicalText() == JAVA_LANG_OBJECT override fun convertCall(qualifier: PsiExpression?, arguments: Array, typeArgumentsConverted: List, codeConverter: CodeConverter): Expression? { if (qualifier == null || qualifier is PsiSuperExpression) return null @@ -33,7 +119,7 @@ enum class SpecialMethod(val qualifiedClassName: String?, val methodName: String } }, - OBJECT_GET_CLASS("java.lang.Object", "getClass", 0) { + OBJECT_GET_CLASS(JAVA_LANG_OBJECT, "getClass", 0) { override fun convertCall(qualifier: PsiExpression?, arguments: Array, typeArgumentsConverted: List, codeConverter: CodeConverter): Expression { val identifier = Identifier("javaClass", false).assignNoPrototype() return if (qualifier != null) QualifiedExpression(codeConverter.convertExpression(qualifier), identifier) else identifier @@ -45,27 +131,27 @@ enum class SpecialMethod(val qualifiedClassName: String?, val methodName: String = BinaryExpression(codeConverter.convertExpression(arguments[0]), codeConverter.convertExpression(arguments[1]), "==") }, - COLLECTIONS_EMPTY_LIST("java.util.Collections", "emptyList", 0) { + COLLECTIONS_EMPTY_LIST(Collections::class.java.name, "emptyList", 0) { override fun convertCall(qualifier: PsiExpression?, arguments: Array, typeArgumentsConverted: List, codeConverter: CodeConverter) = MethodCallExpression.build(null, "emptyList", listOf(), typeArgumentsConverted, false) }, - COLLECTIONS_EMPTY_SET("java.util.Collections", "emptySet", 0) { + COLLECTIONS_EMPTY_SET(Collections::class.java.name, "emptySet", 0) { override fun convertCall(qualifier: PsiExpression?, arguments: Array, typeArgumentsConverted: List, codeConverter: CodeConverter) = MethodCallExpression.build(null, "emptySet", listOf(), typeArgumentsConverted, false) }, - COLLECTIONS_EMPTY_MAP("java.util.Collections", "emptyMap", 0) { + COLLECTIONS_EMPTY_MAP(Collections::class.java.name, "emptyMap", 0) { override fun convertCall(qualifier: PsiExpression?, arguments: Array, typeArgumentsConverted: List, codeConverter: CodeConverter) = MethodCallExpression.build(null, "emptyMap", listOf(), typeArgumentsConverted, false) }, - COLLECTIONS_SINGLETON_LIST("java.util.Collections", "singletonList", 1) { + COLLECTIONS_SINGLETON_LIST(Collections::class.java.name, "singletonList", 1) { override fun convertCall(qualifier: PsiExpression?, arguments: Array, typeArgumentsConverted: List, codeConverter: CodeConverter) = MethodCallExpression.build(null, "listOf", listOf(codeConverter.convertExpression(arguments.single())), typeArgumentsConverted, false) }, - COLLECTIONS_SINGLETON("java.util.Collections", "singleton", 1) { + COLLECTIONS_SINGLETON(Collections::class.java.name, "singleton", 1) { override fun convertCall(qualifier: PsiExpression?, arguments: Array, typeArgumentsConverted: List, codeConverter: CodeConverter) = MethodCallExpression.build(null, "setOf", listOf(codeConverter.convertExpression(arguments.single())), typeArgumentsConverted, false) }, @@ -135,15 +221,16 @@ enum class SpecialMethod(val qualifiedClassName: String?, val methodName: String }, STRING_JOIN(JAVA_LANG_STRING, "join", 2) { - override fun matches(method: PsiMethod) - = super.matches(method) && method.parameterList.parameters.last().type.canonicalText == "java.lang.Iterable" + override fun matches(method: PsiMethod, superMethodsSearcher: SuperMethodsSearcher): Boolean + = super.matches(method, superMethodsSearcher) && method.parameterList.parameters.last().type.canonicalText == "java.lang.Iterable" override fun convertCall(qualifier: PsiExpression?, arguments: Array, typeArgumentsConverted: List, codeConverter: CodeConverter): Expression? = MethodCallExpression.buildNotNull(codeConverter.convertExpression(arguments[1]), "joinToString", codeConverter.convertExpressions(arguments.take(1)), emptyList()) }, STRING_JOIN_VARARG(JAVA_LANG_STRING, "join", null) { - override fun matches(method: PsiMethod): Boolean = super.matches(method) && method.parameterList.let { it.parametersCount == 2 && it.parameters.last().isVarArgs } + override fun matches(method: PsiMethod, superMethodsSearcher: SuperMethodsSearcher): Boolean + = super.matches(method, superMethodsSearcher) && method.parameterList.let { it.parametersCount == 2 && it.parameters.last().isVarArgs } override fun convertCall(qualifier: PsiExpression?, arguments: Array, typeArgumentsConverted: List, codeConverter: CodeConverter): Expression? { if (arguments.size() == 2 && arguments.last().isAssignableToCharSequenceArray()) { @@ -186,59 +273,101 @@ enum class SpecialMethod(val qualifiedClassName: String?, val methodName: String }, STRING_FORMAT_WITH_LOCALE(JAVA_LANG_STRING, "format", null) { - override fun matches(method: PsiMethod) - = super.matches(method) && + override fun matches(method: PsiMethod, superMethodsSearcher: SuperMethodsSearcher): Boolean + = super.matches(method, superMethodsSearcher) && method.parameterList.parametersCount == 3 && method.parameterList.parameters.let { it.first().type.canonicalText == "java.util.Locale" && it.last().isVarArgs } - override fun convertCall(qualifier: PsiExpression?, arguments: Array, typeArgumentsConverted: List, codeConverter: CodeConverter) - = MethodCallExpression.build(codeConverter.convertExpression(arguments[1]), "format", codeConverter.convertExpressions(listOf(arguments[0]) + arguments.drop(2)), emptyList(), false) + override fun convertCall(qualifier: PsiExpression?, arguments: Array, typeArgumentsConverted: List, codeConverter: CodeConverter): Expression? { + if (arguments.size() < 2) return null // incorrect call + return MethodCallExpression.build(codeConverter.convertExpression(arguments[1]), "format", codeConverter.convertExpressions(listOf(arguments[0]) + arguments.drop(2)), emptyList(), false) + } }, STRING_FORMAT(JAVA_LANG_STRING, "format", null) { - override fun matches(method: PsiMethod): Boolean { - return super.matches(method) && + override fun matches(method: PsiMethod, superMethodsSearcher: SuperMethodsSearcher): Boolean { + return super.matches(method, superMethodsSearcher) && method.parameterList.parametersCount == 2 && method.parameterList.parameters.last().isVarArgs } - override fun convertCall(qualifier: PsiExpression?, arguments: Array, typeArgumentsConverted: List, codeConverter: CodeConverter) - = MethodCallExpression.build(codeConverter.convertExpression(arguments.first()), "format", codeConverter.convertExpressions(arguments.drop(1)), emptyList(), false) + override fun convertCall(qualifier: PsiExpression?, arguments: Array, typeArgumentsConverted: List, codeConverter: CodeConverter): Expression? { + if (arguments.isEmpty()) return null // incorrect call + return MethodCallExpression.build(codeConverter.convertExpression(arguments.first()), "format", codeConverter.convertExpressions(arguments.drop(1)), emptyList(), false) + } }, STRING_VALUE_OF_CHAR_ARRAY(JAVA_LANG_STRING, "valueOf", null) { - override fun matches(method: PsiMethod) - = matchesClass(method) && - (matchesName(method) || matchesName(method, "copyValueOf")) && - method.parameterList.parametersCount.let { it == 1 || it == 3} && - method.parameterList.parameters.first().type.canonicalText == "char[]" + override fun matches(method: PsiMethod, superMethodsSearcher: SuperMethodsSearcher): Boolean { + return super.matches(method, superMethodsSearcher) + && method.parameterList.parametersCount.let { it == 1 || it == 3} + && method.parameterList.parameters.first().type.canonicalText == "char[]" + } override fun convertCall(qualifier: PsiExpression?, arguments: Array, typeArgumentsConverted: List, codeConverter: CodeConverter) = MethodCallExpression.build(null, "String", codeConverter.convertExpressions(arguments), emptyList(), false) }, + STRING_COPY_VALUE_OF_CHAR_ARRAY(JAVA_LANG_STRING, "copyValueOf", null) { + override fun matches(method: PsiMethod, superMethodsSearcher: SuperMethodsSearcher): Boolean { + return super.matches(method, superMethodsSearcher) + && method.parameterList.parametersCount.let { it == 1 || it == 3 } + && method.parameterList.parameters.first().type.canonicalText == "char[]" + } + + override fun convertCall(qualifier: PsiExpression?, arguments: Array, typeArgumentsConverted: List, codeConverter: CodeConverter) + = STRING_VALUE_OF_CHAR_ARRAY.convertCall(qualifier, arguments, typeArgumentsConverted, codeConverter) + }, + STRING_VALUE_OF(JAVA_LANG_STRING, "valueOf", 1) { override fun convertCall(qualifier: PsiExpression?, arguments: Array, typeArgumentsConverted: List, codeConverter: CodeConverter) = MethodCallExpression.build(codeConverter.convertExpression(arguments.single()), "toString", emptyList(), emptyList(), false) }, - SYSTEM_OUT_PRINTLN("java.io.PrintStream", "println", null) { + SYSTEM_OUT_PRINTLN(PrintStream::class.java.name, "println", null) { override fun convertCall(qualifier: PsiExpression?, arguments: Array, typeArgumentsConverted: List, codeConverter: CodeConverter) = convertSystemOutMethodCall(methodName, qualifier, arguments, typeArgumentsConverted, codeConverter) }, - SYSTEM_OUT_PRINT("java.io.PrintStream", "print", null) { + SYSTEM_OUT_PRINT(PrintStream::class.java.name, "print", null) { override fun convertCall(qualifier: PsiExpression?, arguments: Array, typeArgumentsConverted: List, codeConverter: CodeConverter) = convertSystemOutMethodCall(methodName, qualifier, arguments, typeArgumentsConverted, codeConverter) }; - open fun matches(method: PsiMethod): Boolean = matchesName(method) && matchesClass(method) && matchesParameterCount(method) + open fun matches(method: PsiMethod, superMethodsSearcher: SuperMethodsSearcher): Boolean + = method.name == methodName && matchesClass(method, superMethodsSearcher) && matchesParameterCount(method) + + protected fun matchesClass(method: PsiMethod, superMethodsSearcher: SuperMethodsSearcher): Boolean { + if (qualifiedClassName == null) return true + val superMethods = superMethodsSearcher.findDeepestSuperMethods(method) + return if (superMethods.isEmpty()) + method.containingClass?.qualifiedName == qualifiedClassName + else + superMethods.any { it.containingClass?.qualifiedName == qualifiedClassName } + } - protected fun matchesName(method: PsiMethod, name: String? = null) = method.name == (name ?: methodName) - protected fun matchesClass(method: PsiMethod) = qualifiedClassName == null || method.containingClass?.qualifiedName == qualifiedClassName protected fun matchesParameterCount(method: PsiMethod) = parameterCount == null || parameterCount == method.parameterList.parametersCount abstract fun convertCall(qualifier: PsiExpression?, arguments: Array, typeArgumentsConverted: List, codeConverter: CodeConverter): Expression? + + protected fun convertMethodCallToPropertyUse(codeConverter: CodeConverter, qualifier: PsiExpression?, propertyName: String = methodName): Expression { + val identifier = Identifier(propertyName, false).assignNoPrototype() + return if (qualifier != null) QualifiedExpression(codeConverter.convertExpression(qualifier), identifier) else identifier + } + + protected fun convertWithChangedName(name: String, qualifier: PsiExpression?, arguments: Array, typeArgumentsConverted: List, codeConverter: CodeConverter) + = MethodCallExpression.buildNotNull(codeConverter.convertExpression(qualifier), name, codeConverter.convertExpressions(arguments), typeArgumentsConverted) + + companion object { + private val valuesByName = values().groupBy { it.methodName } + + fun match(method: PsiMethod, argumentCount: Int, services: JavaToKotlinConverterServices): SpecialMethod? { + val candidates = valuesByName[method.name] ?: return null + return candidates + .firstOrNull { it.matches(method, services.superMethodsSearcher) } + ?.check { it.parameterCount == null || it.parameterCount == argumentCount } // if parameterCount is specified we should make sure that argument count is correct + } + } } private fun convertSystemOutMethodCall( diff --git a/j2k/testData/KotlinApi.kt b/j2k/testData/KotlinApi.kt index 79615235cbf..3b28da1a69e 100644 --- a/j2k/testData/KotlinApi.kt +++ b/j2k/testData/KotlinApi.kt @@ -6,6 +6,8 @@ public open class KotlinClass(public var field: Int) { public open fun foo(mutableCollection: MutableCollection, nullableCollection: Collection?): MutableList = arrayListOf() + fun get(i: Int) = 0 + companion object { public fun staticFun(p: Int): Int = p public var staticVar: Int = 1 diff --git a/j2k/testData/fileOrElement/assignmentExpression/nullability-settings.kt b/j2k/testData/fileOrElement/assignmentExpression/nullability-settings.kt index 5a6bb6d025f..756f549bd86 100644 --- a/j2k/testData/fileOrElement/assignmentExpression/nullability-settings.kt +++ b/j2k/testData/fileOrElement/assignmentExpression/nullability-settings.kt @@ -6,6 +6,6 @@ internal class Foo { fun foo(o: HashSet?) { val o2: HashSet? = o var foo: Int = 0 - foo = o2!!.size() + foo = o2!!.size } } \ No newline at end of file diff --git a/j2k/testData/fileOrElement/assignmentExpression/nullability.kt b/j2k/testData/fileOrElement/assignmentExpression/nullability.kt index 0ca42340b06..0510c450899 100644 --- a/j2k/testData/fileOrElement/assignmentExpression/nullability.kt +++ b/j2k/testData/fileOrElement/assignmentExpression/nullability.kt @@ -4,6 +4,6 @@ internal class Foo { fun foo(o: HashSet) { val o2 = o var foo = 0 - foo = o2.size() + foo = o2.size } } \ No newline at end of file diff --git a/j2k/testData/fileOrElement/codeSimplifications/GetOperator.java b/j2k/testData/fileOrElement/codeSimplifications/GetOperator.java new file mode 100644 index 00000000000..39549367610 --- /dev/null +++ b/j2k/testData/fileOrElement/codeSimplifications/GetOperator.java @@ -0,0 +1,22 @@ +import java.util.HashMap; +import kotlinApi.KotlinClass; + +class X { + int get(int index) { + return 0; + } +} + +class C { + String foo(HashMap map) { + return map.get("a"); + } + + int foo(X x) { + return x.get(0); + } + + int foo(KotlinClass kotlinClass) { + return kotlinClass.get(0); // not operator! + } +} \ No newline at end of file diff --git a/j2k/testData/fileOrElement/codeSimplifications/GetOperator.kt b/j2k/testData/fileOrElement/codeSimplifications/GetOperator.kt new file mode 100644 index 00000000000..c1953f369ff --- /dev/null +++ b/j2k/testData/fileOrElement/codeSimplifications/GetOperator.kt @@ -0,0 +1,23 @@ +// ERROR: Type mismatch: inferred type is kotlin.String? but kotlin.String was expected +import java.util.HashMap +import kotlinApi.KotlinClass + +internal class X { + fun get(index: Int): Int { + return 0 + } +} + +internal class C { + fun foo(map: HashMap): String { + return map["a"] + } + + fun foo(x: X): Int { + return x.get(0) + } + + fun foo(kotlinClass: KotlinClass): Int { + return kotlinClass.get(0) // not operator! + } +} diff --git a/j2k/testData/fileOrElement/codeSimplifications/RedundantTypeCast.kt b/j2k/testData/fileOrElement/codeSimplifications/RedundantTypeCast.kt index e909976dcef..0a46dd84d13 100644 --- a/j2k/testData/fileOrElement/codeSimplifications/RedundantTypeCast.kt +++ b/j2k/testData/fileOrElement/codeSimplifications/RedundantTypeCast.kt @@ -1,7 +1,7 @@ internal class C { fun foo(o: Any) { if (o is String) { - val l = o.length() + val l = o.length } } } diff --git a/j2k/testData/fileOrElement/codeSimplifications/RedundantTypeCastAndInline.kt b/j2k/testData/fileOrElement/codeSimplifications/RedundantTypeCastAndInline.kt index 380c2e05a24..a893f69ea92 100644 --- a/j2k/testData/fileOrElement/codeSimplifications/RedundantTypeCastAndInline.kt +++ b/j2k/testData/fileOrElement/codeSimplifications/RedundantTypeCastAndInline.kt @@ -1,7 +1,7 @@ internal class C { fun foo(o: Any) { if (o is String) { - val l = o.length() + val l = o.length val substring = o.substring(l - 2) } } diff --git a/j2k/testData/fileOrElement/comments/commentsForConstructors.kt b/j2k/testData/fileOrElement/comments/commentsForConstructors.kt index ee4a54d50c8..981712fb82f 100644 --- a/j2k/testData/fileOrElement/comments/commentsForConstructors.kt +++ b/j2k/testData/fileOrElement/comments/commentsForConstructors.kt @@ -7,7 +7,7 @@ internal class A// this is a primary constructor } // end of primary constructor body // this is a secondary constructor 2 - constructor(s: String) : this(s.length()) { + constructor(s: String) : this(s.length) { } // end of secondary constructor 2 body }// this is a secondary constructor 1 // end of secondary constructor 1 body diff --git a/j2k/testData/fileOrElement/constructors/fieldsInitializedFromParams8.kt b/j2k/testData/fileOrElement/constructors/fieldsInitializedFromParams8.kt index 0b43deb548e..8386d7a1e5f 100644 --- a/j2k/testData/fileOrElement/constructors/fieldsInitializedFromParams8.kt +++ b/j2k/testData/fileOrElement/constructors/fieldsInitializedFromParams8.kt @@ -1,3 +1,3 @@ internal open class Base(o: Any, l: Int) -internal class C(private val string: String) : Base(string, string.length()) +internal class C(private val string: String) : Base(string, string.length) diff --git a/j2k/testData/fileOrElement/constructors/fieldsInitializedFromParams9.kt b/j2k/testData/fileOrElement/constructors/fieldsInitializedFromParams9.kt index 34647d758b5..fb83689bbc6 100644 --- a/j2k/testData/fileOrElement/constructors/fieldsInitializedFromParams9.kt +++ b/j2k/testData/fileOrElement/constructors/fieldsInitializedFromParams9.kt @@ -1 +1 @@ -internal class C @JvmOverloads constructor(private val string: String, a: Int = string.length()) +internal class C @JvmOverloads constructor(private val string: String, a: Int = string.length) diff --git a/j2k/testData/fileOrElement/enum/runnableImplementation.kt b/j2k/testData/fileOrElement/enum/runnableImplementation.kt index 45527e6b2a6..24281a18040 100644 --- a/j2k/testData/fileOrElement/enum/runnableImplementation.kt +++ b/j2k/testData/fileOrElement/enum/runnableImplementation.kt @@ -2,6 +2,6 @@ internal enum class Color : Runnable { WHITE, BLACK, RED, YELLOW, BLUE; override fun run() { - println("name()=" + name() + ", toString()=" + toString()) + println("name()=" + name + ", toString()=" + toString()) } } \ No newline at end of file diff --git a/j2k/testData/fileOrElement/for/falseIndicesReversed.kt b/j2k/testData/fileOrElement/for/falseIndicesReversed.kt index 50cb9386344..427934d4332 100644 --- a/j2k/testData/fileOrElement/for/falseIndicesReversed.kt +++ b/j2k/testData/fileOrElement/for/falseIndicesReversed.kt @@ -1,6 +1,6 @@ class A { internal fun foo(collection: Collection) { - for (i in collection.size() downTo 0) { + for (i in collection.size downTo 0) { println(i) } } diff --git a/j2k/testData/fileOrElement/issues/kt-820-string.kt b/j2k/testData/fileOrElement/issues/kt-820-string.kt index a408a592f62..beb777a8450 100644 --- a/j2k/testData/fileOrElement/issues/kt-820-string.kt +++ b/j2k/testData/fileOrElement/issues/kt-820-string.kt @@ -1,6 +1,6 @@ internal object Test { fun toFileSystemSafeName(name: String): String { - val size = name.length() + val size = name.length return name } } \ No newline at end of file diff --git a/j2k/testData/fileOrElement/kotlinApiAccess/CorrectFunNullabilityDetected.kt b/j2k/testData/fileOrElement/kotlinApiAccess/CorrectFunNullabilityDetected.kt index 58eb16f8a60..a183ea61f6a 100644 --- a/j2k/testData/fileOrElement/kotlinApiAccess/CorrectFunNullabilityDetected.kt +++ b/j2k/testData/fileOrElement/kotlinApiAccess/CorrectFunNullabilityDetected.kt @@ -2,6 +2,6 @@ import kotlinApi.* internal class A { fun foo(t: KotlinTrait): Int { - return t.nullableFun()!!.length() + t.notNullableFun().length() + return t.nullableFun()!!.length + t.notNullableFun().length } } \ No newline at end of file diff --git a/j2k/testData/fileOrElement/kotlinApiAccess/CorrectNullabilityDetected.kt b/j2k/testData/fileOrElement/kotlinApiAccess/CorrectNullabilityDetected.kt index ba53d71606e..52aa601a92e 100644 --- a/j2k/testData/fileOrElement/kotlinApiAccess/CorrectNullabilityDetected.kt +++ b/j2k/testData/fileOrElement/kotlinApiAccess/CorrectNullabilityDetected.kt @@ -2,6 +2,6 @@ import kotlinApi.* internal class A { fun foo(c: KotlinClass): Int { - return c.nullableProperty!!.length() + c.property.length() + KotlinClass.nullableStaticVar!! + KotlinClass.staticVar + KotlinClass.nullableStaticFun(1)!! + KotlinClass.staticFun(1) + nullableGlobalFunction("")!!.length() + globalFunction("").length() + return c.nullableProperty!!.length + c.property.length + KotlinClass.nullableStaticVar!! + KotlinClass.staticVar + KotlinClass.nullableStaticFun(1)!! + KotlinClass.staticFun(1) + nullableGlobalFunction("")!!.length + globalFunction("").length } } \ No newline at end of file diff --git a/j2k/testData/fileOrElement/methodCallExpression/specialBuiltinMembers.java b/j2k/testData/fileOrElement/methodCallExpression/specialBuiltinMembers.java new file mode 100644 index 00000000000..8cf4e37cad6 --- /dev/null +++ b/j2k/testData/fileOrElement/methodCallExpression/specialBuiltinMembers.java @@ -0,0 +1,30 @@ +import java.util.*; + +enum E { + A, B, C +} + +class A { + void foo(List list, Collection collection, Map map) { + int a = "".length(); + String b = E.A.name(); + int c = E.A.ordinal(); + int d = list.size() + collection.size(); + int e = map.size(); + int f = map.keySet().size(); + int g = map.values().size(); + int h = map.entrySet().size(); + } + + void bar(List list) { + char c = "a".charAt(0); + byte b = new Integer(10).byteValue(); + int i = new Double(10.1).intValue(); + float f = new Double(10.1).floatValue(); + long l = new Double(10.1).longValue(); + short s = new Double(10.1).shortValue(); + + String removed = list.remove(10); + Boolean isRemoved = list.remove("a"); + } +} diff --git a/j2k/testData/fileOrElement/methodCallExpression/specialBuiltinMembers.kt b/j2k/testData/fileOrElement/methodCallExpression/specialBuiltinMembers.kt new file mode 100644 index 00000000000..43195386811 --- /dev/null +++ b/j2k/testData/fileOrElement/methodCallExpression/specialBuiltinMembers.kt @@ -0,0 +1,30 @@ +import java.util.* + +internal enum class E { + A, B, C +} + +internal class A { + fun foo(list: List, collection: Collection, map: Map) { + val a = "".length + val b = E.A.name + val c = E.A.ordinal + val d = list.size + collection.size + val e = map.size + val f = map.keys.size + val g = map.values.size + val h = map.entries.size + } + + fun bar(list: MutableList) { + val c = "a"[0] + val b = 10.toByte() + val i = 10.1.toInt() + val f = 10.1.toFloat() + val l = 10.1.toLong() + val s = 10.1.toShort() + + val removed = list.removeAt(10) + val isRemoved = list.remove("a") + } +} diff --git a/j2k/testData/fileOrElement/methodCallExpression/stringMethods.kt b/j2k/testData/fileOrElement/methodCallExpression/stringMethods.kt index 198dd80001f..0e5ddbd05a1 100644 --- a/j2k/testData/fileOrElement/methodCallExpression/stringMethods.kt +++ b/j2k/testData/fileOrElement/methodCallExpression/stringMethods.kt @@ -25,12 +25,12 @@ internal class A { fun normalMethods() { val s = "test string" - s.length() + s.length s.isEmpty() - s.charAt(1) + s[1] s.codePointAt(2) s.codePointBefore(2) - s.codePointCount(0, s.length()) + s.codePointCount(0, s.length) s.offsetByCodePoints(0, 4) s.compareTo("test 2") s.concat(" another") diff --git a/j2k/testData/fileOrElement/mutableCollections/Recursion.kt b/j2k/testData/fileOrElement/mutableCollections/Recursion.kt index adcc4ff8053..05bfb5544e3 100644 --- a/j2k/testData/fileOrElement/mutableCollections/Recursion.kt +++ b/j2k/testData/fileOrElement/mutableCollections/Recursion.kt @@ -6,7 +6,7 @@ internal class A { } fun bar(collection: MutableCollection) { - if (collection.size() < 5) { + if (collection.size < 5) { foo(collection) } else { collection.add("a") diff --git a/j2k/testData/fileOrElement/nullability/NullableMethodDotAccess.kt b/j2k/testData/fileOrElement/nullability/NullableMethodDotAccess.kt index 2d25b1cd2ef..fcd836a970c 100644 --- a/j2k/testData/fileOrElement/nullability/NullableMethodDotAccess.kt +++ b/j2k/testData/fileOrElement/nullability/NullableMethodDotAccess.kt @@ -4,6 +4,6 @@ internal class C { } fun foo(): Int { - return getString(true)!!.length() + return getString(true)!!.length } } diff --git a/j2k/testData/fileOrElement/nullability/NullableVariableDotAccess.kt b/j2k/testData/fileOrElement/nullability/NullableVariableDotAccess.kt index 677f73205fd..6abc5bf9aac 100644 --- a/j2k/testData/fileOrElement/nullability/NullableVariableDotAccess.kt +++ b/j2k/testData/fileOrElement/nullability/NullableVariableDotAccess.kt @@ -1,5 +1,5 @@ fun foo(s: String?, b: Boolean): Int { if (s == null) println("null") - if (b) return s!!.length() + if (b) return s!!.length return 10 } \ No newline at end of file diff --git a/j2k/testData/fileOrElement/nullability/autoNotNull.kt b/j2k/testData/fileOrElement/nullability/autoNotNull.kt index e54f147cccc..0bd5b081891 100644 --- a/j2k/testData/fileOrElement/nullability/autoNotNull.kt +++ b/j2k/testData/fileOrElement/nullability/autoNotNull.kt @@ -1,7 +1,7 @@ internal class A { fun foo(s: String?): Int { if (s != null) { - return s.length() + return s.length } return -1 } diff --git a/j2k/tests/org/jetbrains/kotlin/j2k/JavaToKotlinConverterForWebDemoTestGenerated.java b/j2k/tests/org/jetbrains/kotlin/j2k/JavaToKotlinConverterForWebDemoTestGenerated.java index 5319ea30b20..2966cad1541 100644 --- a/j2k/tests/org/jetbrains/kotlin/j2k/JavaToKotlinConverterForWebDemoTestGenerated.java +++ b/j2k/tests/org/jetbrains/kotlin/j2k/JavaToKotlinConverterForWebDemoTestGenerated.java @@ -955,6 +955,12 @@ public class JavaToKotlinConverterForWebDemoTestGenerated extends AbstractJavaTo JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("j2k/testData/fileOrElement/codeSimplifications"), Pattern.compile("^(.+)\\.java$"), true); } + @TestMetadata("GetOperator.java") + public void testGetOperator() throws Exception { + String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/codeSimplifications/GetOperator.java"); + doTest(fileName); + } + @TestMetadata("IfNullReturnToElvis.java") public void testIfNullReturnToElvis() throws Exception { String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/codeSimplifications/IfNullReturnToElvis.java"); @@ -3148,6 +3154,12 @@ public class JavaToKotlinConverterForWebDemoTestGenerated extends AbstractJavaTo doTest(fileName); } + @TestMetadata("specialBuiltinMembers.java") + public void testSpecialBuiltinMembers() throws Exception { + String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/methodCallExpression/specialBuiltinMembers.java"); + doTest(fileName); + } + @TestMetadata("stringMethods.java") public void testStringMethods() throws Exception { String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/methodCallExpression/stringMethods.java"); diff --git a/j2k/tests/org/jetbrains/kotlin/j2k/JavaToKotlinConverterSingleFileTestGenerated.java b/j2k/tests/org/jetbrains/kotlin/j2k/JavaToKotlinConverterSingleFileTestGenerated.java index b38572d62ad..fa39edeffb1 100644 --- a/j2k/tests/org/jetbrains/kotlin/j2k/JavaToKotlinConverterSingleFileTestGenerated.java +++ b/j2k/tests/org/jetbrains/kotlin/j2k/JavaToKotlinConverterSingleFileTestGenerated.java @@ -955,6 +955,12 @@ public class JavaToKotlinConverterSingleFileTestGenerated extends AbstractJavaTo JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("j2k/testData/fileOrElement/codeSimplifications"), Pattern.compile("^(.+)\\.java$"), true); } + @TestMetadata("GetOperator.java") + public void testGetOperator() throws Exception { + String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/codeSimplifications/GetOperator.java"); + doTest(fileName); + } + @TestMetadata("IfNullReturnToElvis.java") public void testIfNullReturnToElvis() throws Exception { String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/codeSimplifications/IfNullReturnToElvis.java"); @@ -3148,6 +3154,12 @@ public class JavaToKotlinConverterSingleFileTestGenerated extends AbstractJavaTo doTest(fileName); } + @TestMetadata("specialBuiltinMembers.java") + public void testSpecialBuiltinMembers() throws Exception { + String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/methodCallExpression/specialBuiltinMembers.java"); + doTest(fileName); + } + @TestMetadata("stringMethods.java") public void testStringMethods() throws Exception { String fileName = JetTestUtils.navigationMetadata("j2k/testData/fileOrElement/methodCallExpression/stringMethods.java");