Add intention to convert SAM lambda to anonymous object #KT-25718 Fixed
This commit is contained in:
committed by
Vyacheslav Gerasimov
parent
eedb69b5e4
commit
d06b04f025
+8
@@ -0,0 +1,8 @@
|
|||||||
|
val runnable = object : Runnable {
|
||||||
|
override fun run() {
|
||||||
|
foo()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun foo() {
|
||||||
|
}
|
||||||
+6
@@ -0,0 +1,6 @@
|
|||||||
|
val runnable = Runnable {
|
||||||
|
foo()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun foo() {
|
||||||
|
}
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
This intention converts a SAM conversion to an anonymous object.
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -1658,6 +1658,11 @@
|
|||||||
<category>Kotlin</category>
|
<category>Kotlin</category>
|
||||||
</intentionAction>
|
</intentionAction>
|
||||||
|
|
||||||
|
<intentionAction>
|
||||||
|
<className>org.jetbrains.kotlin.idea.intentions.SamConversionToAnonymousObjectIntention</className>
|
||||||
|
<category>Kotlin</category>
|
||||||
|
</intentionAction>
|
||||||
|
|
||||||
<lang.inspectionSuppressor language="kotlin" implementationClass="org.jetbrains.kotlin.idea.inspections.KotlinInspectionSuppressor"/>
|
<lang.inspectionSuppressor language="kotlin" implementationClass="org.jetbrains.kotlin.idea.inspections.KotlinInspectionSuppressor"/>
|
||||||
|
|
||||||
<localInspection implementationClass="org.jetbrains.kotlin.idea.intentions.ObjectLiteralToLambdaInspection"
|
<localInspection implementationClass="org.jetbrains.kotlin.idea.intentions.ObjectLiteralToLambdaInspection"
|
||||||
|
|||||||
+53
-38
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.idea.intentions
|
|||||||
|
|
||||||
import com.intellij.codeInsight.intention.LowPriorityAction
|
import com.intellij.codeInsight.intention.LowPriorityAction
|
||||||
import com.intellij.openapi.editor.Editor
|
import com.intellij.openapi.editor.Editor
|
||||||
|
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.impl.AnonymousFunctionDescriptor
|
import org.jetbrains.kotlin.descriptors.impl.AnonymousFunctionDescriptor
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||||
import org.jetbrains.kotlin.idea.core.ShortenReferences
|
import org.jetbrains.kotlin.idea.core.ShortenReferences
|
||||||
@@ -22,15 +23,15 @@ import org.jetbrains.kotlin.psi.psiUtil.endOffset
|
|||||||
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
|
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
|
||||||
import org.jetbrains.kotlin.resolve.bindingContextUtil.getTargetFunctionDescriptor
|
import org.jetbrains.kotlin.resolve.bindingContextUtil.getTargetFunctionDescriptor
|
||||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||||
|
import org.jetbrains.kotlin.types.isFlexible
|
||||||
import org.jetbrains.kotlin.types.typeUtil.isUnit
|
import org.jetbrains.kotlin.types.typeUtil.isUnit
|
||||||
|
import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
|
||||||
|
|
||||||
class LambdaToAnonymousFunctionIntention : SelfTargetingIntention<KtLambdaExpression>(
|
class LambdaToAnonymousFunctionIntention : SelfTargetingIntention<KtLambdaExpression>(
|
||||||
KtLambdaExpression::class.java,
|
KtLambdaExpression::class.java,
|
||||||
"Convert to anonymous function",
|
"Convert to anonymous function",
|
||||||
"Convert lambda expression to anonymous function"
|
"Convert lambda expression to anonymous function"
|
||||||
), LowPriorityAction {
|
), LowPriorityAction {
|
||||||
private val typeSourceCode = IdeDescriptorRenderers.SOURCE_CODE_TYPES
|
|
||||||
|
|
||||||
override fun isApplicableTo(element: KtLambdaExpression, caretOffset: Int): Boolean {
|
override fun isApplicableTo(element: KtLambdaExpression, caretOffset: Int): Boolean {
|
||||||
if (element.getStrictParentOfType<KtValueArgument>() == null) return false
|
if (element.getStrictParentOfType<KtValueArgument>() == null) return false
|
||||||
if (element.getStrictParentOfType<KtFunction>()?.hasModifier(KtTokens.INLINE_KEYWORD) == true) return false
|
if (element.getStrictParentOfType<KtFunction>()?.hasModifier(KtTokens.INLINE_KEYWORD) == true) return false
|
||||||
@@ -41,42 +42,56 @@ class LambdaToAnonymousFunctionIntention : SelfTargetingIntention<KtLambdaExpres
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun applyTo(element: KtLambdaExpression, editor: Editor?) {
|
override fun applyTo(element: KtLambdaExpression, editor: Editor?) {
|
||||||
val functionLiteral = element.functionLiteral
|
val functionDescriptor = element.functionLiteral.descriptor as? AnonymousFunctionDescriptor ?: return
|
||||||
val bodyExpression = functionLiteral.bodyExpression ?: return
|
val resultingFunction = convertLambdaToFunction(element, functionDescriptor) ?: return
|
||||||
val descriptor = functionLiteral.descriptor as? AnonymousFunctionDescriptor ?: return
|
|
||||||
val psiFactory = KtPsiFactory(element)
|
|
||||||
|
|
||||||
val context = element.analyze(BodyResolveMode.PARTIAL)
|
|
||||||
bodyExpression.collectDescendantsOfType<KtReturnExpression>().forEach {
|
|
||||||
if (it.getTargetFunctionDescriptor(context) == descriptor) it.labeledExpression?.delete()
|
|
||||||
}
|
|
||||||
|
|
||||||
val anonymousFunction = psiFactory.createFunction(
|
|
||||||
KtPsiFactory.CallableBuilder(KtPsiFactory.CallableBuilder.Target.FUNCTION).apply {
|
|
||||||
typeParams()
|
|
||||||
descriptor.extensionReceiverParameter?.type?.let {
|
|
||||||
receiver(typeSourceCode.renderType(it))
|
|
||||||
}
|
|
||||||
name("")
|
|
||||||
for (parameter in descriptor.valueParameters) {
|
|
||||||
param(parameter.name.asString(), typeSourceCode.renderType(parameter.type))
|
|
||||||
}
|
|
||||||
descriptor.returnType?.takeIf { !it.isUnit() }?.let {
|
|
||||||
val lastStatement = bodyExpression.statements.lastOrNull()
|
|
||||||
if (lastStatement != null && lastStatement !is KtReturnExpression) {
|
|
||||||
val foldableReturns = BranchedFoldingUtils.getFoldableReturns(lastStatement)
|
|
||||||
if (foldableReturns == null || foldableReturns.isEmpty()) {
|
|
||||||
lastStatement.replace(psiFactory.createExpressionByPattern("return $0", lastStatement))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
returnType(typeSourceCode.renderType(it))
|
|
||||||
} ?: noReturnType()
|
|
||||||
blockBody(" " + bodyExpression.text)
|
|
||||||
}.asString()
|
|
||||||
)
|
|
||||||
|
|
||||||
val resultingFunction = element.replaced(anonymousFunction)
|
|
||||||
ShortenReferences.DEFAULT.process(resultingFunction)
|
|
||||||
(resultingFunction.parent as? KtLambdaArgument)?.also { it.moveInsideParentheses(it.analyze(BodyResolveMode.PARTIAL)) }
|
(resultingFunction.parent as? KtLambdaArgument)?.also { it.moveInsideParentheses(it.analyze(BodyResolveMode.PARTIAL)) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val typeSourceCode = IdeDescriptorRenderers.SOURCE_CODE_TYPES
|
||||||
|
|
||||||
|
fun convertLambdaToFunction(
|
||||||
|
lambda: KtLambdaExpression,
|
||||||
|
functionDescriptor: FunctionDescriptor,
|
||||||
|
functionName: String = "",
|
||||||
|
replaceElement: (KtNamedFunction) -> KtExpression = { lambda.replaced(it) }
|
||||||
|
): KtExpression? {
|
||||||
|
val functionLiteral = lambda.functionLiteral
|
||||||
|
val bodyExpression = functionLiteral.bodyExpression ?: return null
|
||||||
|
|
||||||
|
val context = bodyExpression.analyze(BodyResolveMode.PARTIAL)
|
||||||
|
val functionLiteralDescriptor by lazy { functionLiteral.descriptor }
|
||||||
|
bodyExpression.collectDescendantsOfType<KtReturnExpression>().forEach {
|
||||||
|
val targetDescriptor = it.getTargetFunctionDescriptor(context)
|
||||||
|
if (targetDescriptor == functionDescriptor || targetDescriptor == functionLiteralDescriptor) it.labeledExpression?.delete()
|
||||||
|
}
|
||||||
|
|
||||||
|
val psiFactory = KtPsiFactory(lambda)
|
||||||
|
val function = psiFactory.createFunction(
|
||||||
|
KtPsiFactory.CallableBuilder(KtPsiFactory.CallableBuilder.Target.FUNCTION).apply {
|
||||||
|
typeParams()
|
||||||
|
functionDescriptor.extensionReceiverParameter?.type?.let {
|
||||||
|
receiver(typeSourceCode.renderType(it))
|
||||||
|
}
|
||||||
|
name(functionName)
|
||||||
|
for (parameter in functionDescriptor.valueParameters) {
|
||||||
|
val type = parameter.type.let { if (it.isFlexible()) it.makeNotNullable() else it }
|
||||||
|
param(parameter.name.asString(), typeSourceCode.renderType(type))
|
||||||
|
}
|
||||||
|
functionDescriptor.returnType?.takeIf { !it.isUnit() }?.let {
|
||||||
|
val lastStatement = bodyExpression.statements.lastOrNull()
|
||||||
|
if (lastStatement != null && lastStatement !is KtReturnExpression) {
|
||||||
|
val foldableReturns = BranchedFoldingUtils.getFoldableReturns(lastStatement)
|
||||||
|
if (foldableReturns == null || foldableReturns.isEmpty()) {
|
||||||
|
lastStatement.replace(psiFactory.createExpressionByPattern("return $0", lastStatement))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
returnType(typeSourceCode.renderType(it))
|
||||||
|
} ?: noReturnType()
|
||||||
|
blockBody(" " + bodyExpression.text)
|
||||||
|
}.asString()
|
||||||
|
)
|
||||||
|
return replaceElement(function).also { ShortenReferences.DEFAULT.process(it) }
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+98
@@ -0,0 +1,98 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||||
|
* that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.idea.intentions
|
||||||
|
|
||||||
|
import com.intellij.codeInsight.intention.LowPriorityAction
|
||||||
|
import com.intellij.openapi.editor.Editor
|
||||||
|
import com.intellij.openapi.util.TextRange
|
||||||
|
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||||
|
import org.jetbrains.kotlin.diagnostics.Severity
|
||||||
|
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||||
|
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.SingleAbstractMethodUtils
|
||||||
|
import org.jetbrains.kotlin.psi.KtCallExpression
|
||||||
|
import org.jetbrains.kotlin.psi.KtLambdaExpression
|
||||||
|
import org.jetbrains.kotlin.psi.KtPsiFactory
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.anyDescendantOfType
|
||||||
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
|
import org.jetbrains.kotlin.resolve.calls.callUtil.getType
|
||||||
|
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||||
|
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
|
||||||
|
|
||||||
|
class SamConversionToAnonymousObjectIntention : SelfTargetingRangeIntention<KtCallExpression>(
|
||||||
|
KtCallExpression::class.java, "Convert to anonymous object"
|
||||||
|
), LowPriorityAction {
|
||||||
|
|
||||||
|
override fun applicabilityRange(element: KtCallExpression): TextRange? {
|
||||||
|
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
|
||||||
|
|
||||||
|
val samName = sam.name.asString()
|
||||||
|
if (functionLiteral.anyDescendantOfType<KtCallExpression> { call ->
|
||||||
|
if (call.calleeExpression?.text != samName) return@anyDescendantOfType false
|
||||||
|
val valueArguments = call.valueArguments
|
||||||
|
if (valueArguments.size != samValueParameterSize) return@anyDescendantOfType false
|
||||||
|
val context = call.analyze(BodyResolveMode.PARTIAL)
|
||||||
|
valueArguments.zip(samValueParameters).all { (arg, param) ->
|
||||||
|
arg.getArgumentExpression()?.getType(context)?.isSubtypeOf(param.type) == true
|
||||||
|
}
|
||||||
|
}) return null
|
||||||
|
|
||||||
|
if (bindingContext.diagnostics.forElement(functionLiteral).any { it.severity == Severity.ERROR }) return null
|
||||||
|
|
||||||
|
return callee.textRange
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
convertToAnonymousObject(element, lambda, functionDescriptor, functionName)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun KtCallExpression.getSingleAbstractMethod(context: BindingContext): FunctionDescriptor? {
|
||||||
|
val type = getType(context) ?: return null
|
||||||
|
if (!SingleAbstractMethodUtils.isSamType(type)) return null
|
||||||
|
val javaClass = type.constructor.declarationDescriptor as? JavaClassDescriptor ?: return null
|
||||||
|
return SingleAbstractMethodUtils.getSingleAbstractMethodOrNull(javaClass)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun convertToAnonymousObject(call: KtCallExpression, functionDescriptor: FunctionDescriptor, functionName: String) {
|
||||||
|
val lambda = getLambdaExpression(call) ?: return
|
||||||
|
convertToAnonymousObject(call, lambda, functionDescriptor, functionName)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun convertToAnonymousObject(
|
||||||
|
call: KtCallExpression,
|
||||||
|
lambda: KtLambdaExpression,
|
||||||
|
functionDescriptor: FunctionDescriptor,
|
||||||
|
functionName: String
|
||||||
|
) {
|
||||||
|
val interfaceName = call.calleeExpression?.text ?: return
|
||||||
|
LambdaToAnonymousFunctionIntention.convertLambdaToFunction(lambda, functionDescriptor, functionName = functionName) {
|
||||||
|
it.addModifier(KtTokens.OVERRIDE_KEYWORD)
|
||||||
|
call.replaced(KtPsiFactory(it).createExpression("object : $interfaceName { ${it.text} }"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getLambdaExpression(element: KtCallExpression): KtLambdaExpression? {
|
||||||
|
return element.lambdaArguments.firstOrNull()?.getLambdaExpression()
|
||||||
|
?: element.valueArguments.firstOrNull()?.getArgumentExpression() as? KtLambdaExpression
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,70 +7,48 @@ package org.jetbrains.kotlin.idea.quickfix
|
|||||||
|
|
||||||
import com.intellij.openapi.editor.Editor
|
import com.intellij.openapi.editor.Editor
|
||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
|
import org.jetbrains.kotlin.descriptors.ClassifierDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor
|
import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor
|
||||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||||
|
import org.jetbrains.kotlin.diagnostics.DiagnosticWithParameters3
|
||||||
import org.jetbrains.kotlin.diagnostics.Errors
|
import org.jetbrains.kotlin.diagnostics.Errors
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.forEachDescendantOfType
|
import org.jetbrains.kotlin.idea.intentions.SamConversionToAnonymousObjectIntention
|
||||||
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
|
import org.jetbrains.kotlin.psi.KtCallExpression
|
||||||
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
|
import org.jetbrains.kotlin.psi.KtNameReferenceExpression
|
||||||
|
import org.jetbrains.kotlin.psi.KtReferenceExpression
|
||||||
import org.jetbrains.kotlin.resolve.calls.tower.WrongResolutionToClassifier
|
import org.jetbrains.kotlin.resolve.calls.tower.WrongResolutionToClassifier
|
||||||
import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyClassDescriptor
|
import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyClassDescriptor
|
||||||
import org.jetbrains.kotlin.types.typeUtil.isUnit
|
import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult
|
||||||
|
|
||||||
class ConvertToAnonymousObjectFix(
|
class ConvertToAnonymousObjectFix(element: KtNameReferenceExpression) : KotlinQuickFixAction<KtNameReferenceExpression>(element) {
|
||||||
callExpression: KtCallExpression,
|
|
||||||
private val interfaceName: String,
|
|
||||||
private val functionName: String,
|
|
||||||
private val functionParameters: String,
|
|
||||||
private val functionReturnType: String?
|
|
||||||
) : KotlinQuickFixAction<KtCallExpression>(callExpression) {
|
|
||||||
override fun getFamilyName() = "Convert to anonymous object"
|
override fun getFamilyName() = "Convert to anonymous object"
|
||||||
|
|
||||||
override fun getText() = familyName
|
override fun getText() = familyName
|
||||||
|
|
||||||
override fun invoke(project: Project, editor: Editor?, file: KtFile) {
|
override fun invoke(project: Project, editor: Editor?, file: KtFile) {
|
||||||
val callExpression = element ?: return
|
val nameReference = element ?: return
|
||||||
val lambda = callExpression.lambdaArguments.singleOrNull()?.getLambdaExpression() ?: return
|
val call = nameReference.parent as? KtCallExpression ?: return
|
||||||
|
val functionDescriptor = nameReference.analyze().diagnostics.forElement(nameReference).firstNotNullResult {
|
||||||
val psiFactory = KtPsiFactory(project)
|
if (it.factory == Errors.RESOLUTION_TO_CLASSIFIER) getFunctionDescriptor(Errors.RESOLUTION_TO_CLASSIFIER.cast(it)) else null
|
||||||
val body = lambda.bodyExpression
|
} ?: return
|
||||||
if (body != null) {
|
val functionName = functionDescriptor.name.asString()
|
||||||
body.forEachDescendantOfType<KtReturnExpression> {
|
SamConversionToAnonymousObjectIntention.convertToAnonymousObject(call, functionDescriptor, functionName)
|
||||||
if (it.getLabelName() == interfaceName) it.labeledExpression?.delete()
|
|
||||||
}
|
|
||||||
val last = body.statements.lastOrNull()
|
|
||||||
if (last !is KtReturnExpression) last?.replace(psiFactory.createExpressionByPattern("return $0", last))
|
|
||||||
}
|
|
||||||
|
|
||||||
val anonymousObject = psiFactory.buildExpression {
|
|
||||||
appendFixedText("object : $interfaceName {")
|
|
||||||
appendFixedText("override fun $functionName$functionParameters")
|
|
||||||
if (functionReturnType != null) appendFixedText(": $functionReturnType")
|
|
||||||
appendFixedText("{")
|
|
||||||
if (body != null) appendExpression(body)
|
|
||||||
appendFixedText("}")
|
|
||||||
appendFixedText("}")
|
|
||||||
}
|
|
||||||
callExpression.replace(anonymousObject)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object : KotlinSingleIntentionActionFactory() {
|
companion object : KotlinSingleIntentionActionFactory() {
|
||||||
override fun createAction(diagnostic: Diagnostic): KotlinQuickFixAction<KtCallExpression>? {
|
override fun createAction(diagnostic: Diagnostic): KotlinQuickFixAction<KtNameReferenceExpression>? {
|
||||||
val casted = Errors.RESOLUTION_TO_CLASSIFIER.cast(diagnostic)
|
val casted = Errors.RESOLUTION_TO_CLASSIFIER.cast(diagnostic)
|
||||||
if (casted.b != WrongResolutionToClassifier.INTERFACE_AS_FUNCTION) return null
|
if (casted.b != WrongResolutionToClassifier.INTERFACE_AS_FUNCTION) return null
|
||||||
|
val nameReference = casted.psiElement as? KtNameReferenceExpression ?: return null
|
||||||
val callExpression = casted.psiElement.parent as? KtCallExpression ?: return null
|
if (nameReference.parent as? KtCallExpression == null) return null
|
||||||
if (callExpression.lambdaArguments.singleOrNull()?.getLambdaExpression() == null) return null
|
if (getFunctionDescriptor(casted) == null) return null
|
||||||
val interfaceName = callExpression.calleeExpression?.text ?: return null
|
return ConvertToAnonymousObjectFix(nameReference)
|
||||||
|
|
||||||
val classDescriptor = casted.a as? LazyClassDescriptor ?: return null
|
|
||||||
val function = classDescriptor.declaredCallableMembers.singleOrNull() as? SimpleFunctionDescriptor ?: return null
|
|
||||||
val functionDeclaration = DescriptorToSourceUtils.descriptorToDeclaration(function) as? KtNamedFunction ?: return null
|
|
||||||
val functionName = functionDeclaration.name ?: return null
|
|
||||||
val functionParameters = functionDeclaration.valueParameterList?.text ?: return null
|
|
||||||
val functionReturnType = function.returnType?.takeIf { !it.isUnit() }?.toString()
|
|
||||||
|
|
||||||
return ConvertToAnonymousObjectFix(callExpression, interfaceName, functionName, functionParameters, functionReturnType)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun getFunctionDescriptor(
|
||||||
|
d: DiagnosticWithParameters3<KtReferenceExpression, ClassifierDescriptor, WrongResolutionToClassifier, String>
|
||||||
|
) = (d.a as? LazyClassDescriptor)?.declaredCallableMembers?.singleOrNull() as? SimpleFunctionDescriptor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
org.jetbrains.kotlin.idea.intentions.SamConversionToAnonymousObjectIntention
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
public interface Sam {
|
||||||
|
String test(Boolean b);
|
||||||
|
}
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
public interface Sam {
|
||||||
|
String test(Boolean b);
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
val s = Sam<caret> { b ->
|
||||||
|
if (b) return@Sam "x"
|
||||||
|
"y"
|
||||||
|
}
|
||||||
+6
@@ -0,0 +1,6 @@
|
|||||||
|
val s = object : Sam {
|
||||||
|
override fun test(b: Boolean): String {
|
||||||
|
if (b) return "x"
|
||||||
|
return "y"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
// IS_APPLICABLE: false
|
||||||
|
// DISABLE-ERRORS
|
||||||
|
interface I {
|
||||||
|
fun test()
|
||||||
|
}
|
||||||
|
|
||||||
|
val i = <caret>I {
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
public interface NotSam {
|
||||||
|
void foo();
|
||||||
|
void bar();
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
// IS_APPLICABLE: false
|
||||||
|
// DISABLE-ERRORS
|
||||||
|
val s = <caret>NotSam {
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
public interface Sam {
|
||||||
|
void test(String a, Boolean b);
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
// IS_APPLICABLE: false
|
||||||
|
// DISABLE-ERRORS
|
||||||
|
val s = Sam<caret> {
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
public interface Sam {
|
||||||
|
void test(String str);
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
public interface Sam {
|
||||||
|
void test(String str);
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
fun foo(s: String) {}
|
||||||
|
|
||||||
|
val s = <caret>Sam {
|
||||||
|
foo(it)
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
fun foo(s: String) {}
|
||||||
|
|
||||||
|
val s = object : Sam {
|
||||||
|
override fun test(it: String) {
|
||||||
|
foo(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
public interface Sam {
|
||||||
|
void test(String str);
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
public interface Sam {
|
||||||
|
void test(String str);
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
fun foo(s: String) {}
|
||||||
|
|
||||||
|
val s = <caret>Sam { s ->
|
||||||
|
foo(s)
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
fun foo(s: String) {}
|
||||||
|
|
||||||
|
val s = object : Sam {
|
||||||
|
override fun test(s: String) {
|
||||||
|
foo(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public interface Sam {
|
||||||
|
List<String> test(LocalDate date, LocalDateTime time);
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public interface Sam {
|
||||||
|
List<String> test(LocalDate date, LocalDateTime time);
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
// RUNTIME_WITH_FULL_JDK
|
||||||
|
val s = Sam<caret> { d, t ->
|
||||||
|
val s = "$d$t"
|
||||||
|
listOf(s)
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import java.time.LocalDate
|
||||||
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
|
// RUNTIME_WITH_FULL_JDK
|
||||||
|
val s = object : Sam {
|
||||||
|
override fun test(d: LocalDate, t: LocalDateTime): List<String> {
|
||||||
|
val s = "$d$t"
|
||||||
|
return listOf(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
public interface Sam {
|
||||||
|
void test(String str);
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
// IS_APPLICABLE: false
|
||||||
|
fun test(s: String) {}
|
||||||
|
|
||||||
|
val usedSameFunction = <caret>Sam {
|
||||||
|
test(it)
|
||||||
|
}
|
||||||
@@ -9,7 +9,7 @@ fun foo() {
|
|||||||
fun test() {
|
fun test() {
|
||||||
object : I {
|
object : I {
|
||||||
override fun bar() {
|
override fun bar() {
|
||||||
return foo()
|
foo()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -14976,6 +14976,59 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("idea/testData/intentions/samConversionToAnonymousObject")
|
||||||
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
public static class SamConversionToAnonymousObject extends AbstractIntentionTest {
|
||||||
|
private void runTest(String testDataFilePath) throws Exception {
|
||||||
|
KotlinTestUtils.runTest(this::doTest, TargetBackend.ANY, testDataFilePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testAllFilesPresentInSamConversionToAnonymousObject() throws Exception {
|
||||||
|
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/intentions/samConversionToAnonymousObject"), Pattern.compile("^([\\w\\-_]+)\\.(kt|kts)$"), TargetBackend.ANY, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("labeledReturn.kt")
|
||||||
|
public void testLabeledReturn() throws Exception {
|
||||||
|
runTest("idea/testData/intentions/samConversionToAnonymousObject/labeledReturn.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("notJavaInterface.kt")
|
||||||
|
public void testNotJavaInterface() throws Exception {
|
||||||
|
runTest("idea/testData/intentions/samConversionToAnonymousObject/notJavaInterface.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("notSam.kt")
|
||||||
|
public void testNotSam() throws Exception {
|
||||||
|
runTest("idea/testData/intentions/samConversionToAnonymousObject/notSam.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("parameterError.kt")
|
||||||
|
public void testParameterError() throws Exception {
|
||||||
|
runTest("idea/testData/intentions/samConversionToAnonymousObject/parameterError.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("simple.kt")
|
||||||
|
public void testSimple() throws Exception {
|
||||||
|
runTest("idea/testData/intentions/samConversionToAnonymousObject/simple.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("simple2.kt")
|
||||||
|
public void testSimple2() throws Exception {
|
||||||
|
runTest("idea/testData/intentions/samConversionToAnonymousObject/simple2.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("simple3.kt")
|
||||||
|
public void testSimple3() throws Exception {
|
||||||
|
runTest("idea/testData/intentions/samConversionToAnonymousObject/simple3.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("usedSameFunction.kt")
|
||||||
|
public void testUsedSameFunction() throws Exception {
|
||||||
|
runTest("idea/testData/intentions/samConversionToAnonymousObject/usedSameFunction.kt");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("idea/testData/intentions/simplifyBooleanWithConstants")
|
@TestMetadata("idea/testData/intentions/simplifyBooleanWithConstants")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
|||||||
Reference in New Issue
Block a user