Reformat: simplifiable call chain inspection

This commit is contained in:
Mikhail Glukhikh
2018-05-11 12:16:17 +03:00
parent a584de8a7e
commit 9fa352e5c0
@@ -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
} }
} }