"Convert to anonymous object" intention: suggest for kotlin fun interface

#KT-37749 Fixed
This commit is contained in:
Toshiaki Kameyama
2020-03-26 12:00:18 +09:00
committed by Roman Golyshev
parent 0abe3a6c39
commit 6a1923a477
4 changed files with 37 additions and 8 deletions
@@ -15,11 +15,10 @@ import org.jetbrains.kotlin.idea.KotlinBundle
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.caches.resolve.resolveToCall
import org.jetbrains.kotlin.idea.core.replaced
import org.jetbrains.kotlin.idea.search.usagesSearch.descriptor
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor
import org.jetbrains.kotlin.load.java.sam.JavaSingleAbstractMethodUtils
import org.jetbrains.kotlin.psi.KtCallExpression
import org.jetbrains.kotlin.psi.KtFunctionLiteral
import org.jetbrains.kotlin.psi.KtLambdaExpression
import org.jetbrains.kotlin.psi.KtPsiFactory
import org.jetbrains.kotlin.psi.psiUtil.anyDescendantOfType
@@ -39,13 +38,12 @@ class SamConversionToAnonymousObjectIntention : SelfTargetingRangeIntention<KtCa
val callee = element.calleeExpression ?: return null
val lambda = getLambdaExpression(element) ?: return null
val functionLiteral = lambda.functionLiteral
val descriptor = (functionLiteral.descriptor as? FunctionDescriptor) ?: return null
val bindingContext = functionLiteral.analyze()
val sam = element.getSingleAbstractMethod(bindingContext) ?: return null
val samValueParameters = sam.valueParameters
val samValueParameterSize = samValueParameters.size
if (descriptor.valueParameters.size != samValueParameterSize) return null
if (samValueParameterSize != functionLiteral.functionDescriptor(bindingContext)?.valueParameters?.size) return null
val samName = sam.name.asString()
if (functionLiteral.anyDescendantOfType<KtCallExpression> { call ->
@@ -65,18 +63,22 @@ class SamConversionToAnonymousObjectIntention : SelfTargetingRangeIntention<KtCa
override fun applyTo(element: KtCallExpression, editor: Editor?) {
val lambda = getLambdaExpression(element) ?: return
val functionDescriptor = lambda.functionLiteral.descriptor as? FunctionDescriptor ?: return
val functionName = element.getSingleAbstractMethod(element.analyze(BodyResolveMode.PARTIAL))?.name?.asString() ?: return
val context = element.analyze(BodyResolveMode.PARTIAL)
val functionDescriptor = lambda.functionLiteral.functionDescriptor(context) ?: return
val functionName = element.getSingleAbstractMethod(context)?.name?.asString() ?: return
convertToAnonymousObject(element, lambda, functionDescriptor, functionName)
}
private fun KtCallExpression.getSingleAbstractMethod(context: BindingContext): FunctionDescriptor? {
val type = getType(context) ?: return null
if (!JavaSingleAbstractMethodUtils.isSamType(type)) return null
val javaClass = type.constructor.declarationDescriptor as? JavaClassDescriptor ?: return null
return getSingleAbstractMethodOrNull(javaClass)
val classDescriptor = type.constructor.declarationDescriptor as? ClassDescriptor ?: return null
return getSingleAbstractMethodOrNull(classDescriptor)
}
private fun KtFunctionLiteral.functionDescriptor(context: BindingContext): FunctionDescriptor? =
context[BindingContext.DECLARATION_TO_DESCRIPTOR, this] as? FunctionDescriptor
companion object {
fun convertToAnonymousObject(
call: KtCallExpression,
@@ -0,0 +1,9 @@
fun interface KotlinFace {
fun single()
}
fun useSam(kf: KotlinFace) {}
fun callSam() {
useSam(kf = <caret>KotlinFace {})
}
@@ -0,0 +1,13 @@
fun interface KotlinFace {
fun single()
}
fun useSam(kf: KotlinFace) {}
fun callSam() {
useSam(kf = object : KotlinFace {
override fun single() {
}
})
}
@@ -14790,6 +14790,11 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/testData/intentions/samConversionToAnonymousObject"), Pattern.compile("^([\\w\\-_]+)\\.(kt|kts)$"), null, true);
}
@TestMetadata("funInterface.kt")
public void testFunInterface() throws Exception {
runTest("idea/testData/intentions/samConversionToAnonymousObject/funInterface.kt");
}
@TestMetadata("labeledReturn.kt")
public void testLabeledReturn() throws Exception {
runTest("idea/testData/intentions/samConversionToAnonymousObject/labeledReturn.kt");