Quick fix "add when remaining branches" refactoring + enum / sealed generated name w/o package name

This commit is contained in:
Mikhail Glukhikh
2016-01-12 15:24:23 +03:00
parent 0a3631db6a
commit c3cce53aa2
13 changed files with 58 additions and 39 deletions
@@ -37,7 +37,6 @@ import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.resolve.DescriptorUtils.isEnumClass import org.jetbrains.kotlin.resolve.DescriptorUtils.isEnumClass
import org.jetbrains.kotlin.resolve.DescriptorUtils.isEnumEntry import org.jetbrains.kotlin.resolve.DescriptorUtils.isEnumEntry
import org.jetbrains.kotlin.resolve.bindingContextUtil.isUsedAsExpression import org.jetbrains.kotlin.resolve.bindingContextUtil.isUsedAsExpression
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import java.util.* import java.util.*
interface WhenMissingCase { interface WhenMissingCase {
@@ -132,7 +131,9 @@ private object WhenOnBooleanExhaustivenessChecker : WhenExhaustivenessChecker {
private class ClassMissingCase(val descriptor: ClassDescriptor): WhenMissingCase { private class ClassMissingCase(val descriptor: ClassDescriptor): WhenMissingCase {
override fun toString() = descriptor.name.identifier.let { if (descriptor.kind.isSingleton) it else "is $it" } override fun toString() = descriptor.name.identifier.let { if (descriptor.kind.isSingleton) it else "is $it" }
override val branchConditionText = descriptor.fqNameSafe.asString().let { if (descriptor.kind.isSingleton) it else "is $it" } override val branchConditionText = DescriptorUtils.getFqNameFromTopLevelClass(descriptor).asString().let {
if (descriptor.kind.isSingleton) it else "is $it"
}
} }
private abstract class WhenOnClassExhaustivenessChecker : WhenExhaustivenessChecker { private abstract class WhenOnClassExhaustivenessChecker : WhenExhaustivenessChecker {
@@ -150,8 +150,6 @@ remove.val.var.from.parameter=Remove ''{0}'' from parameter
add.override.to.equals.hashCode.toString=Add 'override' to equals, hashCode, toString in project add.override.to.equals.hashCode.toString=Add 'override' to equals, hashCode, toString in project
add.when.else.branch.action.family.name=Add else branch add.when.else.branch.action.family.name=Add else branch
add.when.else.branch.action=Add else branch add.when.else.branch.action=Add else branch
add.when.remaining.branches.action.family.name=Add remaining branches
add.when.remaining.branches.action=Add remaining branches
move.when.else.branch.to.the.end.action=Move else branch to the end move.when.else.branch.to.the.end.action=Move else branch to the end
move.when.else.branch.to.the.end.family.name=Move else branch to the end move.when.else.branch.to.the.end.family.name=Move else branch to the end
change.to.property.name.family.name=Change to property name change.to.property.name.family.name=Change to property name
@@ -16,20 +16,18 @@
package org.jetbrains.kotlin.idea.quickfix package org.jetbrains.kotlin.idea.quickfix
import com.intellij.codeInsight.CodeInsightUtilCore
import com.intellij.openapi.editor.Editor import com.intellij.openapi.editor.Editor
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile import com.intellij.psi.PsiFile
import com.intellij.psi.util.PsiTreeUtil
import org.jetbrains.kotlin.cfg.WhenChecker import org.jetbrains.kotlin.cfg.WhenChecker
import org.jetbrains.kotlin.cfg.hasUnknown import org.jetbrains.kotlin.cfg.hasUnknown
import org.jetbrains.kotlin.diagnostics.Diagnostic import org.jetbrains.kotlin.diagnostics.Diagnostic
import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtPsiFactory import org.jetbrains.kotlin.psi.KtPsiFactory
import org.jetbrains.kotlin.psi.KtWhenEntry
import org.jetbrains.kotlin.psi.KtWhenExpression import org.jetbrains.kotlin.psi.KtWhenExpression
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
class AddWhenRemainingBranchesFix(expression: KtWhenExpression) : KotlinQuickFixAction<KtWhenExpression>(expression) { class AddWhenRemainingBranchesFix(expression: KtWhenExpression) : KotlinQuickFixAction<KtWhenExpression>(expression) {
@@ -37,10 +35,11 @@ class AddWhenRemainingBranchesFix(expression: KtWhenExpression) : KotlinQuickFix
override fun getText() = "Add remaining branches" override fun getText() = "Add remaining branches"
override fun isAvailable(project: Project, editor: Editor?, file: PsiFile): Boolean { override fun isAvailable(project: Project, editor: Editor?, file: PsiFile): Boolean =
return super.isAvailable(project, editor, file) && element.closeBrace != null && super.isAvailable(project, editor, file) && element.closeBrace != null &&
!WhenChecker.getNecessaryCases(element, element.analyze()).hasUnknown with(WhenChecker.getNecessaryCases(element, element.analyze())) {
} isNotEmpty() && !hasUnknown
}
override fun invoke(project: Project, editor: Editor?, file: KtFile) { override fun invoke(project: Project, editor: Editor?, file: KtFile) {
val necessaryCases = WhenChecker.getNecessaryCases(element, element.analyze()) val necessaryCases = WhenChecker.getNecessaryCases(element, element.analyze())
@@ -48,33 +47,18 @@ class AddWhenRemainingBranchesFix(expression: KtWhenExpression) : KotlinQuickFix
val whenCloseBrace = element.closeBrace val whenCloseBrace = element.closeBrace
assert(whenCloseBrace != null) { "isAvailable should check if close brace exist" } assert(whenCloseBrace != null) { "isAvailable should check if close brace exist" }
val psiFactory = KtPsiFactory(file) val psiFactory = KtPsiFactory(file)
var insertedBranch: PsiElement? = null
for (case in necessaryCases) { for (case in necessaryCases) {
val entry = psiFactory.createWhenEntry("${case.branchConditionText} -> throw AssertionError(\"\")") val entry = psiFactory.createWhenEntry("${case.branchConditionText} -> TODO()")
insertedBranch = element.addBefore(entry, whenCloseBrace) val insertedBranch = element.addBefore(entry, whenCloseBrace)
element.addAfter(psiFactory.createNewLine(), insertedBranch) element.addAfter(psiFactory.createNewLine(), insertedBranch)
}
if (insertedBranch != null) {
val insertedWhenEntry = CodeInsightUtilCore.forcePsiPostprocessAndRestoreElement(insertedBranch) as KtWhenEntry
val textRange = insertedWhenEntry.textRange
val indexOfOpenBrace = insertedWhenEntry.text.indexOf('{')
editor!!.caretModel.moveToOffset(textRange.startOffset + indexOfOpenBrace + 1)
} }
} }
companion object { companion object : KotlinSingleIntentionActionFactory() {
@JvmStatic override fun createAction(diagnostic: Diagnostic): KotlinQuickFixAction<out PsiElement>? {
fun createFactory(): KotlinSingleIntentionActionFactory { val whenExpression = diagnostic.psiElement.getNonStrictParentOfType<KtWhenExpression>() ?: return null
return object : KotlinSingleIntentionActionFactory() { return AddWhenRemainingBranchesFix(whenExpression)
public override fun createAction(diagnostic: Diagnostic): KotlinQuickFixAction<out PsiElement>? {
val element = diagnostic.psiElement
val whenExpression = PsiTreeUtil.getParentOfType(element, KtWhenExpression::class.java, false) ?: return null
return AddWhenRemainingBranchesFix(whenExpression)
}
}
} }
} }
} }
@@ -189,7 +189,7 @@ class QuickFixRegistrar : QuickFixContributor {
ELSE_MISPLACED_IN_WHEN.registerFactory(MoveWhenElseBranchFix.createFactory()) ELSE_MISPLACED_IN_WHEN.registerFactory(MoveWhenElseBranchFix.createFactory())
NO_ELSE_IN_WHEN.registerFactory(AddWhenElseBranchFix.createFactory()) NO_ELSE_IN_WHEN.registerFactory(AddWhenElseBranchFix.createFactory())
NO_ELSE_IN_WHEN.registerFactory(AddWhenRemainingBranchesFix.createFactory()) NO_ELSE_IN_WHEN.registerFactory(AddWhenRemainingBranchesFix)
BREAK_OR_CONTINUE_IN_WHEN.registerFactory(AddLoopLabelFix) BREAK_OR_CONTINUE_IN_WHEN.registerFactory(AddLoopLabelFix)
NO_TYPE_ARGUMENTS_ON_RHS.registerFactory(AddStarProjectionsFix.createFactoryForIsExpression()) NO_TYPE_ARGUMENTS_ON_RHS.registerFactory(AddStarProjectionsFix.createFactoryForIsExpression())
@@ -1,4 +1,5 @@
// "Add remaining branches" "true" // "Add remaining branches" "true"
// ERROR: Unresolved reference: TODO
fun test(b: Boolean) = wh<caret>en(b) { fun test(b: Boolean) = wh<caret>en(b) {
false -> 0 false -> 0
} }
@@ -1,5 +1,6 @@
// "Add remaining branches" "true" // "Add remaining branches" "true"
// ERROR: Unresolved reference: TODO
fun test(b: Boolean) = when(b) { fun test(b: Boolean) = when(b) {
false -> 0 false -> 0
true -> throw AssertionError("") true -> TODO()
} }
@@ -1,4 +1,6 @@
// "Add remaining branches" "true" // "Add remaining branches" "true"
// ERROR: Unresolved reference: TODO
// ERROR: Unresolved reference: TODO
enum class Color { R, G, B } enum class Color { R, G, B }
fun test(c: Color) = wh<caret>en(c) { fun test(c: Color) = wh<caret>en(c) {
Color.B -> 0xff Color.B -> 0xff
@@ -1,7 +1,9 @@
// "Add remaining branches" "true" // "Add remaining branches" "true"
// ERROR: Unresolved reference: TODO
// ERROR: Unresolved reference: TODO
enum class Color { R, G, B } enum class Color { R, G, B }
fun test(c: Color) = when(c) { fun test(c: Color) = when(c) {
Color.B -> 0xff Color.B -> 0xff
Color.R -> throw AssertionError("") Color.R -> TODO()
Color.G -> throw AssertionError("") Color.G -> TODO()
} }
@@ -0,0 +1,8 @@
// "Add remaining branches" "true"
// ERROR: Unresolved reference: TODO
// ERROR: Unresolved reference: TODO
package test
enum class Color { R, G, B }
fun test(c: Color) = wh<caret>en(c) {
Color.B -> 0xff
}
@@ -0,0 +1,10 @@
// "Add remaining branches" "true"
// ERROR: Unresolved reference: TODO
// ERROR: Unresolved reference: TODO
package test
enum class Color { R, G, B }
fun test(c: Color) = when(c) {
Color.B -> 0xff
Color.R -> TODO()
Color.G -> TODO()
}
@@ -1,4 +1,7 @@
// "Add remaining branches" "true" // "Add remaining branches" "true"
// ERROR: Unresolved reference: TODO
// ERROR: Unresolved reference: TODO
// ERROR: Unresolved reference: TODO
sealed class Variant { sealed class Variant {
object Singleton : Variant() object Singleton : Variant()
@@ -1,4 +1,7 @@
// "Add remaining branches" "true" // "Add remaining branches" "true"
// ERROR: Unresolved reference: TODO
// ERROR: Unresolved reference: TODO
// ERROR: Unresolved reference: TODO
sealed class Variant { sealed class Variant {
object Singleton : Variant() object Singleton : Variant()
@@ -8,7 +11,7 @@ sealed class Variant {
} }
fun test(v: Variant?) = when(v) { fun test(v: Variant?) = when(v) {
Variant.Singleton -> "s" Variant.Singleton -> "s"
is Variant.Something -> throw AssertionError("") is Variant.Something -> TODO()
Variant.Another -> throw AssertionError("") Variant.Another -> TODO()
null -> throw AssertionError("") null -> TODO()
} }
@@ -7580,6 +7580,12 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
doTest(fileName); doTest(fileName);
} }
@TestMetadata("addRemainingBranchesInNonDefaultPackage.kt")
public void testAddRemainingBranchesInNonDefaultPackage() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/when/addRemainingBranchesInNonDefaultPackage.kt");
doTest(fileName);
}
@TestMetadata("addRemainingBranchesSealed.kt") @TestMetadata("addRemainingBranchesSealed.kt")
public void testAddRemainingBranchesSealed() throws Exception { public void testAddRemainingBranchesSealed() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/when/addRemainingBranchesSealed.kt"); String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/when/addRemainingBranchesSealed.kt");