diff --git a/idea/src/org/jetbrains/kotlin/idea/codeInliner/CodeToInlineBuilder.kt b/idea/src/org/jetbrains/kotlin/idea/codeInliner/CodeToInlineBuilder.kt index 8289c600a88..65774e8c721 100644 --- a/idea/src/org/jetbrains/kotlin/idea/codeInliner/CodeToInlineBuilder.kt +++ b/idea/src/org/jetbrains/kotlin/idea/codeInliner/CodeToInlineBuilder.kt @@ -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() == functionLiteral + hasCantInferParameter || hasUnresolvedItOrThis + } + } + private fun insertExplicitTypeArguments(codeToInline: MutableCodeToInline, bindingContext: BindingContext, analyze: () -> BindingContext): BindingContext { val typeArgsToAdd = ArrayList>() codeToInline.forEachDescendantOfType { diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/SpecifyExplicitLambdaSignatureIntention.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/SpecifyExplicitLambdaSignatureIntention.kt index 801d2c50cbf..a1381579cdb 100644 --- a/idea/src/org/jetbrains/kotlin/idea/intentions/SpecifyExplicitLambdaSignatureIntention.kt +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/SpecifyExplicitLambdaSignatureIntention.kt @@ -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 Int = { it } + val b = bar() +} \ No newline at end of file diff --git a/idea/testData/refactoring/inline/function/expressionBody/FunctionalType.kt.after b/idea/testData/refactoring/inline/function/expressionBody/FunctionalType.kt.after new file mode 100644 index 00000000000..3665ebd6596 --- /dev/null +++ b/idea/testData/refactoring/inline/function/expressionBody/FunctionalType.kt.after @@ -0,0 +1,3 @@ +fun foo() { + val b = { it: Int -> it } +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/refactoring/inline/InlineTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/refactoring/inline/InlineTestGenerated.java index f319d2bf5e9..eb1c174a5ef 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/refactoring/inline/InlineTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/refactoring/inline/InlineTestGenerated.java @@ -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");