diff --git a/ChangeLog.md b/ChangeLog.md index b0970539833..ea1362bc013 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -337,6 +337,7 @@ These artifacts include extensions for the types available in the latter JDKs, s - [`KT-14459`](https://youtrack.jetbrains.com/issue/KT-14459) Initialize with Constructor Parameter: Fix IDE freeze on properties in generic class - [`KT-14044`](https://youtrack.jetbrains.com/issue/KT-14044) Fix exception on deleting unused declaration in IDEA 2016.3 - [`KT-14019`](https://youtrack.jetbrains.com/issue/KT-14019) Create from Usage: Support generation of abstract members for superclasses +- [`KT-14246`](https://youtrack.jetbrains.com/issue/KT-14246) Intentions: Convert function type parameter to receiver ##### New features diff --git a/compiler/frontend/src/org/jetbrains/kotlin/psi/KtFunctionType.java b/compiler/frontend/src/org/jetbrains/kotlin/psi/KtFunctionType.java index 0a030091b56..192a18c1a54 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/psi/KtFunctionType.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/psi/KtFunctionType.java @@ -75,9 +75,14 @@ public class KtFunctionType extends KtElementImplStubemptyList(); } + @Nullable + public KtFunctionTypeReceiver getReceiver() { + return getStubOrPsiChild(KtStubElementTypes.FUNCTION_TYPE_RECEIVER); + } + @Nullable public KtTypeReference getReceiverTypeReference() { - KtFunctionTypeReceiver receiverDeclaration = getStubOrPsiChild(KtStubElementTypes.FUNCTION_TYPE_RECEIVER); + KtFunctionTypeReceiver receiverDeclaration = getReceiver(); if (receiverDeclaration == null) { return null; } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/psi/KtParameterList.java b/compiler/frontend/src/org/jetbrains/kotlin/psi/KtParameterList.java index 49148fa948c..2195b0ec90a 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/psi/KtParameterList.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/psi/KtParameterList.java @@ -64,6 +64,10 @@ public class KtParameterList extends KtElementImplStub B").typeElement as KtFunctionType).receiver!!.apply { this.typeReference.replace(typeReference) } + } + fun createTypeAlias(name: String, typeParameters: List, typeElement: KtTypeElement): KtTypeAlias { return createTypeAlias(name, typeParameters, "X").apply { getTypeReference()!!.replace(createType(typeElement)) } } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/psi/KtValueArgumentList.java b/compiler/frontend/src/org/jetbrains/kotlin/psi/KtValueArgumentList.java index 0f79c53b63c..ac9f7a4d43f 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/psi/KtValueArgumentList.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/psi/KtValueArgumentList.java @@ -69,4 +69,8 @@ public class KtValueArgumentList extends KtElementImpl { assert argument.getParent() == this; EditCommaSeparatedListHelper.INSTANCE.removeItem(argument); } + + public void removeArgument(int index) { + removeArgument(getArguments().get(index)); + } } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/psi/TypeRefHelpers.kt b/compiler/frontend/src/org/jetbrains/kotlin/psi/TypeRefHelpers.kt index 9fffaad4907..5ea806741a3 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/psi/TypeRefHelpers.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/psi/TypeRefHelpers.kt @@ -20,10 +20,8 @@ import com.intellij.psi.PsiElement import com.intellij.psi.PsiErrorElement import com.intellij.psi.PsiWhiteSpace import org.jetbrains.kotlin.lexer.KtTokens -import org.jetbrains.kotlin.psi.KtCallableDeclaration -import org.jetbrains.kotlin.psi.KtFunctionType -import org.jetbrains.kotlin.psi.KtPsiFactory -import org.jetbrains.kotlin.psi.KtTypeReference +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf import org.jetbrains.kotlin.psi.psiUtil.siblings import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull @@ -56,18 +54,21 @@ fun setTypeReference(declaration: KtCallableDeclaration, addAfter: PsiElement?, } } -fun KtCallableDeclaration.setReceiverTypeReference(typeRef: KtTypeReference?): KtTypeReference? { +private inline fun T.doSetReceiverTypeReference( + typeRef: KtTypeReference?, + getReceiverTypeReference: T.() -> KtTypeReference?, + addReceiverTypeReference: T.(typeRef: KtTypeReference) -> KtTypeReference +): KtTypeReference? { val needParentheses = typeRef != null && typeRef.typeElement is KtFunctionType && !typeRef.hasParentheses() - val oldTypeRef = receiverTypeReference + val oldTypeRef = getReceiverTypeReference() if (typeRef != null) { val newTypeRef = if (oldTypeRef != null) { oldTypeRef.replace(typeRef) as KtTypeReference } else { - val anchor = nameIdentifier ?: valueParameterList - val newTypeRef = addBefore(typeRef, anchor) as KtTypeReference - addAfter(KtPsiFactory(project).createDot(), newTypeRef) + val newTypeRef = addReceiverTypeReference(typeRef) + addAfter(KtPsiFactory(project).createDot(), newTypeRef.parentsWithSelf.first { it.parent == this }) newTypeRef } if (needParentheses) { @@ -79,9 +80,27 @@ fun KtCallableDeclaration.setReceiverTypeReference(typeRef: KtTypeReference?): K } else { if (oldTypeRef != null) { - val dot = oldTypeRef.siblings(forward = true).firstOrNull { it.node.elementType == KtTokens.DOT } - deleteChildRange(oldTypeRef, dot ?: oldTypeRef) + val dotSibling = oldTypeRef.parent as? KtFunctionTypeReceiver ?: oldTypeRef + val dot = dotSibling.siblings(forward = true).firstOrNull { it.node.elementType == KtTokens.DOT } + deleteChildRange(dotSibling, dot ?: dotSibling) } return null } -} \ No newline at end of file +} + +fun KtCallableDeclaration.setReceiverTypeReference(typeRef: KtTypeReference?) = + doSetReceiverTypeReference( + typeRef, + { receiverTypeReference }, + { this.addBefore(it, nameIdentifier ?: valueParameterList) as KtTypeReference } + ) + +fun KtFunctionType.setReceiverTypeReference(typeRef: KtTypeReference?) = + doSetReceiverTypeReference( + typeRef, + { receiverTypeReference }, + { + (addBefore(KtPsiFactory(project).createFunctionTypeReceiver(it), + parameterList ?: firstChild) as KtFunctionTypeReceiver).typeReference + } + ) \ No newline at end of file diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/resolvedCallUtil.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/resolvedCallUtil.kt index c6947fae9c5..38de9be8519 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/resolvedCallUtil.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/resolvedCallUtil.kt @@ -17,9 +17,9 @@ package org.jetbrains.kotlin.resolve.calls.resolvedCallUtil import org.jetbrains.kotlin.descriptors.DeclarationDescriptor -import org.jetbrains.kotlin.psi.KtPsiUtil -import org.jetbrains.kotlin.psi.KtThisExpression +import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall import org.jetbrains.kotlin.resolve.calls.callUtil.isSafeCall import org.jetbrains.kotlin.resolve.calls.context.CallResolutionContext import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall @@ -112,3 +112,9 @@ fun ResolvedCall<*>.hasBothReceivers() = dispatchReceiver != null && extensionRe fun ResolvedCall<*>.getDispatchReceiverWithSmartCast(): ReceiverValue? = getReceiverValueWithSmartCast(dispatchReceiver, smartCastDispatchReceiverType) + +fun KtCallElement.getArgumentByParameterIndex(index: Int, context: BindingContext): List { + val resolvedCall = getResolvedCall(context) ?: return emptyList() + val parameterToProcess = resolvedCall.resultingDescriptor.valueParameters.getOrNull(index) ?: return emptyList() + return resolvedCall.valueArguments[parameterToProcess]?.arguments ?: emptyList() +} \ No newline at end of file diff --git a/idea/resources/intentionDescriptions/ConvertFunctionTypeParameterToReceiverIntention/after.kt.template b/idea/resources/intentionDescriptions/ConvertFunctionTypeParameterToReceiverIntention/after.kt.template new file mode 100644 index 00000000000..7e787a3bee0 --- /dev/null +++ b/idea/resources/intentionDescriptions/ConvertFunctionTypeParameterToReceiverIntention/after.kt.template @@ -0,0 +1,3 @@ +fun foo(f: Int.(s: String) -> Boolean) = 1.f("2") + +fun bar = foo { s -> s.length() > this } \ No newline at end of file diff --git a/idea/resources/intentionDescriptions/ConvertFunctionTypeParameterToReceiverIntention/before.kt.template b/idea/resources/intentionDescriptions/ConvertFunctionTypeParameterToReceiverIntention/before.kt.template new file mode 100644 index 00000000000..5e29a099d4b --- /dev/null +++ b/idea/resources/intentionDescriptions/ConvertFunctionTypeParameterToReceiverIntention/before.kt.template @@ -0,0 +1,3 @@ +fun foo(f: (n: Int, s: String) -> Boolean) = f(1, "2") + +fun bar = foo { n, s -> s.length() > n } \ No newline at end of file diff --git a/idea/resources/intentionDescriptions/ConvertFunctionTypeParameterToReceiverIntention/description.html b/idea/resources/intentionDescriptions/ConvertFunctionTypeParameterToReceiverIntention/description.html new file mode 100644 index 00000000000..2071752724e --- /dev/null +++ b/idea/resources/intentionDescriptions/ConvertFunctionTypeParameterToReceiverIntention/description.html @@ -0,0 +1,5 @@ + + +This intention converts given parameter of a function type used in a function parameter to receiver + + \ No newline at end of file diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index cdabd048326..0f9b58e2d69 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -1456,6 +1456,11 @@ Kotlin + + org.jetbrains.kotlin.idea.intentions.ConvertFunctionTypeParameterToReceiverIntention + Kotlin + + ( + KtTypeReference::class.java, + "Convert function type parameter to receiver" +) { + abstract class AbstractUsageInfo(element: T) : UsageInfo(element) { + @Suppress("UNCHECKED_CAST") + override fun getElement() = super.getElement() as T? + + abstract fun process(data: ConversionData, elementsToShorten: MutableList) + } + + class FunctionDefinitionInfo(element: KtFunction) : AbstractUsageInfo(element) { + override fun process(data: ConversionData, elementsToShorten: MutableList) { + val function = element ?: return + val functionParameter = function.valueParameters.getOrNull(data.functionParameterIndex) ?: return + val functionType = functionParameter.typeReference?.typeElement as? KtFunctionType ?: return + val functionTypeParameterList = functionType.parameterList ?: return + val parameterToMove = functionTypeParameterList.parameters.getOrNull(data.typeParameterIndex) ?: return + val typeReferenceToMove = parameterToMove.typeReference ?: return + functionType.setReceiverTypeReference(typeReferenceToMove) + functionTypeParameterList.removeParameter(parameterToMove) + } + } + + class ParameterCallInfo(element: KtCallExpression) : AbstractUsageInfo(element) { + override fun process(data: ConversionData, elementsToShorten: MutableList) { + val callExpression = element ?: return + val argumentList = callExpression.valueArgumentList ?: return + val expressionToMove = argumentList.arguments.getOrNull(data.typeParameterIndex)?.getArgumentExpression() ?: return + val callWithReceiver = KtPsiFactory(callExpression).createExpressionByPattern("$0.$1", expressionToMove, callExpression) as KtQualifiedExpression + (callWithReceiver.selectorExpression as KtCallExpression).valueArgumentList!!.removeArgument(data.typeParameterIndex) + callExpression.replace(callWithReceiver) + } + } + + class InternalReferencePassInfo(element: KtSimpleNameExpression) : AbstractUsageInfo(element) { + override fun process(data: ConversionData, elementsToShorten: MutableList) { + val expression = element ?: return + val lambdaType = data.lambdaType + val validator = CollectingNameValidator() + val parameterNames = lambdaType.arguments + .dropLast(1) + .map { KotlinNameSuggester.suggestNamesByType(it.type, validator, "p").first() } + val receiver = parameterNames.getOrNull(data.typeParameterIndex) ?: return + val arguments = parameterNames.filter { it != receiver } + val adapterLambda = KtPsiFactory(expression).createLambdaExpression( + parameterNames.joinToString(), + "$receiver.${expression.text}(${arguments.joinToString()})" + ) + expression.replaced(adapterLambda).let { + MoveLambdaOutsideParenthesesIntention.moveFunctionLiteralOutsideParenthesesIfPossible(it) + } + } + } + + class LambdaInfo(element: KtExpression) : AbstractUsageInfo(element) { + override fun process(data: ConversionData, elementsToShorten: MutableList) { + val expression = element ?: return + val context = expression.analyze(BodyResolveMode.PARTIAL) + val psiFactory = KtPsiFactory(expression) + + if (expression is KtLambdaExpression || (expression !is KtSimpleNameExpression && expression !is KtCallableReferenceExpression)) { + expression.forEachDescendantOfType { + if (it.getLabelName() != null) return@forEachDescendantOfType + val descriptor = context[BindingContext.REFERENCE_TARGET, it.instanceReference] ?: return@forEachDescendantOfType + it.replace(psiFactory.createExpression(explicateReceiverOf(descriptor))) + } + } + + if (expression is KtLambdaExpression) { + expression.valueParameters.getOrNull(data.typeParameterIndex)?.let { parameterToConvert -> + val thisRefExpr = psiFactory.createThisExpression() + for (ref in ReferencesSearch.search(parameterToConvert, LocalSearchScope(expression))) { + (ref.element as? KtSimpleNameExpression)?.replace(thisRefExpr) + } + expression.functionLiteral.valueParameterList!!.removeParameter(parameterToConvert) + } + return + } + + val originalLambdaTypes = data.lambdaType + val originalParameterTypes = originalLambdaTypes.arguments.dropLast(1).map { it.type } + + val calleeText = when (expression) { + is KtSimpleNameExpression -> expression.text + is KtCallableReferenceExpression -> "(${expression.text})" + else -> generateVariable(expression) + } + + val parameterNameValidator = CollectingNameValidator( + if (expression !is KtCallableReferenceExpression) listOf(calleeText) else emptyList() + ) + val parameterNamesWithReceiver = originalParameterTypes.mapIndexed { i, type -> + if (i != data.typeParameterIndex) KotlinNameSuggester.suggestNamesByType(type, parameterNameValidator, "p").first() else "this" + } + val parameterNames = parameterNamesWithReceiver.filter { it != "this" } + + val body = psiFactory.createExpression(parameterNamesWithReceiver.joinToString(prefix = "$calleeText(", postfix = ")")) + + val replacingLambda = psiFactory.buildExpression { + appendFixedText("{ ") + appendFixedText(parameterNames.joinToString()) + appendFixedText(" -> ") + appendExpression(body) + appendFixedText(" }") + } as KtLambdaExpression + + expression.replaced(replacingLambda).let { + MoveLambdaOutsideParenthesesIntention.moveFunctionLiteralOutsideParenthesesIfPossible(it) + } + } + + private fun generateVariable(expression: KtExpression): String { + var baseCallee: String = "" + KotlinIntroduceVariableHandler.doRefactoring(project, null, expression, false, emptyList()) { + baseCallee = it.name!! + } + return baseCallee + } + } + + private inner class Converter( + private val data: ConversionData + ) : CallableRefactoring(data.function.project, data.functionDescriptor, text) { + override fun performRefactoring(descriptorsForChange: Collection) { + val callables = getAffectedCallables(project, descriptorsForChange) + + val conflicts = MultiMap() + + val usages = ArrayList>() + + project.runSynchronouslyWithProgress("Looking for usages and conflicts...", true) { + runReadAction { + val progressStep = 1.0/callables.size + for ((i, callable) in callables.withIndex()) { + ProgressManager.getInstance().progressIndicator.fraction = (i + 1) * progressStep + + if (callable !is PsiNamedElement) continue + + if (!checkModifiable(callable)) { + val renderedCallable = RefactoringUIUtil.getDescription(callable, true).capitalize() + conflicts.putValue(callable, "Can't modify $renderedCallable") + } + + val references = callable.toLightMethods().flatMapTo(LinkedHashSet()) { MethodReferencesSearch.search(it) } + usageLoop@ for (ref in references) { + val refElement = ref.element ?: continue + when (ref) { + is KtSimpleReference<*> -> processExternalUsage(conflicts, refElement, usages) + is KtReference -> continue@usageLoop + else -> { + if (data.isFirstParameter) continue@usageLoop + conflicts.putValue( + refElement, + "Can't replace non-Kotlin reference with call expression: " + StringUtil.htmlEmphasize(refElement.text) + ) + } + } + } + + if (callable is KtFunction) { + usages += FunctionDefinitionInfo(callable) + processInternalUsages(callable, usages) + } + } + } + } + + project.checkConflictsInteractively(conflicts) { + project.executeWriteCommand(text) { + val elementsToShorten = ArrayList() + usages.forEach { it.process(data, elementsToShorten) } + ShortenReferences.DEFAULT.process(elementsToShorten) + } + } + } + + private fun processExternalUsage(conflicts: MultiMap, refElement: PsiElement, usages: java.util.ArrayList>) { + val callElement = refElement.getParentOfTypeAndBranch { calleeExpression } + if (callElement != null) { + val context = callElement.analyze(BodyResolveMode.PARTIAL) + val expressionToProcess = getArgumentExpressionToProcess(callElement, context) ?: return + + if (!data.isFirstParameter + && callElement is KtConstructorDelegationCall + && expressionToProcess !is KtLambdaExpression + && expressionToProcess !is KtSimpleNameExpression + && expressionToProcess !is KtCallableReferenceExpression) { + conflicts.putValue( + expressionToProcess, + "Following expression won't be processed since refactoring can't preserve its semantics: ${expressionToProcess.text}" + ) + return + } + + if (!checkThisExpressionsAreExplicatable(conflicts, context, expressionToProcess)) return + + if (data.isFirstParameter && expressionToProcess !is KtLambdaExpression) return + + usages += LambdaInfo(expressionToProcess) + return + } + + if (data.isFirstParameter) return + + val callableReference = refElement.getParentOfTypeAndBranch { callableReference } + if (callableReference != null) { + conflicts.putValue( + refElement, + "Callable reference transformation is not supported: " + StringUtil.htmlEmphasize(callableReference.text) + ) + return + } + } + + private fun getArgumentExpressionToProcess(callElement: KtCallElement, context: BindingContext): KtExpression? { + return callElement + .getArgumentByParameterIndex(data.functionParameterIndex, context) + .singleOrNull() + ?.getArgumentExpression() + ?.let { KtPsiUtil.safeDeparenthesize(it) } + } + + private fun checkThisExpressionsAreExplicatable(conflicts: MultiMap, context: BindingContext, expressionToProcess: KtExpression): Boolean { + for (thisExpr in expressionToProcess.collectDescendantsOfType()) { + if (thisExpr.getLabelName() != null) continue + val descriptor = context[BindingContext.REFERENCE_TARGET, thisExpr.instanceReference] ?: continue + if (explicateReceiverOf(descriptor) == "this") { + conflicts.putValue( + thisExpr, + "Following expression won't be processed since refactoring can't preserve its semantics: ${thisExpr.text}" + ) + return false + } + } + return true + } + + private fun processInternalUsages(callable: KtFunction, usages: ArrayList>) { + val body = when (callable) { + is KtConstructor<*> -> callable.containingClassOrObject?.getBody() + else -> callable.bodyExpression + } + if (body != null) { + val functionParameter = callable.valueParameters.getOrNull(data.functionParameterIndex) ?: return + for (ref in ReferencesSearch.search(functionParameter, LocalSearchScope(body))) { + val element = ref.element as? KtSimpleNameExpression ?: continue + val callExpression = element.getParentOfTypeAndBranch { calleeExpression } + if (callExpression != null) { + usages += ParameterCallInfo(callExpression) + } + else if (!data.isFirstParameter) { + usages += InternalReferencePassInfo(element) + } + } + } + } + } + + class ConversionData( + val typeParameterIndex: Int, + val functionParameterIndex: Int, + val lambdaType: KotlinType, + val function: KtFunction + ) { + val isFirstParameter: Boolean get() = typeParameterIndex == 0 + val functionDescriptor by lazy { function.resolveToDescriptor() as FunctionDescriptor } + } + + private fun KtTypeReference.getConversionData(): ConversionData? { + val parameter = parent as? KtParameter ?: return null + val functionType = parameter.getParentOfTypeAndBranch { parameterList } ?: return null + if (functionType.receiverTypeReference != null) return null + val lambdaType = functionType.getAbbreviatedTypeOrType(functionType.analyze(BodyResolveMode.PARTIAL)) ?: return null + val containingParameter = (functionType.parent as? KtTypeReference)?.parent as? KtParameter ?: return null + val ownerFunction = containingParameter.ownerFunction ?: return null + val typeParameterIndex = functionType.parameters.indexOf(parameter) + val functionParameterIndex = ownerFunction.valueParameters.indexOf(containingParameter) + return ConversionData(typeParameterIndex, functionParameterIndex, lambdaType, ownerFunction) + } + + override fun startInWriteAction(): Boolean = false + + override fun applicabilityRange(element: KtTypeReference): TextRange? { + val data = element.getConversionData() ?: return null + + val elementBefore = data.function.valueParameters[data.functionParameterIndex].typeReference!!.typeElement as KtFunctionType + val elementAfter = elementBefore.copied().apply { + setReceiverTypeReference(element) + parameterList!!.removeParameter(data.typeParameterIndex) + } + text = "Convert '${elementBefore.text}' to '${elementAfter.text}'" + + return element.textRange + } + + override fun applyTo(element: KtTypeReference, editor: Editor?) { + element.getConversionData()?.let { Converter(it).run() } + } +} diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/MoveLambdaOutsideParenthesesIntention.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/MoveLambdaOutsideParenthesesIntention.kt index 85ab73612d7..7e5afdb0ab9 100644 --- a/idea/src/org/jetbrains/kotlin/idea/intentions/MoveLambdaOutsideParenthesesIntention.kt +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/MoveLambdaOutsideParenthesesIntention.kt @@ -56,6 +56,13 @@ class MoveLambdaOutsideParenthesesIntention : SelfTargetingIntention 1 - val commonParent = PsiTreeUtil.findCommonParent(allReplaces.map { it.substringContextOrThis }) as KtElement + val commonParent = if (allReplaces.isNotEmpty()) { + PsiTreeUtil.findCommonParent(allReplaces.map { it.substringContextOrThis }) as KtElement + } + else { + expression.parent as KtElement + } var commonContainer = commonParent.getContainer()!! if (commonContainer != container && container.isAncestor(commonContainer, true)) { commonContainer = container diff --git a/idea/testData/intentions/convertFunctionTypeParameterToReceiver/.intention b/idea/testData/intentions/convertFunctionTypeParameterToReceiver/.intention new file mode 100644 index 00000000000..ce8225bf4b0 --- /dev/null +++ b/idea/testData/intentions/convertFunctionTypeParameterToReceiver/.intention @@ -0,0 +1 @@ +org.jetbrains.kotlin.idea.intentions.ConvertFunctionTypeParameterToReceiverIntention diff --git a/idea/testData/intentions/convertFunctionTypeParameterToReceiver/alreadyHasReceiver.kt b/idea/testData/intentions/convertFunctionTypeParameterToReceiver/alreadyHasReceiver.kt new file mode 100644 index 00000000000..7f55dcd68cd --- /dev/null +++ b/idea/testData/intentions/convertFunctionTypeParameterToReceiver/alreadyHasReceiver.kt @@ -0,0 +1,4 @@ +// IS_APPLICABLE: false +fun foo(f: Int.(Boolean) -> String) { + +} \ No newline at end of file diff --git a/idea/testData/intentions/convertFunctionTypeParameterToReceiver/cantReplaceWithThis.kt b/idea/testData/intentions/convertFunctionTypeParameterToReceiver/cantReplaceWithThis.kt new file mode 100644 index 00000000000..b52a51d7a47 --- /dev/null +++ b/idea/testData/intentions/convertFunctionTypeParameterToReceiver/cantReplaceWithThis.kt @@ -0,0 +1,10 @@ +// SHOULD_FAIL_WITH: Following expression won't be processed since refactoring can't preserve its semantics: this +val o = object { + fun bar() { + foo { i, b -> "$this: $i $b" } + } +} + +fun foo(f: (Int, Boolean) -> String) { + +} \ No newline at end of file diff --git a/idea/testData/intentions/convertFunctionTypeParameterToReceiver/firstParameter.kt b/idea/testData/intentions/convertFunctionTypeParameterToReceiver/firstParameter.kt new file mode 100644 index 00000000000..542c26f5daf --- /dev/null +++ b/idea/testData/intentions/convertFunctionTypeParameterToReceiver/firstParameter.kt @@ -0,0 +1,22 @@ +fun foo(f: (Int, Boolean) -> String) { + f(1, false) + bar(f) +} + +fun bar(f: (Int, Boolean) -> String) { + +} + +fun lambda(): (Int, Boolean) -> String = { i, b -> "$i $b"} + +fun baz(f: (Int, Boolean) -> String) { + fun g(i: Int, b: Boolean) = "" + + foo(f) + + foo(::g) + + foo(lambda()) + + foo { i, b -> "${i + 1} $b" } +} \ No newline at end of file diff --git a/idea/testData/intentions/convertFunctionTypeParameterToReceiver/firstParameter.kt.after b/idea/testData/intentions/convertFunctionTypeParameterToReceiver/firstParameter.kt.after new file mode 100644 index 00000000000..b208c0d3d63 --- /dev/null +++ b/idea/testData/intentions/convertFunctionTypeParameterToReceiver/firstParameter.kt.after @@ -0,0 +1,22 @@ +fun foo(f: Int.(Boolean) -> String) { + 1.f(false) + bar(f) +} + +fun bar(f: (Int, Boolean) -> String) { + +} + +fun lambda(): (Int, Boolean) -> String = { i, b -> "$i $b"} + +fun baz(f: (Int, Boolean) -> String) { + fun g(i: Int, b: Boolean) = "" + + foo(f) + + foo(::g) + + foo(lambda()) + + foo { b -> "${this + 1} $b" } +} \ No newline at end of file diff --git a/idea/testData/intentions/convertFunctionTypeParameterToReceiver/firstParameterPrimaryConstructor.kt b/idea/testData/intentions/convertFunctionTypeParameterToReceiver/firstParameterPrimaryConstructor.kt new file mode 100644 index 00000000000..edcf4b8bd97 --- /dev/null +++ b/idea/testData/intentions/convertFunctionTypeParameterToReceiver/firstParameterPrimaryConstructor.kt @@ -0,0 +1,41 @@ +open class Foo(f: (Int, Boolean) -> String) { + constructor(a: Int, f: (Int, Boolean) -> String) : this(f) + constructor(a: Int) : this(::g) + constructor(a: Int, b: Int) : this(lambda()) + constructor(a: Int, b: Int, c: Int) : this({ i, b -> "${i + 1} $b" }) + + init { + f(1, false) + bar(f) + } +} + +fun bar(f: (Int, Boolean) -> String) { + +} + +fun lambda(): (Int, Boolean) -> String = { i, b -> "$i $b"} + +fun g(i: Int, b: Boolean) = "" + +fun baz(f: (Int, Boolean) -> String) { + Foo(f) + + Foo(::g) + + Foo(lambda()) + + Foo { i, b -> "${i + 1} $b" } +} + +class Baz1(f: (Int, Boolean) -> String) : Foo(f) +class Baz2 : Foo(::g) +class Baz3 : Foo(lambda()) +class Baz4 : Foo({ i, b -> "${i + 1} $b" }) + +class Baz5 : Foo { + constructor(f: (Int, Boolean) -> String) : super(f) + constructor(a: Int) : super(::g) + constructor(a: Int, b: Int) : super(lambda()) + constructor(a: Int, b: Int, c: Int) : super({ i, b -> "${i + 1} $b" }) +} \ No newline at end of file diff --git a/idea/testData/intentions/convertFunctionTypeParameterToReceiver/firstParameterPrimaryConstructor.kt.after b/idea/testData/intentions/convertFunctionTypeParameterToReceiver/firstParameterPrimaryConstructor.kt.after new file mode 100644 index 00000000000..a0adfd5d6be --- /dev/null +++ b/idea/testData/intentions/convertFunctionTypeParameterToReceiver/firstParameterPrimaryConstructor.kt.after @@ -0,0 +1,41 @@ +open class Foo(f: Int.(Boolean) -> String) { + constructor(a: Int, f: (Int, Boolean) -> String) : this(f) + constructor(a: Int) : this(::g) + constructor(a: Int, b: Int) : this(lambda()) + constructor(a: Int, b: Int, c: Int) : this({ b -> "${this + 1} $b" }) + + init { + 1.f(false) + bar(f) + } +} + +fun bar(f: (Int, Boolean) -> String) { + +} + +fun lambda(): (Int, Boolean) -> String = { i, b -> "$i $b"} + +fun g(i: Int, b: Boolean) = "" + +fun baz(f: (Int, Boolean) -> String) { + Foo(f) + + Foo(::g) + + Foo(lambda()) + + Foo { b -> "${this + 1} $b" } +} + +class Baz1(f: (Int, Boolean) -> String) : Foo(f) +class Baz2 : Foo(::g) +class Baz3 : Foo(lambda()) +class Baz4 : Foo({ b -> "${this + 1} $b" }) + +class Baz5 : Foo { + constructor(f: (Int, Boolean) -> String) : super(f) + constructor(a: Int) : super(::g) + constructor(a: Int, b: Int) : super(lambda()) + constructor(a: Int, b: Int, c: Int) : super({ b -> "${this + 1} $b" }) +} \ No newline at end of file diff --git a/idea/testData/intentions/convertFunctionTypeParameterToReceiver/firstParameterSecondaryConstructor.kt b/idea/testData/intentions/convertFunctionTypeParameterToReceiver/firstParameterSecondaryConstructor.kt new file mode 100644 index 00000000000..82320e8e90f --- /dev/null +++ b/idea/testData/intentions/convertFunctionTypeParameterToReceiver/firstParameterSecondaryConstructor.kt @@ -0,0 +1,41 @@ +open class Foo { + constructor(f: (Int, Boolean) -> String) { + f(1, false) + bar(f) + } + + constructor(a: Int, f: (Int, Boolean) -> String) : this(f) + constructor(a: Int) : this(::g) + constructor(a: Int, b: Int) : this(lambda()) + constructor(a: Int, b: Int, c: Int) : this({ i, b -> "${i + 1} $b" }) +} + +fun bar(f: (Int, Boolean) -> String) { + +} + +fun lambda(): (Int, Boolean) -> String = { i, b -> "$i $b"} + +fun g(i: Int, b: Boolean) = "" + +fun baz(f: (Int, Boolean) -> String) { + Foo(f) + + Foo(::g) + + Foo(lambda()) + + Foo { i, b -> "${i + 1} $b" } +} + +class Baz1(f: (Int, Boolean) -> String) : Foo(f) +class Baz2 : Foo(::g) +class Baz3 : Foo(lambda()) +class Baz4 : Foo({ i, b -> "${i + 1} $b" }) + +class Baz5 : Foo { + constructor(f: (Int, Boolean) -> String) : super(f) + constructor(a: Int) : super(::g) + constructor(a: Int, b: Int) : super(lambda()) + constructor(a: Int, b: Int, c: Int) : super({ i, b -> "${i + 1} $b" }) +} \ No newline at end of file diff --git a/idea/testData/intentions/convertFunctionTypeParameterToReceiver/firstParameterSecondaryConstructor.kt.after b/idea/testData/intentions/convertFunctionTypeParameterToReceiver/firstParameterSecondaryConstructor.kt.after new file mode 100644 index 00000000000..b70542f0972 --- /dev/null +++ b/idea/testData/intentions/convertFunctionTypeParameterToReceiver/firstParameterSecondaryConstructor.kt.after @@ -0,0 +1,41 @@ +open class Foo { + constructor(f: Int.(Boolean) -> String) { + 1.f(false) + bar(f) + } + + constructor(a: Int, f: (Int, Boolean) -> String) : this(f) + constructor(a: Int) : this(::g) + constructor(a: Int, b: Int) : this(lambda()) + constructor(a: Int, b: Int, c: Int) : this({ b -> "${this + 1} $b" }) +} + +fun bar(f: (Int, Boolean) -> String) { + +} + +fun lambda(): (Int, Boolean) -> String = { i, b -> "$i $b"} + +fun g(i: Int, b: Boolean) = "" + +fun baz(f: (Int, Boolean) -> String) { + Foo(f) + + Foo(::g) + + Foo(lambda()) + + Foo { b -> "${this + 1} $b" } +} + +class Baz1(f: (Int, Boolean) -> String) : Foo(f) +class Baz2 : Foo(::g) +class Baz3 : Foo(lambda()) +class Baz4 : Foo({ b -> "${this + 1} $b" }) + +class Baz5 : Foo { + constructor(f: (Int, Boolean) -> String) : super(f) + constructor(a: Int) : super(::g) + constructor(a: Int, b: Int) : super(lambda()) + constructor(a: Int, b: Int, c: Int) : super({ b -> "${this + 1} $b" }) +} \ No newline at end of file diff --git a/idea/testData/intentions/convertFunctionTypeParameterToReceiver/firstParameterWithJavaUsages.1.java b/idea/testData/intentions/convertFunctionTypeParameterToReceiver/firstParameterWithJavaUsages.1.java new file mode 100644 index 00000000000..3dc7b577814 --- /dev/null +++ b/idea/testData/intentions/convertFunctionTypeParameterToReceiver/firstParameterWithJavaUsages.1.java @@ -0,0 +1,6 @@ +class J extends K { + @Override + public void foo(@NotNull Function2 f) { + super.foo(f); + } +} \ No newline at end of file diff --git a/idea/testData/intentions/convertFunctionTypeParameterToReceiver/firstParameterWithJavaUsages.1.java.after b/idea/testData/intentions/convertFunctionTypeParameterToReceiver/firstParameterWithJavaUsages.1.java.after new file mode 100644 index 00000000000..45b9641cd7f --- /dev/null +++ b/idea/testData/intentions/convertFunctionTypeParameterToReceiver/firstParameterWithJavaUsages.1.java.after @@ -0,0 +1,6 @@ +class J extends K { + @Override + public void foo(@NotNull Function2 f) { + super.foo(f); + } +} diff --git a/idea/testData/intentions/convertFunctionTypeParameterToReceiver/firstParameterWithJavaUsages.kt b/idea/testData/intentions/convertFunctionTypeParameterToReceiver/firstParameterWithJavaUsages.kt new file mode 100644 index 00000000000..3f4763435b3 --- /dev/null +++ b/idea/testData/intentions/convertFunctionTypeParameterToReceiver/firstParameterWithJavaUsages.kt @@ -0,0 +1,5 @@ +open class K { + open fun foo(f: (Int, Boolean) -> String) { + + } +} \ No newline at end of file diff --git a/idea/testData/intentions/convertFunctionTypeParameterToReceiver/firstParameterWithJavaUsages.kt.after b/idea/testData/intentions/convertFunctionTypeParameterToReceiver/firstParameterWithJavaUsages.kt.after new file mode 100644 index 00000000000..abf67c09cae --- /dev/null +++ b/idea/testData/intentions/convertFunctionTypeParameterToReceiver/firstParameterWithJavaUsages.kt.after @@ -0,0 +1,5 @@ +open class K { + open fun foo(f: Int.(Boolean) -> String) { + + } +} \ No newline at end of file diff --git a/idea/testData/intentions/convertFunctionTypeParameterToReceiver/nonFirstParameter.kt b/idea/testData/intentions/convertFunctionTypeParameterToReceiver/nonFirstParameter.kt new file mode 100644 index 00000000000..c41f551d0d0 --- /dev/null +++ b/idea/testData/intentions/convertFunctionTypeParameterToReceiver/nonFirstParameter.kt @@ -0,0 +1,22 @@ +fun foo(f: (Int, Boolean) -> String) { + f(1, false) + bar(f) +} + +fun bar(f: (Int, Boolean) -> String) { + +} + +fun lambda(): (Int, Boolean) -> String = { i, b -> "$i $b"} + +fun baz(f: (Int, Boolean) -> String) { + fun g(i: Int, b: Boolean) = "" + + foo(f) + + foo(::g) + + foo(lambda()) + + foo { i, b -> "${i + 1} $b" } +} \ No newline at end of file diff --git a/idea/testData/intentions/convertFunctionTypeParameterToReceiver/nonFirstParameter.kt.after b/idea/testData/intentions/convertFunctionTypeParameterToReceiver/nonFirstParameter.kt.after new file mode 100644 index 00000000000..80e0c559209 --- /dev/null +++ b/idea/testData/intentions/convertFunctionTypeParameterToReceiver/nonFirstParameter.kt.after @@ -0,0 +1,23 @@ +fun foo(f: Boolean.(Int) -> String) { + false.f(1) + bar { i, b -> b.f(i) } +} + +fun bar(f: (Int, Boolean) -> String) { + +} + +fun lambda(): (Int, Boolean) -> String = { i, b -> "$i $b"} + +fun baz(f: (Int, Boolean) -> String) { + fun g(i: Int, b: Boolean) = "" + + foo { i -> f(i, this) } + + foo { i -> (::g)(i, this) } + + val f1 = lambda() + foo { i -> f1(i, this) } + + foo { i -> "${i + 1} ${this}" } +} \ No newline at end of file diff --git a/idea/testData/intentions/convertFunctionTypeParameterToReceiver/nonFirstParameterCallableReferenceUsage.kt b/idea/testData/intentions/convertFunctionTypeParameterToReceiver/nonFirstParameterCallableReferenceUsage.kt new file mode 100644 index 00000000000..115bee2c3f7 --- /dev/null +++ b/idea/testData/intentions/convertFunctionTypeParameterToReceiver/nonFirstParameterCallableReferenceUsage.kt @@ -0,0 +1,8 @@ +// SHOULD_FAIL_WITH: Callable reference transformation is not supported: ::foo +fun foo(f: (Int, Boolean) -> String) { + +} + +fun baz(f: (Int, Boolean) -> String) { + val x = ::foo +} \ No newline at end of file diff --git a/idea/testData/intentions/convertFunctionTypeParameterToReceiver/nonFirstParameterPrimaryConstructor.kt b/idea/testData/intentions/convertFunctionTypeParameterToReceiver/nonFirstParameterPrimaryConstructor.kt new file mode 100644 index 00000000000..75b93bd3c23 --- /dev/null +++ b/idea/testData/intentions/convertFunctionTypeParameterToReceiver/nonFirstParameterPrimaryConstructor.kt @@ -0,0 +1,39 @@ +open class Foo(f: (Int, Boolean) -> String) { + constructor(a: Int, f: (Int, Boolean) -> String) : this(f) + constructor(a: Int) : this(::g) + constructor(a: Int, b: Int, c: Int) : this({ i, b -> "${i + 1} $b" }) + + init { + f(1, false) + bar(f) + } +} + +fun bar(f: (Int, Boolean) -> String) { + +} + +fun lambda(): (Int, Boolean) -> String = { i, b -> "$i $b"} + +fun g(i: Int, b: Boolean) = "" + +fun baz(f: (Int, Boolean) -> String) { + Foo(f) + + Foo(::g) + + Foo(lambda()) + + Foo { i, b -> "${i + 1} $b" } +} + +class Baz1(f: (Int, Boolean) -> String) : Foo(f) +class Baz2 : Foo(::g) +class Baz3 : Foo(lambda()) +class Baz4 : Foo({ i, b -> "${i + 1} $b" }) + +class Baz5 : Foo { + constructor(f: (Int, Boolean) -> String) : super(f) + constructor(a: Int) : super(::g) + constructor(a: Int, b: Int, c: Int) : super({ i, b -> "${i + 1} $b" }) +} \ No newline at end of file diff --git a/idea/testData/intentions/convertFunctionTypeParameterToReceiver/nonFirstParameterPrimaryConstructor.kt.after b/idea/testData/intentions/convertFunctionTypeParameterToReceiver/nonFirstParameterPrimaryConstructor.kt.after new file mode 100644 index 00000000000..9e1d864c053 --- /dev/null +++ b/idea/testData/intentions/convertFunctionTypeParameterToReceiver/nonFirstParameterPrimaryConstructor.kt.after @@ -0,0 +1,43 @@ +open class Foo(f: Boolean.(Int) -> String) { + constructor(a: Int, f: (Int, Boolean) -> String) : this({ i -> f(i, this) }) + constructor(a: Int) : this({ i -> (::g)(i, this) }) + constructor(a: Int, b: Int, c: Int) : this({ i -> "${i + 1} ${this}" }) + + init { + false.f(1) + bar { i, b -> b.f(i) } + } +} + +fun bar(f: (Int, Boolean) -> String) { + +} + +fun lambda(): (Int, Boolean) -> String = { i, b -> "$i $b"} + +fun g(i: Int, b: Boolean) = "" + +fun baz(f: (Int, Boolean) -> String) { + Foo { i -> f(i, this) } + + Foo { i -> (::g)(i, this) } + + val f1 = lambda() + Foo { i -> f1(i, this) } + + Foo { i -> "${i + 1} ${this}" } +} + +class Baz1(f: (Int, Boolean) -> String) : Foo({ i -> f(i, this) }) +class Baz2 : Foo({ i -> (::g)(i, this) }) + +val f = lambda() + +class Baz3 : Foo({ i -> f(i, this) }) +class Baz4 : Foo({ i -> "${i + 1} ${this}" }) + +class Baz5 : Foo { + constructor(f: (Int, Boolean) -> String) : super({ i -> f(i, this) }) + constructor(a: Int) : super({ i -> (::g)(i, this) }) + constructor(a: Int, b: Int, c: Int) : super({ i -> "${i + 1} ${this}" }) +} \ No newline at end of file diff --git a/idea/testData/intentions/convertFunctionTypeParameterToReceiver/nonFirstParameterSecondaryConstructor.kt b/idea/testData/intentions/convertFunctionTypeParameterToReceiver/nonFirstParameterSecondaryConstructor.kt new file mode 100644 index 00000000000..86b532fc2a5 --- /dev/null +++ b/idea/testData/intentions/convertFunctionTypeParameterToReceiver/nonFirstParameterSecondaryConstructor.kt @@ -0,0 +1,39 @@ +open class Foo { + constructor(f: (Int, Boolean) -> String){ + f(1, false) + bar(f) + } + + constructor(a: Int, f: (Int, Boolean) -> String) : this(f) + constructor(a: Int) : this(::g) + constructor(a: Int, b: Int, c: Int) : this({ i, b -> "${i + 1} $b" }) +} + +fun bar(f: (Int, Boolean) -> String) { + +} + +fun lambda(): (Int, Boolean) -> String = { i, b -> "$i $b"} + +fun g(i: Int, b: Boolean) = "" + +fun baz(f: (Int, Boolean) -> String) { + Foo(f) + + Foo(::g) + + Foo(lambda()) + + Foo { i, b -> "${i + 1} $b" } +} + +class Baz1(f: (Int, Boolean) -> String) : Foo(f) +class Baz2 : Foo(::g) +class Baz3 : Foo(lambda()) +class Baz4 : Foo({ i, b -> "${i + 1} $b" }) + +class Baz5 : Foo { + constructor(f: (Int, Boolean) -> String) : super(f) + constructor(a: Int) : super(::g) + constructor(a: Int, b: Int, c: Int) : super({ i, b -> "${i + 1} $b" }) +} \ No newline at end of file diff --git a/idea/testData/intentions/convertFunctionTypeParameterToReceiver/nonFirstParameterSecondaryConstructor.kt.after b/idea/testData/intentions/convertFunctionTypeParameterToReceiver/nonFirstParameterSecondaryConstructor.kt.after new file mode 100644 index 00000000000..c0e9021425f --- /dev/null +++ b/idea/testData/intentions/convertFunctionTypeParameterToReceiver/nonFirstParameterSecondaryConstructor.kt.after @@ -0,0 +1,43 @@ +open class Foo { + constructor(f: Boolean.(Int) -> String){ + false.f(1) + bar { i, b -> b.f(i) } + } + + constructor(a: Int, f: (Int, Boolean) -> String) : this({ i -> f(i, this) }) + constructor(a: Int) : this({ i -> (::g)(i, this) }) + constructor(a: Int, b: Int, c: Int) : this({ i -> "${i + 1} ${this}" }) +} + +fun bar(f: (Int, Boolean) -> String) { + +} + +fun lambda(): (Int, Boolean) -> String = { i, b -> "$i $b"} + +fun g(i: Int, b: Boolean) = "" + +fun baz(f: (Int, Boolean) -> String) { + Foo { i -> f(i, this) } + + Foo { i -> (::g)(i, this) } + + val f1 = lambda() + Foo { i -> f1(i, this) } + + Foo { i -> "${i + 1} ${this}" } +} + +class Baz1(f: (Int, Boolean) -> String) : Foo({ i -> f(i, this) }) +class Baz2 : Foo({ i -> (::g)(i, this) }) + +val f = lambda() + +class Baz3 : Foo({ i -> f(i, this) }) +class Baz4 : Foo({ i -> "${i + 1} ${this}" }) + +class Baz5 : Foo { + constructor(f: (Int, Boolean) -> String) : super({ i -> f(i, this) }) + constructor(a: Int) : super({ i -> (::g)(i, this) }) + constructor(a: Int, b: Int, c: Int) : super({ i -> "${i + 1} ${this}" }) +} \ No newline at end of file diff --git a/idea/testData/intentions/convertFunctionTypeParameterToReceiver/nonFirstParameterSuperDelegationCall.kt b/idea/testData/intentions/convertFunctionTypeParameterToReceiver/nonFirstParameterSuperDelegationCall.kt new file mode 100644 index 00000000000..a8ecbc2ba89 --- /dev/null +++ b/idea/testData/intentions/convertFunctionTypeParameterToReceiver/nonFirstParameterSuperDelegationCall.kt @@ -0,0 +1,8 @@ +// SHOULD_FAIL_WITH: Following expression won't be processed since refactoring can't preserve its semantics: lambda() +open class Foo(f: (Int, Boolean) -> String) + +fun lambda(): (Int, Boolean) -> String = { i, b -> "$i $b"} + +class Baz5 : Foo { + constructor() : super(lambda()) +} \ No newline at end of file diff --git a/idea/testData/intentions/convertFunctionTypeParameterToReceiver/nonFirstParameterThisDelegationCall.kt b/idea/testData/intentions/convertFunctionTypeParameterToReceiver/nonFirstParameterThisDelegationCall.kt new file mode 100644 index 00000000000..c258f3c6e91 --- /dev/null +++ b/idea/testData/intentions/convertFunctionTypeParameterToReceiver/nonFirstParameterThisDelegationCall.kt @@ -0,0 +1,6 @@ +// SHOULD_FAIL_WITH: Following expression won't be processed since refactoring can't preserve its semantics: lambda() +open class Foo(f: (Int, Boolean) -> String) { + constructor() : this(lambda()) +} + +fun lambda(): (Int, Boolean) -> String = { i, b -> "$i $b"} \ No newline at end of file diff --git a/idea/testData/intentions/convertFunctionTypeParameterToReceiver/nonFirstParameterWithJavaUsages.1.java b/idea/testData/intentions/convertFunctionTypeParameterToReceiver/nonFirstParameterWithJavaUsages.1.java new file mode 100644 index 00000000000..3dc7b577814 --- /dev/null +++ b/idea/testData/intentions/convertFunctionTypeParameterToReceiver/nonFirstParameterWithJavaUsages.1.java @@ -0,0 +1,6 @@ +class J extends K { + @Override + public void foo(@NotNull Function2 f) { + super.foo(f); + } +} \ No newline at end of file diff --git a/idea/testData/intentions/convertFunctionTypeParameterToReceiver/nonFirstParameterWithJavaUsages.kt b/idea/testData/intentions/convertFunctionTypeParameterToReceiver/nonFirstParameterWithJavaUsages.kt new file mode 100644 index 00000000000..0bb9c752148 --- /dev/null +++ b/idea/testData/intentions/convertFunctionTypeParameterToReceiver/nonFirstParameterWithJavaUsages.kt @@ -0,0 +1,6 @@ +// SHOULD_FAIL_WITH: Can't replace non-Kotlin reference with call expression: super.foo +open class K { + open fun foo(f: (Int, Boolean) -> String) { + + } +} \ No newline at end of file diff --git a/idea/testData/intentions/convertFunctionTypeParameterToReceiver/notInFunctionParameter.kt b/idea/testData/intentions/convertFunctionTypeParameterToReceiver/notInFunctionParameter.kt new file mode 100644 index 00000000000..7fa8f194b74 --- /dev/null +++ b/idea/testData/intentions/convertFunctionTypeParameterToReceiver/notInFunctionParameter.kt @@ -0,0 +1,2 @@ +// IS_APPLICABLE: false +fun foo(f: (Int, Boolean) -> String): (Int, Boolean) -> String = f \ No newline at end of file diff --git a/idea/testData/intentions/convertFunctionTypeParameterToReceiver/notOnFunctionTypeParameter.kt b/idea/testData/intentions/convertFunctionTypeParameterToReceiver/notOnFunctionTypeParameter.kt new file mode 100644 index 00000000000..060091fc6a5 --- /dev/null +++ b/idea/testData/intentions/convertFunctionTypeParameterToReceiver/notOnFunctionTypeParameter.kt @@ -0,0 +1,4 @@ +// IS_APPLICABLE: false +fun foo(f: (Int, Boolean) -> String) { + +} \ No newline at end of file diff --git a/idea/testData/intentions/convertFunctionTypeParameterToReceiver/overrides.kt b/idea/testData/intentions/convertFunctionTypeParameterToReceiver/overrides.kt new file mode 100644 index 00000000000..ffbff512c3c --- /dev/null +++ b/idea/testData/intentions/convertFunctionTypeParameterToReceiver/overrides.kt @@ -0,0 +1,11 @@ +open class A { + open fun foo(f: (Int, Boolean) -> String) { + + } +} + +class B : A() { + override fun foo(f: (Int, Boolean) -> String) { + + } +} \ No newline at end of file diff --git a/idea/testData/intentions/convertFunctionTypeParameterToReceiver/overrides.kt.after b/idea/testData/intentions/convertFunctionTypeParameterToReceiver/overrides.kt.after new file mode 100644 index 00000000000..8cf765146b7 --- /dev/null +++ b/idea/testData/intentions/convertFunctionTypeParameterToReceiver/overrides.kt.after @@ -0,0 +1,11 @@ +open class A { + open fun foo(f: Boolean.(Int) -> String) { + + } +} + +class B : A() { + override fun foo(f: Boolean.(Int) -> String) { + + } +} \ 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 9d96f6db734..1b3240efc46 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java @@ -3996,6 +3996,111 @@ public class IntentionTestGenerated extends AbstractIntentionTest { } } + @TestMetadata("idea/testData/intentions/convertFunctionTypeParameterToReceiver") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class ConvertFunctionTypeParameterToReceiver extends AbstractIntentionTest { + public void testAllFilesPresentInConvertFunctionTypeParameterToReceiver() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/intentions/convertFunctionTypeParameterToReceiver"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), TargetBackend.ANY, true); + } + + @TestMetadata("alreadyHasReceiver.kt") + public void testAlreadyHasReceiver() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertFunctionTypeParameterToReceiver/alreadyHasReceiver.kt"); + doTest(fileName); + } + + @TestMetadata("cantReplaceWithThis.kt") + public void testCantReplaceWithThis() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertFunctionTypeParameterToReceiver/cantReplaceWithThis.kt"); + doTest(fileName); + } + + @TestMetadata("firstParameter.kt") + public void testFirstParameter() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertFunctionTypeParameterToReceiver/firstParameter.kt"); + doTest(fileName); + } + + @TestMetadata("firstParameterPrimaryConstructor.kt") + public void testFirstParameterPrimaryConstructor() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertFunctionTypeParameterToReceiver/firstParameterPrimaryConstructor.kt"); + doTest(fileName); + } + + @TestMetadata("firstParameterSecondaryConstructor.kt") + public void testFirstParameterSecondaryConstructor() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertFunctionTypeParameterToReceiver/firstParameterSecondaryConstructor.kt"); + doTest(fileName); + } + + @TestMetadata("firstParameterWithJavaUsages.kt") + public void testFirstParameterWithJavaUsages() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertFunctionTypeParameterToReceiver/firstParameterWithJavaUsages.kt"); + doTest(fileName); + } + + @TestMetadata("nonFirstParameter.kt") + public void testNonFirstParameter() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertFunctionTypeParameterToReceiver/nonFirstParameter.kt"); + doTest(fileName); + } + + @TestMetadata("nonFirstParameterCallableReferenceUsage.kt") + public void testNonFirstParameterCallableReferenceUsage() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertFunctionTypeParameterToReceiver/nonFirstParameterCallableReferenceUsage.kt"); + doTest(fileName); + } + + @TestMetadata("nonFirstParameterPrimaryConstructor.kt") + public void testNonFirstParameterPrimaryConstructor() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertFunctionTypeParameterToReceiver/nonFirstParameterPrimaryConstructor.kt"); + doTest(fileName); + } + + @TestMetadata("nonFirstParameterSecondaryConstructor.kt") + public void testNonFirstParameterSecondaryConstructor() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertFunctionTypeParameterToReceiver/nonFirstParameterSecondaryConstructor.kt"); + doTest(fileName); + } + + @TestMetadata("nonFirstParameterSuperDelegationCall.kt") + public void testNonFirstParameterSuperDelegationCall() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertFunctionTypeParameterToReceiver/nonFirstParameterSuperDelegationCall.kt"); + doTest(fileName); + } + + @TestMetadata("nonFirstParameterThisDelegationCall.kt") + public void testNonFirstParameterThisDelegationCall() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertFunctionTypeParameterToReceiver/nonFirstParameterThisDelegationCall.kt"); + doTest(fileName); + } + + @TestMetadata("nonFirstParameterWithJavaUsages.kt") + public void testNonFirstParameterWithJavaUsages() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertFunctionTypeParameterToReceiver/nonFirstParameterWithJavaUsages.kt"); + doTest(fileName); + } + + @TestMetadata("notInFunctionParameter.kt") + public void testNotInFunctionParameter() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertFunctionTypeParameterToReceiver/notInFunctionParameter.kt"); + doTest(fileName); + } + + @TestMetadata("notOnFunctionTypeParameter.kt") + public void testNotOnFunctionTypeParameter() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertFunctionTypeParameterToReceiver/notOnFunctionTypeParameter.kt"); + doTest(fileName); + } + + @TestMetadata("overrides.kt") + public void testOverrides() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertFunctionTypeParameterToReceiver/overrides.kt"); + doTest(fileName); + } + } + @TestMetadata("idea/testData/intentions/convertIfWithThrowToAssert") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)