Inliner: specify explicit lambda signature for calls with lambdas

So #KT-17213 Fixed
So #KT-17395 Fixed
This commit is contained in:
Mikhail Glukhikh
2017-04-04 15:38:14 +03:00
parent 02e9a3d027
commit f8e1f5e613
10 changed files with 102 additions and 6 deletions
@@ -27,9 +27,12 @@ import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.search.searches.ReferencesSearch
import com.intellij.ui.GuiUtils
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
import org.jetbrains.kotlin.idea.core.targetDescriptors
import org.jetbrains.kotlin.idea.intentions.ConvertReferenceToLambdaIntention
import org.jetbrains.kotlin.idea.intentions.SpecifyExplicitLambdaSignatureIntention
import org.jetbrains.kotlin.idea.references.KtSimpleNameReference
import org.jetbrains.kotlin.idea.search.usagesSearch.descriptor
import org.jetbrains.kotlin.idea.stubindex.KotlinSourceFilterScope
@@ -122,7 +125,7 @@ fun UsageReplacementStrategy.replaceUsages(
val targetDeclaration = targetPsiElement as? KtNamedDeclaration
val usagesChildrenFirst = usages.sortChildrenFirst()
for (usage in usagesChildrenFirst) {
usages@ for (usage in usagesChildrenFirst) {
try {
if (!usage.isValid) {
invalidUsagesFound = true
@@ -130,11 +133,35 @@ fun UsageReplacementStrategy.replaceUsages(
}
val usageParent = usage.parent
if (usageParent is KtCallableReferenceExpression) {
val grandParent = usageParent.parent
ConvertReferenceToLambdaIntention().applyTo(usageParent, null)
(grandParent as? KtElement)?.let { doRefactoringInside(it, targetDeclaration?.name, targetDeclaration?.descriptor) }
continue
when (usageParent) {
is KtCallableReferenceExpression -> {
val grandParent = usageParent.parent
ConvertReferenceToLambdaIntention().applyTo(usageParent, null)
(grandParent as? KtElement)?.let {
doRefactoringInside(it, targetDeclaration?.name, targetDeclaration?.descriptor)
}
continue@usages
}
is KtCallElement -> {
val lambdaArguments = usageParent.lambdaArguments
if (lambdaArguments.isNotEmpty()) {
val grandParent = usageParent.parent
val specifySignature = SpecifyExplicitLambdaSignatureIntention()
for (lambdaArgument in lambdaArguments) {
val lambdaExpression = lambdaArgument.getLambdaExpression()
val functionDescriptor =
lambdaExpression.functionLiteral.resolveToDescriptorIfAny() as? FunctionDescriptor ?: continue
if (functionDescriptor.valueParameters.isNotEmpty()) {
specifySignature.applyTo(lambdaExpression, null)
}
}
(grandParent as? KtElement)?.let {
doRefactoringInside(it, targetDeclaration?.name, targetDeclaration?.descriptor)
}
continue@usages
}
}
}
//TODO: keep the import if we don't know how to replace some of the usages
@@ -0,0 +1,7 @@
class Declaration {
fun lambdaType(p: Int, f: (Int) -> Int) = f(p)
}
fun call(declaration: Declaration) {
declaration.<caret>lambdaType(6) { it + 7 }
}
@@ -0,0 +1,6 @@
class Declaration {
}
fun call(declaration: Declaration) {
({ it: Int -> it + 7 })(6)
}
@@ -0,0 +1,6 @@
class Declaration {
fun lambdaType(p: Int, f: (Int) -> Int) = f(p)
}
fun call(declaration: Declaration) {
declaration.<caret>lambdaType(8) { x -> x + 9 }}
@@ -0,0 +1,6 @@
class Declaration {
}
fun call(declaration: Declaration) {
({ x: Int -> x + 9 })(8)
}
@@ -0,0 +1,7 @@
class Declaration {
fun lambdaType(p: Int, f: (Int) -> Int) = f(p)
}
fun call(declaration: Declaration) {
declaration.<caret>lambdaType(10) { _ -> 11 }
}
@@ -0,0 +1,6 @@
class Declaration {
}
fun call(declaration: Declaration) {
({ _: Int -> 11 })(10)
}
@@ -0,0 +1,4 @@
fun <T, U> operateLambda(p: T, f: (T) -> U) = f(p)
fun returnLabel() {
<caret>operateLambda(1) {}
}
@@ -0,0 +1,3 @@
fun returnLabel() {
({ it: Int -> })(1)
}
@@ -142,6 +142,30 @@ public class InlineTestGenerated extends AbstractInlineTest {
doTest(fileName);
}
@TestMetadata("Lambda.kt")
public void testLambda() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/inline/function/expressionBody/Lambda.kt");
doTest(fileName);
}
@TestMetadata("Lambda1.kt")
public void testLambda1() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/inline/function/expressionBody/Lambda1.kt");
doTest(fileName);
}
@TestMetadata("Lambda2.kt")
public void testLambda2() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/inline/function/expressionBody/Lambda2.kt");
doTest(fileName);
}
@TestMetadata("LambdaGeneric.kt")
public void testLambdaGeneric() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/inline/function/expressionBody/LambdaGeneric.kt");
doTest(fileName);
}
@TestMetadata("MultipleInComposition.kt")
public void testMultipleInComposition() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/inline/function/expressionBody/MultipleInComposition.kt");