Intentions: Fix insertion of necessary lambda parameter for run/let and apply/also conversions

#KT-22931 Fixed
This commit is contained in:
Alexey Sedunov
2018-05-29 20:59:21 +03:00
parent f17053f970
commit fbb4914bfb
6 changed files with 63 additions and 15 deletions
@@ -114,28 +114,24 @@ class Replacement<T : PsiElement> private constructor(
get() = elementPointer.element!!.endOffset get() = elementPointer.element!!.endOffset
} }
class ReplacementCollection { class ReplacementCollection(private val project: Project) {
private lateinit var project: Project
private val replacements = mutableListOf<Replacement<out PsiElement>>() private val replacements = mutableListOf<Replacement<out PsiElement>>()
var createParameter: KtPsiFactory.() -> PsiElement? = { null } var createParameter: KtPsiFactory.() -> PsiElement? = { null }
var elementToRename: PsiElement? = null var elementToRename: PsiElement? = null
fun <T : PsiElement> add(element: T, replacementFactory: KtPsiFactory.(T) -> PsiElement) { fun <T : PsiElement> add(element: T, replacementFactory: KtPsiFactory.(T) -> PsiElement) {
project = element.project
replacements.add(Replacement.create(element, replacementFactory)) replacements.add(Replacement.create(element, replacementFactory))
} }
fun apply() { fun apply() {
if (replacements.isNotEmpty()) { val factory = KtPsiFactory(project)
val factory = KtPsiFactory(project) elementToRename = factory.createParameter()
elementToRename = factory.createParameter()
// Calls need to be processed in outside-in order // Calls need to be processed in outside-in order
replacements.sortBy { it.endOffset } replacements.sortBy { it.endOffset }
for (replacement in replacements) { for (replacement in replacements) {
replacement.apply(factory) replacement.apply(factory)
}
} }
} }
@@ -157,12 +153,10 @@ abstract class ConvertScopeFunctionFix(private val counterpartName: String) : Lo
functionLiteral.valueParameterList?.delete() functionLiteral.valueParameterList?.delete()
functionLiteral.arrow?.delete() functionLiteral.arrow?.delete()
val replacements = ReplacementCollection() val replacements = ReplacementCollection(project)
analyzeLambda(bindingContext, lambda, lambdaDescriptor, replacements) analyzeLambda(bindingContext, lambda, lambdaDescriptor, replacements)
callee.replace(KtPsiFactory(project).createExpression(counterpartName) as KtNameReferenceExpression) callee.replace(KtPsiFactory(project).createExpression(counterpartName) as KtNameReferenceExpression)
if (replacements.isNotEmpty()) { replacements.apply()
replacements.apply()
}
postprocessLambda(lambda) postprocessLambda(lambda)
if (replacements.isNotEmpty() && replacements.elementToRename != null && !ApplicationManager.getApplication().isUnitTestMode) { if (replacements.isNotEmpty() && replacements.elementToRename != null && !ApplicationManager.getApplication().isUnitTestMode) {
@@ -0,0 +1,11 @@
// WITH_RUNTIME
// FIX: Convert to 'also'
class Employee(val firstName: String, val manager: Employee?)
fun test(employee: Employee) {
val person = employee.also {
it.manager?.<caret>apply {
println("${it.firstName} has a manager")
}
}
}
@@ -0,0 +1,11 @@
// WITH_RUNTIME
// FIX: Convert to 'also'
class Employee(val firstName: String, val manager: Employee?)
fun test(employee: Employee) {
val person = employee.also {
it.manager?.<caret>also { employee1 ->
println("${it.firstName} has a manager")
}
}
}
@@ -0,0 +1,11 @@
// WITH_RUNTIME
// FIX: Convert to 'let'
class Employee(val firstName: String, val manager: Employee?)
fun test(employee: Employee) {
val person = employee.also {
it.manager?.<caret>run {
println("${it.firstName} has a manager")
}
}
}
@@ -0,0 +1,11 @@
// WITH_RUNTIME
// FIX: Convert to 'let'
class Employee(val firstName: String, val manager: Employee?)
fun test(employee: Employee) {
val person = employee.also {
it.manager?.<caret>let { employee1 ->
println("${it.firstName} has a manager")
}
}
}
@@ -4040,6 +4040,11 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest {
runTest("idea/testData/inspectionsLocal/scopeFunctions/applyToAlso/arrow.kt"); runTest("idea/testData/inspectionsLocal/scopeFunctions/applyToAlso/arrow.kt");
} }
@TestMetadata("capturedIt.kt")
public void testCapturedIt() throws Exception {
runTest("idea/testData/inspectionsLocal/scopeFunctions/applyToAlso/capturedIt.kt");
}
@TestMetadata("doubleNestedLambdas.kt") @TestMetadata("doubleNestedLambdas.kt")
public void testDoubleNestedLambdas() throws Exception { public void testDoubleNestedLambdas() throws Exception {
runTest("idea/testData/inspectionsLocal/scopeFunctions/applyToAlso/doubleNestedLambdas.kt"); runTest("idea/testData/inspectionsLocal/scopeFunctions/applyToAlso/doubleNestedLambdas.kt");
@@ -4146,6 +4151,11 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/inspectionsLocal/scopeFunctions/runToLet"), Pattern.compile("^([\\w\\-_]+)\\.(kt|kts)$"), TargetBackend.ANY, true); KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/inspectionsLocal/scopeFunctions/runToLet"), Pattern.compile("^([\\w\\-_]+)\\.(kt|kts)$"), TargetBackend.ANY, true);
} }
@TestMetadata("capturedIt.kt")
public void testCapturedIt() throws Exception {
runTest("idea/testData/inspectionsLocal/scopeFunctions/runToLet/capturedIt.kt");
}
@TestMetadata("simple.kt") @TestMetadata("simple.kt")
public void testSimple() throws Exception { public void testSimple() throws Exception {
runTest("idea/testData/inspectionsLocal/scopeFunctions/runToLet/simple.kt"); runTest("idea/testData/inspectionsLocal/scopeFunctions/runToLet/simple.kt");