"Simplify using destructuring declaration" is now applicable for function literals #KT-13941 Fixed

This commit is contained in:
Mikhail Glukhikh
2016-10-03 12:18:29 +03:00
parent 42aea59253
commit df0cf3da84
19 changed files with 193 additions and 9 deletions
@@ -244,15 +244,16 @@ class KtPsiFactory(private val project: Project) {
return (createFunction("fun foo() {$text}").bodyExpression as KtBlockExpression).statements.first() as KtDestructuringDeclaration
}
fun createDestructuringDeclarationInFor(text: String): KtDestructuringDeclaration {
return ((createFunction("fun foo() {for ($text in foo) {} }").bodyExpression as KtBlockExpression).statements.first() as KtForExpression).destructuringParameter!!
}
fun createDestructuringParameter(text: String): KtDestructuringDeclaration {
val dummyFun = createFunction("fun foo() { for ($text in foo) {} }")
return ((dummyFun.bodyExpression as KtBlockExpression).statements.first() as KtForExpression).destructuringParameter!!
}
fun createDestructuringParameterForLambda(text: String): KtParameter {
val dummyFun = createFunction("fun foo() = { $text -> }")
return (dummyFun.bodyExpression as KtLambdaExpression).functionLiteral.valueParameters.first()
}
fun <TDeclaration : KtDeclaration> createDeclaration(text: String): TDeclaration {
val file = createFile(text)
val declarations = file.declarations
@@ -46,6 +46,8 @@ class DestructureIntention : SelfTargetingRangeIntention<KtParameter>(
) {
override fun applyTo(element: KtParameter, editor: Editor?) {
val forLoop = element.parent as? KtForExpression
val functionLiteral = element.parent?.parent as? KtFunctionLiteral
if (forLoop == null && functionLiteral == null) return
val (usagesToRemove, removeSelectorInLoopRange) = collectUsagesToRemove(element, forLoop) ?: return
val loopRange = forLoop?.loopRange
@@ -62,16 +64,23 @@ class DestructureIntention : SelfTargetingRangeIntention<KtParameter>(
}
names.add(name)
}
element.replace(factory.createDestructuringDeclarationInFor("(${names.joinToString()})"))
if (forLoop != null) {
element.replace(factory.createDestructuringParameter("(${names.joinToString()})"))
if (removeSelectorInLoopRange && loopRange is KtDotQualifiedExpression) {
loopRange.replace(loopRange.receiverExpression)
if (removeSelectorInLoopRange && loopRange is KtDotQualifiedExpression) {
loopRange.replace(loopRange.receiverExpression)
}
}
else if (functionLiteral != null) {
element.replace(factory.createDestructuringParameterForLambda("(${names.joinToString()})"))
}
}
override fun applicabilityRange(element: KtParameter): TextRange? {
val forLoop = element.parent as? KtForExpression ?: return null
val usagesToRemove = collectUsagesToRemove(element, forLoop)
val forLoopIfAny = element.parent as? KtForExpression
if (forLoopIfAny == null && element.parent?.parent !is KtFunctionLiteral) return null
val usagesToRemove = collectUsagesToRemove(element, forLoopIfAny)
if (usagesToRemove != null && usagesToRemove.first.isNotEmpty()) {
return element.textRange
}
@@ -0,0 +1 @@
org.jetbrains.kotlin.idea.intentions.DestructureIntention
@@ -0,0 +1,11 @@
// WITH_RUNTIME
data class XY(val x: Int, val y: Int)
fun test(xys: Array<XY>) {
xys.forEach { xy<caret> ->
val x = xy.x
println(x)
val y = xy.y + x
println(y)
}
}
@@ -0,0 +1,10 @@
// WITH_RUNTIME
data class XY(val x: Int, val y: Int)
fun test(xys: Array<XY>) {
xys.forEach { (x, y) ->
println(x)
val y = y + x
println(y)
}
}
@@ -0,0 +1,11 @@
// WITH_RUNTIME
fun foo() {
val list = listOf(MyClass(1, 2, 3), MyClass(2, 3, 4))
list.forEach { klass<caret> ->
val a = klass.a
val b = klass.b
}
}
data class MyClass(val a: Int, val b: Int, val c: Int)
@@ -0,0 +1,9 @@
// WITH_RUNTIME
fun foo() {
val list = listOf(MyClass(1, 2, 3), MyClass(2, 3, 4))
list.forEach { (a, b) ->
}
}
data class MyClass(val a: Int, val b: Int, val c: Int)
@@ -0,0 +1,5 @@
// WITH_RUNTIME
data class XY(val x: String, val y: String)
fun foo(list: List<XY>) = list.fold("") { prev, <caret>xy -> prev + xy.x + xy.y }
@@ -0,0 +1,5 @@
// WITH_RUNTIME
data class XY(val x: String, val y: String)
fun foo(list: List<XY>) = list.fold("") { prev, (x, y) -> prev + x + y }
@@ -0,0 +1,9 @@
// WITH_RUNTIME
data class My(val first: String, val second: Int)
fun foo(list: List<My>) {
list.forEach { my<caret> ->
println(my.second)
}
}
@@ -0,0 +1,9 @@
// WITH_RUNTIME
data class My(val first: String, val second: Int)
fun foo(list: List<My>) {
list.forEach { (first, second) ->
println(second)
}
}
@@ -0,0 +1,5 @@
// WITH_RUNTIME
data class XY(val x: String, val y: String)
fun foo(list: List<XY>) = list.map { <caret>it -> it.x + it.y }
@@ -0,0 +1,5 @@
// WITH_RUNTIME
data class XY(val x: String, val y: String)
fun foo(list: List<XY>) = list.map { (x, y) -> x + y }
@@ -0,0 +1,11 @@
// IS_APPLICABLE: false
// WITH_RUNTIME
data class XY(val x: String, val y: String)
fun test(xys: Array<XY?>) {
xys.forEach { xy<caret> ->
val x = xy?.x
val y = xy?.y
println(x + y)
}
}
@@ -0,0 +1,5 @@
data class XY(val x: String, val y: String)
fun convert(xy: XY, foo: (XY) -> String) = foo(xy)
fun foo(xy: XY) = convert(xy) { <caret>it -> it.x + it.y }
@@ -0,0 +1,5 @@
data class XY(val x: String, val y: String)
fun convert(xy: XY, foo: (XY) -> String) = foo(xy)
fun foo(xy: XY) = convert(xy) { (x, y) -> x + y }
@@ -0,0 +1,9 @@
data class XY(val x: String, val y: String)
fun convert(xy: XY, foo: (XY) -> String) = foo(xy)
fun foo(xy: XY) = convert(xy) { <caret>it ->
val x = it.x
val y = it.y
x + y
}
@@ -0,0 +1,7 @@
data class XY(val x: String, val y: String)
fun convert(xy: XY, foo: (XY) -> String) = foo(xy)
fun foo(xy: XY) = convert(xy) { (x, y) ->
x + y
}
@@ -6244,6 +6244,63 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
}
@TestMetadata("idea/testData/intentions/destructuringInLambda")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class DestructuringInLambda extends AbstractIntentionTest {
public void testAllFilesPresentInDestructuringInLambda() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/intentions/destructuringInLambda"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), true);
}
@TestMetadata("dependentLocal.kt")
public void testDependentLocal() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/destructuringInLambda/dependentLocal.kt");
doTest(fileName);
}
@TestMetadata("firstProperties.kt")
public void testFirstProperties() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/destructuringInLambda/firstProperties.kt");
doTest(fileName);
}
@TestMetadata("fold.kt")
public void testFold() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/destructuringInLambda/fold.kt");
doTest(fileName);
}
@TestMetadata("last.kt")
public void testLast() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/destructuringInLambda/last.kt");
doTest(fileName);
}
@TestMetadata("list.kt")
public void testList() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/destructuringInLambda/list.kt");
doTest(fileName);
}
@TestMetadata("nullable.kt")
public void testNullable() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/destructuringInLambda/nullable.kt");
doTest(fileName);
}
@TestMetadata("simple.kt")
public void testSimple() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/destructuringInLambda/simple.kt");
doTest(fileName);
}
@TestMetadata("variables.kt")
public void testVariables() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/destructuringInLambda/variables.kt");
doTest(fileName);
}
}
@TestMetadata("idea/testData/intentions/ifNullToElvis")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)