Inline refactoring: should remove redundant Unit

#KT-19443 Fixed
This commit is contained in:
Dmitry Gridin
2020-06-22 22:09:26 +07:00
parent af24ce5e03
commit 99f958c8c4
14 changed files with 189 additions and 18 deletions
@@ -15,6 +15,7 @@ import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
import org.jetbrains.kotlin.idea.caches.resolve.resolveImportReference import org.jetbrains.kotlin.idea.caches.resolve.resolveImportReference
import org.jetbrains.kotlin.idea.caches.resolve.resolveToCall import org.jetbrains.kotlin.idea.caches.resolve.resolveToCall
import org.jetbrains.kotlin.idea.core.* import org.jetbrains.kotlin.idea.core.*
import org.jetbrains.kotlin.idea.inspections.RedundantUnitExpressionInspection
import org.jetbrains.kotlin.idea.intentions.RemoveExplicitTypeArgumentsIntention import org.jetbrains.kotlin.idea.intentions.RemoveExplicitTypeArgumentsIntention
import org.jetbrains.kotlin.idea.util.CommentSaver import org.jetbrains.kotlin.idea.util.CommentSaver
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
@@ -407,27 +408,29 @@ class CodeInliner<TCallElement : KtElement>(
} }
private fun postProcessInsertedCode(range: PsiChildRange, lexicalScope: LexicalScope?): PsiChildRange { private fun postProcessInsertedCode(range: PsiChildRange, lexicalScope: LexicalScope?): PsiChildRange {
val elements = range.filterIsInstance<KtElement>().toList() val pointers = range.filterIsInstance<KtElement>().map { it.createSmartPointer() }.toList()
if (elements.isEmpty()) return PsiChildRange.EMPTY if (pointers.isEmpty()) return PsiChildRange.EMPTY
lexicalScope?.let { lexicalScope?.let {
renameDuplicates(elements.dropLast(1).filterIsInstance<KtNamedDeclaration>(), it) renameDuplicates(pointers.dropLast(1).mapNotNull { pointer -> pointer.element as? KtNamedDeclaration }, it)
} }
elements.forEach { for (pointer in pointers) {
introduceNamedArguments(it) val element = pointer.element ?: continue
introduceNamedArguments(element)
restoreFunctionLiteralArguments(it) restoreFunctionLiteralArguments(element)
//TODO: do this earlier //TODO: do this earlier
dropArgumentsForDefaultValues(it) dropArgumentsForDefaultValues(element)
simplifySpreadArrayOfArguments(it) simplifySpreadArrayOfArguments(element)
removeExplicitTypeArguments(it) removeExplicitTypeArguments(element)
removeRedundantUnitExpressions(element)
} }
val shortenFilter = { element: PsiElement -> val shortenFilter = { element: PsiElement ->
if (element[USER_CODE_KEY]) { if (element[USER_CODE_KEY]) {
ShortenReferences.FilterResult.SKIP ShortenReferences.FilterResult.SKIP
@@ -440,11 +443,13 @@ class CodeInliner<TCallElement : KtElement>(
} }
} }
val newElements = elements.map { val newElements = pointers.mapNotNull {
ShortenReferences { ShortenReferences.Options(removeThis = true) }.process(it, elementFilter = shortenFilter) it.element?.let { element ->
ShortenReferences { ShortenReferences.Options(removeThis = true) }.process(element, elementFilter = shortenFilter)
}
} }
newElements.forEach { element -> for (element in newElements) {
// clean up user data // clean up user data
element.forEachDescendantOfType<KtExpression> { element.forEachDescendantOfType<KtExpression> {
it.clear(USER_CODE_KEY) it.clear(USER_CODE_KEY)
@@ -460,7 +465,15 @@ class CodeInliner<TCallElement : KtElement>(
} }
} }
return PsiChildRange(newElements.first(), newElements.last()) return if (newElements.isEmpty()) PsiChildRange.EMPTY else PsiChildRange(newElements.first(), newElements.last())
}
private fun removeRedundantUnitExpressions(result: KtElement) {
result.forEachDescendantOfType<KtReferenceExpression> {
if (RedundantUnitExpressionInspection.isRedundantUnit(it)) {
it.delete()
}
}
} }
private fun introduceNamedArguments(result: KtElement) { private fun introduceNamedArguments(result: KtElement) {
@@ -2,9 +2,9 @@ fun <T> doIt(p: () -> T): T = TODO()
fun g(p: String?) { fun g(p: String?) {
p?.let { Unit } p?.let { }
} }
fun h() = Unit fun h() = Unit
fun x() = doIt { Unit } fun x() = doIt { }
@@ -0,0 +1,16 @@
fun <T> doIt(p: () -> T): T = p()
fun Any.doDo() = Unit
abstract class A {
abstract fun a()
}
fun foo<caret>() {
null?.doDo()
}
class B : A() {
override fun a() = doIt {
foo()
}
}
@@ -0,0 +1,13 @@
fun <T> doIt(p: () -> T): T = p()
fun Any.doDo() = Unit
abstract class A {
abstract fun a()
}
class B : A() {
override fun a() = doIt {
null?.doDo()
Unit
}
}
@@ -0,0 +1,16 @@
fun <T> doIt(p: () -> T): T = p()
fun Any.doDo() = Unit
abstract class A {
abstract fun a()
}
fun foo<caret>() {
1.let { it.let { it.let { it.let { null?.doDo() } } } }
}
class B : A() {
override fun a() = doIt {
foo()
}
}
@@ -0,0 +1,13 @@
fun <T> doIt(p: () -> T): T = p()
fun Any.doDo() = Unit
abstract class A {
abstract fun a()
}
class B : A() {
override fun a() = doIt {
1.let { it.let { it.let { it.let { null?.doDo() } } } }
Unit
}
}
@@ -0,0 +1,14 @@
fun <T> doIt(p: () -> T): T = p()
fun Any.doDo() = Unit
abstract class A {
abstract fun a()
}
fun foo<caret>(): Unit = doIt {
1.let { it.let { it.let { it.let { null?.doDo() } } } }
}
class B : A() {
override fun a() = foo()
}
@@ -0,0 +1,12 @@
fun <T> doIt(p: () -> T): T = p()
fun Any.doDo() = Unit
abstract class A {
abstract fun a()
}
class B : A() {
override fun a() = doIt {
1.let<Int, Unit> { it.let { it.let { it.let { null?.doDo() } } } }
}
}
@@ -9,7 +9,6 @@ class C {
p?.let { p?.let {
println(3) println(3)
println(4) println(4)
Unit
} }
if (other != null) { if (other != null) {
@@ -26,6 +25,5 @@ class C {
fun x() = doIt { fun x() = doIt {
println(9) println(9)
println(10) println(10)
Unit
} }
} }
@@ -0,0 +1,12 @@
class C {
fun <caret>f(p1: Int, p2: Int) {
println(p1)
nonUnit(p2)
}
fun nonUnit(p: Int): Int = p
fun <T> doIt(p: () -> T): T = TODO()
fun x() = doIt<Unit> { f(9, 10) }
}
@@ -0,0 +1,11 @@
class C {
fun nonUnit(p: Int): Int = p
fun <T> doIt(p: () -> T): T = TODO()
fun x() = doIt<Unit> {
println(9)
nonUnit(10)
}
}
@@ -0,0 +1,16 @@
fun <T> doIt(p: () -> T): T = p()
fun Any.doDo() = Unit
abstract class A {
abstract fun a()
}
fun foo<caret>() {
1.let { it.let { it.let { it.let { it?.doDo() } } } }
}
class B : A() {
override fun a() = doIt {
foo()
}
}
@@ -0,0 +1,12 @@
fun <T> doIt(p: () -> T): T = p()
fun Any.doDo() = Unit
abstract class A {
abstract fun a()
}
class B : A() {
override fun a() = doIt {
1.let { it.let { it.let { it.let { it?.doDo() } } } }
}
}
@@ -75,6 +75,21 @@ public class InlineTestGenerated extends AbstractInlineTest {
runTest("idea/testData/refactoring/inline/function/MultipleReturns.kt"); runTest("idea/testData/refactoring/inline/function/MultipleReturns.kt");
} }
@TestMetadata("NullableUnitReturnType.kt")
public void testNullableUnitReturnType() throws Exception {
runTest("idea/testData/refactoring/inline/function/NullableUnitReturnType.kt");
}
@TestMetadata("NullableUnitReturnType2.kt")
public void testNullableUnitReturnType2() throws Exception {
runTest("idea/testData/refactoring/inline/function/NullableUnitReturnType2.kt");
}
@TestMetadata("NullableUnitReturnType3.kt")
public void testNullableUnitReturnType3() throws Exception {
runTest("idea/testData/refactoring/inline/function/NullableUnitReturnType3.kt");
}
@TestMetadata("OuterClassReceiver.kt") @TestMetadata("OuterClassReceiver.kt")
public void testOuterClassReceiver() throws Exception { public void testOuterClassReceiver() throws Exception {
runTest("idea/testData/refactoring/inline/function/OuterClassReceiver.kt"); runTest("idea/testData/refactoring/inline/function/OuterClassReceiver.kt");
@@ -135,6 +150,16 @@ public class InlineTestGenerated extends AbstractInlineTest {
runTest("idea/testData/refactoring/inline/function/UnitReturnType2.kt"); runTest("idea/testData/refactoring/inline/function/UnitReturnType2.kt");
} }
@TestMetadata("UnitReturnType3.kt")
public void testUnitReturnType3() throws Exception {
runTest("idea/testData/refactoring/inline/function/UnitReturnType3.kt");
}
@TestMetadata("UnitReturnType4.kt")
public void testUnitReturnType4() throws Exception {
runTest("idea/testData/refactoring/inline/function/UnitReturnType4.kt");
}
@TestMetadata("idea/testData/refactoring/inline/function/expressionBody") @TestMetadata("idea/testData/refactoring/inline/function/expressionBody")
@TestDataPath("$PROJECT_ROOT") @TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class) @RunWith(JUnit3RunnerWithInners.class)