Quick fix for enum entry delimiter syntax fix, together with a set of tests.

Possible NPE in getNextSiblingIgnoringWhitespace fixed. Some warnings also removed.
Last comma is replaced by semicolon. Comments are taken into account.
This commit is contained in:
Mikhail Glukhikh
2015-05-08 16:29:36 +03:00
parent da3d083dc0
commit edd269f5ff
27 changed files with 372 additions and 18 deletions
@@ -0,0 +1,83 @@
/*
* Copyright 2010-2015 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.quickfix
import com.intellij.codeInsight.intention.IntentionAction
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.diagnostics.Diagnostic
import org.jetbrains.kotlin.idea.quickfix.quickfixUtil.createIntentionFactory
import org.jetbrains.kotlin.idea.quickfix.quickfixUtil.createIntentionForFirstParentOfType
import org.jetbrains.kotlin.lexer.JetTokens
import org.jetbrains.kotlin.psi
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getChildOfType
import org.jetbrains.kotlin.psi.psiUtil.getChildrenOfType
import org.jetbrains.kotlin.psi.psiUtil.getNextSiblingIgnoringWhitespaceAndComments
import org.jetbrains.kotlin.psi.stubs.elements.JetStubElementTypes
import org.jetbrains.kotlin.resolve.DeclarationsChecker
class DeprecatedEnumEntryDelimiterSyntaxFix(element: JetEnumEntry): JetIntentionAction<JetEnumEntry>(element) {
override fun getFamilyName(): String = getText()
override fun getText(): String = "Insert lacking comma(s) / semicolon(s)"
override fun invoke(project: Project, editor: Editor?, file: JetFile?) = insertLackingCommaSemicolon(element)
companion object : JetSingleIntentionActionFactory() {
override fun createAction(diagnostic: Diagnostic): IntentionAction? =
diagnostic.createIntentionForFirstParentOfType(::DeprecatedEnumEntryDelimiterSyntaxFix)
public fun createWholeProjectFixFactory(): JetSingleIntentionActionFactory = createIntentionFactory {
JetWholeProjectForEachElementOfTypeFix.createByPredicate<JetEnumEntry>(
predicate = { DeclarationsChecker.enumEntryUsesDeprecatedOrNoDelimiter(it) },
taskProcessor = { insertLackingCommaSemicolon(it) },
modalTitle = "Replacing deprecated enum entry delimiter syntax",
name = "Insert lacking comma(s) / semicolon(s) in the whole project",
familyName = "Insert lacking comma(s) / semicolon(s) in the whole project"
)
}
private fun insertLackingCommaSemicolon(enumEntry: JetEnumEntry) {
val body = enumEntry.getParent() as JetClassBody
val entries = body.getChildrenOfType<JetEnumEntry>()
val psiFactory = JetPsiFactory(body)
for ((entryIndex, entry) in entries.withIndex()) {
var next = entry.getNextSiblingIgnoringWhitespaceAndComments()
var nextType = next?.getNode()?.getElementType()
if (entryIndex < entries.size() - 1) {
if (nextType != JetTokens.COMMA) {
// Classic case like ENUM_ENTRY1 ENUM_ENTRY2
body.addAfter(psiFactory.createComma(), entry)
}
}
else {
if (nextType == JetTokens.COMMA) {
// ENUM_ENTRY_LAST, fun foo()
next!!.replace(psiFactory.createSemicolon())
}
else if (nextType != JetTokens.SEMICOLON && nextType != JetTokens.RBRACE) {
// ENUM_ENTRY_LAST fun foo()
body.addAfter(psiFactory.createSemicolon(), entry)
}
}
}
}
}
}
@@ -328,5 +328,8 @@ public class QuickFixRegistrar {
QuickFixes.factories.put(ENUM_ENTRY_USES_DEPRECATED_SUPER_CONSTRUCTOR, DeprecatedEnumEntrySuperConstructorSyntaxFix.Companion);
QuickFixes.factories.put(ENUM_ENTRY_USES_DEPRECATED_SUPER_CONSTRUCTOR, DeprecatedEnumEntrySuperConstructorSyntaxFix.Companion.createWholeProjectFixFactory());
QuickFixes.factories.put(ENUM_ENTRY_USES_DEPRECATED_OR_NO_DELIMITER, DeprecatedEnumEntryDelimiterSyntaxFix.Companion);
QuickFixes.factories.put(ENUM_ENTRY_USES_DEPRECATED_OR_NO_DELIMITER, DeprecatedEnumEntryDelimiterSyntaxFix.Companion.createWholeProjectFixFactory());
}
}
@@ -0,0 +1,7 @@
// "Insert lacking comma(s) / semicolon(s)" "true"
enum class MyEnum {
FIRST,
SECOND<caret>,
val zzz = 42
}
@@ -0,0 +1,7 @@
// "Insert lacking comma(s) / semicolon(s)" "true"
enum class MyEnum {
FIRST,
SECOND;
val zzz = 42
}
@@ -0,0 +1,9 @@
// "Insert lacking comma(s) / semicolon(s)" "true"
enum class MyEnum {
FIRST SECOND,
THIRD
FOURTH<caret> FIFTH SIXTH,
SEVENTH EIGHTH
}
@@ -0,0 +1,9 @@
// "Insert lacking comma(s) / semicolon(s)" "true"
enum class MyEnum {
FIRST, SECOND,
THIRD,
FOURTH, FIFTH, SIXTH,
SEVENTH, EIGHTH
}
@@ -0,0 +1,6 @@
// "Insert lacking comma(s) / semicolon(s)" "true"
enum class MyEnum {
FIRST<caret> SECOND
}
@@ -0,0 +1,6 @@
// "Insert lacking comma(s) / semicolon(s)" "true"
enum class MyEnum {
FIRST, SECOND
}
@@ -0,0 +1,8 @@
// "Insert lacking comma(s) / semicolon(s)" "true"
enum class MyEnum {
FIRST<caret>
/* The first one */
SECOND
/* The last one */
}
@@ -0,0 +1,8 @@
// "Insert lacking comma(s) / semicolon(s)" "true"
enum class MyEnum {
FIRST<caret>,
/* The first one */
SECOND
/* The last one */
}
@@ -0,0 +1,6 @@
// "Insert lacking comma(s) / semicolon(s)" "true"
enum class MyEnum {
A B C D<caret> E F G H I J
fun foo() = 42
}
@@ -0,0 +1,6 @@
// "Insert lacking comma(s) / semicolon(s)" "true"
enum class MyEnum {
A, B, C, D, E, F, G, H, I, J;
fun foo() = 42
}
@@ -0,0 +1,8 @@
// "Insert lacking comma(s) / semicolon(s)" "true"
enum class MyEnum(val z: Int) {
A: MyEnum(3)
B<caret>: MyEnum(7)
C: MyEnum(12)
fun foo() = z * 2
}
@@ -0,0 +1,8 @@
// "Insert lacking comma(s) / semicolon(s)" "true"
enum class MyEnum(val z: Int) {
A: MyEnum(3),
B: MyEnum(7),
C: MyEnum(12);
fun foo() = z * 2
}
@@ -0,0 +1,11 @@
// "Insert lacking comma(s) / semicolon(s)" "true"
enum class MyEnum {
A {
override fun foo(): Int = 13
}
B C<caret> {
override fun foo(): Int = 23
}
open fun foo(): Int = 42
}
@@ -0,0 +1,11 @@
// "Insert lacking comma(s) / semicolon(s)" "true"
enum class MyEnum {
A {
override fun foo(): Int = 13
},
B, C<caret> {
override fun foo(): Int = 23
};
open fun foo(): Int = 42
}
@@ -0,0 +1,6 @@
// "Insert lacking comma(s) / semicolon(s)" "true"
enum class MyEnum(val z: Int) {
A(3) B<caret>(7) C(12)
fun foo() = z * 2
}
@@ -0,0 +1,6 @@
// "Insert lacking comma(s) / semicolon(s)" "true"
enum class MyEnum(val z: Int) {
A(3), B(7), C(12);
fun foo() = z * 2
}
@@ -0,0 +1,6 @@
// "Insert lacking comma(s) / semicolon(s)" "true"
enum class MyEnum {
FIRST, SECOND<caret>
val zzz = 42
}
@@ -0,0 +1,6 @@
// "Insert lacking comma(s) / semicolon(s)" "true"
enum class MyEnum {
FIRST, SECOND;
val zzz = 42
}
@@ -0,0 +1,7 @@
// "Insert lacking comma(s) / semicolon(s)" "true"
enum class MyEnum {
FIRST, SECOND<caret>
/* The last one*/
fun foo() = 1
}
@@ -0,0 +1,7 @@
// "Insert lacking comma(s) / semicolon(s)" "true"
enum class MyEnum {
FIRST, SECOND<caret>;
/* The last one*/
fun foo() = 1
}
@@ -0,0 +1,22 @@
// "Insert lacking comma(s) / semicolon(s) in the whole project" "true"
enum class First {
RED GREEN,
BLUE
}
enum class Second(val code: Int) {
NORTH(2) SOUTH(4),
EAST(6) WEST(8)
}
enum class Third {
OK {
override fun diag(): String = "OK"
}
ERROR<caret> {
override fun diag(): String = "Failed"
}
open fun diag(): String = ""
}
@@ -0,0 +1,22 @@
// "Insert lacking comma(s) / semicolon(s) in the whole project" "true"
enum class First {
RED, GREEN,
BLUE
}
enum class Second(val code: Int) {
NORTH(2), SOUTH(4),
EAST(6), WEST(8)
}
enum class Third {
OK {
override fun diag(): String = "OK"
},
ERROR {
override fun diag(): String = "Failed"
};
open fun diag(): String = ""
}
@@ -3082,6 +3082,81 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
}
}
@TestMetadata("idea/testData/quickfix/migration/enumDelimiter")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class EnumDelimiter extends AbstractQuickFixTest {
public void testAllFilesPresentInEnumDelimiter() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/migration/enumDelimiter"), Pattern.compile("^(\\w+)\\.kt$"), true);
}
@TestMetadata("commaNoSemicolon.kt")
public void testCommaNoSemicolon() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/migration/enumDelimiter/commaNoSemicolon.kt");
doTest(fileName);
}
@TestMetadata("missedCommas.kt")
public void testMissedCommas() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/migration/enumDelimiter/missedCommas.kt");
doTest(fileName);
}
@TestMetadata("noComma.kt")
public void testNoComma() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/migration/enumDelimiter/noComma.kt");
doTest(fileName);
}
@TestMetadata("noCommaComment.kt")
public void testNoCommaComment() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/migration/enumDelimiter/noCommaComment.kt");
doTest(fileName);
}
@TestMetadata("noDelimiter.kt")
public void testNoDelimiter() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/migration/enumDelimiter/noDelimiter.kt");
doTest(fileName);
}
@TestMetadata("noDelimiterWithInitializer.kt")
public void testNoDelimiterWithInitializer() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/migration/enumDelimiter/noDelimiterWithInitializer.kt");
doTest(fileName);
}
@TestMetadata("noDelimiterWithOverload.kt")
public void testNoDelimiterWithOverload() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/migration/enumDelimiter/noDelimiterWithOverload.kt");
doTest(fileName);
}
@TestMetadata("noDelimiterWithShortConstructor.kt")
public void testNoDelimiterWithShortConstructor() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/migration/enumDelimiter/noDelimiterWithShortConstructor.kt");
doTest(fileName);
}
@TestMetadata("noSemicolon.kt")
public void testNoSemicolon() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/migration/enumDelimiter/noSemicolon.kt");
doTest(fileName);
}
@TestMetadata("noSemicolonComment.kt")
public void testNoSemicolonComment() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/migration/enumDelimiter/noSemicolonComment.kt");
doTest(fileName);
}
@TestMetadata("wholeProject.kt")
public void testWholeProject() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/migration/enumDelimiter/wholeProject.kt");
doTest(fileName);
}
}
@TestMetadata("idea/testData/quickfix/migration/lambdaSyntax")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)