diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/RedundantSamConstructorInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/RedundantSamConstructorInspection.kt index c6d941f0c2a..4d6f25d86b8 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/RedundantSamConstructorInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/RedundantSamConstructorInspection.kt @@ -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): LocalQuickFix { + private fun createQuickFix(expressions: Collection): 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): Boolean { + private fun canBeReplaced(parentCall: KtCallExpression, + samConstructorCallArgumentMap: Map): 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() - 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): DelegatingCall(original) { + private class CallWithConvertedArguments( + original: Call, + val callArgumentMapToConvert: Map + ) : DelegatingCall(original) { private val newArguments: List 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 { - return samConstructorArgumentsToBeConverted(functionCall).map { it.toCallExpression()!! } - } - - private fun samConstructorArgumentsToBeConverted(functionCall: KtCallExpression): List { - if (functionCall.valueArguments.all { !canBeSamConstructorCall(it) }) { + fun samConstructorCallsToBeConverted(functionCall: KtCallExpression): Collection { + 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 { it.getLabelNameAsName() == samConstructorName } - } + private fun KtValueArgument.hasLabeledReturnPreventingConversion(samConstructorName: Name) = + anyDescendantOfType { it.getLabelNameAsName() == samConstructorName } } }