diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/collections/SimplifiableCallChainInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/collections/SimplifiableCallChainInspection.kt index b78a809e761..262e02bc84e 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/collections/SimplifiableCallChainInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/collections/SimplifiableCallChainInspection.kt @@ -23,6 +23,7 @@ import org.jetbrains.kotlin.idea.inspections.AbstractKotlinInspection import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.anyDescendantOfType import org.jetbrains.kotlin.psi.psiUtil.startOffset +import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall import org.jetbrains.kotlin.resolve.calls.model.DefaultValueArgument import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameOrNull @@ -37,8 +38,9 @@ class SimplifiableCallChainInspection : AbstractKotlinInspection() { override fun visitQualifiedExpression(expression: KtQualifiedExpression) { super.visitQualifiedExpression(expression) - val firstQualifiedExpression = expression.receiverExpression as? KtQualifiedExpression ?: return - val firstCallExpression = firstQualifiedExpression.selectorExpression as? KtCallExpression ?: return + val firstExpression = expression.receiverExpression + val firstCallExpression = getCallExpression(firstExpression) ?: return + val secondCallExpression = expression.selectorExpression as? KtCallExpression ?: return val firstCalleeExpression = firstCallExpression.calleeExpression ?: return @@ -48,15 +50,20 @@ class SimplifiableCallChainInspection : AbstractKotlinInspection() { ] ?: return val context = expression.analyze() - val firstResolvedCall = firstQualifiedExpression.getResolvedCall(context) ?: return + val firstResolvedCall = firstExpression.getResolvedCall(context) ?: return val conversion = actualConversions.firstOrNull { firstResolvedCall.resultingDescriptor.fqNameOrNull()?.asString() == it.firstFqName } ?: return + + val builtIns = context[BindingContext.EXPRESSION_TYPE_INFO, expression]?.type?.builtIns ?: return + // Do not apply on maps due to lack of relevant stdlib functions - val firstReceiverType = firstResolvedCall.extensionReceiver?.type ?: return - val builtIns = firstReceiverType.builtIns - val mapType = builtIns.map.defaultType - if (firstReceiverType.isSubtypeOf(mapType)) return + val firstReceiverType = firstResolvedCall.extensionReceiver?.type + if (firstReceiverType != null) { + val mapType = builtIns.map.defaultType + if (firstReceiverType.isSubtypeOf(mapType)) return + } + // Do not apply for lambdas with return inside val lambdaArgument = firstCallExpression.lambdaArguments.firstOrNull() if (lambdaArgument?.anyDescendantOfType() == true) return @@ -111,7 +118,9 @@ class SimplifiableCallChainInspection : AbstractKotlinInspection() { Conversion("kotlin.collections.map", "kotlin.collections.joinTo", "joinTo"), Conversion("kotlin.collections.map", "kotlin.collections.joinToString", "joinToString"), - Conversion("kotlin.collections.map", "kotlin.collections.filterNotNull", "mapNotNull") + Conversion("kotlin.collections.map", "kotlin.collections.filterNotNull", "mapNotNull"), + + Conversion("kotlin.collections.listOf", "kotlin.collections.filterNotNull", "listOfNotNull") ) private val conversionGroups = conversions.groupBy { it.firstName to it.secondName } @@ -124,5 +133,9 @@ class SimplifiableCallChainInspection : AbstractKotlinInspection() { val secondName = secondFqName.convertToShort() } + fun getCallExpression(firstExpression: KtExpression) = + ((firstExpression as? KtQualifiedExpression)?.selectorExpression as? KtCallExpression + ?: firstExpression as? KtCallExpression) + } } \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/collections/SimplifyCallChainFix.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/collections/SimplifyCallChainFix.kt index db29d0ec09f..61eafb4c7be 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/collections/SimplifyCallChainFix.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/collections/SimplifyCallChainFix.kt @@ -31,20 +31,28 @@ class SimplifyCallChainFix(val newName: String) : LocalQuickFix { override fun applyFix(project: Project, descriptor: ProblemDescriptor) { (descriptor.psiElement as? KtQualifiedExpression)?.let { secondQualifiedExpression -> val factory = KtPsiFactory(secondQualifiedExpression) - val firstQualifiedExpression = secondQualifiedExpression.receiverExpression as? KtQualifiedExpression ?: return - val operationSign = if (firstQualifiedExpression is KtSafeQualifiedExpression) "?." else "." - val firstCallExpression = firstQualifiedExpression.selectorExpression as? KtCallExpression ?: return + val firstExpression = secondQualifiedExpression.receiverExpression + + val operationSign = when (firstExpression) { + is KtSafeQualifiedExpression -> "?." + is KtQualifiedExpression -> "." + else -> "" + } + + val receiverExpression = (firstExpression as? KtQualifiedExpression)?.receiverExpression + + val firstCallExpression = SimplifiableCallChainInspection.getCallExpression(firstExpression) ?: return val secondCallExpression = secondQualifiedExpression.selectorExpression as? KtCallExpression ?: return val lastArgumentPrefix = if (newName.startsWith("joinTo")) "transform = " else "" val arguments = secondCallExpression.valueArgumentList?.arguments.orEmpty().map { it.text } + - firstCallExpression.valueArgumentList?.arguments.orEmpty().map { "$lastArgumentPrefix${it.text}"} + firstCallExpression.valueArgumentList?.arguments.orEmpty().map { "$lastArgumentPrefix${it.text}" } val lambdaArgument = firstCallExpression.lambdaArguments.singleOrNull() val argumentsText = arguments.ifNotEmpty { joinToString(prefix = "(", postfix = ")") } ?: "" val newQualifiedExpression = if (lambdaArgument != null) factory.createExpressionByPattern( "$0$1$2 $3 $4", - firstQualifiedExpression.receiverExpression, + receiverExpression ?: "", operationSign, newName, argumentsText, @@ -52,7 +60,7 @@ class SimplifyCallChainFix(val newName: String) : LocalQuickFix { ) else factory.createExpressionByPattern( "$0$1$2 $3", - firstQualifiedExpression.receiverExpression, + receiverExpression ?: "", operationSign, newName, argumentsText diff --git a/idea/testData/inspectionsLocal/collections/simplifiableCallChain/listOfNotNull.kt b/idea/testData/inspectionsLocal/collections/simplifiableCallChain/listOfNotNull.kt new file mode 100644 index 00000000000..bfcb4c4e1c3 --- /dev/null +++ b/idea/testData/inspectionsLocal/collections/simplifiableCallChain/listOfNotNull.kt @@ -0,0 +1,4 @@ +// WITH_RUNTIME + +val s: String? = null +val x = listOf(s).filterNotNull() \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/collections/simplifiableCallChain/listOfNotNull.kt.after b/idea/testData/inspectionsLocal/collections/simplifiableCallChain/listOfNotNull.kt.after new file mode 100644 index 00000000000..61962257e22 --- /dev/null +++ b/idea/testData/inspectionsLocal/collections/simplifiableCallChain/listOfNotNull.kt.after @@ -0,0 +1,4 @@ +// WITH_RUNTIME + +val s: String? = null +val x = listOfNotNull(s) \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java index cb98ddb079c..768de1e8144 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java @@ -250,6 +250,12 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest { doTest(fileName); } + @TestMetadata("listOfNotNull.kt") + public void testListOfNotNull() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/collections/simplifiableCallChain/listOfNotNull.kt"); + doTest(fileName); + } + @TestMetadata("mapNotNull.kt") public void testMapNotNull() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/collections/simplifiableCallChain/mapNotNull.kt");