Support listOfNotNull in SimplifiableCallChainInspection #KT-20410 Fixed
This commit is contained in:
committed by
Mikhail Glukhikh
parent
82a9c35194
commit
91c35a6c59
+21
-8
@@ -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<KtReturnExpression>() == 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)
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
val s: String? = null
|
||||
val x = <caret>listOf(s).filterNotNull()
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
val s: String? = null
|
||||
val x = <caret>listOfNotNull(s)
|
||||
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user