From c06e2d994e09c7f4d53a7db856e847a516ff6a2b Mon Sep 17 00:00:00 2001 From: Valentin Kipyatkov Date: Tue, 4 Oct 2016 20:44:09 +0300 Subject: [PATCH] KT-13884 Exception "Invalid root block PSI element" on replacing trivial when-expression to if #KT-13884 Fixed --- .../jetbrains/kotlin/psi/createByPattern.kt | 22 ++++++++++++++----- .../intentions/WhenToIfIntention.kt | 1 + .../branched/ifWhen/whenToIf/kt13884.kt | 6 +++++ .../intentions/IntentionTestGenerated.java | 6 +++++ 4 files changed, 29 insertions(+), 6 deletions(-) create mode 100644 idea/testData/intentions/branched/ifWhen/whenToIf/kt13884.kt diff --git a/compiler/frontend/src/org/jetbrains/kotlin/psi/createByPattern.kt b/compiler/frontend/src/org/jetbrains/kotlin/psi/createByPattern.kt index c101bf7ce10..21a6853f7a6 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/psi/createByPattern.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/psi/createByPattern.kt @@ -47,28 +47,30 @@ private abstract class ArgumentType(val klass: Class) private class PlainTextArgumentType(klass: Class, val toPlainText: (T) -> String) : ArgumentType(klass) private abstract class PsiElementPlaceholderArgumentType(klass: Class, val placeholderClass: Class) : ArgumentType(klass) { - abstract fun replacePlaceholderElement(placeholder: TPlaceholder, argument: T) + abstract fun replacePlaceholderElement(placeholder: TPlaceholder, argument: T): PsiChildRange } private class PsiElementArgumentType(klass: Class) : PsiElementPlaceholderArgumentType(klass, klass) { - override fun replacePlaceholderElement(placeholder: T, argument: T) { + override fun replacePlaceholderElement(placeholder: T, argument: T): PsiChildRange { // if argument element has generated flag then it has not been formatted yet and we should do this manually // (because we cleared this flag for the whole tree above and PostprocessReformattingAspect won't format anything) val reformat = CodeEditUtil.isNodeGenerated(argument.node) - val result = placeholder.replace(argument) + var result = placeholder.replace(argument) if (reformat) { - CodeStyleManager.getInstance(result.project).reformat(result, true) + result = CodeStyleManager.getInstance(result.project).reformat(result, true) } + return PsiChildRange.singleElement(result) } } private object PsiChildRangeArgumentType : PsiElementPlaceholderArgumentType(PsiChildRange::class.java, KtElement::class.java) { - override fun replacePlaceholderElement(placeholder: KtElement, argument: PsiChildRange) { + override fun replacePlaceholderElement(placeholder: KtElement, argument: PsiChildRange): PsiChildRange { val project = placeholder.project val codeStyleManager = CodeStyleManager.getInstance(project) if (argument.isEmpty) { placeholder.delete() + return PsiChildRange.EMPTY } else { val first = placeholder.parent.addRangeBefore(argument.first!!, argument.last!!, placeholder) @@ -79,6 +81,7 @@ private object PsiChildRangeArgumentType : PsiElementPlaceholderArgumentType createByPattern(pattern: String, vararg args: Any, fa if (element is KtFunctionLiteral) { element = element.getParent() as KtLambdaExpression } - (argumentTypes[n] as PsiElementPlaceholderArgumentType).replacePlaceholderElement(element, args[n]) + @Suppress("UNCHECKED_CAST") + val argumentType = argumentTypes[n] as PsiElementPlaceholderArgumentType + val range = argumentType.replacePlaceholderElement(element, args[n]) + + if (element == resultElement) { + assert(range.first == range.last) + resultElement = range.first as TElement + } } codeStyleManager.adjustLineIndent(resultElement.containingFile, resultElement.textRange) diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/branchedTransformations/intentions/WhenToIfIntention.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/branchedTransformations/intentions/WhenToIfIntention.kt index cdb4a518c98..fa41b425532 100644 --- a/idea/src/org/jetbrains/kotlin/idea/intentions/branchedTransformations/intentions/WhenToIfIntention.kt +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/branchedTransformations/intentions/WhenToIfIntention.kt @@ -33,6 +33,7 @@ class WhenToIfIntention : SelfTargetingRangeIntention(KtWhenEx if (entries.isEmpty()) return null val lastEntry = entries.last() if (entries.any { it != lastEntry && it.isElse }) return null + if (entries.all { it.isElse }) return null // 'when' with only 'else' branch is not supported return element.whenKeyword.textRange } diff --git a/idea/testData/intentions/branched/ifWhen/whenToIf/kt13884.kt b/idea/testData/intentions/branched/ifWhen/whenToIf/kt13884.kt new file mode 100644 index 00000000000..54537d7a050 --- /dev/null +++ b/idea/testData/intentions/branched/ifWhen/whenToIf/kt13884.kt @@ -0,0 +1,6 @@ +//IS_APPLICABLE: false +fun foo() { + when { + else -> {} + } +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java index e8257c06b4a..34391b7a375 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java @@ -1771,6 +1771,12 @@ public class IntentionTestGenerated extends AbstractIntentionTest { doTest(fileName); } + @TestMetadata("kt13884.kt") + public void testKt13884() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/branched/ifWhen/whenToIf/kt13884.kt"); + doTest(fileName); + } + @TestMetadata("whenWithDotQualifiedExpression.kt") public void testWhenWithDotQualifiedExpression() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/branched/ifWhen/whenToIf/whenWithDotQualifiedExpression.kt");