KT-12504 Intention to make open class with only private constructors sealed (#1193)

* Intention to make open class with only private constructors sealed #KT-12504 Fixed

* Fixed #KT-12504
This commit is contained in:
Toshiaki Kameyama
2017-07-19 20:36:36 +09:00
committed by Dmitry Jemerov
parent dd2a87dbf7
commit a8da79a130
23 changed files with 197 additions and 0 deletions
@@ -0,0 +1,2 @@
sealed class Klass(private val s: String) {
}
@@ -0,0 +1,2 @@
open class Klass private constructor(private val s: String) {
}
@@ -0,0 +1,5 @@
<html>
<body>
This intention converts an open or abstract class with only private constructors to a sealed class
</body>
</html>
+5
View File
@@ -1593,6 +1593,11 @@
<category>Kotlin</category>
</intentionAction>
<intentionAction>
<className>org.jetbrains.kotlin.idea.intentions.ConvertClassToSealedClassIntention</className>
<category>Kotlin</category>
</intentionAction>
<localInspection implementationClass="org.jetbrains.kotlin.idea.intentions.ObjectLiteralToLambdaInspection"
displayName="Object literal can be converted to lambda"
groupPath="Kotlin"
@@ -0,0 +1,65 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.idea.intentions
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.util.TextRange
import org.jetbrains.kotlin.lexer.KtTokens.*
import org.jetbrains.kotlin.psi.KtClass
import org.jetbrains.kotlin.psi.KtPrimaryConstructor
import org.jetbrains.kotlin.psi.psiUtil.endOffset
import org.jetbrains.kotlin.psi.psiUtil.startOffset
class ConvertClassToSealedClassIntention : SelfTargetingRangeIntention<KtClass>(KtClass::class.java, "Convert to sealed class") {
override fun applicabilityRange(element: KtClass): TextRange? {
if (element.modifierList == null) return null
if (!element.hasModifier(OPEN_KEYWORD) && !element.hasModifier(ABSTRACT_KEYWORD)) return null
val constructors = listOfNotNull(element.primaryConstructor) + element.secondaryConstructors
if (constructors.isEmpty()) return null
if (!constructors.all { it.hasModifier(PRIVATE_KEYWORD) && it.getAnnotationEntries().isEmpty() }) return null
val nameIdentifier = element.nameIdentifier ?: return null
return TextRange(element.startOffset, nameIdentifier.endOffset)
}
override fun applyTo(element: KtClass, editor: Editor?) {
element.modifierList?.run {
getModifier(OPEN_KEYWORD)?.delete()
getModifier(ABSTRACT_KEYWORD)?.delete()
}
element.addModifier(SEALED_KEYWORD)
element.primaryConstructor?.run {
if (element.secondaryConstructors.isEmpty() && valueParameters.isEmpty()) {
this.delete()
}
else {
val newConstructor = this.copy() as KtPrimaryConstructor
newConstructor.modifierList?.getModifier(PRIVATE_KEYWORD)?.delete()
newConstructor.getConstructorKeyword()?.delete()
this.replace(newConstructor)
}
}
element.secondaryConstructors.forEach {
it.modifierList?.getModifier(PRIVATE_KEYWORD)?.delete()
}
}
}
@@ -0,0 +1 @@
org.jetbrains.kotlin.idea.intentions.ConvertClassToSealedClassIntention
@@ -0,0 +1 @@
<caret>abstract class Test private constructor()
@@ -0,0 +1 @@
sealed class Test
@@ -0,0 +1,2 @@
// IS_APPLICABLE: false
open class Test<caret>
@@ -0,0 +1,2 @@
// IS_APPLICABLE: false
class Test<caret> private constructor()
@@ -0,0 +1,2 @@
// IS_APPLICABLE: false
open class Test<caret> constructor()
@@ -0,0 +1,5 @@
// IS_APPLICABLE: false
open class Test<caret> private constructor() {
constructor(i: Int) : this() {
}
}
@@ -0,0 +1,4 @@
open class Test<caret> {
private constructor(i: Int) {
}
}
@@ -0,0 +1,4 @@
sealed class Test {
constructor(i: Int) {
}
}
@@ -0,0 +1 @@
open class Test<caret> private constructor()
@@ -0,0 +1 @@
sealed class Test
@@ -0,0 +1,3 @@
// IS_APPLICABLE: false
annotation class Inject
open class Test<caret> @Inject private constructor()
@@ -0,0 +1 @@
open class Test<caret> private constructor(s: String)
@@ -0,0 +1 @@
sealed class Test(s: String)
@@ -0,0 +1,4 @@
open class Test<caret> private constructor() {
private constructor(i: Int) : this() {
}
}
@@ -0,0 +1,4 @@
sealed class Test() {
constructor(i: Int) : this() {
}
}
@@ -0,0 +1,6 @@
// IS_APPLICABLE: false
annotation class Inject
open class Test<caret> private constructor() {
private @Inject constructor(i: Int) : this() {
}
}
@@ -3990,6 +3990,81 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
}
}
@TestMetadata("idea/testData/intentions/convertClassToSealedClass")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class ConvertClassToSealedClass extends AbstractIntentionTest {
@TestMetadata("abstract.kt")
public void testAbstract() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertClassToSealedClass/abstract.kt");
doTest(fileName);
}
public void testAllFilesPresentInConvertClassToSealedClass() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/intentions/convertClassToSealedClass"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), TargetBackend.ANY, true);
}
@TestMetadata("noConstructor.kt")
public void testNoConstructor() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertClassToSealedClass/noConstructor.kt");
doTest(fileName);
}
@TestMetadata("notOpenAndAbstract.kt")
public void testNotOpenAndAbstract() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertClassToSealedClass/notOpenAndAbstract.kt");
doTest(fileName);
}
@TestMetadata("notPrivatePrimaryConstructor.kt")
public void testNotPrivatePrimaryConstructor() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertClassToSealedClass/notPrivatePrimaryConstructor.kt");
doTest(fileName);
}
@TestMetadata("notPrivateSecondaryConstructor.kt")
public void testNotPrivateSecondaryConstructor() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertClassToSealedClass/notPrivateSecondaryConstructor.kt");
doTest(fileName);
}
@TestMetadata("onlySecondaryConstructor.kt")
public void testOnlySecondaryConstructor() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertClassToSealedClass/onlySecondaryConstructor.kt");
doTest(fileName);
}
@TestMetadata("primaryConstructor.kt")
public void testPrimaryConstructor() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertClassToSealedClass/primaryConstructor.kt");
doTest(fileName);
}
@TestMetadata("primaryConstructorWithAnnotation.kt")
public void testPrimaryConstructorWithAnnotation() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertClassToSealedClass/primaryConstructorWithAnnotation.kt");
doTest(fileName);
}
@TestMetadata("primaryConstructorWithValueParameter.kt")
public void testPrimaryConstructorWithValueParameter() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertClassToSealedClass/primaryConstructorWithValueParameter.kt");
doTest(fileName);
}
@TestMetadata("secondaryConstructor.kt")
public void testSecondaryConstructor() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertClassToSealedClass/secondaryConstructor.kt");
doTest(fileName);
}
@TestMetadata("secondaryConstructorWithAnnotation.kt")
public void testSecondaryConstructorWithAnnotation() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertClassToSealedClass/secondaryConstructorWithAnnotation.kt");
doTest(fileName);
}
}
@TestMetadata("idea/testData/intentions/convertEnumToSealedClass")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)