KotlinIntroduceVariableHandler: fix for declarations with expression body

#KT-38449 Fixed
This commit is contained in:
Dmitry Gridin
2020-04-22 21:15:03 +07:00
parent d21e383094
commit 8e35b7a29c
11 changed files with 62 additions and 13 deletions
@@ -265,7 +265,9 @@ internal class ExpressionReplacementPerformer(
private fun <TElement : KtElement> withElementToBeReplacedPreserved(action: () -> TElement): TElement { private fun <TElement : KtElement> withElementToBeReplacedPreserved(action: () -> TElement): TElement {
elementToBeReplaced.putCopyableUserData(ELEMENT_TO_BE_REPLACED_KEY, Unit) elementToBeReplaced.putCopyableUserData(ELEMENT_TO_BE_REPLACED_KEY, Unit)
val result = action() val result = action()
elementToBeReplaced = result.findDescendantOfType { it.getCopyableUserData(ELEMENT_TO_BE_REPLACED_KEY) != null }!! elementToBeReplaced = result.findDescendantOfType { it.getCopyableUserData(ELEMENT_TO_BE_REPLACED_KEY) != null }
?: error("Element `elementToBeReplaced` not found")
elementToBeReplaced.putCopyableUserData(ELEMENT_TO_BE_REPLACED_KEY, null) elementToBeReplaced.putCopyableUserData(ELEMENT_TO_BE_REPLACED_KEY, null)
return result return result
} }
@@ -44,11 +44,11 @@ class ConvertToBlockBodyIntention : SelfTargetingIntention<KtDeclarationWithBody
override fun allowCaretInsideElement(element: PsiElement) = element !is KtDeclaration && super.allowCaretInsideElement(element) override fun allowCaretInsideElement(element: PsiElement) = element !is KtDeclaration && super.allowCaretInsideElement(element)
override fun applyTo(element: KtDeclarationWithBody, editor: Editor?) { override fun applyTo(element: KtDeclarationWithBody, editor: Editor?) {
convert(element) convert(element, true)
} }
companion object { companion object {
fun convert(declaration: KtDeclarationWithBody): KtDeclarationWithBody { fun convert(declaration: KtDeclarationWithBody, withReformat: Boolean = false): KtDeclarationWithBody {
val body = declaration.bodyExpression!! val body = declaration.bodyExpression!!
fun generateBody(returnsValue: Boolean): KtExpression { fun generateBody(returnsValue: Boolean): KtExpression {
@@ -86,13 +86,10 @@ class ConvertToBlockBodyIntention : SelfTargetingIntention<KtDeclarationWithBody
declaration.equalsToken!!.delete() declaration.equalsToken!!.delete()
val replaced = body.replace(newBody) val replaced = body.replace(newBody)
declaration.containingKtFile.adjustLineIndent(replaced.startOffset, replaced.endOffset) if (withReformat) declaration.containingKtFile.adjustLineIndent(replaced.startOffset, replaced.endOffset)
return declaration return declaration
} }
private fun KtNamedFunction.returnType(): KotlinType? { private fun KtNamedFunction.returnType(): KotlinType? = resolveToDescriptorIfAny()?.returnType
val descriptor = resolveToDescriptorIfAny() ?: return null
return descriptor.returnType
}
} }
} }
@@ -273,7 +273,7 @@ class UsePropertyAccessSyntaxIntention : SelfTargetingOffsetIndependentIntention
val callParent = call.parent val callParent = call.parent
var callToConvert = callExpression var callToConvert = callExpression
if (callParent is KtDeclarationWithBody && call == callParent.bodyExpression) { if (callParent is KtDeclarationWithBody && call == callParent.bodyExpression) {
ConvertToBlockBodyIntention.convert(callParent) ConvertToBlockBodyIntention.convert(callParent, true)
val firstStatement = callParent.bodyBlockExpression?.statements?.first() val firstStatement = callParent.bodyBlockExpression?.statements?.first()
callToConvert = (firstStatement as? KtQualifiedExpression)?.selectorExpression as? KtCallExpression callToConvert = (firstStatement as? KtQualifiedExpression)?.selectorExpression as? KtCallExpression
?: firstStatement as? KtCallExpression ?: firstStatement as? KtCallExpression
@@ -46,7 +46,7 @@ object CreateLocalVariableActionFactory : KotlinSingleIntentionActionFactory() {
val actualContainer = when (container) { val actualContainer = when (container) {
is KtBlockExpression -> container is KtBlockExpression -> container
else -> ConvertToBlockBodyIntention.convert(container as KtDeclarationWithBody).bodyExpression!! else -> ConvertToBlockBodyIntention.convert(container as KtDeclarationWithBody, true).bodyExpression!!
} as KtBlockExpression } as KtBlockExpression
if (actualContainer != container) { if (actualContainer != container) {
@@ -164,8 +164,8 @@ fun PsiElement.findExpressionByCopyableDataAndClearIt(key: Key<Boolean>): KtExpr
return result return result
} }
fun PsiElement.findElementByCopyableDataAndClearIt(key: Key<Boolean>): PsiElement { fun PsiElement.findElementByCopyableDataAndClearIt(key: Key<Boolean>): PsiElement? {
val result = findDescendantOfType<PsiElement> { it.getCopyableUserData(key) != null }!! val result = findDescendantOfType<PsiElement> { it.getCopyableUserData(key) != null } ?: return null
result.putCopyableUserData(key, null) result.putCopyableUserData(key, null)
return result return result
} }
@@ -311,7 +311,13 @@ object KotlinIntroduceVariableHandler : RefactoringActionHandler {
} ?: newReplace } ?: newReplace
} }
runRefactoring(isVar, newExpression ?: return, newCommonContainer, newCommonParent, newAllReplaces) runRefactoring(
isVar,
newExpression ?: return,
newCommonContainer,
newCommonParent ?: return,
newAllReplaces
)
} }
} }
@@ -0,0 +1,7 @@
fun b(body: () -> Int) = body()
class A {
fun test() = b {
<selection>24</selection>
}
}
@@ -0,0 +1,10 @@
fun b(body: () -> Int) = body()
class A {
fun test(): Int {
val i = 24
return b {
i
}
}
}
@@ -0,0 +1,7 @@
fun b(body: () -> Unit): Unit = body()
class A {
fun test() = b {
<selection>24</selection>
}
}
@@ -0,0 +1,10 @@
fun b(body: () -> Unit): Unit = body()
class A {
fun test() {
val i = 24
b {
i
}
}
}
@@ -220,6 +220,16 @@ public class ExtractionTestGenerated extends AbstractExtractionTest {
runTest("idea/testData/refactoring/introduceVariable/kt21530_withParam.kt"); runTest("idea/testData/refactoring/introduceVariable/kt21530_withParam.kt");
} }
@TestMetadata("kt38449_int.kt")
public void testKt38449_int() throws Exception {
runTest("idea/testData/refactoring/introduceVariable/kt38449_int.kt");
}
@TestMetadata("kt38449_unit.kt")
public void testKt38449_unit() throws Exception {
runTest("idea/testData/refactoring/introduceVariable/kt38449_unit.kt");
}
@TestMetadata("LoopRange.kt") @TestMetadata("LoopRange.kt")
public void testLoopRange() throws Exception { public void testLoopRange() throws Exception {
runTest("idea/testData/refactoring/introduceVariable/LoopRange.kt"); runTest("idea/testData/refactoring/introduceVariable/LoopRange.kt");