"Convert to anonymous object" intention: suggest for kotlin fun interface
#KT-37749 Fixed
This commit is contained in:
committed by
Roman Golyshev
parent
0abe3a6c39
commit
6a1923a477
+10
-8
@@ -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.analyze
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveToCall
|
import org.jetbrains.kotlin.idea.caches.resolve.resolveToCall
|
||||||
import org.jetbrains.kotlin.idea.core.replaced
|
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.lexer.KtTokens
|
||||||
import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor
|
|
||||||
import org.jetbrains.kotlin.load.java.sam.JavaSingleAbstractMethodUtils
|
import org.jetbrains.kotlin.load.java.sam.JavaSingleAbstractMethodUtils
|
||||||
import org.jetbrains.kotlin.psi.KtCallExpression
|
import org.jetbrains.kotlin.psi.KtCallExpression
|
||||||
|
import org.jetbrains.kotlin.psi.KtFunctionLiteral
|
||||||
import org.jetbrains.kotlin.psi.KtLambdaExpression
|
import org.jetbrains.kotlin.psi.KtLambdaExpression
|
||||||
import org.jetbrains.kotlin.psi.KtPsiFactory
|
import org.jetbrains.kotlin.psi.KtPsiFactory
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.anyDescendantOfType
|
import org.jetbrains.kotlin.psi.psiUtil.anyDescendantOfType
|
||||||
@@ -39,13 +38,12 @@ class SamConversionToAnonymousObjectIntention : SelfTargetingRangeIntention<KtCa
|
|||||||
val callee = element.calleeExpression ?: return null
|
val callee = element.calleeExpression ?: return null
|
||||||
val lambda = getLambdaExpression(element) ?: return null
|
val lambda = getLambdaExpression(element) ?: return null
|
||||||
val functionLiteral = lambda.functionLiteral
|
val functionLiteral = lambda.functionLiteral
|
||||||
val descriptor = (functionLiteral.descriptor as? FunctionDescriptor) ?: return null
|
|
||||||
val bindingContext = functionLiteral.analyze()
|
val bindingContext = functionLiteral.analyze()
|
||||||
val sam = element.getSingleAbstractMethod(bindingContext) ?: return null
|
val sam = element.getSingleAbstractMethod(bindingContext) ?: return null
|
||||||
|
|
||||||
val samValueParameters = sam.valueParameters
|
val samValueParameters = sam.valueParameters
|
||||||
val samValueParameterSize = samValueParameters.size
|
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()
|
val samName = sam.name.asString()
|
||||||
if (functionLiteral.anyDescendantOfType<KtCallExpression> { call ->
|
if (functionLiteral.anyDescendantOfType<KtCallExpression> { call ->
|
||||||
@@ -65,18 +63,22 @@ class SamConversionToAnonymousObjectIntention : SelfTargetingRangeIntention<KtCa
|
|||||||
|
|
||||||
override fun applyTo(element: KtCallExpression, editor: Editor?) {
|
override fun applyTo(element: KtCallExpression, editor: Editor?) {
|
||||||
val lambda = getLambdaExpression(element) ?: return
|
val lambda = getLambdaExpression(element) ?: return
|
||||||
val functionDescriptor = lambda.functionLiteral.descriptor as? FunctionDescriptor ?: return
|
val context = element.analyze(BodyResolveMode.PARTIAL)
|
||||||
val functionName = element.getSingleAbstractMethod(element.analyze(BodyResolveMode.PARTIAL))?.name?.asString() ?: return
|
val functionDescriptor = lambda.functionLiteral.functionDescriptor(context) ?: return
|
||||||
|
val functionName = element.getSingleAbstractMethod(context)?.name?.asString() ?: return
|
||||||
convertToAnonymousObject(element, lambda, functionDescriptor, functionName)
|
convertToAnonymousObject(element, lambda, functionDescriptor, functionName)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun KtCallExpression.getSingleAbstractMethod(context: BindingContext): FunctionDescriptor? {
|
private fun KtCallExpression.getSingleAbstractMethod(context: BindingContext): FunctionDescriptor? {
|
||||||
val type = getType(context) ?: return null
|
val type = getType(context) ?: return null
|
||||||
if (!JavaSingleAbstractMethodUtils.isSamType(type)) return null
|
if (!JavaSingleAbstractMethodUtils.isSamType(type)) return null
|
||||||
val javaClass = type.constructor.declarationDescriptor as? JavaClassDescriptor ?: return null
|
val classDescriptor = type.constructor.declarationDescriptor as? ClassDescriptor ?: return null
|
||||||
return getSingleAbstractMethodOrNull(javaClass)
|
return getSingleAbstractMethodOrNull(classDescriptor)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun KtFunctionLiteral.functionDescriptor(context: BindingContext): FunctionDescriptor? =
|
||||||
|
context[BindingContext.DECLARATION_TO_DESCRIPTOR, this] as? FunctionDescriptor
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun convertToAnonymousObject(
|
fun convertToAnonymousObject(
|
||||||
call: KtCallExpression,
|
call: KtCallExpression,
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
fun interface KotlinFace {
|
||||||
|
fun single()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun useSam(kf: KotlinFace) {}
|
||||||
|
|
||||||
|
fun callSam() {
|
||||||
|
useSam(kf = <caret>KotlinFace {})
|
||||||
|
}
|
||||||
+13
@@ -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);
|
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")
|
@TestMetadata("labeledReturn.kt")
|
||||||
public void testLabeledReturn() throws Exception {
|
public void testLabeledReturn() throws Exception {
|
||||||
runTest("idea/testData/intentions/samConversionToAnonymousObject/labeledReturn.kt");
|
runTest("idea/testData/intentions/samConversionToAnonymousObject/labeledReturn.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user