diff --git a/idea/resources/intentionDescriptions/SamConversionToAnonymousObjectIntention/after.kt.template b/idea/resources/intentionDescriptions/SamConversionToAnonymousObjectIntention/after.kt.template new file mode 100644 index 00000000000..df98db65904 --- /dev/null +++ b/idea/resources/intentionDescriptions/SamConversionToAnonymousObjectIntention/after.kt.template @@ -0,0 +1,8 @@ +val runnable = object : Runnable { + override fun run() { + foo() + } +} + +fun foo() { +} \ No newline at end of file diff --git a/idea/resources/intentionDescriptions/SamConversionToAnonymousObjectIntention/before.kt.template b/idea/resources/intentionDescriptions/SamConversionToAnonymousObjectIntention/before.kt.template new file mode 100644 index 00000000000..9bbda653a1c --- /dev/null +++ b/idea/resources/intentionDescriptions/SamConversionToAnonymousObjectIntention/before.kt.template @@ -0,0 +1,6 @@ +val runnable = Runnable { + foo() +} + +fun foo() { +} \ No newline at end of file diff --git a/idea/resources/intentionDescriptions/SamConversionToAnonymousObjectIntention/description.html b/idea/resources/intentionDescriptions/SamConversionToAnonymousObjectIntention/description.html new file mode 100644 index 00000000000..81f2a8e30dc --- /dev/null +++ b/idea/resources/intentionDescriptions/SamConversionToAnonymousObjectIntention/description.html @@ -0,0 +1,5 @@ + + +This intention converts a SAM conversion to an anonymous object. + + diff --git a/idea/src/META-INF/plugin-common.xml b/idea/src/META-INF/plugin-common.xml index 5d850d4baec..46ba01f04f9 100644 --- a/idea/src/META-INF/plugin-common.xml +++ b/idea/src/META-INF/plugin-common.xml @@ -1658,6 +1658,11 @@ Kotlin + + org.jetbrains.kotlin.idea.intentions.SamConversionToAnonymousObjectIntention + Kotlin + + ( KtLambdaExpression::class.java, "Convert to anonymous function", "Convert lambda expression to anonymous function" ), LowPriorityAction { - private val typeSourceCode = IdeDescriptorRenderers.SOURCE_CODE_TYPES - override fun isApplicableTo(element: KtLambdaExpression, caretOffset: Int): Boolean { if (element.getStrictParentOfType() == null) return false if (element.getStrictParentOfType()?.hasModifier(KtTokens.INLINE_KEYWORD) == true) return false @@ -41,42 +42,56 @@ class LambdaToAnonymousFunctionIntention : SelfTargetingIntention().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) + val functionDescriptor = element.functionLiteral.descriptor as? AnonymousFunctionDescriptor ?: return + val resultingFunction = convertLambdaToFunction(element, functionDescriptor) ?: return (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().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) } + } + } } diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/SamConversionToAnonymousObjectIntention.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/SamConversionToAnonymousObjectIntention.kt new file mode 100644 index 00000000000..e6d08f820d3 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/SamConversionToAnonymousObjectIntention.kt @@ -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::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 { 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 + } + } +} \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/ConvertToAnonymousObjectFix.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/ConvertToAnonymousObjectFix.kt index dec3625cc8e..35e564d189a 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/ConvertToAnonymousObjectFix.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/ConvertToAnonymousObjectFix.kt @@ -7,70 +7,48 @@ package org.jetbrains.kotlin.idea.quickfix import com.intellij.openapi.editor.Editor import com.intellij.openapi.project.Project +import org.jetbrains.kotlin.descriptors.ClassifierDescriptor import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor import org.jetbrains.kotlin.diagnostics.Diagnostic +import org.jetbrains.kotlin.diagnostics.DiagnosticWithParameters3 import org.jetbrains.kotlin.diagnostics.Errors -import org.jetbrains.kotlin.psi.* -import org.jetbrains.kotlin.psi.psiUtil.forEachDescendantOfType -import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils +import org.jetbrains.kotlin.idea.caches.resolve.analyze +import org.jetbrains.kotlin.idea.intentions.SamConversionToAnonymousObjectIntention +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.lazy.descriptors.LazyClassDescriptor -import org.jetbrains.kotlin.types.typeUtil.isUnit +import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult -class ConvertToAnonymousObjectFix( - callExpression: KtCallExpression, - private val interfaceName: String, - private val functionName: String, - private val functionParameters: String, - private val functionReturnType: String? -) : KotlinQuickFixAction(callExpression) { +class ConvertToAnonymousObjectFix(element: KtNameReferenceExpression) : KotlinQuickFixAction(element) { override fun getFamilyName() = "Convert to anonymous object" override fun getText() = familyName override fun invoke(project: Project, editor: Editor?, file: KtFile) { - val callExpression = element ?: return - val lambda = callExpression.lambdaArguments.singleOrNull()?.getLambdaExpression() ?: return - - val psiFactory = KtPsiFactory(project) - val body = lambda.bodyExpression - if (body != null) { - body.forEachDescendantOfType { - 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) + val nameReference = element ?: return + val call = nameReference.parent as? KtCallExpression ?: return + val functionDescriptor = nameReference.analyze().diagnostics.forElement(nameReference).firstNotNullResult { + if (it.factory == Errors.RESOLUTION_TO_CLASSIFIER) getFunctionDescriptor(Errors.RESOLUTION_TO_CLASSIFIER.cast(it)) else null + } ?: return + val functionName = functionDescriptor.name.asString() + SamConversionToAnonymousObjectIntention.convertToAnonymousObject(call, functionDescriptor, functionName) } companion object : KotlinSingleIntentionActionFactory() { - override fun createAction(diagnostic: Diagnostic): KotlinQuickFixAction? { + override fun createAction(diagnostic: Diagnostic): KotlinQuickFixAction? { val casted = Errors.RESOLUTION_TO_CLASSIFIER.cast(diagnostic) if (casted.b != WrongResolutionToClassifier.INTERFACE_AS_FUNCTION) return null - - val callExpression = casted.psiElement.parent as? KtCallExpression ?: return null - if (callExpression.lambdaArguments.singleOrNull()?.getLambdaExpression() == null) return null - val interfaceName = callExpression.calleeExpression?.text ?: return null - - 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) + val nameReference = casted.psiElement as? KtNameReferenceExpression ?: return null + if (nameReference.parent as? KtCallExpression == null) return null + if (getFunctionDescriptor(casted) == null) return null + return ConvertToAnonymousObjectFix(nameReference) } + + private fun getFunctionDescriptor( + d: DiagnosticWithParameters3 + ) = (d.a as? LazyClassDescriptor)?.declaredCallableMembers?.singleOrNull() as? SimpleFunctionDescriptor } } \ No newline at end of file diff --git a/idea/testData/intentions/samConversionToAnonymousObject/.intention b/idea/testData/intentions/samConversionToAnonymousObject/.intention new file mode 100644 index 00000000000..dd661b56e99 --- /dev/null +++ b/idea/testData/intentions/samConversionToAnonymousObject/.intention @@ -0,0 +1 @@ +org.jetbrains.kotlin.idea.intentions.SamConversionToAnonymousObjectIntention \ No newline at end of file diff --git a/idea/testData/intentions/samConversionToAnonymousObject/labeledReturn.1.java b/idea/testData/intentions/samConversionToAnonymousObject/labeledReturn.1.java new file mode 100644 index 00000000000..f5575e47ae6 --- /dev/null +++ b/idea/testData/intentions/samConversionToAnonymousObject/labeledReturn.1.java @@ -0,0 +1,3 @@ +public interface Sam { + String test(Boolean b); +} \ No newline at end of file diff --git a/idea/testData/intentions/samConversionToAnonymousObject/labeledReturn.1.java.after b/idea/testData/intentions/samConversionToAnonymousObject/labeledReturn.1.java.after new file mode 100644 index 00000000000..f5575e47ae6 --- /dev/null +++ b/idea/testData/intentions/samConversionToAnonymousObject/labeledReturn.1.java.after @@ -0,0 +1,3 @@ +public interface Sam { + String test(Boolean b); +} \ No newline at end of file diff --git a/idea/testData/intentions/samConversionToAnonymousObject/labeledReturn.kt b/idea/testData/intentions/samConversionToAnonymousObject/labeledReturn.kt new file mode 100644 index 00000000000..8a2e3a4464d --- /dev/null +++ b/idea/testData/intentions/samConversionToAnonymousObject/labeledReturn.kt @@ -0,0 +1,4 @@ +val s = Sam { b -> + if (b) return@Sam "x" + "y" +} \ No newline at end of file diff --git a/idea/testData/intentions/samConversionToAnonymousObject/labeledReturn.kt.after b/idea/testData/intentions/samConversionToAnonymousObject/labeledReturn.kt.after new file mode 100644 index 00000000000..be58323e721 --- /dev/null +++ b/idea/testData/intentions/samConversionToAnonymousObject/labeledReturn.kt.after @@ -0,0 +1,6 @@ +val s = object : Sam { + override fun test(b: Boolean): String { + if (b) return "x" + return "y" + } +} \ No newline at end of file diff --git a/idea/testData/intentions/samConversionToAnonymousObject/notJavaInterface.kt b/idea/testData/intentions/samConversionToAnonymousObject/notJavaInterface.kt new file mode 100644 index 00000000000..61c1cd586bb --- /dev/null +++ b/idea/testData/intentions/samConversionToAnonymousObject/notJavaInterface.kt @@ -0,0 +1,8 @@ +// IS_APPLICABLE: false +// DISABLE-ERRORS +interface I { + fun test() +} + +val i = I { +} diff --git a/idea/testData/intentions/samConversionToAnonymousObject/notSam.1.java b/idea/testData/intentions/samConversionToAnonymousObject/notSam.1.java new file mode 100644 index 00000000000..4bb915b48ab --- /dev/null +++ b/idea/testData/intentions/samConversionToAnonymousObject/notSam.1.java @@ -0,0 +1,4 @@ +public interface NotSam { + void foo(); + void bar(); +} diff --git a/idea/testData/intentions/samConversionToAnonymousObject/notSam.kt b/idea/testData/intentions/samConversionToAnonymousObject/notSam.kt new file mode 100644 index 00000000000..22048ab2226 --- /dev/null +++ b/idea/testData/intentions/samConversionToAnonymousObject/notSam.kt @@ -0,0 +1,4 @@ +// IS_APPLICABLE: false +// DISABLE-ERRORS +val s = NotSam { +} \ No newline at end of file diff --git a/idea/testData/intentions/samConversionToAnonymousObject/parameterError.1.java b/idea/testData/intentions/samConversionToAnonymousObject/parameterError.1.java new file mode 100644 index 00000000000..a6486d33927 --- /dev/null +++ b/idea/testData/intentions/samConversionToAnonymousObject/parameterError.1.java @@ -0,0 +1,3 @@ +public interface Sam { + void test(String a, Boolean b); +} \ No newline at end of file diff --git a/idea/testData/intentions/samConversionToAnonymousObject/parameterError.kt b/idea/testData/intentions/samConversionToAnonymousObject/parameterError.kt new file mode 100644 index 00000000000..022bfe459b0 --- /dev/null +++ b/idea/testData/intentions/samConversionToAnonymousObject/parameterError.kt @@ -0,0 +1,4 @@ +// IS_APPLICABLE: false +// DISABLE-ERRORS +val s = Sam { +} \ No newline at end of file diff --git a/idea/testData/intentions/samConversionToAnonymousObject/simple.1.java b/idea/testData/intentions/samConversionToAnonymousObject/simple.1.java new file mode 100644 index 00000000000..e55d5944858 --- /dev/null +++ b/idea/testData/intentions/samConversionToAnonymousObject/simple.1.java @@ -0,0 +1,3 @@ +public interface Sam { + void test(String str); +} \ No newline at end of file diff --git a/idea/testData/intentions/samConversionToAnonymousObject/simple.1.java.after b/idea/testData/intentions/samConversionToAnonymousObject/simple.1.java.after new file mode 100644 index 00000000000..e55d5944858 --- /dev/null +++ b/idea/testData/intentions/samConversionToAnonymousObject/simple.1.java.after @@ -0,0 +1,3 @@ +public interface Sam { + void test(String str); +} \ No newline at end of file diff --git a/idea/testData/intentions/samConversionToAnonymousObject/simple.kt b/idea/testData/intentions/samConversionToAnonymousObject/simple.kt new file mode 100644 index 00000000000..d72b15fe356 --- /dev/null +++ b/idea/testData/intentions/samConversionToAnonymousObject/simple.kt @@ -0,0 +1,5 @@ +fun foo(s: String) {} + +val s = Sam { + foo(it) +} \ No newline at end of file diff --git a/idea/testData/intentions/samConversionToAnonymousObject/simple.kt.after b/idea/testData/intentions/samConversionToAnonymousObject/simple.kt.after new file mode 100644 index 00000000000..84958b66597 --- /dev/null +++ b/idea/testData/intentions/samConversionToAnonymousObject/simple.kt.after @@ -0,0 +1,7 @@ +fun foo(s: String) {} + +val s = object : Sam { + override fun test(it: String) { + foo(it) + } +} \ No newline at end of file diff --git a/idea/testData/intentions/samConversionToAnonymousObject/simple2.1.java b/idea/testData/intentions/samConversionToAnonymousObject/simple2.1.java new file mode 100644 index 00000000000..e55d5944858 --- /dev/null +++ b/idea/testData/intentions/samConversionToAnonymousObject/simple2.1.java @@ -0,0 +1,3 @@ +public interface Sam { + void test(String str); +} \ No newline at end of file diff --git a/idea/testData/intentions/samConversionToAnonymousObject/simple2.1.java.after b/idea/testData/intentions/samConversionToAnonymousObject/simple2.1.java.after new file mode 100644 index 00000000000..e55d5944858 --- /dev/null +++ b/idea/testData/intentions/samConversionToAnonymousObject/simple2.1.java.after @@ -0,0 +1,3 @@ +public interface Sam { + void test(String str); +} \ No newline at end of file diff --git a/idea/testData/intentions/samConversionToAnonymousObject/simple2.kt b/idea/testData/intentions/samConversionToAnonymousObject/simple2.kt new file mode 100644 index 00000000000..6f0df21a789 --- /dev/null +++ b/idea/testData/intentions/samConversionToAnonymousObject/simple2.kt @@ -0,0 +1,5 @@ +fun foo(s: String) {} + +val s = Sam { s -> + foo(s) +} \ No newline at end of file diff --git a/idea/testData/intentions/samConversionToAnonymousObject/simple2.kt.after b/idea/testData/intentions/samConversionToAnonymousObject/simple2.kt.after new file mode 100644 index 00000000000..a57a7f11db9 --- /dev/null +++ b/idea/testData/intentions/samConversionToAnonymousObject/simple2.kt.after @@ -0,0 +1,7 @@ +fun foo(s: String) {} + +val s = object : Sam { + override fun test(s: String) { + foo(s) + } +} \ No newline at end of file diff --git a/idea/testData/intentions/samConversionToAnonymousObject/simple3.1.java b/idea/testData/intentions/samConversionToAnonymousObject/simple3.1.java new file mode 100644 index 00000000000..6edb5a14122 --- /dev/null +++ b/idea/testData/intentions/samConversionToAnonymousObject/simple3.1.java @@ -0,0 +1,7 @@ +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.List; + +public interface Sam { + List test(LocalDate date, LocalDateTime time); +} \ No newline at end of file diff --git a/idea/testData/intentions/samConversionToAnonymousObject/simple3.1.java.after b/idea/testData/intentions/samConversionToAnonymousObject/simple3.1.java.after new file mode 100644 index 00000000000..6edb5a14122 --- /dev/null +++ b/idea/testData/intentions/samConversionToAnonymousObject/simple3.1.java.after @@ -0,0 +1,7 @@ +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.List; + +public interface Sam { + List test(LocalDate date, LocalDateTime time); +} \ No newline at end of file diff --git a/idea/testData/intentions/samConversionToAnonymousObject/simple3.kt b/idea/testData/intentions/samConversionToAnonymousObject/simple3.kt new file mode 100644 index 00000000000..47f2d71c00e --- /dev/null +++ b/idea/testData/intentions/samConversionToAnonymousObject/simple3.kt @@ -0,0 +1,5 @@ +// RUNTIME_WITH_FULL_JDK +val s = Sam { d, t -> + val s = "$d$t" + listOf(s) +} \ No newline at end of file diff --git a/idea/testData/intentions/samConversionToAnonymousObject/simple3.kt.after b/idea/testData/intentions/samConversionToAnonymousObject/simple3.kt.after new file mode 100644 index 00000000000..cb6056af24a --- /dev/null +++ b/idea/testData/intentions/samConversionToAnonymousObject/simple3.kt.after @@ -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 { + val s = "$d$t" + return listOf(s) + } +} \ No newline at end of file diff --git a/idea/testData/intentions/samConversionToAnonymousObject/usedSameFunction.1.java b/idea/testData/intentions/samConversionToAnonymousObject/usedSameFunction.1.java new file mode 100644 index 00000000000..e55d5944858 --- /dev/null +++ b/idea/testData/intentions/samConversionToAnonymousObject/usedSameFunction.1.java @@ -0,0 +1,3 @@ +public interface Sam { + void test(String str); +} \ No newline at end of file diff --git a/idea/testData/intentions/samConversionToAnonymousObject/usedSameFunction.kt b/idea/testData/intentions/samConversionToAnonymousObject/usedSameFunction.kt new file mode 100644 index 00000000000..ddd0b44bf34 --- /dev/null +++ b/idea/testData/intentions/samConversionToAnonymousObject/usedSameFunction.kt @@ -0,0 +1,6 @@ +// IS_APPLICABLE: false +fun test(s: String) {} + +val usedSameFunction = Sam { + test(it) +} \ No newline at end of file diff --git a/idea/testData/quickfix/convertToAnonymousObject/unit.kt.after b/idea/testData/quickfix/convertToAnonymousObject/unit.kt.after index e0b3e389b3e..1bdfc511d14 100644 --- a/idea/testData/quickfix/convertToAnonymousObject/unit.kt.after +++ b/idea/testData/quickfix/convertToAnonymousObject/unit.kt.after @@ -9,7 +9,7 @@ fun foo() { fun test() { object : I { override fun bar() { - return foo() + foo() } } } \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java index c97f0bb109d..ca603f1e801 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java @@ -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") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)