Reformat: simplifiable call chain inspection
This commit is contained in:
+72
-71
@@ -20,93 +20,94 @@ import org.jetbrains.kotlin.types.typeUtil.builtIns
|
|||||||
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
|
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
|
||||||
|
|
||||||
class SimplifiableCallChainInspection : AbstractKotlinInspection() {
|
class SimplifiableCallChainInspection : AbstractKotlinInspection() {
|
||||||
|
|
||||||
|
|
||||||
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean) =
|
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean) =
|
||||||
qualifiedExpressionVisitor(fun(expression) {
|
qualifiedExpressionVisitor(fun(expression) {
|
||||||
val firstExpression = expression.receiverExpression
|
val firstExpression = expression.receiverExpression
|
||||||
val firstCallExpression = getCallExpression(firstExpression) ?: 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
|
||||||
val secondCalleeExpression = secondCallExpression.calleeExpression ?: return
|
val secondCalleeExpression = secondCallExpression.calleeExpression ?: return
|
||||||
val actualConversions = conversionGroups[
|
val actualConversions = conversionGroups[
|
||||||
firstCalleeExpression.text to secondCalleeExpression.text
|
firstCalleeExpression.text to secondCalleeExpression.text
|
||||||
] ?: return
|
] ?: return
|
||||||
|
|
||||||
val context = expression.analyze()
|
val context = expression.analyze()
|
||||||
val firstResolvedCall = firstExpression.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
|
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
|
val firstReceiverType = firstResolvedCall.extensionReceiver?.type
|
||||||
val firstReceiverRawType = firstReceiverType?.constructor?.declarationDescriptor?.defaultType
|
val firstReceiverRawType = firstReceiverType?.constructor?.declarationDescriptor?.defaultType
|
||||||
if (firstReceiverRawType != null) {
|
if (firstReceiverRawType != null) {
|
||||||
if (firstReceiverRawType.isSubtypeOf(builtIns.map.defaultType) ||
|
if (firstReceiverRawType.isSubtypeOf(builtIns.map.defaultType) ||
|
||||||
firstReceiverRawType.isSubtypeOf(builtIns.mutableMap.defaultType)) return
|
firstReceiverRawType.isSubtypeOf(builtIns.mutableMap.defaultType)
|
||||||
}
|
) 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
|
||||||
|
|
||||||
val secondResolvedCall = expression.getResolvedCall(context) ?: return
|
val secondResolvedCall = expression.getResolvedCall(context) ?: return
|
||||||
val secondResultingDescriptor = secondResolvedCall.resultingDescriptor
|
val secondResultingDescriptor = secondResolvedCall.resultingDescriptor
|
||||||
if (secondResultingDescriptor.fqNameOrNull()?.asString() != conversion.secondFqName) return
|
if (secondResultingDescriptor.fqNameOrNull()?.asString() != conversion.secondFqName) return
|
||||||
if (secondResolvedCall.valueArguments.any { (parameter, resolvedArgument) ->
|
if (secondResolvedCall.valueArguments.any { (parameter, resolvedArgument) ->
|
||||||
parameter.type.isFunctionOfAnyKind() &&
|
parameter.type.isFunctionOfAnyKind() &&
|
||||||
resolvedArgument !is DefaultValueArgument
|
resolvedArgument !is DefaultValueArgument
|
||||||
}) return
|
|
||||||
|
|
||||||
if (conversion.replacement.startsWith("joinTo")) {
|
|
||||||
// Function parameter in map must have String result type
|
|
||||||
if (!firstResolvedCall.hasLastFunctionalParameterWithResult(context) {
|
|
||||||
it.isSubtypeOf(builtIns.charSequence.defaultType)
|
|
||||||
}) return
|
|
||||||
}
|
}
|
||||||
|
) return
|
||||||
|
|
||||||
val descriptor = holder.manager.createProblemDescriptor(
|
if (conversion.replacement.startsWith("joinTo")) {
|
||||||
expression,
|
// Function parameter in map must have String result type
|
||||||
firstCalleeExpression.textRange.shiftRight(-expression.startOffset),
|
if (!firstResolvedCall.hasLastFunctionalParameterWithResult(context) {
|
||||||
"Call chain on collection type may be simplified",
|
it.isSubtypeOf(builtIns.charSequence.defaultType)
|
||||||
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
|
}
|
||||||
isOnTheFly,
|
) return
|
||||||
SimplifyCallChainFix(conversion.replacement)
|
}
|
||||||
)
|
|
||||||
holder.registerProblem(descriptor)
|
val descriptor = holder.manager.createProblemDescriptor(
|
||||||
})
|
expression,
|
||||||
|
firstCalleeExpression.textRange.shiftRight(-expression.startOffset),
|
||||||
|
"Call chain on collection type may be simplified",
|
||||||
|
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
|
||||||
|
isOnTheFly,
|
||||||
|
SimplifyCallChainFix(conversion.replacement)
|
||||||
|
)
|
||||||
|
holder.registerProblem(descriptor)
|
||||||
|
})
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
private val conversions = listOf(
|
private val conversions = listOf(
|
||||||
Conversion("kotlin.collections.filter", "kotlin.collections.first", "first"),
|
Conversion("kotlin.collections.filter", "kotlin.collections.first", "first"),
|
||||||
Conversion("kotlin.collections.filter", "kotlin.collections.firstOrNull", "firstOrNull"),
|
Conversion("kotlin.collections.filter", "kotlin.collections.firstOrNull", "firstOrNull"),
|
||||||
Conversion("kotlin.collections.filter", "kotlin.collections.last", "last"),
|
Conversion("kotlin.collections.filter", "kotlin.collections.last", "last"),
|
||||||
Conversion("kotlin.collections.filter", "kotlin.collections.lastOrNull", "lastOrNull"),
|
Conversion("kotlin.collections.filter", "kotlin.collections.lastOrNull", "lastOrNull"),
|
||||||
Conversion("kotlin.collections.filter", "kotlin.collections.single", "single"),
|
Conversion("kotlin.collections.filter", "kotlin.collections.single", "single"),
|
||||||
Conversion("kotlin.collections.filter", "kotlin.collections.singleOrNull", "singleOrNull"),
|
Conversion("kotlin.collections.filter", "kotlin.collections.singleOrNull", "singleOrNull"),
|
||||||
Conversion("kotlin.collections.filter", "kotlin.collections.isNotEmpty", "any"),
|
Conversion("kotlin.collections.filter", "kotlin.collections.isNotEmpty", "any"),
|
||||||
Conversion("kotlin.collections.filter", "kotlin.collections.List.isEmpty", "none"),
|
Conversion("kotlin.collections.filter", "kotlin.collections.List.isEmpty", "none"),
|
||||||
|
|
||||||
Conversion("kotlin.text.filter", "kotlin.text.first", "first"),
|
Conversion("kotlin.text.filter", "kotlin.text.first", "first"),
|
||||||
Conversion("kotlin.text.filter", "kotlin.text.firstOrNull", "firstOrNull"),
|
Conversion("kotlin.text.filter", "kotlin.text.firstOrNull", "firstOrNull"),
|
||||||
Conversion("kotlin.text.filter", "kotlin.text.last", "last"),
|
Conversion("kotlin.text.filter", "kotlin.text.last", "last"),
|
||||||
Conversion("kotlin.text.filter", "kotlin.text.lastOrNull", "lastOrNull"),
|
Conversion("kotlin.text.filter", "kotlin.text.lastOrNull", "lastOrNull"),
|
||||||
Conversion("kotlin.text.filter", "kotlin.text.single", "single"),
|
Conversion("kotlin.text.filter", "kotlin.text.single", "single"),
|
||||||
Conversion("kotlin.text.filter", "kotlin.text.singleOrNull", "singleOrNull"),
|
Conversion("kotlin.text.filter", "kotlin.text.singleOrNull", "singleOrNull"),
|
||||||
Conversion("kotlin.text.filter", "kotlin.text.isNotEmpty", "any"),
|
Conversion("kotlin.text.filter", "kotlin.text.isNotEmpty", "any"),
|
||||||
Conversion("kotlin.text.filter", "kotlin.text.isEmpty", "none"),
|
Conversion("kotlin.text.filter", "kotlin.text.isEmpty", "none"),
|
||||||
|
|
||||||
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")
|
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 }
|
||||||
@@ -120,8 +121,8 @@ class SimplifiableCallChainInspection : AbstractKotlinInspection() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun getCallExpression(firstExpression: KtExpression) =
|
fun getCallExpression(firstExpression: KtExpression) =
|
||||||
((firstExpression as? KtQualifiedExpression)?.selectorExpression as? KtCallExpression
|
(firstExpression as? KtQualifiedExpression)?.selectorExpression as? KtCallExpression
|
||||||
?: firstExpression as? KtCallExpression)
|
?: firstExpression as? KtCallExpression
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user