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
|
||||
|
||||
class SimplifiableCallChainInspection : AbstractKotlinInspection() {
|
||||
|
||||
|
||||
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean) =
|
||||
qualifiedExpressionVisitor(fun(expression) {
|
||||
val firstExpression = expression.receiverExpression
|
||||
val firstCallExpression = getCallExpression(firstExpression) ?: return
|
||||
qualifiedExpressionVisitor(fun(expression) {
|
||||
val firstExpression = expression.receiverExpression
|
||||
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 secondCalleeExpression = secondCallExpression.calleeExpression ?: return
|
||||
val actualConversions = conversionGroups[
|
||||
firstCalleeExpression.text to secondCalleeExpression.text
|
||||
] ?: return
|
||||
val firstCalleeExpression = firstCallExpression.calleeExpression ?: return
|
||||
val secondCalleeExpression = secondCallExpression.calleeExpression ?: return
|
||||
val actualConversions = conversionGroups[
|
||||
firstCalleeExpression.text to secondCalleeExpression.text
|
||||
] ?: return
|
||||
|
||||
val context = expression.analyze()
|
||||
val firstResolvedCall = firstExpression.getResolvedCall(context) ?: return
|
||||
val conversion = actualConversions.firstOrNull {
|
||||
firstResolvedCall.resultingDescriptor.fqNameOrNull()?.asString() == it.firstFqName
|
||||
} ?: return
|
||||
val context = expression.analyze()
|
||||
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
|
||||
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
|
||||
val firstReceiverRawType = firstReceiverType?.constructor?.declarationDescriptor?.defaultType
|
||||
if (firstReceiverRawType != null) {
|
||||
if (firstReceiverRawType.isSubtypeOf(builtIns.map.defaultType) ||
|
||||
firstReceiverRawType.isSubtypeOf(builtIns.mutableMap.defaultType)) return
|
||||
}
|
||||
// Do not apply on maps due to lack of relevant stdlib functions
|
||||
val firstReceiverType = firstResolvedCall.extensionReceiver?.type
|
||||
val firstReceiverRawType = firstReceiverType?.constructor?.declarationDescriptor?.defaultType
|
||||
if (firstReceiverRawType != null) {
|
||||
if (firstReceiverRawType.isSubtypeOf(builtIns.map.defaultType) ||
|
||||
firstReceiverRawType.isSubtypeOf(builtIns.mutableMap.defaultType)
|
||||
) return
|
||||
}
|
||||
|
||||
// Do not apply for lambdas with return inside
|
||||
val lambdaArgument = firstCallExpression.lambdaArguments.firstOrNull()
|
||||
if (lambdaArgument?.anyDescendantOfType<KtReturnExpression>() == true) return
|
||||
// Do not apply for lambdas with return inside
|
||||
val lambdaArgument = firstCallExpression.lambdaArguments.firstOrNull()
|
||||
if (lambdaArgument?.anyDescendantOfType<KtReturnExpression>() == true) return
|
||||
|
||||
val secondResolvedCall = expression.getResolvedCall(context) ?: return
|
||||
val secondResultingDescriptor = secondResolvedCall.resultingDescriptor
|
||||
if (secondResultingDescriptor.fqNameOrNull()?.asString() != conversion.secondFqName) return
|
||||
if (secondResolvedCall.valueArguments.any { (parameter, resolvedArgument) ->
|
||||
val secondResolvedCall = expression.getResolvedCall(context) ?: return
|
||||
val secondResultingDescriptor = secondResolvedCall.resultingDescriptor
|
||||
if (secondResultingDescriptor.fqNameOrNull()?.asString() != conversion.secondFqName) return
|
||||
if (secondResolvedCall.valueArguments.any { (parameter, resolvedArgument) ->
|
||||
parameter.type.isFunctionOfAnyKind() &&
|
||||
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
|
||||
resolvedArgument !is DefaultValueArgument
|
||||
}
|
||||
) return
|
||||
|
||||
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)
|
||||
})
|
||||
if (conversion.replacement.startsWith("joinTo")) {
|
||||
// Function parameter in map must have String result type
|
||||
if (!firstResolvedCall.hasLastFunctionalParameterWithResult(context) {
|
||||
it.isSubtypeOf(builtIns.charSequence.defaultType)
|
||||
}
|
||||
) return
|
||||
}
|
||||
|
||||
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 {
|
||||
|
||||
private val conversions = listOf(
|
||||
Conversion("kotlin.collections.filter", "kotlin.collections.first", "first"),
|
||||
Conversion("kotlin.collections.filter", "kotlin.collections.firstOrNull", "firstOrNull"),
|
||||
Conversion("kotlin.collections.filter", "kotlin.collections.last", "last"),
|
||||
Conversion("kotlin.collections.filter", "kotlin.collections.lastOrNull", "lastOrNull"),
|
||||
Conversion("kotlin.collections.filter", "kotlin.collections.single", "single"),
|
||||
Conversion("kotlin.collections.filter", "kotlin.collections.singleOrNull", "singleOrNull"),
|
||||
Conversion("kotlin.collections.filter", "kotlin.collections.isNotEmpty", "any"),
|
||||
Conversion("kotlin.collections.filter", "kotlin.collections.List.isEmpty", "none"),
|
||||
Conversion("kotlin.collections.filter", "kotlin.collections.first", "first"),
|
||||
Conversion("kotlin.collections.filter", "kotlin.collections.firstOrNull", "firstOrNull"),
|
||||
Conversion("kotlin.collections.filter", "kotlin.collections.last", "last"),
|
||||
Conversion("kotlin.collections.filter", "kotlin.collections.lastOrNull", "lastOrNull"),
|
||||
Conversion("kotlin.collections.filter", "kotlin.collections.single", "single"),
|
||||
Conversion("kotlin.collections.filter", "kotlin.collections.singleOrNull", "singleOrNull"),
|
||||
Conversion("kotlin.collections.filter", "kotlin.collections.isNotEmpty", "any"),
|
||||
Conversion("kotlin.collections.filter", "kotlin.collections.List.isEmpty", "none"),
|
||||
|
||||
Conversion("kotlin.text.filter", "kotlin.text.first", "first"),
|
||||
Conversion("kotlin.text.filter", "kotlin.text.firstOrNull", "firstOrNull"),
|
||||
Conversion("kotlin.text.filter", "kotlin.text.last", "last"),
|
||||
Conversion("kotlin.text.filter", "kotlin.text.lastOrNull", "lastOrNull"),
|
||||
Conversion("kotlin.text.filter", "kotlin.text.single", "single"),
|
||||
Conversion("kotlin.text.filter", "kotlin.text.singleOrNull", "singleOrNull"),
|
||||
Conversion("kotlin.text.filter", "kotlin.text.isNotEmpty", "any"),
|
||||
Conversion("kotlin.text.filter", "kotlin.text.isEmpty", "none"),
|
||||
Conversion("kotlin.text.filter", "kotlin.text.first", "first"),
|
||||
Conversion("kotlin.text.filter", "kotlin.text.firstOrNull", "firstOrNull"),
|
||||
Conversion("kotlin.text.filter", "kotlin.text.last", "last"),
|
||||
Conversion("kotlin.text.filter", "kotlin.text.lastOrNull", "lastOrNull"),
|
||||
Conversion("kotlin.text.filter", "kotlin.text.single", "single"),
|
||||
Conversion("kotlin.text.filter", "kotlin.text.singleOrNull", "singleOrNull"),
|
||||
Conversion("kotlin.text.filter", "kotlin.text.isNotEmpty", "any"),
|
||||
Conversion("kotlin.text.filter", "kotlin.text.isEmpty", "none"),
|
||||
|
||||
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.joinTo", "joinTo"),
|
||||
Conversion("kotlin.collections.map", "kotlin.collections.joinToString", "joinToString"),
|
||||
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 }
|
||||
@@ -120,8 +121,8 @@ class SimplifiableCallChainInspection : AbstractKotlinInspection() {
|
||||
}
|
||||
|
||||
fun getCallExpression(firstExpression: KtExpression) =
|
||||
((firstExpression as? KtQualifiedExpression)?.selectorExpression as? KtCallExpression
|
||||
?: firstExpression as? KtCallExpression)
|
||||
(firstExpression as? KtQualifiedExpression)?.selectorExpression as? KtCallExpression
|
||||
?: firstExpression as? KtCallExpression
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user