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.CallableDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
|
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
|
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.core.asExpression
|
||||||
import org.jetbrains.kotlin.idea.imports.importableFqName
|
import org.jetbrains.kotlin.idea.imports.importableFqName
|
||||||
import org.jetbrains.kotlin.idea.intentions.InsertExplicitTypeArgumentsIntention
|
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.canBeResolvedViaImport
|
||||||
import org.jetbrains.kotlin.idea.references.mainReference
|
import org.jetbrains.kotlin.idea.references.mainReference
|
||||||
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
|
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.idea.util.getResolutionScope
|
||||||
import org.jetbrains.kotlin.name.FqName
|
import org.jetbrains.kotlin.name.FqName
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.getReceiverExpression
|
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.BindingContext
|
||||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
||||||
import org.jetbrains.kotlin.resolve.calls.model.isReallySuccess
|
import org.jetbrains.kotlin.resolve.calls.model.isReallySuccess
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension
|
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.resolve.scopes.receivers.ImplicitReceiver
|
||||||
|
import org.jetbrains.kotlin.types.ErrorUtils
|
||||||
|
import org.jetbrains.kotlin.utils.sure
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
class CodeToInlineBuilder(
|
class CodeToInlineBuilder(
|
||||||
@@ -57,9 +65,58 @@ class CodeToInlineBuilder(
|
|||||||
|
|
||||||
insertExplicitReceivers(codeToInline, bindingContext)
|
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()
|
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 {
|
private fun insertExplicitTypeArguments(codeToInline: MutableCodeToInline, bindingContext: BindingContext, analyze: () -> BindingContext): BindingContext {
|
||||||
val typeArgsToAdd = ArrayList<Pair<KtCallExpression, KtTypeArgumentList>>()
|
val typeArgsToAdd = ArrayList<Pair<KtCallExpression, KtTypeArgumentList>>()
|
||||||
codeToInline.forEachDescendantOfType<KtCallExpression> {
|
codeToInline.forEachDescendantOfType<KtCallExpression> {
|
||||||
|
|||||||
+22
-17
@@ -19,7 +19,6 @@ package org.jetbrains.kotlin.idea.intentions
|
|||||||
import com.intellij.codeInsight.intention.LowPriorityAction
|
import com.intellij.codeInsight.intention.LowPriorityAction
|
||||||
import com.intellij.openapi.editor.Editor
|
import com.intellij.openapi.editor.Editor
|
||||||
import com.intellij.psi.PsiWhiteSpace
|
import com.intellij.psi.PsiWhiteSpace
|
||||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||||
import org.jetbrains.kotlin.idea.core.ShortenReferences
|
import org.jetbrains.kotlin.idea.core.ShortenReferences
|
||||||
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
|
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
|
||||||
@@ -46,30 +45,36 @@ class SpecifyExplicitLambdaSignatureIntention : SelfTargetingIntention<KtLambdaE
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun applyTo(element: KtLambdaExpression, editor: Editor?) {
|
override fun applyTo(element: KtLambdaExpression, editor: Editor?) {
|
||||||
val psiFactory = KtPsiFactory(element)
|
|
||||||
val functionLiteral = element.functionLiteral
|
val functionLiteral = element.functionLiteral
|
||||||
val functionDescriptor = element.analyze(BodyResolveMode.PARTIAL)[BindingContext.FUNCTION, functionLiteral]!!
|
val functionDescriptor = element.analyze(BodyResolveMode.PARTIAL)[BindingContext.FUNCTION, functionLiteral]!!
|
||||||
|
|
||||||
val parameterString = functionDescriptor.valueParameters
|
val parameterString = functionDescriptor.valueParameters
|
||||||
.map { "${it.name}: ${IdeDescriptorRenderers.SOURCE_CODE.renderType(it.type)}" }
|
.map { "${it.name}: ${IdeDescriptorRenderers.SOURCE_CODE.renderType(it.type)}" }
|
||||||
.joinToString(", ")
|
.joinToString(", ")
|
||||||
|
applyWithParameters(element, parameterString)
|
||||||
|
}
|
||||||
|
|
||||||
val newParameterList = psiFactory.createLambdaParameterListIfAny(parameterString)
|
companion object {
|
||||||
val oldParameterList = functionLiteral.valueParameterList
|
fun applyWithParameters(element: KtLambdaExpression, parameterString: String) {
|
||||||
if (oldParameterList != null && newParameterList != null) {
|
val psiFactory = KtPsiFactory(element)
|
||||||
oldParameterList.replace(newParameterList)
|
val functionLiteral = element.functionLiteral
|
||||||
}
|
val newParameterList = psiFactory.createLambdaParameterListIfAny(parameterString)
|
||||||
else {
|
val oldParameterList = functionLiteral.valueParameterList
|
||||||
val openBraceElement = functionLiteral.lBrace
|
if (oldParameterList != null && newParameterList != null) {
|
||||||
val nextSibling = openBraceElement.nextSibling
|
oldParameterList.replace(newParameterList)
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
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);
|
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")
|
@TestMetadata("MultipleInComposition.kt")
|
||||||
public void testMultipleInComposition() throws Exception {
|
public void testMultipleInComposition() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/inline/function/expressionBody/MultipleInComposition.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/inline/function/expressionBody/MultipleInComposition.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user