diff --git a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/inspections/ReplaceWithStringBuilderAppendRangeInspection.kt b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/inspections/ReplaceWithStringBuilderAppendRangeInspection.kt index a744e57cb147..c81c62e30629 100644 --- a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/inspections/ReplaceWithStringBuilderAppendRangeInspection.kt +++ b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/inspections/ReplaceWithStringBuilderAppendRangeInspection.kt @@ -12,10 +12,12 @@ import org.jetbrains.kotlin.idea.base.resources.KotlinBundle import org.jetbrains.kotlin.idea.caches.resolve.resolveToCall import org.jetbrains.kotlin.idea.codeinsight.api.classic.inspections.AbstractKotlinInspection import org.jetbrains.kotlin.idea.refactoring.fqName.fqName +import org.jetbrains.kotlin.idea.util.getDataFlowAwareTypes import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.platform.jvm.isJvm import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe +import org.jetbrains.kotlin.types.isNullable import org.jetbrains.kotlin.types.typeUtil.isInt import org.jetbrains.kotlin.utils.addToStdlib.safeAs @@ -61,6 +63,7 @@ class ReplaceWithStringBuilderAppendRangeInspection : AbstractKotlinInspection() val callExpression = calleeExpression.parent as? KtCallExpression ?: return val args = callExpression.valueArguments + val firstArg = args.getOrNull(0)?.getArgumentExpression() ?: return val secondArg = args.getOrNull(1)?.getArgumentExpression() ?: return val thirdArg = args.getOrNull(2)?.getArgumentExpression() ?: return @@ -74,11 +77,19 @@ class ReplaceWithStringBuilderAppendRangeInspection : AbstractKotlinInspection() } else if (secondArgAsInt != 0) { thirdArg.replace(psiFactory.createExpressionByPattern("$0 + $1", secondArg, thirdArg)) } + + if (firstArg.isNullable()) { + firstArg.replace(psiFactory.createExpressionByPattern("$0!!", firstArg)) + } } private fun KtPsiFactory.createCalleeExpression(functionName: String): KtExpression = (createExpression("$functionName()") as KtCallExpression).calleeExpression!! private fun KtExpression.toIntOrNull(): Int? = safeAs()?.text?.toIntOrNull() + + private fun KtExpression.isNullable(): Boolean { + return getDataFlowAwareTypes(this).any { it.isNullable() } + } } } diff --git a/plugins/kotlin/idea/tests/test/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java b/plugins/kotlin/idea/tests/test/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java index 0589afe5f546..bdc1a6fdf8a7 100644 --- a/plugins/kotlin/idea/tests/test/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java +++ b/plugins/kotlin/idea/tests/test/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java @@ -13294,6 +13294,16 @@ public abstract class LocalInspectionTestGenerated extends AbstractLocalInspecti runTest("testData/inspectionsLocal/replaceWithStringBuilderAppendRange/hasReceiver.kt"); } + @TestMetadata("nullableCharArray.kt") + public void testNullableCharArray() throws Exception { + runTest("testData/inspectionsLocal/replaceWithStringBuilderAppendRange/nullableCharArray.kt"); + } + + @TestMetadata("nullableCharArray2.kt") + public void testNullableCharArray2() throws Exception { + runTest("testData/inspectionsLocal/replaceWithStringBuilderAppendRange/nullableCharArray2.kt"); + } + @TestMetadata("precedence.kt") public void testPrecedence() throws Exception { runTest("testData/inspectionsLocal/replaceWithStringBuilderAppendRange/precedence.kt"); diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/replaceWithStringBuilderAppendRange/nullableCharArray.kt b/plugins/kotlin/idea/tests/testData/inspectionsLocal/replaceWithStringBuilderAppendRange/nullableCharArray.kt new file mode 100644 index 000000000000..565f5de21ad0 --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/replaceWithStringBuilderAppendRange/nullableCharArray.kt @@ -0,0 +1,6 @@ +// WITH_STDLIB +fun test(charArray: CharArray?, len: Int): String { + return buildString { + append(charArray, 0, len) + } +} \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/replaceWithStringBuilderAppendRange/nullableCharArray.kt.after b/plugins/kotlin/idea/tests/testData/inspectionsLocal/replaceWithStringBuilderAppendRange/nullableCharArray.kt.after new file mode 100644 index 000000000000..856cced1f69a --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/replaceWithStringBuilderAppendRange/nullableCharArray.kt.after @@ -0,0 +1,6 @@ +// WITH_STDLIB +fun test(charArray: CharArray?, len: Int): String { + return buildString { + appendRange(charArray!!, 0, len) + } +} \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/replaceWithStringBuilderAppendRange/nullableCharArray2.kt b/plugins/kotlin/idea/tests/testData/inspectionsLocal/replaceWithStringBuilderAppendRange/nullableCharArray2.kt new file mode 100644 index 000000000000..a642462555f4 --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/replaceWithStringBuilderAppendRange/nullableCharArray2.kt @@ -0,0 +1,8 @@ +// WITH_STDLIB +fun test(charArray: CharArray?, len: Int): String { + return buildString { + if (charArray != null) { + append(charArray, 0, len) + } + } +} \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/replaceWithStringBuilderAppendRange/nullableCharArray2.kt.after b/plugins/kotlin/idea/tests/testData/inspectionsLocal/replaceWithStringBuilderAppendRange/nullableCharArray2.kt.after new file mode 100644 index 000000000000..161c8a5b29e3 --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/replaceWithStringBuilderAppendRange/nullableCharArray2.kt.after @@ -0,0 +1,8 @@ +// WITH_STDLIB +fun test(charArray: CharArray?, len: Int): String { + return buildString { + if (charArray != null) { + appendRange(charArray, 0, len) + } + } +} \ No newline at end of file