If necessary, add parameter types while inlining lambda
This commit is contained in:
@@ -19,21 +19,29 @@ package org.jetbrains.kotlin.idea.codeInliner
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
|
||||
import org.jetbrains.kotlin.diagnostics.Errors
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
|
||||
import org.jetbrains.kotlin.idea.core.asExpression
|
||||
import org.jetbrains.kotlin.idea.imports.importableFqName
|
||||
import org.jetbrains.kotlin.idea.intentions.InsertExplicitTypeArgumentsIntention
|
||||
import org.jetbrains.kotlin.idea.intentions.SpecifyExplicitLambdaSignatureIntention
|
||||
import org.jetbrains.kotlin.idea.references.canBeResolvedViaImport
|
||||
import org.jetbrains.kotlin.idea.references.mainReference
|
||||
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
|
||||
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
|
||||
import org.jetbrains.kotlin.idea.util.getResolutionScope
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getReceiverExpression
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.calls.model.isReallySuccess
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension
|
||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitReceiver
|
||||
import org.jetbrains.kotlin.types.ErrorUtils
|
||||
import org.jetbrains.kotlin.utils.sure
|
||||
import java.util.*
|
||||
|
||||
class CodeToInlineBuilder(
|
||||
@@ -57,9 +65,58 @@ class CodeToInlineBuilder(
|
||||
|
||||
insertExplicitReceivers(codeToInline, bindingContext)
|
||||
|
||||
if (mainExpression != null) {
|
||||
val functionLiteralExpression = mainExpression.unpackFunctionLiteral(true)
|
||||
if (functionLiteralExpression != null) {
|
||||
val functionLiteralParameterTypes = getParametersForFunctionLiteral(functionLiteralExpression, bindingContext)
|
||||
if (functionLiteralParameterTypes != null) {
|
||||
codeToInline.addPostInsertionAction(mainExpression) { inlinedExpression ->
|
||||
addFunctionLiteralParameterTypes(functionLiteralParameterTypes, inlinedExpression)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return codeToInline.toNonMutable()
|
||||
}
|
||||
|
||||
private fun getParametersForFunctionLiteral(functionLiteralExpression: KtLambdaExpression, context: BindingContext): String? {
|
||||
val lambdaDescriptor = context.get(BindingContext.FUNCTION, functionLiteralExpression.functionLiteral)
|
||||
if (lambdaDescriptor == null || ErrorUtils.containsErrorType(lambdaDescriptor)) return null
|
||||
return lambdaDescriptor.valueParameters.joinToString {
|
||||
it.name.asString() + ": " + IdeDescriptorRenderers.SOURCE_CODE.renderType(it.type)
|
||||
}
|
||||
}
|
||||
|
||||
private fun addFunctionLiteralParameterTypes(parameters: String, inlinedExpression: KtExpression) {
|
||||
val containingFile = inlinedExpression.containingKtFile
|
||||
val resolutionFacade = containingFile.getResolutionFacade()
|
||||
|
||||
val lambdaExpr = inlinedExpression.unpackFunctionLiteral(true).sure {
|
||||
"can't find function literal expression for " + inlinedExpression.text
|
||||
}
|
||||
if (!needToAddParameterTypes(lambdaExpr, resolutionFacade)) return
|
||||
SpecifyExplicitLambdaSignatureIntention.applyWithParameters(lambdaExpr, parameters)
|
||||
}
|
||||
|
||||
private fun needToAddParameterTypes(
|
||||
lambdaExpression: KtLambdaExpression,
|
||||
resolutionFacade: ResolutionFacade
|
||||
): Boolean {
|
||||
val functionLiteral = lambdaExpression.functionLiteral
|
||||
val context = resolutionFacade.analyze(lambdaExpression, BodyResolveMode.PARTIAL_WITH_DIAGNOSTICS)
|
||||
return context.diagnostics.any { diagnostic ->
|
||||
val factory = diagnostic.factory
|
||||
val element = diagnostic.psiElement
|
||||
val hasCantInferParameter = factory == Errors.CANNOT_INFER_PARAMETER_TYPE &&
|
||||
element.parent.parent == functionLiteral
|
||||
val hasUnresolvedItOrThis = factory == Errors.UNRESOLVED_REFERENCE &&
|
||||
element.text == "it" &&
|
||||
element.getStrictParentOfType<KtFunctionLiteral>() == functionLiteral
|
||||
hasCantInferParameter || hasUnresolvedItOrThis
|
||||
}
|
||||
}
|
||||
|
||||
private fun insertExplicitTypeArguments(codeToInline: MutableCodeToInline, bindingContext: BindingContext, analyze: () -> BindingContext): BindingContext {
|
||||
val typeArgsToAdd = ArrayList<Pair<KtCallExpression, KtTypeArgumentList>>()
|
||||
codeToInline.forEachDescendantOfType<KtCallExpression> {
|
||||
|
||||
+22
-17
@@ -19,7 +19,6 @@ package org.jetbrains.kotlin.idea.intentions
|
||||
import com.intellij.codeInsight.intention.LowPriorityAction
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.psi.PsiWhiteSpace
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||
import org.jetbrains.kotlin.idea.core.ShortenReferences
|
||||
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
|
||||
@@ -46,30 +45,36 @@ class SpecifyExplicitLambdaSignatureIntention : SelfTargetingIntention<KtLambdaE
|
||||
}
|
||||
|
||||
override fun applyTo(element: KtLambdaExpression, editor: Editor?) {
|
||||
val psiFactory = KtPsiFactory(element)
|
||||
val functionLiteral = element.functionLiteral
|
||||
val functionDescriptor = element.analyze(BodyResolveMode.PARTIAL)[BindingContext.FUNCTION, functionLiteral]!!
|
||||
|
||||
val parameterString = functionDescriptor.valueParameters
|
||||
.map { "${it.name}: ${IdeDescriptorRenderers.SOURCE_CODE.renderType(it.type)}" }
|
||||
.joinToString(", ")
|
||||
applyWithParameters(element, parameterString)
|
||||
}
|
||||
|
||||
val newParameterList = psiFactory.createLambdaParameterListIfAny(parameterString)
|
||||
val oldParameterList = functionLiteral.valueParameterList
|
||||
if (oldParameterList != null && newParameterList != null) {
|
||||
oldParameterList.replace(newParameterList)
|
||||
}
|
||||
else {
|
||||
val openBraceElement = functionLiteral.lBrace
|
||||
val nextSibling = openBraceElement.nextSibling
|
||||
val addNewline = nextSibling is PsiWhiteSpace && nextSibling.text?.contains("\n") ?: false
|
||||
val (whitespace, arrow) = psiFactory.createWhitespaceAndArrow()
|
||||
functionLiteral.addRangeAfter(whitespace, arrow, openBraceElement)
|
||||
newParameterList?.let { functionLiteral.addAfter(it, openBraceElement) }
|
||||
if (addNewline) {
|
||||
functionLiteral.addAfter(psiFactory.createNewLine(), openBraceElement)
|
||||
companion object {
|
||||
fun applyWithParameters(element: KtLambdaExpression, parameterString: String) {
|
||||
val psiFactory = KtPsiFactory(element)
|
||||
val functionLiteral = element.functionLiteral
|
||||
val newParameterList = psiFactory.createLambdaParameterListIfAny(parameterString)
|
||||
val oldParameterList = functionLiteral.valueParameterList
|
||||
if (oldParameterList != null && newParameterList != null) {
|
||||
oldParameterList.replace(newParameterList)
|
||||
}
|
||||
else {
|
||||
val openBraceElement = functionLiteral.lBrace
|
||||
val nextSibling = openBraceElement.nextSibling
|
||||
val addNewline = nextSibling is PsiWhiteSpace && nextSibling.text?.contains("\n") ?: false
|
||||
val (whitespace, arrow) = psiFactory.createWhitespaceAndArrow()
|
||||
functionLiteral.addRangeAfter(whitespace, arrow, openBraceElement)
|
||||
newParameterList?.let { functionLiteral.addAfter(it, openBraceElement) }
|
||||
if (addNewline) {
|
||||
functionLiteral.addAfter(psiFactory.createNewLine(), openBraceElement)
|
||||
}
|
||||
}
|
||||
ShortenReferences.DEFAULT.process(element.valueParameters)
|
||||
}
|
||||
ShortenReferences.DEFAULT.process(element.valueParameters)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
fun foo() {
|
||||
fun bar(): (Int) -> Int = { it }
|
||||
val b = <caret>bar()
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
fun foo() {
|
||||
val b = { it: Int -> it }
|
||||
}
|
||||
@@ -136,6 +136,12 @@ public class InlineTestGenerated extends AbstractInlineTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("FunctionalType.kt")
|
||||
public void testFunctionalType() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/inline/function/expressionBody/FunctionalType.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("MultipleInComposition.kt")
|
||||
public void testMultipleInComposition() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/inline/function/expressionBody/MultipleInComposition.kt");
|
||||
|
||||
Reference in New Issue
Block a user