Intentions: Convert function type receiver to parameter
#KT-14246 Fixed
This commit is contained in:
@@ -338,6 +338,7 @@ These artifacts include extensions for the types available in the latter JDKs, s
|
||||
- [`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
|
||||
- [`KT-14246`](https://youtrack.jetbrains.com/issue/KT-14246) Intentions: Convert function type receiver to parameter
|
||||
|
||||
##### New features
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ object EditCommaSeparatedListHelper {
|
||||
fun <TItem: KtElement> addItemAfter(list: KtElement, allItems: List<TItem>, item: TItem, anchor: TItem?, prefix: KtToken = KtTokens.LPAR): TItem {
|
||||
assert(anchor == null || anchor.parent == list)
|
||||
if (allItems.isEmpty()) {
|
||||
if (list.firstChild.node.elementType == prefix) {
|
||||
if (list.firstChild?.node?.elementType == prefix) {
|
||||
return list.addAfter(item, list.firstChild) as TItem
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -112,6 +112,10 @@ class KtPsiFactory(private val project: Project) {
|
||||
return (createType("A.() -> B").typeElement as KtFunctionType).receiver!!.apply { this.typeReference.replace(typeReference) }
|
||||
}
|
||||
|
||||
fun createFunctionTypeParameter(typeReference: KtTypeReference): KtParameter {
|
||||
return (createType("(A) -> B").typeElement as KtFunctionType).parameters.first().apply { this.typeReference!!.replace(typeReference) }
|
||||
}
|
||||
|
||||
fun createTypeAlias(name: String, typeParameters: List<String>, typeElement: KtTypeElement): KtTypeAlias {
|
||||
return createTypeAlias(name, typeParameters, "X").apply { getTypeReference()!!.replace(createType(typeElement)) }
|
||||
}
|
||||
|
||||
@@ -475,4 +475,25 @@ fun isTypeConstructorReference(e: PsiElement): Boolean {
|
||||
|
||||
fun KtParameter.isPropertyParameter() = ownerFunction is KtPrimaryConstructor && hasValOrVar()
|
||||
|
||||
fun isDoubleColonReceiver(expression: KtExpression) = expression.getParentOfTypeAndBranch<KtDoubleColonExpression> { this.receiverExpression } != null
|
||||
fun isDoubleColonReceiver(expression: KtExpression) = expression.getParentOfTypeAndBranch<KtDoubleColonExpression> { this.receiverExpression } != null
|
||||
|
||||
fun KtFunctionLiteral.getOrCreateParameterList(): KtParameterList {
|
||||
valueParameterList?.let { return it }
|
||||
|
||||
val psiFactory = KtPsiFactory(this)
|
||||
|
||||
val anchor = lBrace
|
||||
val newParameterList = addAfter(psiFactory.createLambdaParameterList("x"), anchor) as KtParameterList
|
||||
newParameterList.removeParameter(0)
|
||||
if (arrow == null) {
|
||||
val whitespaceAndArrow = psiFactory.createWhitespaceAndArrow()
|
||||
addRangeAfter(whitespaceAndArrow.first, whitespaceAndArrow.second, newParameterList)
|
||||
}
|
||||
return newParameterList
|
||||
}
|
||||
|
||||
fun KtCallExpression.getOrCreateValueArgumentList(): KtValueArgumentList {
|
||||
valueArgumentList?.let { return it }
|
||||
return addAfter(KtPsiFactory(this).createCallArguments("()"),
|
||||
typeArgumentList ?: calleeExpression) as KtValueArgumentList
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
fun foo(f: (i: <spot>Int</spot>, s: String) -> Boolean) = f(1, "2")
|
||||
|
||||
fun bar = foo { i, s -> s.length() > n }
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
fun foo(f: <spot>Int</spot>.(s: String) -> Boolean) = 1.f("2")
|
||||
|
||||
fun bar = foo { s -> s.length() > this }
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
<html>
|
||||
<body>
|
||||
This intention converts receiver of a function type used in a function parameter to its first parameter
|
||||
</body>
|
||||
</html>
|
||||
@@ -1461,6 +1461,11 @@
|
||||
<category>Kotlin</category>
|
||||
</intentionAction>
|
||||
|
||||
<intentionAction>
|
||||
<className>org.jetbrains.kotlin.idea.intentions.ConvertFunctionTypeReceiverToParameterIntention</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,28 @@
|
||||
/*
|
||||
* 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.psi.PsiElement
|
||||
import com.intellij.usageView.UsageInfo
|
||||
import org.jetbrains.kotlin.psi.KtElement
|
||||
|
||||
abstract class AbstractProcessableUsageInfo<out T : PsiElement, in D: Any>(element: T) : UsageInfo(element) {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun getElement() = super.getElement() as T?
|
||||
|
||||
abstract fun process(data: D, elementsToShorten: MutableList<KtElement>)
|
||||
}
|
||||
+11
-15
@@ -26,7 +26,6 @@ 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
|
||||
@@ -60,14 +59,7 @@ class ConvertFunctionTypeParameterToReceiverIntention : SelfTargetingRangeIntent
|
||||
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) {
|
||||
class FunctionDefinitionInfo(element: KtFunction) : AbstractProcessableUsageInfo<KtFunction, ConversionData>(element) {
|
||||
override fun process(data: ConversionData, elementsToShorten: MutableList<KtElement>) {
|
||||
val function = element ?: return
|
||||
val functionParameter = function.valueParameters.getOrNull(data.functionParameterIndex) ?: return
|
||||
@@ -80,7 +72,7 @@ class ConvertFunctionTypeParameterToReceiverIntention : SelfTargetingRangeIntent
|
||||
}
|
||||
}
|
||||
|
||||
class ParameterCallInfo(element: KtCallExpression) : AbstractUsageInfo<KtCallExpression>(element) {
|
||||
class ParameterCallInfo(element: KtCallExpression) : AbstractProcessableUsageInfo<KtCallExpression, ConversionData>(element) {
|
||||
override fun process(data: ConversionData, elementsToShorten: MutableList<KtElement>) {
|
||||
val callExpression = element ?: return
|
||||
val argumentList = callExpression.valueArgumentList ?: return
|
||||
@@ -91,7 +83,7 @@ class ConvertFunctionTypeParameterToReceiverIntention : SelfTargetingRangeIntent
|
||||
}
|
||||
}
|
||||
|
||||
class InternalReferencePassInfo(element: KtSimpleNameExpression) : AbstractUsageInfo<KtSimpleNameExpression>(element) {
|
||||
class InternalReferencePassInfo(element: KtSimpleNameExpression) : AbstractProcessableUsageInfo<KtSimpleNameExpression, ConversionData>(element) {
|
||||
override fun process(data: ConversionData, elementsToShorten: MutableList<KtElement>) {
|
||||
val expression = element ?: return
|
||||
val lambdaType = data.lambdaType
|
||||
@@ -111,7 +103,7 @@ class ConvertFunctionTypeParameterToReceiverIntention : SelfTargetingRangeIntent
|
||||
}
|
||||
}
|
||||
|
||||
class LambdaInfo(element: KtExpression) : AbstractUsageInfo<KtExpression>(element) {
|
||||
class LambdaInfo(element: KtExpression) : AbstractProcessableUsageInfo<KtExpression, ConversionData>(element) {
|
||||
override fun process(data: ConversionData, elementsToShorten: MutableList<KtElement>) {
|
||||
val expression = element ?: return
|
||||
val context = expression.analyze(BodyResolveMode.PARTIAL)
|
||||
@@ -185,7 +177,7 @@ class ConvertFunctionTypeParameterToReceiverIntention : SelfTargetingRangeIntent
|
||||
|
||||
val conflicts = MultiMap<PsiElement, String>()
|
||||
|
||||
val usages = ArrayList<AbstractUsageInfo<*>>()
|
||||
val usages = ArrayList<AbstractProcessableUsageInfo<*, ConversionData>>()
|
||||
|
||||
project.runSynchronouslyWithProgress("Looking for usages and conflicts...", true) {
|
||||
runReadAction {
|
||||
@@ -233,7 +225,11 @@ class ConvertFunctionTypeParameterToReceiverIntention : SelfTargetingRangeIntent
|
||||
}
|
||||
}
|
||||
|
||||
private fun processExternalUsage(conflicts: MultiMap<PsiElement, String>, refElement: PsiElement, usages: java.util.ArrayList<AbstractUsageInfo<*>>) {
|
||||
private fun processExternalUsage(
|
||||
conflicts: MultiMap<PsiElement, String>,
|
||||
refElement: PsiElement,
|
||||
usages: ArrayList<AbstractProcessableUsageInfo<*, ConversionData>>
|
||||
) {
|
||||
val callElement = refElement.getParentOfTypeAndBranch<KtCallElement> { calleeExpression }
|
||||
if (callElement != null) {
|
||||
val context = callElement.analyze(BodyResolveMode.PARTIAL)
|
||||
@@ -294,7 +290,7 @@ class ConvertFunctionTypeParameterToReceiverIntention : SelfTargetingRangeIntent
|
||||
return true
|
||||
}
|
||||
|
||||
private fun processInternalUsages(callable: KtFunction, usages: ArrayList<AbstractUsageInfo<*>>) {
|
||||
private fun processInternalUsages(callable: KtFunction, usages: ArrayList<AbstractProcessableUsageInfo<*, ConversionData>>) {
|
||||
val body = when (callable) {
|
||||
is KtConstructor<*> -> callable.containingClassOrObject?.getBody()
|
||||
else -> callable.bodyExpression
|
||||
|
||||
+224
@@ -0,0 +1,224 @@
|
||||
/*
|
||||
* 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.psi.PsiElement
|
||||
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.util.containers.MultiMap
|
||||
import org.jetbrains.kotlin.asJava.toLightMethods
|
||||
import org.jetbrains.kotlin.builtins.getReceiverTypeFromFunctionType
|
||||
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.codeInsight.DescriptorToSourceUtilsIde
|
||||
import org.jetbrains.kotlin.idea.core.*
|
||||
import org.jetbrains.kotlin.idea.refactoring.CallableRefactoring
|
||||
import org.jetbrains.kotlin.idea.refactoring.checkConflictsInteractively
|
||||
import org.jetbrains.kotlin.idea.refactoring.getAffectedCallables
|
||||
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.*
|
||||
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 ConvertFunctionTypeReceiverToParameterIntention : SelfTargetingRangeIntention<KtTypeReference>(
|
||||
KtTypeReference::class.java,
|
||||
"Convert function type receiver to parameter"
|
||||
) {
|
||||
class ConversionData(
|
||||
val functionParameterIndex: Int,
|
||||
val lambdaReceiverType: KotlinType,
|
||||
val function: KtFunction
|
||||
) {
|
||||
val functionDescriptor by lazy { function.resolveToDescriptor() as FunctionDescriptor }
|
||||
}
|
||||
|
||||
class FunctionDefinitionInfo(element: KtFunction) : AbstractProcessableUsageInfo<KtFunction, ConversionData>(element) {
|
||||
override fun process(data: ConversionData, elementsToShorten: MutableList<KtElement>) {
|
||||
val function = element as? KtFunction ?: return
|
||||
val functionParameter = function.valueParameters.getOrNull(data.functionParameterIndex) ?: return
|
||||
val functionType = functionParameter.typeReference?.typeElement as? KtFunctionType ?: return
|
||||
val functionTypeParameterList = functionType.parameterList ?: return
|
||||
val functionTypeReceiver = functionType.receiverTypeReference ?: return
|
||||
val parameterToAdd = KtPsiFactory(project).createFunctionTypeParameter(functionTypeReceiver)
|
||||
functionTypeParameterList.addParameterBefore(parameterToAdd, functionTypeParameterList.parameters.firstOrNull())
|
||||
functionType.setReceiverTypeReference(null)
|
||||
}
|
||||
}
|
||||
|
||||
class ParameterCallInfo(element: KtCallExpression) : AbstractProcessableUsageInfo<KtCallExpression, ConversionData>(element) {
|
||||
override fun process(data: ConversionData, elementsToShorten: MutableList<KtElement>) {
|
||||
val callExpression = element ?: return
|
||||
val qualifiedExpression = callExpression.getQualifiedExpressionForSelector() ?: return
|
||||
val receiverExpression = qualifiedExpression.receiverExpression
|
||||
val argumentList = callExpression.getOrCreateValueArgumentList()
|
||||
argumentList.addArgumentBefore(KtPsiFactory(project).createArgument(receiverExpression), argumentList.arguments.firstOrNull())
|
||||
qualifiedExpression.replace(callExpression)
|
||||
}
|
||||
}
|
||||
|
||||
class LambdaInfo(element: KtLambdaExpression) : AbstractProcessableUsageInfo<KtLambdaExpression, ConversionData>(element) {
|
||||
override fun process(data: ConversionData, elementsToShorten: MutableList<KtElement>) {
|
||||
val lambda = element?.functionLiteral ?: return
|
||||
val context = lambda.analyze(BodyResolveMode.PARTIAL)
|
||||
|
||||
val psiFactory = KtPsiFactory(project)
|
||||
|
||||
val validator = CollectingNameValidator(
|
||||
lambda.valueParameters.mapNotNull { it.name },
|
||||
NewDeclarationNameValidator(lambda.bodyExpression!!, null, NewDeclarationNameValidator.Target.VARIABLES)
|
||||
)
|
||||
val newParameterName = KotlinNameSuggester.suggestNamesByType(data.lambdaReceiverType, validator, "p").first()
|
||||
val newParameterRefExpression = psiFactory.createExpression(newParameterName)
|
||||
|
||||
lambda.forEachDescendantOfType<KtThisExpression> {
|
||||
val thisTarget = context[BindingContext.REFERENCE_TARGET, it.instanceReference] ?: return@forEachDescendantOfType
|
||||
if (DescriptorToSourceUtilsIde.getAnyDeclaration(project, thisTarget) == lambda) {
|
||||
it.replace(newParameterRefExpression)
|
||||
}
|
||||
}
|
||||
|
||||
val lambdaParameterList = lambda.getOrCreateParameterList()
|
||||
val parameterToAdd = psiFactory.createLambdaParameterList(newParameterName).parameters.first()
|
||||
lambdaParameterList.addParameterBefore(parameterToAdd, lambdaParameterList.parameters.firstOrNull())
|
||||
}
|
||||
}
|
||||
|
||||
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<AbstractProcessableUsageInfo<*, ConversionData>>()
|
||||
|
||||
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 KtFunction) 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) }
|
||||
for (ref in references) {
|
||||
if (ref !is KtSimpleReference<*>) continue
|
||||
processExternalUsage(ref, usages)
|
||||
}
|
||||
|
||||
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(ref: KtSimpleReference<*>, usages: java.util.ArrayList<AbstractProcessableUsageInfo<*, ConversionData>>) {
|
||||
val callElement = ref.element.getParentOfTypeAndBranch<KtCallElement> { calleeExpression } ?: return
|
||||
val context = callElement.analyze(BodyResolveMode.PARTIAL)
|
||||
val expressionToProcess = callElement
|
||||
.getArgumentByParameterIndex(data.functionParameterIndex, context)
|
||||
.singleOrNull()
|
||||
?.getArgumentExpression()
|
||||
?.let { KtPsiUtil.safeDeparenthesize(it) }
|
||||
?: return
|
||||
if (expressionToProcess is KtLambdaExpression) {
|
||||
usages += LambdaInfo(expressionToProcess)
|
||||
}
|
||||
}
|
||||
|
||||
private fun processInternalUsages(callable: KtFunction, usages: java.util.ArrayList<AbstractProcessableUsageInfo<*, ConversionData>>) {
|
||||
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 } ?: continue
|
||||
usages += ParameterCallInfo(callExpression)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun KtTypeReference.getConversionData(): ConversionData? {
|
||||
val functionTypeReceiver = parent as? KtFunctionTypeReceiver ?: return null
|
||||
val functionType = functionTypeReceiver.parent as? KtFunctionType ?: return null
|
||||
val lambdaReceiverType = functionType
|
||||
.getAbbreviatedTypeOrType(functionType.analyze(BodyResolveMode.PARTIAL))
|
||||
?.getReceiverTypeFromFunctionType()
|
||||
?: return null
|
||||
val containingParameter = (functionType.parent as? KtTypeReference)?.parent as? KtParameter ?: return null
|
||||
val ownerFunction = containingParameter.ownerFunction ?: return null
|
||||
val functionParameterIndex = ownerFunction.valueParameters.indexOf(containingParameter)
|
||||
return ConversionData(functionParameterIndex, lambdaReceiverType, 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 {
|
||||
parameterList!!.addParameterBefore(
|
||||
KtPsiFactory(element).createFunctionTypeParameter(element),
|
||||
parameterList!!.parameters.firstOrNull()
|
||||
)
|
||||
setReceiverTypeReference(null)
|
||||
}
|
||||
text = "Convert '${elementBefore.text}' to '${elementAfter.text}'"
|
||||
|
||||
return element.textRange
|
||||
}
|
||||
|
||||
override fun applyTo(element: KtTypeReference, editor: Editor?) {
|
||||
element.getConversionData()?.let { Converter(it).run() }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
org.jetbrains.kotlin.idea.intentions.ConvertFunctionTypeReceiverToParameterIntention
|
||||
@@ -0,0 +1,22 @@
|
||||
fun foo(f: <caret>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" }
|
||||
}
|
||||
+22
@@ -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" }
|
||||
}
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
// IS_APPLICABLE: false
|
||||
fun foo(f: Int.(Boolean) -> String): Int.(<caret>Boolean) -> String = f
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
// IS_APPLICABLE: false
|
||||
fun foo(f: Int.(<caret>Boolean) -> String) {
|
||||
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
fun foo(f: <caret>Int.(() -> Unit) -> String) {
|
||||
1.f {}
|
||||
}
|
||||
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
fun foo(f: <caret>(Int, () -> Unit) -> String) {
|
||||
f(1) {}
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
fun foo(f: <caret>Int.() -> String) {
|
||||
1.f()
|
||||
bar(f)
|
||||
}
|
||||
|
||||
fun bar(f: (Int) -> String) {
|
||||
|
||||
}
|
||||
|
||||
fun lambda(): (Int) -> String = { i -> "$i"}
|
||||
|
||||
fun baz(f: (Int) -> String) {
|
||||
fun g(i: Int) = ""
|
||||
|
||||
foo(f)
|
||||
|
||||
foo(::g)
|
||||
|
||||
foo(lambda())
|
||||
|
||||
foo { "${this + 1}" }
|
||||
}
|
||||
Vendored
+22
@@ -0,0 +1,22 @@
|
||||
fun foo(f: <caret>(Int) -> String) {
|
||||
f(1)
|
||||
bar(f)
|
||||
}
|
||||
|
||||
fun bar(f: (Int) -> String) {
|
||||
|
||||
}
|
||||
|
||||
fun lambda(): (Int) -> String = { i -> "$i"}
|
||||
|
||||
fun baz(f: (Int) -> String) {
|
||||
fun g(i: Int) = ""
|
||||
|
||||
foo(f)
|
||||
|
||||
foo(::g)
|
||||
|
||||
foo(lambda())
|
||||
|
||||
foo { i -> "${i + 1}" }
|
||||
}
|
||||
+41
@@ -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({ 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" })
|
||||
}
|
||||
Vendored
+41
@@ -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" })
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
open class Foo {
|
||||
constructor(f: <caret>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" })
|
||||
}
|
||||
Vendored
+41
@@ -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" })
|
||||
}
|
||||
@@ -4101,6 +4101,57 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/intentions/convertFunctionTypeReceiverToParameter")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class ConvertFunctionTypeReceiverToParameter extends AbstractIntentionTest {
|
||||
public void testAllFilesPresentInConvertFunctionTypeReceiverToParameter() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/intentions/convertFunctionTypeReceiverToParameter"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), TargetBackend.ANY, true);
|
||||
}
|
||||
|
||||
@TestMetadata("function.kt")
|
||||
public void testFunction() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertFunctionTypeReceiverToParameter/function.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("notInFunctionParameter.kt")
|
||||
public void testNotInFunctionParameter() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertFunctionTypeReceiverToParameter/notInFunctionParameter.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("notOnReceiver.kt")
|
||||
public void testNotOnReceiver() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertFunctionTypeReceiverToParameter/notOnReceiver.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("onlyLambdaArgument.kt")
|
||||
public void testOnlyLambdaArgument() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertFunctionTypeReceiverToParameter/onlyLambdaArgument.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("parameterlessFunction.kt")
|
||||
public void testParameterlessFunction() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertFunctionTypeReceiverToParameter/parameterlessFunction.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("primaryConstructor.kt")
|
||||
public void testPrimaryConstructor() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertFunctionTypeReceiverToParameter/primaryConstructor.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("secondaryConstructor.kt")
|
||||
public void testSecondaryConstructor() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertFunctionTypeReceiverToParameter/secondaryConstructor.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/intentions/convertIfWithThrowToAssert")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
Reference in New Issue
Block a user