From 983339e1c9bf7fdf3ce8c0cef1a000816b97d66c Mon Sep 17 00:00:00 2001 From: Mikhail Glukhikh Date: Tue, 12 May 2015 15:31:04 +0300 Subject: [PATCH] Detection of semicolon between enum entries together with the correct delimiter quick fix for this case. Error message reported directly on semicolon. --- .../jetbrains/kotlin/parsing/JetParsing.java | 32 +++++-- .../tests/enum/enumCommaSemicolonBetween.kt | 10 +++ .../tests/enum/enumCommaSemicolonBetween.txt | 60 +++++++++++++ .../tests/enum/enumSemicolonBetween.kt | 8 ++ .../tests/enum/enumSemicolonBetween.txt | 60 +++++++++++++ compiler/testData/psi/EnumSemicolonBetween.kt | 6 ++ .../testData/psi/EnumSemicolonBetween.txt | 37 ++++++++ .../psi/EnumSemicolonBetweenWithMembers.kt | 10 +++ .../psi/EnumSemicolonBetweenWithMembers.txt | 87 +++++++++++++++++++ .../checkers/JetDiagnosticsTestGenerated.java | 12 +++ .../parsing/JetParsingTestGenerated.java | 12 +++ .../DeprecatedEnumEntryDelimiterSyntaxFix.kt | 7 +- .../enumDelimiter/commaSemicolonDelimiter.kt | 6 ++ .../commaSemicolonDelimiter.kt.after | 6 ++ .../enumDelimiter/semicolonDelimiter.kt | 6 ++ .../enumDelimiter/semicolonDelimiter.kt.after | 6 ++ .../semicolonDelimiterExceptLast.kt | 6 ++ .../semicolonDelimiterExceptLast.kt.after | 6 ++ .../idea/quickfix/QuickFixTestGenerated.java | 18 ++++ 19 files changed, 387 insertions(+), 8 deletions(-) create mode 100644 compiler/testData/diagnostics/tests/enum/enumCommaSemicolonBetween.kt create mode 100644 compiler/testData/diagnostics/tests/enum/enumCommaSemicolonBetween.txt create mode 100644 compiler/testData/diagnostics/tests/enum/enumSemicolonBetween.kt create mode 100644 compiler/testData/diagnostics/tests/enum/enumSemicolonBetween.txt create mode 100644 compiler/testData/psi/EnumSemicolonBetween.kt create mode 100644 compiler/testData/psi/EnumSemicolonBetween.txt create mode 100644 compiler/testData/psi/EnumSemicolonBetweenWithMembers.kt create mode 100644 compiler/testData/psi/EnumSemicolonBetweenWithMembers.txt create mode 100644 idea/testData/quickfix/migration/enumDelimiter/commaSemicolonDelimiter.kt create mode 100644 idea/testData/quickfix/migration/enumDelimiter/commaSemicolonDelimiter.kt.after create mode 100644 idea/testData/quickfix/migration/enumDelimiter/semicolonDelimiter.kt create mode 100644 idea/testData/quickfix/migration/enumDelimiter/semicolonDelimiter.kt.after create mode 100644 idea/testData/quickfix/migration/enumDelimiter/semicolonDelimiterExceptLast.kt create mode 100644 idea/testData/quickfix/migration/enumDelimiter/semicolonDelimiterExceptLast.kt.after diff --git a/compiler/frontend/src/org/jetbrains/kotlin/parsing/JetParsing.java b/compiler/frontend/src/org/jetbrains/kotlin/parsing/JetParsing.java index ff2274faffc..26a3c8a304e 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/parsing/JetParsing.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/parsing/JetParsing.java @@ -622,7 +622,7 @@ public class JetParsing extends AbstractJetParsing { public enum NameParsingMode { REQUIRED, ALLOWED, - PROHIBITED; + PROHIBITED } /* @@ -771,8 +771,29 @@ public class JetParsing extends AbstractJetParsing { if (!parseEnumEntry()) { break; } - // TODO: syntax without COMMA is deprecated (only last entry is an exception), KT-7605 - consumeIf(COMMA); + // TODO: syntax with SEMICOLON between enum entries is deprecated, KT-7605 + if (at(SEMICOLON)) { + // Semicolon can be legally here only if member follows + PsiBuilder.Marker temp = mark(); + advance(); // SEMICOLON + ModifierDetector detector = new ModifierDetector(); + parseModifierList(detector, ONLY_ESCAPED_REGULAR_ANNOTATIONS); + if (!atSet(SOFT_KEYWORDS_AT_MEMBER_START) && at(IDENTIFIER)) { + // Otherwise it's old syntax that's not supported + temp.rollbackTo(); + // Despite of the error, try to restore and parse next enum entry + temp = mark(); + advance(); // SEMICOLON + temp.error("Expecting ','"); + } + else { + temp.rollbackTo(); + } + } + else { + // TODO: syntax without COMMA is deprecated (only last entry is an exception), KT-7605 + consumeIf(COMMA); + } } } @@ -903,10 +924,7 @@ public class JetParsing extends AbstractJetParsing { private IElementType parseMemberDeclarationRest(boolean isEnum, boolean isDefault) { IElementType keywordToken = tt(); IElementType declType = null; - if (keywordToken == CLASS_KEYWORD) { - declType = parseClass(isEnum); - } - else if (keywordToken == TRAIT_KEYWORD || keywordToken == INTERFACE_KEYWORD) { + if (keywordToken == CLASS_KEYWORD || keywordToken == TRAIT_KEYWORD || keywordToken == INTERFACE_KEYWORD) { declType = parseClass(isEnum); } else if (keywordToken == FUN_KEYWORD) { diff --git a/compiler/testData/diagnostics/tests/enum/enumCommaSemicolonBetween.kt b/compiler/testData/diagnostics/tests/enum/enumCommaSemicolonBetween.kt new file mode 100644 index 00000000000..0ba8d8ed041 --- /dev/null +++ b/compiler/testData/diagnostics/tests/enum/enumCommaSemicolonBetween.kt @@ -0,0 +1,10 @@ +enum class MyEnum { + // Should be comma instead of semicolon + A; + B, + C; + // Semicolon missed, comma is optional + D, + + fun foo() = 0 +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/enum/enumCommaSemicolonBetween.txt b/compiler/testData/diagnostics/tests/enum/enumCommaSemicolonBetween.txt new file mode 100644 index 00000000000..980df59bd12 --- /dev/null +++ b/compiler/testData/diagnostics/tests/enum/enumCommaSemicolonBetween.txt @@ -0,0 +1,60 @@ +package + +internal final enum class MyEnum : kotlin.Enum { + public enum entry A : MyEnum { + private constructor A() + public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: MyEnum): kotlin.Int + public final override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + internal final override /*1*/ /*fake_override*/ fun foo(): kotlin.Int + public final override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public final override /*1*/ /*fake_override*/ fun name(): kotlin.String + public final override /*1*/ /*fake_override*/ fun ordinal(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } + + public enum entry B : MyEnum { + private constructor B() + public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: MyEnum): kotlin.Int + public final override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + internal final override /*1*/ /*fake_override*/ fun foo(): kotlin.Int + public final override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public final override /*1*/ /*fake_override*/ fun name(): kotlin.String + public final override /*1*/ /*fake_override*/ fun ordinal(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } + + public enum entry C : MyEnum { + private constructor C() + public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: MyEnum): kotlin.Int + public final override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + internal final override /*1*/ /*fake_override*/ fun foo(): kotlin.Int + public final override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public final override /*1*/ /*fake_override*/ fun name(): kotlin.String + public final override /*1*/ /*fake_override*/ fun ordinal(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } + + public enum entry D : MyEnum { + private constructor D() + public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: MyEnum): kotlin.Int + public final override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + internal final override /*1*/ /*fake_override*/ fun foo(): kotlin.Int + public final override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public final override /*1*/ /*fake_override*/ fun name(): kotlin.String + public final override /*1*/ /*fake_override*/ fun ordinal(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } + + private constructor MyEnum() + public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: MyEnum): kotlin.Int + public final override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + internal final fun foo(): kotlin.Int + public final override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public final override /*1*/ /*fake_override*/ fun name(): kotlin.String + public final override /*1*/ /*fake_override*/ fun ordinal(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + + // Static members + public final /*synthesized*/ fun valueOf(/*0*/ value: kotlin.String): MyEnum + public final /*synthesized*/ fun values(): kotlin.Array +} diff --git a/compiler/testData/diagnostics/tests/enum/enumSemicolonBetween.kt b/compiler/testData/diagnostics/tests/enum/enumSemicolonBetween.kt new file mode 100644 index 00000000000..02bbdf073e6 --- /dev/null +++ b/compiler/testData/diagnostics/tests/enum/enumSemicolonBetween.kt @@ -0,0 +1,8 @@ +enum class MyEnum { + A; + B; + C; + D + + fun foo() = 0 +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/enum/enumSemicolonBetween.txt b/compiler/testData/diagnostics/tests/enum/enumSemicolonBetween.txt new file mode 100644 index 00000000000..980df59bd12 --- /dev/null +++ b/compiler/testData/diagnostics/tests/enum/enumSemicolonBetween.txt @@ -0,0 +1,60 @@ +package + +internal final enum class MyEnum : kotlin.Enum { + public enum entry A : MyEnum { + private constructor A() + public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: MyEnum): kotlin.Int + public final override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + internal final override /*1*/ /*fake_override*/ fun foo(): kotlin.Int + public final override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public final override /*1*/ /*fake_override*/ fun name(): kotlin.String + public final override /*1*/ /*fake_override*/ fun ordinal(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } + + public enum entry B : MyEnum { + private constructor B() + public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: MyEnum): kotlin.Int + public final override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + internal final override /*1*/ /*fake_override*/ fun foo(): kotlin.Int + public final override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public final override /*1*/ /*fake_override*/ fun name(): kotlin.String + public final override /*1*/ /*fake_override*/ fun ordinal(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } + + public enum entry C : MyEnum { + private constructor C() + public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: MyEnum): kotlin.Int + public final override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + internal final override /*1*/ /*fake_override*/ fun foo(): kotlin.Int + public final override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public final override /*1*/ /*fake_override*/ fun name(): kotlin.String + public final override /*1*/ /*fake_override*/ fun ordinal(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } + + public enum entry D : MyEnum { + private constructor D() + public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: MyEnum): kotlin.Int + public final override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + internal final override /*1*/ /*fake_override*/ fun foo(): kotlin.Int + public final override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public final override /*1*/ /*fake_override*/ fun name(): kotlin.String + public final override /*1*/ /*fake_override*/ fun ordinal(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } + + private constructor MyEnum() + public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: MyEnum): kotlin.Int + public final override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + internal final fun foo(): kotlin.Int + public final override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public final override /*1*/ /*fake_override*/ fun name(): kotlin.String + public final override /*1*/ /*fake_override*/ fun ordinal(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + + // Static members + public final /*synthesized*/ fun valueOf(/*0*/ value: kotlin.String): MyEnum + public final /*synthesized*/ fun values(): kotlin.Array +} diff --git a/compiler/testData/psi/EnumSemicolonBetween.kt b/compiler/testData/psi/EnumSemicolonBetween.kt new file mode 100644 index 00000000000..5ce341826c7 --- /dev/null +++ b/compiler/testData/psi/EnumSemicolonBetween.kt @@ -0,0 +1,6 @@ +enum class Color { + NORTH; + SOUTH; + WEST; + EAST +} \ No newline at end of file diff --git a/compiler/testData/psi/EnumSemicolonBetween.txt b/compiler/testData/psi/EnumSemicolonBetween.txt new file mode 100644 index 00000000000..4ac708a8527 --- /dev/null +++ b/compiler/testData/psi/EnumSemicolonBetween.txt @@ -0,0 +1,37 @@ +JetFile: EnumSemicolonBetween.kt + PACKAGE_DIRECTIVE + + CLASS + MODIFIER_LIST + PsiElement(enum)('enum') + PsiWhiteSpace(' ') + PsiElement(class)('class') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('Color') + PsiWhiteSpace(' ') + CLASS_BODY + PsiElement(LBRACE)('{') + PsiWhiteSpace('\n ') + ENUM_ENTRY + OBJECT_DECLARATION_NAME + PsiElement(IDENTIFIER)('NORTH') + PsiErrorElement:Expecting ',' + PsiElement(SEMICOLON)(';') + PsiWhiteSpace('\n ') + ENUM_ENTRY + OBJECT_DECLARATION_NAME + PsiElement(IDENTIFIER)('SOUTH') + PsiErrorElement:Expecting ',' + PsiElement(SEMICOLON)(';') + PsiWhiteSpace('\n ') + ENUM_ENTRY + OBJECT_DECLARATION_NAME + PsiElement(IDENTIFIER)('WEST') + PsiErrorElement:Expecting ',' + PsiElement(SEMICOLON)(';') + PsiWhiteSpace('\n ') + ENUM_ENTRY + OBJECT_DECLARATION_NAME + PsiElement(IDENTIFIER)('EAST') + PsiWhiteSpace('\n') + PsiElement(RBRACE)('}') \ No newline at end of file diff --git a/compiler/testData/psi/EnumSemicolonBetweenWithMembers.kt b/compiler/testData/psi/EnumSemicolonBetweenWithMembers.kt new file mode 100644 index 00000000000..8c1bea46333 --- /dev/null +++ b/compiler/testData/psi/EnumSemicolonBetweenWithMembers.kt @@ -0,0 +1,10 @@ +enum class Color { + NORTH; + fun foo() = 1 + SOUTH; + companion object { + fun bar() = 2 + } + WEST; + EAST +} \ No newline at end of file diff --git a/compiler/testData/psi/EnumSemicolonBetweenWithMembers.txt b/compiler/testData/psi/EnumSemicolonBetweenWithMembers.txt new file mode 100644 index 00000000000..8940dc5a323 --- /dev/null +++ b/compiler/testData/psi/EnumSemicolonBetweenWithMembers.txt @@ -0,0 +1,87 @@ +JetFile: EnumSemicolonBetweenWithMembers.kt + PACKAGE_DIRECTIVE + + CLASS + MODIFIER_LIST + PsiElement(enum)('enum') + PsiWhiteSpace(' ') + PsiElement(class)('class') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('Color') + PsiWhiteSpace(' ') + CLASS_BODY + PsiElement(LBRACE)('{') + PsiWhiteSpace('\n ') + ENUM_ENTRY + OBJECT_DECLARATION_NAME + PsiElement(IDENTIFIER)('NORTH') + PsiElement(SEMICOLON)(';') + PsiWhiteSpace('\n ') + FUN + PsiElement(fun)('fun') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('foo') + VALUE_PARAMETER_LIST + PsiElement(LPAR)('(') + PsiElement(RPAR)(')') + PsiWhiteSpace(' ') + PsiElement(EQ)('=') + PsiWhiteSpace(' ') + INTEGER_CONSTANT + PsiElement(INTEGER_LITERAL)('1') + PsiWhiteSpace('\n ') + MODIFIER_LIST + ANNOTATION_ENTRY + CONSTRUCTOR_CALLEE + TYPE_REFERENCE + USER_TYPE + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('SOUTH') + PsiErrorElement:Expecting member declaration + PsiElement(SEMICOLON)(';') + PsiWhiteSpace('\n ') + OBJECT_DECLARATION + MODIFIER_LIST + PsiElement(companion)('companion') + PsiWhiteSpace(' ') + PsiElement(object)('object') + PsiWhiteSpace(' ') + CLASS_BODY + PsiElement(LBRACE)('{') + PsiWhiteSpace('\n ') + FUN + PsiElement(fun)('fun') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('bar') + VALUE_PARAMETER_LIST + PsiElement(LPAR)('(') + PsiElement(RPAR)(')') + PsiWhiteSpace(' ') + PsiElement(EQ)('=') + PsiWhiteSpace(' ') + INTEGER_CONSTANT + PsiElement(INTEGER_LITERAL)('2') + PsiWhiteSpace('\n ') + PsiElement(RBRACE)('}') + PsiWhiteSpace('\n ') + MODIFIER_LIST + ANNOTATION_ENTRY + CONSTRUCTOR_CALLEE + TYPE_REFERENCE + USER_TYPE + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('WEST') + PsiErrorElement:Expecting member declaration + PsiElement(SEMICOLON)(';') + PsiWhiteSpace('\n ') + MODIFIER_LIST + ANNOTATION_ENTRY + CONSTRUCTOR_CALLEE + TYPE_REFERENCE + USER_TYPE + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('EAST') + PsiErrorElement:Expecting member declaration + + PsiWhiteSpace('\n') + PsiElement(RBRACE)('}') \ No newline at end of file diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java index 21adbd335d7..c3aa35caf79 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java @@ -4182,6 +4182,12 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest { doTest(fileName); } + @TestMetadata("enumCommaSemicolonBetween.kt") + public void testEnumCommaSemicolonBetween() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/enum/enumCommaSemicolonBetween.kt"); + doTest(fileName); + } + @TestMetadata("enumEntryCannotHaveClassObject.kt") public void testEnumEntryCannotHaveClassObject() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/enum/enumEntryCannotHaveClassObject.kt"); @@ -4260,6 +4266,12 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest { doTest(fileName); } + @TestMetadata("enumSemicolonBetween.kt") + public void testEnumSemicolonBetween() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/enum/enumSemicolonBetween.kt"); + doTest(fileName); + } + @TestMetadata("enumStarImport.kt") public void testEnumStarImport() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/enum/enumStarImport.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/parsing/JetParsingTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/parsing/JetParsingTestGenerated.java index 0a2f4f464f3..52a5a9470f3 100644 --- a/compiler/tests/org/jetbrains/kotlin/parsing/JetParsingTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/parsing/JetParsingTestGenerated.java @@ -223,6 +223,18 @@ public class JetParsingTestGenerated extends AbstractJetParsingTest { doParsingTest(fileName); } + @TestMetadata("EnumSemicolonBetween.kt") + public void testEnumSemicolonBetween() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/psi/EnumSemicolonBetween.kt"); + doParsingTest(fileName); + } + + @TestMetadata("EnumSemicolonBetweenWithMembers.kt") + public void testEnumSemicolonBetweenWithMembers() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/psi/EnumSemicolonBetweenWithMembers.kt"); + doParsingTest(fileName); + } + @TestMetadata("EnumShortCommas.kt") public void testEnumShortCommas() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/psi/EnumShortCommas.kt"); diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/DeprecatedEnumEntryDelimiterSyntaxFix.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/DeprecatedEnumEntryDelimiterSyntaxFix.kt index b38a3501c71..a5d52d9521a 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/DeprecatedEnumEntryDelimiterSyntaxFix.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/DeprecatedEnumEntryDelimiterSyntaxFix.kt @@ -20,6 +20,7 @@ import com.intellij.codeInsight.intention.IntentionAction import com.intellij.openapi.editor.Editor import com.intellij.openapi.project.Project import com.intellij.psi.PsiElement +import com.intellij.psi.PsiErrorElement import org.jetbrains.kotlin.diagnostics.Diagnostic import org.jetbrains.kotlin.idea.quickfix.quickfixUtil.createIntentionFactory import org.jetbrains.kotlin.idea.quickfix.quickfixUtil.createIntentionForFirstParentOfType @@ -62,7 +63,11 @@ class DeprecatedEnumEntryDelimiterSyntaxFix(element: JetEnumEntry): JetIntention var next = entry.getNextSiblingIgnoringWhitespaceAndComments() var nextType = next?.getNode()?.getElementType() if (entryIndex < entries.size() - 1) { - if (nextType != JetTokens.COMMA) { + if (next is PsiErrorElement && next.getFirstChild()?.getNode()?.getElementType() == JetTokens.SEMICOLON) { + // Fix for syntax error like ENUM_ENTRY1; ENUM_ENTRY2; ENUM_ENTRY3 + next.replace(psiFactory.createComma()) + } + else if (nextType != JetTokens.COMMA) { // Classic case like ENUM_ENTRY1 ENUM_ENTRY2 body.addAfter(psiFactory.createComma(), entry) } diff --git a/idea/testData/quickfix/migration/enumDelimiter/commaSemicolonDelimiter.kt b/idea/testData/quickfix/migration/enumDelimiter/commaSemicolonDelimiter.kt new file mode 100644 index 00000000000..3c743725856 --- /dev/null +++ b/idea/testData/quickfix/migration/enumDelimiter/commaSemicolonDelimiter.kt @@ -0,0 +1,6 @@ +// "Insert lacking comma(s) / semicolon(s)" "true" + +enum class MyEnum { + A, B; C D, + fun foo() = 42 +} \ No newline at end of file diff --git a/idea/testData/quickfix/migration/enumDelimiter/commaSemicolonDelimiter.kt.after b/idea/testData/quickfix/migration/enumDelimiter/commaSemicolonDelimiter.kt.after new file mode 100644 index 00000000000..8cfac6011cb --- /dev/null +++ b/idea/testData/quickfix/migration/enumDelimiter/commaSemicolonDelimiter.kt.after @@ -0,0 +1,6 @@ +// "Insert lacking comma(s) / semicolon(s)" "true" + +enum class MyEnum { + A, B, C, D; + fun foo() = 42 +} \ No newline at end of file diff --git a/idea/testData/quickfix/migration/enumDelimiter/semicolonDelimiter.kt b/idea/testData/quickfix/migration/enumDelimiter/semicolonDelimiter.kt new file mode 100644 index 00000000000..f3e9bf0e0d3 --- /dev/null +++ b/idea/testData/quickfix/migration/enumDelimiter/semicolonDelimiter.kt @@ -0,0 +1,6 @@ +// "Insert lacking comma(s) / semicolon(s)" "true" + +enum class MyEnum { + A; B; C; D; + fun foo() = 42 +} \ No newline at end of file diff --git a/idea/testData/quickfix/migration/enumDelimiter/semicolonDelimiter.kt.after b/idea/testData/quickfix/migration/enumDelimiter/semicolonDelimiter.kt.after new file mode 100644 index 00000000000..b247ee9c031 --- /dev/null +++ b/idea/testData/quickfix/migration/enumDelimiter/semicolonDelimiter.kt.after @@ -0,0 +1,6 @@ +// "Insert lacking comma(s) / semicolon(s)" "true" + +enum class MyEnum { + A, B, C, D; + fun foo() = 42 +} \ No newline at end of file diff --git a/idea/testData/quickfix/migration/enumDelimiter/semicolonDelimiterExceptLast.kt b/idea/testData/quickfix/migration/enumDelimiter/semicolonDelimiterExceptLast.kt new file mode 100644 index 00000000000..937dd48a88d --- /dev/null +++ b/idea/testData/quickfix/migration/enumDelimiter/semicolonDelimiterExceptLast.kt @@ -0,0 +1,6 @@ +// "Insert lacking comma(s) / semicolon(s)" "true" + +enum class MyEnum { + A; B; C; D + fun foo() = 42 +} \ No newline at end of file diff --git a/idea/testData/quickfix/migration/enumDelimiter/semicolonDelimiterExceptLast.kt.after b/idea/testData/quickfix/migration/enumDelimiter/semicolonDelimiterExceptLast.kt.after new file mode 100644 index 00000000000..8cfac6011cb --- /dev/null +++ b/idea/testData/quickfix/migration/enumDelimiter/semicolonDelimiterExceptLast.kt.after @@ -0,0 +1,6 @@ +// "Insert lacking comma(s) / semicolon(s)" "true" + +enum class MyEnum { + A, B, C, D; + fun foo() = 42 +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java index 1621dc75e32..31088b47dac 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java @@ -3096,6 +3096,12 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest { doTest(fileName); } + @TestMetadata("commaSemicolonDelimiter.kt") + public void testCommaSemicolonDelimiter() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/migration/enumDelimiter/commaSemicolonDelimiter.kt"); + doTest(fileName); + } + @TestMetadata("missedCommas.kt") public void testMissedCommas() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/migration/enumDelimiter/missedCommas.kt"); @@ -3150,6 +3156,18 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest { doTest(fileName); } + @TestMetadata("semicolonDelimiter.kt") + public void testSemicolonDelimiter() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/migration/enumDelimiter/semicolonDelimiter.kt"); + doTest(fileName); + } + + @TestMetadata("semicolonDelimiterExceptLast.kt") + public void testSemicolonDelimiterExceptLast() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/migration/enumDelimiter/semicolonDelimiterExceptLast.kt"); + doTest(fileName); + } + @TestMetadata("wholeProject.kt") public void testWholeProject() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/migration/enumDelimiter/wholeProject.kt");