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