Add / preserve semicolon after empty companion object #KT-21179 Fixed
This commit is contained in:
committed by
Mikhail Glukhikh
parent
45e5cc190f
commit
0eec3ef1f8
@@ -25,9 +25,7 @@ import com.intellij.psi.PsiElementVisitor
|
||||
import com.intellij.psi.PsiWhiteSpace
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getChildrenOfType
|
||||
import org.jetbrains.kotlin.psi.psiUtil.nextLeaf
|
||||
import org.jetbrains.kotlin.psi.psiUtil.prevLeaf
|
||||
import org.jetbrains.kotlin.psi.psiUtil.*
|
||||
|
||||
class RedundantSemicolonInspection : AbstractKotlinInspection(), CleanupLocalInspectionTool {
|
||||
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor {
|
||||
@@ -77,6 +75,22 @@ class RedundantSemicolonInspection : AbstractKotlinInspection(), CleanupLocalIns
|
||||
return false // case with statement starting with '{' and call on the previous line
|
||||
}
|
||||
|
||||
if (isRequiredForCompanion(semicolon)) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
private fun isRequiredForCompanion(semicolon: PsiElement): Boolean {
|
||||
val prev = semicolon.getPrevSiblingIgnoringWhitespaceAndComments() as? KtObjectDeclaration ?: return false
|
||||
if (!prev.isCompanion()) return false
|
||||
if (prev.nameIdentifier != null || prev.getChildOfType<KtClassBody>() != null) return false
|
||||
|
||||
val next = semicolon.getNextSiblingIgnoringWhitespaceAndComments() ?: return false
|
||||
val firstChildNode = next.firstChild?.node ?: return false
|
||||
if (KtTokens.KEYWORDS.contains(firstChildNode.elementType)) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
@@ -19,11 +19,12 @@ package org.jetbrains.kotlin.idea.intentions
|
||||
import com.intellij.codeInspection.CleanupLocalInspectionTool
|
||||
import com.intellij.codeInspection.ProblemHighlightType
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.idea.editor.fixers.range
|
||||
import org.jetbrains.kotlin.idea.inspections.IntentionBasedInspection
|
||||
import org.jetbrains.kotlin.psi.KtClass
|
||||
import org.jetbrains.kotlin.psi.KtClassBody
|
||||
import org.jetbrains.kotlin.psi.KtObjectDeclaration
|
||||
import org.jetbrains.kotlin.psi.KtSecondaryConstructor
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.endOffset
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getNextSiblingIgnoringWhitespaceAndComments
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
|
||||
|
||||
@@ -34,7 +35,25 @@ class RemoveEmptyClassBodyInspection :
|
||||
}
|
||||
|
||||
class RemoveEmptyClassBodyIntention : SelfTargetingOffsetIndependentIntention<KtClassBody>(KtClassBody::class.java, "Remove empty class body") {
|
||||
override fun applyTo(element: KtClassBody, editor: Editor?) = element.delete()
|
||||
|
||||
override fun applyTo(element: KtClassBody, editor: Editor?) {
|
||||
val parent = element.parent
|
||||
element.delete()
|
||||
addSemicolonAfterEmptyCompanion(parent, editor)
|
||||
}
|
||||
|
||||
private fun addSemicolonAfterEmptyCompanion(element: PsiElement, editor: Editor?) {
|
||||
if (element !is KtObjectDeclaration) return
|
||||
if (!element.isCompanion() || element.nameIdentifier != null) return
|
||||
|
||||
val next = element.getNextSiblingIgnoringWhitespaceAndComments() ?: return
|
||||
if (next.node.elementType == KtTokens.SEMICOLON) return
|
||||
val firstChildNode = next.firstChild?.node ?: return
|
||||
if (firstChildNode.elementType in KtTokens.KEYWORDS) return
|
||||
|
||||
element.parent.addAfter(KtPsiFactory(element).createSemicolon(), element)
|
||||
editor?.caretModel?.moveToOffset(element.endOffset + 1)
|
||||
}
|
||||
|
||||
override fun isApplicableTo(element: KtClassBody): Boolean {
|
||||
element.getStrictParentOfType<KtObjectDeclaration>()?.let {
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
org.jetbrains.kotlin.idea.inspections.RedundantSemicolonInspection
|
||||
@@ -0,0 +1,7 @@
|
||||
class Test {
|
||||
companion object<caret>;
|
||||
|
||||
fun test() {}
|
||||
}
|
||||
|
||||
fun Test.Companion.foo() {}
|
||||
@@ -0,0 +1,7 @@
|
||||
class Test {
|
||||
companion object
|
||||
|
||||
fun test() {}
|
||||
}
|
||||
|
||||
fun Test.Companion.foo() {}
|
||||
@@ -0,0 +1,8 @@
|
||||
// PROBLEM: none
|
||||
class Test {
|
||||
companion object<caret>;
|
||||
|
||||
init {}
|
||||
}
|
||||
|
||||
fun Test.Companion.foo() {}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
// PROBLEM: none
|
||||
class Test {
|
||||
companion object<caret>;
|
||||
|
||||
private fun test() {}
|
||||
}
|
||||
|
||||
fun Test.Companion.foo() {}
|
||||
@@ -0,0 +1,7 @@
|
||||
class Test {
|
||||
companion object<caret>;
|
||||
|
||||
val bar = 1
|
||||
}
|
||||
|
||||
fun Test.Companion.foo() {}
|
||||
@@ -0,0 +1,7 @@
|
||||
class Test {
|
||||
companion object
|
||||
|
||||
val bar = 1
|
||||
}
|
||||
|
||||
fun Test.Companion.foo() {}
|
||||
@@ -0,0 +1,5 @@
|
||||
class Test {
|
||||
companion object<caret>;
|
||||
}
|
||||
|
||||
fun Test.Companion.foo() {}
|
||||
@@ -0,0 +1,5 @@
|
||||
class Test {
|
||||
companion object
|
||||
}
|
||||
|
||||
fun Test.Companion.foo() {}
|
||||
@@ -0,0 +1,7 @@
|
||||
class Test {
|
||||
companion object {}<caret>;
|
||||
|
||||
inline fun test() {}
|
||||
}
|
||||
|
||||
fun Test.Companion.foo() {}
|
||||
@@ -0,0 +1,7 @@
|
||||
class Test {
|
||||
companion object {}
|
||||
|
||||
inline fun test() {}
|
||||
}
|
||||
|
||||
fun Test.Companion.foo() {}
|
||||
@@ -0,0 +1,7 @@
|
||||
class Test {
|
||||
companion object Foo<caret>;
|
||||
|
||||
inline fun test() {}
|
||||
}
|
||||
|
||||
fun Test.Foo.foo() {}
|
||||
@@ -0,0 +1,7 @@
|
||||
class Test {
|
||||
companion object Foo
|
||||
|
||||
inline fun test() {}
|
||||
}
|
||||
|
||||
fun Test.Foo.foo() {}
|
||||
@@ -0,0 +1,7 @@
|
||||
class Test {
|
||||
companion object <caret>{}
|
||||
|
||||
fun test() {}
|
||||
}
|
||||
|
||||
fun Test.Companion.foo() {}
|
||||
@@ -0,0 +1,7 @@
|
||||
class Test {
|
||||
companion object
|
||||
|
||||
fun test() {}
|
||||
}
|
||||
|
||||
fun Test.Companion.foo() {}
|
||||
@@ -0,0 +1,7 @@
|
||||
class Test {
|
||||
companion object <caret>{}
|
||||
|
||||
init {}
|
||||
}
|
||||
|
||||
fun Test.Companion.foo() {}
|
||||
@@ -0,0 +1,7 @@
|
||||
class Test {
|
||||
companion object;<caret>
|
||||
|
||||
init {}
|
||||
}
|
||||
|
||||
fun Test.Companion.foo() {}
|
||||
@@ -0,0 +1,7 @@
|
||||
class Test {
|
||||
companion object <caret>{}
|
||||
|
||||
private fun test() {}
|
||||
}
|
||||
|
||||
fun Test.Companion.foo() {}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
class Test {
|
||||
companion object;<caret>
|
||||
|
||||
private fun test() {}
|
||||
}
|
||||
|
||||
fun Test.Companion.foo() {}
|
||||
@@ -0,0 +1,7 @@
|
||||
class Test {
|
||||
companion object <caret>{}
|
||||
|
||||
val bar = 1
|
||||
}
|
||||
|
||||
fun Test.Companion.foo() {}
|
||||
@@ -0,0 +1,7 @@
|
||||
class Test {
|
||||
companion object
|
||||
|
||||
val bar = 1
|
||||
}
|
||||
|
||||
fun Test.Companion.foo() {}
|
||||
@@ -0,0 +1,5 @@
|
||||
class Test {
|
||||
companion object <caret>{}
|
||||
}
|
||||
|
||||
fun Test.Companion.foo() {}
|
||||
@@ -0,0 +1,5 @@
|
||||
class Test {
|
||||
companion object
|
||||
}
|
||||
|
||||
fun Test.Companion.foo() {}
|
||||
@@ -0,0 +1,7 @@
|
||||
class Test {
|
||||
companion object Foo <caret>{}
|
||||
|
||||
inline fun test() {}
|
||||
}
|
||||
|
||||
fun Test.Foo.foo() {}
|
||||
@@ -0,0 +1,7 @@
|
||||
class Test {
|
||||
companion object Foo
|
||||
|
||||
inline fun test() {}
|
||||
}
|
||||
|
||||
fun Test.Foo.foo() {}
|
||||
@@ -0,0 +1,7 @@
|
||||
class Test {
|
||||
companion object <caret>{};
|
||||
|
||||
inline fun test() {}
|
||||
}
|
||||
|
||||
fun Test.Companion.foo() {}
|
||||
@@ -0,0 +1,7 @@
|
||||
class Test {
|
||||
companion object;
|
||||
|
||||
inline fun test() {}
|
||||
}
|
||||
|
||||
fun Test.Companion.foo() {}
|
||||
@@ -1833,6 +1833,57 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest {
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/inspectionsLocal/redundantSemicolon")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class RedundantSemicolon extends AbstractLocalInspectionTest {
|
||||
public void testAllFilesPresentInRedundantSemicolon() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/inspectionsLocal/redundantSemicolon"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), TargetBackend.ANY, true);
|
||||
}
|
||||
|
||||
@TestMetadata("companionBeforeFun.kt")
|
||||
public void testCompanionBeforeFun() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/redundantSemicolon/companionBeforeFun.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("companionBeforeInit.kt")
|
||||
public void testCompanionBeforeInit() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/redundantSemicolon/companionBeforeInit.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("companionBeforePrivateFun.kt")
|
||||
public void testCompanionBeforePrivateFun() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/redundantSemicolon/companionBeforePrivateFun.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("companionBeforeVal.kt")
|
||||
public void testCompanionBeforeVal() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/redundantSemicolon/companionBeforeVal.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("companionInLast.kt")
|
||||
public void testCompanionInLast() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/redundantSemicolon/companionInLast.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("companionWithBody.kt")
|
||||
public void testCompanionWithBody() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/redundantSemicolon/companionWithBody.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("companionWithName.kt")
|
||||
public void testCompanionWithName() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/redundantSemicolon/companionWithName.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/inspectionsLocal/redundantSetter")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
@@ -12896,6 +12896,48 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("companionBeforeFun.kt")
|
||||
public void testCompanionBeforeFun() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/removeEmptyClassBody/companionBeforeFun.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("companionBeforeInit.kt")
|
||||
public void testCompanionBeforeInit() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/removeEmptyClassBody/companionBeforeInit.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("companionBeforePrivateFun.kt")
|
||||
public void testCompanionBeforePrivateFun() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/removeEmptyClassBody/companionBeforePrivateFun.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("companionBeforeVal.kt")
|
||||
public void testCompanionBeforeVal() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/removeEmptyClassBody/companionBeforeVal.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("companionInLast.kt")
|
||||
public void testCompanionInLast() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/removeEmptyClassBody/companionInLast.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("companionWithName.kt")
|
||||
public void testCompanionWithName() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/removeEmptyClassBody/companionWithName.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("companionWithSemicolon.kt")
|
||||
public void testCompanionWithSemicolon() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/removeEmptyClassBody/companionWithSemicolon.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("emptyClass.kt")
|
||||
public void testEmptyClass() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/removeEmptyClassBody/emptyClass.kt");
|
||||
|
||||
Reference in New Issue
Block a user