Intentions: Convert function type parameter to receiver
#KT-14246 In Progress
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
@@ -75,9 +75,14 @@ public class KtFunctionType extends KtElementImplStub<KotlinPlaceHolderStub<KtFu
|
||||
return list != null ? list.getParameters() : Collections.<KtParameter>emptyList();
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
|
||||
@@ -64,6 +64,10 @@ public class KtParameterList extends KtElementImplStub<KotlinPlaceHolderStub<KtP
|
||||
EditCommaSeparatedListHelper.INSTANCE.removeItem(parameter);
|
||||
}
|
||||
|
||||
public void removeParameter(int index) {
|
||||
removeParameter(getParameters().get(index));
|
||||
}
|
||||
|
||||
public KtFunction getOwnerFunction() {
|
||||
PsiElement parent = getParentByStub();
|
||||
if (!(parent instanceof KtFunction)) return null;
|
||||
|
||||
@@ -108,6 +108,10 @@ class KtPsiFactory(private val project: Project) {
|
||||
return if (typeReference?.text == type) typeReference else null
|
||||
}
|
||||
|
||||
fun createFunctionTypeReceiver(typeReference: KtTypeReference): KtFunctionTypeReceiver {
|
||||
return (createType("A.() -> B").typeElement as KtFunctionType).receiver!!.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)) }
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 : KtElement> 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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
)
|
||||
@@ -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<ValueArgument> {
|
||||
val resolvedCall = getResolvedCall(context) ?: return emptyList()
|
||||
val parameterToProcess = resolvedCall.resultingDescriptor.valueParameters.getOrNull(index) ?: return emptyList()
|
||||
return resolvedCall.valueArguments[parameterToProcess]?.arguments ?: emptyList()
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
fun foo(f: Int.(s: String) -> Boolean) = 1.f("2")
|
||||
|
||||
fun bar = foo { s -> s.length() > this }
|
||||
+3
@@ -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 }
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
<html>
|
||||
<body>
|
||||
This intention converts given parameter of a function type used in a function parameter to receiver
|
||||
</body>
|
||||
</html>
|
||||
@@ -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"
|
||||
|
||||
+358
@@ -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 {
|
||||
|
||||
+6
-1
@@ -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
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
// IS_APPLICABLE: false
|
||||
fun foo(f: Int.(<caret>Boolean) -> String) {
|
||||
|
||||
}
|
||||
+10
@@ -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) {
|
||||
|
||||
}
|
||||
+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" }
|
||||
}
|
||||
+22
@@ -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" }
|
||||
}
|
||||
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(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" })
|
||||
}
|
||||
+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" })
|
||||
}
|
||||
+41
@@ -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" })
|
||||
}
|
||||
Vendored
+6
@@ -0,0 +1,6 @@
|
||||
class J extends K {
|
||||
@Override
|
||||
public void foo(@NotNull Function2<? super Integer, ? super Boolean, String> f) {
|
||||
super.foo(f);
|
||||
}
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
class J extends K {
|
||||
@Override
|
||||
public void foo(@NotNull Function2<? super Integer, ? super Boolean, String> f) {
|
||||
super.foo(f);
|
||||
}
|
||||
}
|
||||
Vendored
+5
@@ -0,0 +1,5 @@
|
||||
open class K {
|
||||
open fun foo(f: (<caret>Int, Boolean) -> String) {
|
||||
|
||||
}
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
open class K {
|
||||
open fun foo(f: Int.(<caret>Boolean) -> String) {
|
||||
|
||||
}
|
||||
}
|
||||
+22
@@ -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" }
|
||||
}
|
||||
+23
@@ -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}" }
|
||||
}
|
||||
+8
@@ -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
|
||||
}
|
||||
+39
@@ -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" })
|
||||
}
|
||||
+43
@@ -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}" })
|
||||
}
|
||||
+39
@@ -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" })
|
||||
}
|
||||
+43
@@ -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}" })
|
||||
}
|
||||
+8
@@ -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())
|
||||
}
|
||||
+6
@@ -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"}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
class J extends K {
|
||||
@Override
|
||||
public void foo(@NotNull Function2<? super Integer, ? super Boolean, String> f) {
|
||||
super.foo(f);
|
||||
}
|
||||
}
|
||||
Vendored
+6
@@ -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) {
|
||||
|
||||
}
|
||||
}
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
// IS_APPLICABLE: false
|
||||
fun foo(f: (Int, Boolean) -> String): (<caret>Int, Boolean) -> String = f
|
||||
Vendored
+4
@@ -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) {
|
||||
|
||||
}
|
||||
}
|
||||
+11
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user