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
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
}
}