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.*
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.anyDescendantOfType
|
import org.jetbrains.kotlin.psi.psiUtil.anyDescendantOfType
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.startOffset
|
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.callUtil.getResolvedCall
|
||||||
import org.jetbrains.kotlin.resolve.calls.model.DefaultValueArgument
|
import org.jetbrains.kotlin.resolve.calls.model.DefaultValueArgument
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameOrNull
|
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameOrNull
|
||||||
@@ -37,8 +38,9 @@ class SimplifiableCallChainInspection : AbstractKotlinInspection() {
|
|||||||
override fun visitQualifiedExpression(expression: KtQualifiedExpression) {
|
override fun visitQualifiedExpression(expression: KtQualifiedExpression) {
|
||||||
super.visitQualifiedExpression(expression)
|
super.visitQualifiedExpression(expression)
|
||||||
|
|
||||||
val firstQualifiedExpression = expression.receiverExpression as? KtQualifiedExpression ?: return
|
val firstExpression = expression.receiverExpression
|
||||||
val firstCallExpression = firstQualifiedExpression.selectorExpression as? KtCallExpression ?: return
|
val firstCallExpression = getCallExpression(firstExpression) ?: return
|
||||||
|
|
||||||
val secondCallExpression = expression.selectorExpression as? KtCallExpression ?: return
|
val secondCallExpression = expression.selectorExpression as? KtCallExpression ?: return
|
||||||
|
|
||||||
val firstCalleeExpression = firstCallExpression.calleeExpression ?: return
|
val firstCalleeExpression = firstCallExpression.calleeExpression ?: return
|
||||||
@@ -48,15 +50,20 @@ class SimplifiableCallChainInspection : AbstractKotlinInspection() {
|
|||||||
] ?: return
|
] ?: return
|
||||||
|
|
||||||
val context = expression.analyze()
|
val context = expression.analyze()
|
||||||
val firstResolvedCall = firstQualifiedExpression.getResolvedCall(context) ?: return
|
val firstResolvedCall = firstExpression.getResolvedCall(context) ?: return
|
||||||
val conversion = actualConversions.firstOrNull {
|
val conversion = actualConversions.firstOrNull {
|
||||||
firstResolvedCall.resultingDescriptor.fqNameOrNull()?.asString() == it.firstFqName
|
firstResolvedCall.resultingDescriptor.fqNameOrNull()?.asString() == it.firstFqName
|
||||||
} ?: return
|
} ?: return
|
||||||
|
|
||||||
|
val builtIns = context[BindingContext.EXPRESSION_TYPE_INFO, expression]?.type?.builtIns ?: return
|
||||||
|
|
||||||
// Do not apply on maps due to lack of relevant stdlib functions
|
// Do not apply on maps due to lack of relevant stdlib functions
|
||||||
val firstReceiverType = firstResolvedCall.extensionReceiver?.type ?: return
|
val firstReceiverType = firstResolvedCall.extensionReceiver?.type
|
||||||
val builtIns = firstReceiverType.builtIns
|
if (firstReceiverType != null) {
|
||||||
val mapType = builtIns.map.defaultType
|
val mapType = builtIns.map.defaultType
|
||||||
if (firstReceiverType.isSubtypeOf(mapType)) return
|
if (firstReceiverType.isSubtypeOf(mapType)) return
|
||||||
|
}
|
||||||
|
|
||||||
// Do not apply for lambdas with return inside
|
// Do not apply for lambdas with return inside
|
||||||
val lambdaArgument = firstCallExpression.lambdaArguments.firstOrNull()
|
val lambdaArgument = firstCallExpression.lambdaArguments.firstOrNull()
|
||||||
if (lambdaArgument?.anyDescendantOfType<KtReturnExpression>() == true) return
|
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.joinTo", "joinTo"),
|
||||||
Conversion("kotlin.collections.map", "kotlin.collections.joinToString", "joinToString"),
|
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 }
|
private val conversionGroups = conversions.groupBy { it.firstName to it.secondName }
|
||||||
@@ -124,5 +133,9 @@ class SimplifiableCallChainInspection : AbstractKotlinInspection() {
|
|||||||
val secondName = secondFqName.convertToShort()
|
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) {
|
override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
|
||||||
(descriptor.psiElement as? KtQualifiedExpression)?.let { secondQualifiedExpression ->
|
(descriptor.psiElement as? KtQualifiedExpression)?.let { secondQualifiedExpression ->
|
||||||
val factory = KtPsiFactory(secondQualifiedExpression)
|
val factory = KtPsiFactory(secondQualifiedExpression)
|
||||||
val firstQualifiedExpression = secondQualifiedExpression.receiverExpression as? KtQualifiedExpression ?: return
|
val firstExpression = secondQualifiedExpression.receiverExpression
|
||||||
val operationSign = if (firstQualifiedExpression is KtSafeQualifiedExpression) "?." else "."
|
|
||||||
val firstCallExpression = firstQualifiedExpression.selectorExpression as? KtCallExpression ?: return
|
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 secondCallExpression = secondQualifiedExpression.selectorExpression as? KtCallExpression ?: return
|
||||||
|
|
||||||
val lastArgumentPrefix = if (newName.startsWith("joinTo")) "transform = " else ""
|
val lastArgumentPrefix = if (newName.startsWith("joinTo")) "transform = " else ""
|
||||||
val arguments = secondCallExpression.valueArgumentList?.arguments.orEmpty().map { it.text } +
|
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 lambdaArgument = firstCallExpression.lambdaArguments.singleOrNull()
|
||||||
|
|
||||||
val argumentsText = arguments.ifNotEmpty { joinToString(prefix = "(", postfix = ")") } ?: ""
|
val argumentsText = arguments.ifNotEmpty { joinToString(prefix = "(", postfix = ")") } ?: ""
|
||||||
val newQualifiedExpression = if (lambdaArgument != null) factory.createExpressionByPattern(
|
val newQualifiedExpression = if (lambdaArgument != null) factory.createExpressionByPattern(
|
||||||
"$0$1$2 $3 $4",
|
"$0$1$2 $3 $4",
|
||||||
firstQualifiedExpression.receiverExpression,
|
receiverExpression ?: "",
|
||||||
operationSign,
|
operationSign,
|
||||||
newName,
|
newName,
|
||||||
argumentsText,
|
argumentsText,
|
||||||
@@ -52,7 +60,7 @@ class SimplifyCallChainFix(val newName: String) : LocalQuickFix {
|
|||||||
)
|
)
|
||||||
else factory.createExpressionByPattern(
|
else factory.createExpressionByPattern(
|
||||||
"$0$1$2 $3",
|
"$0$1$2 $3",
|
||||||
firstQualifiedExpression.receiverExpression,
|
receiverExpression ?: "",
|
||||||
operationSign,
|
operationSign,
|
||||||
newName,
|
newName,
|
||||||
argumentsText
|
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);
|
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")
|
@TestMetadata("mapNotNull.kt")
|
||||||
public void testMapNotNull() throws Exception {
|
public void testMapNotNull() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/collections/simplifiableCallChain/mapNotNull.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/collections/simplifiableCallChain/mapNotNull.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user