Anti-KNPE refactoring of RedundantSamConstructorInspection
So EA-84354 fixed
This commit is contained in:
+38
-30
@@ -30,6 +30,7 @@ import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||
import org.jetbrains.kotlin.load.java.descriptors.SamAdapterDescriptor
|
||||
import org.jetbrains.kotlin.load.java.descriptors.SamConstructorDescriptor
|
||||
import org.jetbrains.kotlin.load.java.sam.SingleAbstractMethodUtils
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.anyDescendantOfType
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext
|
||||
@@ -46,6 +47,7 @@ import org.jetbrains.kotlin.resolve.scopes.SyntheticScopes
|
||||
import org.jetbrains.kotlin.resolve.scopes.collectSyntheticMemberFunctions
|
||||
import org.jetbrains.kotlin.synthetic.SamAdapterExtensionFunctionDescriptor
|
||||
import org.jetbrains.kotlin.types.TypeUtils
|
||||
import org.jetbrains.kotlin.utils.keysToMapExceptNulls
|
||||
|
||||
class RedundantSamConstructorInspection : AbstractKotlinInspection() {
|
||||
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor {
|
||||
@@ -60,7 +62,7 @@ class RedundantSamConstructorInspection : AbstractKotlinInspection() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun createQuickFix(expressions: List<KtCallExpression>): LocalQuickFix {
|
||||
private fun createQuickFix(expressions: Collection<KtCallExpression>): LocalQuickFix {
|
||||
return object : LocalQuickFix {
|
||||
override fun getName() = "Remove redundant SAM-constructors"
|
||||
override fun getFamilyName() = name
|
||||
@@ -77,11 +79,12 @@ class RedundantSamConstructorInspection : AbstractKotlinInspection() {
|
||||
|
||||
val samConstructorCalls = samConstructorCallsToBeConverted(expression)
|
||||
if (samConstructorCalls.isEmpty()) return
|
||||
if (samConstructorCalls.size == 1) {
|
||||
val single = samConstructorCalls.single()
|
||||
val single = samConstructorCalls.singleOrNull()
|
||||
if (single != null) {
|
||||
val calleeExpression = single.calleeExpression ?: return
|
||||
val problemDescriptor = holder.manager.
|
||||
createProblemDescriptor(single.calleeExpression!!,
|
||||
single.typeArgumentList ?: single.calleeExpression!!,
|
||||
createProblemDescriptor(calleeExpression,
|
||||
single.typeArgumentList ?: calleeExpression,
|
||||
"Redundant SAM-constructor",
|
||||
ProblemHighlightType.LIKE_UNUSED_SYMBOL,
|
||||
isOnTheFly,
|
||||
@@ -110,7 +113,8 @@ class RedundantSamConstructorInspection : AbstractKotlinInspection() {
|
||||
return callExpression.getQualifiedExpressionForSelectorOrThis().replace(functionalArgument) as KtLambdaExpression
|
||||
}
|
||||
|
||||
private fun canBeReplaced(parentCall: KtCallExpression, samConstructorArguments: List<KtValueArgument>): Boolean {
|
||||
private fun canBeReplaced(parentCall: KtCallExpression,
|
||||
samConstructorCallArgumentMap: Map<KtValueArgument, KtCallExpression>): Boolean {
|
||||
val context = parentCall.analyze(BodyResolveMode.PARTIAL)
|
||||
|
||||
val calleeExpression = parentCall.calleeExpression ?: return false
|
||||
@@ -120,7 +124,7 @@ class RedundantSamConstructorInspection : AbstractKotlinInspection() {
|
||||
|
||||
val dataFlow = context.getDataFlowInfoBefore(parentCall)
|
||||
val callResolver = parentCall.getResolutionFacade().frontendService<CallResolver>()
|
||||
val newCall = CallWithConvertedArguments(originalCall.call, samConstructorArguments)
|
||||
val newCall = CallWithConvertedArguments(originalCall.call, samConstructorCallArgumentMap)
|
||||
|
||||
val qualifiedExpression = parentCall.getQualifiedExpressionForSelectorOrThis()
|
||||
val expectedType = context[BindingContext.EXPECTED_EXPRESSION_TYPE, qualifiedExpression] ?: TypeUtils.NO_EXPECTED_TYPE
|
||||
@@ -133,14 +137,17 @@ class RedundantSamConstructorInspection : AbstractKotlinInspection() {
|
||||
return samAdapterOriginalDescriptor.original == originalCall.resultingDescriptor.original
|
||||
}
|
||||
|
||||
private class CallWithConvertedArguments(original: Call, val argumentsToConvert: Collection<ValueArgument>): DelegatingCall(original) {
|
||||
private class CallWithConvertedArguments(
|
||||
original: Call,
|
||||
val callArgumentMapToConvert: Map<KtValueArgument, KtCallExpression>
|
||||
) : DelegatingCall(original) {
|
||||
private val newArguments: List<ValueArgument>
|
||||
|
||||
init {
|
||||
val factory = KtPsiFactory(callElement)
|
||||
newArguments = original.valueArguments.map { argument ->
|
||||
if (argument !in argumentsToConvert) return@map argument
|
||||
val newExpression = argument.toCallExpression()!!.samConstructorValueArgument()!!.getArgumentExpression()!!
|
||||
val call = callArgumentMapToConvert[argument]
|
||||
val newExpression = call?.samConstructorValueArgument()?.getArgumentExpression() ?: return@map argument
|
||||
factory.createArgument(newExpression, argument.getArgumentName()?.asName)
|
||||
}
|
||||
}
|
||||
@@ -148,12 +155,9 @@ class RedundantSamConstructorInspection : AbstractKotlinInspection() {
|
||||
override fun getValueArguments() = newArguments
|
||||
}
|
||||
|
||||
fun samConstructorCallsToBeConverted(functionCall: KtCallExpression): List<KtCallExpression> {
|
||||
return samConstructorArgumentsToBeConverted(functionCall).map { it.toCallExpression()!! }
|
||||
}
|
||||
|
||||
private fun samConstructorArgumentsToBeConverted(functionCall: KtCallExpression): List<KtValueArgument> {
|
||||
if (functionCall.valueArguments.all { !canBeSamConstructorCall(it) }) {
|
||||
fun samConstructorCallsToBeConverted(functionCall: KtCallExpression): Collection<KtCallExpression> {
|
||||
val valueArguments = functionCall.valueArguments
|
||||
if (valueArguments.all { !canBeSamConstructorCall(it) }) {
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
@@ -161,22 +165,29 @@ class RedundantSamConstructorInspection : AbstractKotlinInspection() {
|
||||
val functionResolvedCall = functionCall.getResolvedCall(bindingContext) ?: return emptyList()
|
||||
if (!functionResolvedCall.isReallySuccess()) return emptyList()
|
||||
|
||||
val samConstructorCallArguments = functionCall.valueArguments.filter {
|
||||
it.toCallExpression()?.getResolvedCall(bindingContext)?.resultingDescriptor?.original is SamConstructorDescriptor
|
||||
val samConstructorCallArgumentMap = valueArguments.keysToMapExceptNulls { it.toCallExpression() }.filter {
|
||||
(_, call) -> call.getResolvedCall(bindingContext)?.resultingDescriptor?.original is SamConstructorDescriptor
|
||||
}
|
||||
|
||||
if (samConstructorCallArguments.isEmpty()) return emptyList()
|
||||
if (samConstructorCallArgumentMap.isEmpty()) return emptyList()
|
||||
|
||||
if (samConstructorCallArguments.any { hasLabeledReturnPreventingConversion(it.toCallExpression()!!) }) return emptyList()
|
||||
if (samConstructorCallArgumentMap.values.any {
|
||||
val samValueArgument = it.samConstructorValueArgument()
|
||||
val samConstructorName = (it.calleeExpression as? KtSimpleNameExpression)?.getReferencedNameAsName()
|
||||
samValueArgument == null || samConstructorName == null ||
|
||||
samValueArgument.hasLabeledReturnPreventingConversion(samConstructorName)
|
||||
}) return emptyList()
|
||||
|
||||
val originalFunctionDescriptor = functionResolvedCall.resultingDescriptor.original as? FunctionDescriptor ?: return emptyList()
|
||||
val containingClass = originalFunctionDescriptor.containingDeclaration as? ClassDescriptor ?: return emptyList()
|
||||
|
||||
// SAM adapters for static functions
|
||||
for (staticFunWithSameName in containingClass.staticScope.getContributedFunctions(functionResolvedCall.resultingDescriptor.name, NoLookupLocation.FROM_IDE)) {
|
||||
val contributedFunctions = containingClass.staticScope.getContributedFunctions(
|
||||
functionResolvedCall.resultingDescriptor.name, NoLookupLocation.FROM_IDE)
|
||||
for (staticFunWithSameName in contributedFunctions) {
|
||||
if (staticFunWithSameName is SamAdapterDescriptor<*>) {
|
||||
if (isSamAdapterSuitableForCall(staticFunWithSameName, originalFunctionDescriptor, samConstructorCallArguments.size)) {
|
||||
return samConstructorCallArguments.takeIf { canBeReplaced(functionCall, it) } ?: emptyList()
|
||||
if (isSamAdapterSuitableForCall(staticFunWithSameName, originalFunctionDescriptor, samConstructorCallArgumentMap.size)) {
|
||||
return samConstructorCallArgumentMap.takeIf { canBeReplaced(functionCall, it) }?.values ?: emptyList()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -189,8 +200,8 @@ class RedundantSamConstructorInspection : AbstractKotlinInspection() {
|
||||
NoLookupLocation.FROM_IDE)
|
||||
for (syntheticExtension in syntheticExtensions) {
|
||||
val samAdapter = syntheticExtension as? SamAdapterExtensionFunctionDescriptor ?: continue
|
||||
if (isSamAdapterSuitableForCall(samAdapter, originalFunctionDescriptor, samConstructorCallArguments.size)) {
|
||||
return samConstructorCallArguments.takeIf { canBeReplaced(functionCall, it) } ?: emptyList()
|
||||
if (isSamAdapterSuitableForCall(samAdapter, originalFunctionDescriptor, samConstructorCallArgumentMap.size)) {
|
||||
return samConstructorCallArgumentMap.takeIf { canBeReplaced(functionCall, it) }?.values ?: emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -227,10 +238,7 @@ class RedundantSamConstructorInspection : AbstractKotlinInspection() {
|
||||
return parametersWithSamTypeCount == samConstructorsCount
|
||||
}
|
||||
|
||||
private fun hasLabeledReturnPreventingConversion(samConstructorCall: KtCallExpression): Boolean {
|
||||
val argument = samConstructorCall.samConstructorValueArgument()!!
|
||||
val samConstructorName = (samConstructorCall.calleeExpression as KtSimpleNameExpression).getReferencedNameAsName()
|
||||
return argument.anyDescendantOfType<KtReturnExpression> { it.getLabelNameAsName() == samConstructorName }
|
||||
}
|
||||
private fun KtValueArgument.hasLabeledReturnPreventingConversion(samConstructorName: Name) =
|
||||
anyDescendantOfType<KtReturnExpression> { it.getLabelNameAsName() == samConstructorName }
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user