Intentions: Convert function type parameter to receiver

#KT-14246 In Progress
This commit is contained in:
Alexey Sedunov
2016-11-29 17:37:39 +03:00
parent 9cadfd01ae
commit d4ed2d2022
43 changed files with 1066 additions and 16 deletions
@@ -0,0 +1,3 @@
fun foo(f: Int.(s: String) -> Boolean) = 1.f("2")
fun bar = foo { s -> s.length() > this }
@@ -0,0 +1,3 @@
fun foo(f: (n: <spot>Int</spot>, s: String) -> Boolean) = f(1, "2")
fun bar = foo { n, s -> s.length() > n }
@@ -0,0 +1,5 @@
<html>
<body>
This intention converts given parameter of a function type used in a function parameter to receiver
</body>
</html>
+5
View File
@@ -1456,6 +1456,11 @@
<category>Kotlin</category>
</intentionAction>
<intentionAction>
<className>org.jetbrains.kotlin.idea.intentions.ConvertFunctionTypeParameterToReceiverIntention</className>
<category>Kotlin</category>
</intentionAction>
<localInspection implementationClass="org.jetbrains.kotlin.idea.intentions.ObjectLiteralToLambdaInspection"
displayName="Object literal can be converted to lambda"
groupName="Kotlin"
@@ -0,0 +1,358 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.idea.intentions
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.progress.ProgressManager
import com.intellij.openapi.util.TextRange
import com.intellij.openapi.util.text.StringUtil
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiNamedElement
import com.intellij.psi.search.LocalSearchScope
import com.intellij.psi.search.searches.MethodReferencesSearch
import com.intellij.psi.search.searches.ReferencesSearch
import com.intellij.refactoring.util.RefactoringUIUtil
import com.intellij.usageView.UsageInfo
import com.intellij.util.containers.MultiMap
import org.jetbrains.kotlin.asJava.toLightMethods
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
import org.jetbrains.kotlin.idea.core.*
import org.jetbrains.kotlin.idea.refactoring.CallableRefactoring
import org.jetbrains.kotlin.idea.refactoring.changeSignature.usages.explicateReceiverOf
import org.jetbrains.kotlin.idea.refactoring.checkConflictsInteractively
import org.jetbrains.kotlin.idea.refactoring.getAffectedCallables
import org.jetbrains.kotlin.idea.refactoring.introduce.introduceVariable.KotlinIntroduceVariableHandler
import org.jetbrains.kotlin.idea.references.KtReference
import org.jetbrains.kotlin.idea.references.KtSimpleReference
import org.jetbrains.kotlin.idea.runSynchronouslyWithProgress
import org.jetbrains.kotlin.idea.util.application.executeWriteCommand
import org.jetbrains.kotlin.idea.util.application.runReadAction
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.collectDescendantsOfType
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
import org.jetbrains.kotlin.psi.psiUtil.forEachDescendantOfType
import org.jetbrains.kotlin.psi.psiUtil.getParentOfTypeAndBranch
import org.jetbrains.kotlin.psi.typeRefHelpers.setReceiverTypeReference
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.bindingContextUtil.getAbbreviatedTypeOrType
import org.jetbrains.kotlin.resolve.calls.resolvedCallUtil.getArgumentByParameterIndex
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.types.KotlinType
class ConvertFunctionTypeParameterToReceiverIntention : SelfTargetingRangeIntention<KtTypeReference>(
KtTypeReference::class.java,
"Convert function type parameter to receiver"
) {
abstract class AbstractUsageInfo<out T : PsiElement>(element: T) : UsageInfo(element) {
@Suppress("UNCHECKED_CAST")
override fun getElement() = super.getElement() as T?
abstract fun process(data: ConversionData, elementsToShorten: MutableList<KtElement>)
}
class FunctionDefinitionInfo(element: KtFunction) : AbstractUsageInfo<KtFunction>(element) {
override fun process(data: ConversionData, elementsToShorten: MutableList<KtElement>) {
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<KtCallExpression>(element) {
override fun process(data: ConversionData, elementsToShorten: MutableList<KtElement>) {
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<KtSimpleNameExpression>(element) {
override fun process(data: ConversionData, elementsToShorten: MutableList<KtElement>) {
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<KtExpression>(element) {
override fun process(data: ConversionData, elementsToShorten: MutableList<KtElement>) {
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<KtThisExpression> {
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<CallableDescriptor>(data.function.project, data.functionDescriptor, text) {
override fun performRefactoring(descriptorsForChange: Collection<CallableDescriptor>) {
val callables = getAffectedCallables(project, descriptorsForChange)
val conflicts = MultiMap<PsiElement, String>()
val usages = ArrayList<AbstractUsageInfo<*>>()
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<KtElement>()
usages.forEach { it.process(data, elementsToShorten) }
ShortenReferences.DEFAULT.process(elementsToShorten)
}
}
}
private fun processExternalUsage(conflicts: MultiMap<PsiElement, String>, refElement: PsiElement, usages: java.util.ArrayList<AbstractUsageInfo<*>>) {
val callElement = refElement.getParentOfTypeAndBranch<KtCallElement> { 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<KtCallableReferenceExpression> { 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<PsiElement, String>, context: BindingContext, expressionToProcess: KtExpression): Boolean {
for (thisExpr in expressionToProcess.collectDescendantsOfType<KtThisExpression>()) {
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<AbstractUsageInfo<*>>) {
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<KtCallExpression> { 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<KtFunctionType> { 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() }
}
}
@@ -56,6 +56,13 @@ class MoveLambdaOutsideParenthesesIntention : SelfTargetingIntention<KtCallExpre
return true
}
fun moveFunctionLiteralOutsideParenthesesIfPossible(expression: KtLambdaExpression) {
val call = ((expression.parent as? KtValueArgument)?.parent as? KtValueArgumentList)?.parent as? KtCallExpression ?: return
if (canMove(call)) {
call.moveFunctionLiteralOutsideParentheses()
}
}
}
override fun isApplicableTo(element: KtCallExpression, caretOffset: Int): Boolean {
@@ -524,7 +524,12 @@ object KotlinIntroduceVariableHandler : RefactoringActionHandler {
|| expression.shouldReplaceOccurrence(bindingContext, container)
|| allReplaces.size > 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
@@ -0,0 +1 @@
org.jetbrains.kotlin.idea.intentions.ConvertFunctionTypeParameterToReceiverIntention
@@ -0,0 +1,4 @@
// IS_APPLICABLE: false
fun foo(f: Int.(<caret>Boolean) -> String) {
}
@@ -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: (<caret>Int, Boolean) -> String) {
}
@@ -0,0 +1,22 @@
fun foo(f: (<caret>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" }
}
@@ -0,0 +1,22 @@
fun foo(f: Int.(<caret>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" }
}
@@ -0,0 +1,41 @@
open class Foo(f: (<caret>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" })
}
@@ -0,0 +1,41 @@
open class Foo(f: Int.(<caret>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" })
}
@@ -0,0 +1,41 @@
open class Foo {
constructor(f: (<caret>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" })
}
@@ -0,0 +1,41 @@
open class Foo {
constructor(f: Int.(<caret>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" })
}
@@ -0,0 +1,6 @@
class J extends K {
@Override
public void foo(@NotNull Function2<? super Integer, ? super Boolean, String> f) {
super.foo(f);
}
}
@@ -0,0 +1,6 @@
class J extends K {
@Override
public void foo(@NotNull Function2<? super Integer, ? super Boolean, String> f) {
super.foo(f);
}
}
@@ -0,0 +1,5 @@
open class K {
open fun foo(f: (<caret>Int, Boolean) -> String) {
}
}
@@ -0,0 +1,5 @@
open class K {
open fun foo(f: Int.(<caret>Boolean) -> String) {
}
}
@@ -0,0 +1,22 @@
fun foo(f: (Int, <caret>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" }
}
@@ -0,0 +1,23 @@
fun foo(f: Boolean.(Int<caret>) -> 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}" }
}
@@ -0,0 +1,8 @@
// SHOULD_FAIL_WITH: Callable reference transformation is not supported: ::foo
fun foo(f: (Int, <caret>Boolean) -> String) {
}
fun baz(f: (Int, Boolean) -> String) {
val x = ::foo
}
@@ -0,0 +1,39 @@
open class Foo(f: (Int, <caret>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" })
}
@@ -0,0 +1,43 @@
open class Foo(f: Boolean.(Int<caret>) -> 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}" })
}
@@ -0,0 +1,39 @@
open class Foo {
constructor(f: (Int, <caret>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" })
}
@@ -0,0 +1,43 @@
open class Foo {
constructor(f: Boolean.(Int<caret>) -> 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}" })
}
@@ -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, <caret>Boolean) -> String)
fun lambda(): (Int, Boolean) -> String = { i, b -> "$i $b"}
class Baz5 : Foo {
constructor() : super(lambda())
}
@@ -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, <caret>Boolean) -> String) {
constructor() : this(lambda())
}
fun lambda(): (Int, Boolean) -> String = { i, b -> "$i $b"}
@@ -0,0 +1,6 @@
class J extends K {
@Override
public void foo(@NotNull Function2<? super Integer, ? super Boolean, String> f) {
super.foo(f);
}
}
@@ -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, <caret>Boolean) -> String) {
}
}
@@ -0,0 +1,2 @@
// IS_APPLICABLE: false
fun foo(f: (Int, Boolean) -> String): (<caret>Int, Boolean) -> String = f
@@ -0,0 +1,4 @@
// IS_APPLICABLE: false
fun foo(f: (Int, Boolean) -> <caret>String) {
}
@@ -0,0 +1,11 @@
open class A {
open fun foo(f: (Int, <caret>Boolean) -> String) {
}
}
class B : A() {
override fun foo(f: (Int, Boolean) -> String) {
}
}
@@ -0,0 +1,11 @@
open class A {
open fun foo(f: Boolean.(Int<caret>) -> String) {
}
}
class B : A() {
override fun foo(f: Boolean.(Int) -> String) {
}
}
@@ -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)