From 71b406d792c9b1faafd415ce2e2d81357ddfd591 Mon Sep 17 00:00:00 2001 From: Nikolay Krasko Date: Thu, 16 Jul 2015 15:29:19 +0300 Subject: [PATCH] Fix class kind detector: prioritize enum over annotations. Introduce new error about enum annotations classes. --- .../jetbrains/kotlin/psi/JetClassOrObject.kt | 14 +++-- .../kotlin/resolve/DeclarationsChecker.java | 13 +++-- .../resolve/lazy/data/JetClassInfo.java | 12 ++-- .../tests/enum/enumWithAnnotationKeyword.kt | 3 + .../tests/enum/enumWithAnnotationKeyword.txt | 27 +++++++++ .../testData/psi/EnumWithAnnotationKeyword.kt | 3 + .../psi/EnumWithAnnotationKeyword.txt | 35 ++++++++++++ .../testData/psi/InterfaceWithEnumKeyword.kt | 7 +++ .../testData/psi/InterfaceWithEnumKeyword.txt | 57 +++++++++++++++++++ .../checkers/JetDiagnosticsTestGenerated.java | 6 ++ .../parsing/JetParsingTestGenerated.java | 12 ++++ 11 files changed, 173 insertions(+), 16 deletions(-) create mode 100644 compiler/testData/diagnostics/tests/enum/enumWithAnnotationKeyword.kt create mode 100644 compiler/testData/diagnostics/tests/enum/enumWithAnnotationKeyword.txt create mode 100644 compiler/testData/psi/EnumWithAnnotationKeyword.kt create mode 100644 compiler/testData/psi/EnumWithAnnotationKeyword.txt create mode 100644 compiler/testData/psi/InterfaceWithEnumKeyword.kt create mode 100644 compiler/testData/psi/InterfaceWithEnumKeyword.txt diff --git a/compiler/frontend/src/org/jetbrains/kotlin/psi/JetClassOrObject.kt b/compiler/frontend/src/org/jetbrains/kotlin/psi/JetClassOrObject.kt index e41a4a0e9f1..ccd7a473169 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/psi/JetClassOrObject.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/psi/JetClassOrObject.kt @@ -66,15 +66,17 @@ abstract public class JetClassOrObject : JetTypeParameterListOwnerStub = getBody()?.getSecondaryConstructors().orEmpty() deprecated(value = "It's no more possible to determine it exactly using AST. Use ClassDescriptor methods instead, e.g. getKind()") - public fun isAnnotation(): Boolean = hasAnnotation(KotlinBuiltIns.FQ_NAMES.annotation.shortName().asString()) + public fun isAnnotation(): Boolean = getBuiltInAnnotationEntry() != null - private fun hasAnnotation(name: String): Boolean { - for (entry in getAnnotationEntries()) { + public fun getBuiltInAnnotationEntry(): JetAnnotationEntry? = getAnnotation(KotlinBuiltIns.FQ_NAMES.annotation.shortName().asString()) + + private fun getAnnotation(name: String): JetAnnotationEntry? { + return getAnnotationEntries().firstOrNull() { entry -> val typeReference = entry.getTypeReference() - val userType = typeReference?.getStubOrPsiChild(JetStubElementTypes.USER_TYPE) ?: continue - if (name == userType.getReferencedName()) return true + val userType = typeReference?.getStubOrPsiChild(JetStubElementTypes.USER_TYPE) + + name == userType?.getReferencedName() } - return false } public override fun delete() { diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/DeclarationsChecker.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/DeclarationsChecker.java index 0ef598639a6..d79a2ce185b 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/DeclarationsChecker.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/DeclarationsChecker.java @@ -254,15 +254,20 @@ public class DeclarationsChecker { checkTraitModifiers(aClass); checkConstructorInTrait(aClass); } - else if (classDescriptor.getKind() == ClassKind.ANNOTATION_CLASS) { - checkAnnotationClassWithBody(aClass); - checkValOnAnnotationParameter(aClass); - } else if (aClass.isEnum()) { checkEnumModifiers(aClass); if (aClass.isLocal()) { trace.report(LOCAL_ENUM_NOT_ALLOWED.on(aClass, classDescriptor)); } + if (classDescriptor.getKind() == ClassKind.ANNOTATION_CLASS) { + JetAnnotationEntry entry = aClass.getBuiltInAnnotationEntry(); + assert entry != null : "getBuiltinAnnotationEntry() should be synchronized with isAnnotation()"; + trace.report(WRONG_ANNOTATION_TARGET.on(entry, "enum class")); + } + } + else if (classDescriptor.getKind() == ClassKind.ANNOTATION_CLASS) { + checkAnnotationClassWithBody(aClass); + checkValOnAnnotationParameter(aClass); } else if (aClass.hasModifier(JetTokens.SEALED_KEYWORD)) { checkSealedModifiers(aClass); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/lazy/data/JetClassInfo.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/lazy/data/JetClassInfo.java index 2e5d0a5898d..5c8e8ff6215 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/lazy/data/JetClassInfo.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/lazy/data/JetClassInfo.java @@ -19,9 +19,9 @@ package org.jetbrains.kotlin.resolve.lazy.data; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.descriptors.ClassKind; -import org.jetbrains.kotlin.psi.*; - -import java.util.List; +import org.jetbrains.kotlin.psi.JetClass; +import org.jetbrains.kotlin.psi.JetEnumEntry; +import org.jetbrains.kotlin.psi.JetTypeParameterList; public class JetClassInfo extends JetClassOrObjectInfo { private final ClassKind kind; @@ -34,12 +34,12 @@ public class JetClassInfo extends JetClassOrObjectInfo { else if (element.isInterface()) { this.kind = ClassKind.INTERFACE; } - else if (element.isAnnotation()) { - this.kind = ClassKind.ANNOTATION_CLASS; - } else if (element.isEnum()) { this.kind = ClassKind.ENUM_CLASS; } + else if (element.isAnnotation()) { + this.kind = ClassKind.ANNOTATION_CLASS; + } else { this.kind = ClassKind.CLASS; } diff --git a/compiler/testData/diagnostics/tests/enum/enumWithAnnotationKeyword.kt b/compiler/testData/diagnostics/tests/enum/enumWithAnnotationKeyword.kt new file mode 100644 index 00000000000..2405fd9523a --- /dev/null +++ b/compiler/testData/diagnostics/tests/enum/enumWithAnnotationKeyword.kt @@ -0,0 +1,3 @@ +data annotation enum class E { + D +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/enum/enumWithAnnotationKeyword.txt b/compiler/testData/diagnostics/tests/enum/enumWithAnnotationKeyword.txt new file mode 100644 index 00000000000..68956113db3 --- /dev/null +++ b/compiler/testData/diagnostics/tests/enum/enumWithAnnotationKeyword.txt @@ -0,0 +1,27 @@ +package + +kotlin.data() kotlin.annotation.annotation() internal final enum class E : kotlin.Enum { + public enum entry D : E { + private constructor D() + public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: E): kotlin.Int + public final override /*1*/ /*fake_override*/ fun copy(): E + public final override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + 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 E() + public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: E): kotlin.Int + public final /*synthesized*/ fun copy(): E + public final override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + 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): E + public final /*synthesized*/ fun values(): kotlin.Array +} diff --git a/compiler/testData/psi/EnumWithAnnotationKeyword.kt b/compiler/testData/psi/EnumWithAnnotationKeyword.kt new file mode 100644 index 00000000000..d0db8d9d964 --- /dev/null +++ b/compiler/testData/psi/EnumWithAnnotationKeyword.kt @@ -0,0 +1,3 @@ +data annotation enum class E { + D +} \ No newline at end of file diff --git a/compiler/testData/psi/EnumWithAnnotationKeyword.txt b/compiler/testData/psi/EnumWithAnnotationKeyword.txt new file mode 100644 index 00000000000..55d95c601bc --- /dev/null +++ b/compiler/testData/psi/EnumWithAnnotationKeyword.txt @@ -0,0 +1,35 @@ +JetFile: EnumWithAnnotationKeyword.kt + PACKAGE_DIRECTIVE + + IMPORT_LIST + + CLASS + MODIFIER_LIST + ANNOTATION_ENTRY + CONSTRUCTOR_CALLEE + TYPE_REFERENCE + USER_TYPE + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('data') + PsiWhiteSpace(' ') + ANNOTATION_ENTRY + CONSTRUCTOR_CALLEE + TYPE_REFERENCE + USER_TYPE + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('annotation') + PsiWhiteSpace(' ') + PsiElement(enum)('enum') + PsiWhiteSpace(' ') + PsiElement(class)('class') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('E') + PsiWhiteSpace(' ') + CLASS_BODY + PsiElement(LBRACE)('{') + PsiWhiteSpace('\n ') + ENUM_ENTRY + OBJECT_DECLARATION_NAME + PsiElement(IDENTIFIER)('D') + PsiWhiteSpace('\n') + PsiElement(RBRACE)('}') \ No newline at end of file diff --git a/compiler/testData/psi/InterfaceWithEnumKeyword.kt b/compiler/testData/psi/InterfaceWithEnumKeyword.kt new file mode 100644 index 00000000000..1cca818e525 --- /dev/null +++ b/compiler/testData/psi/InterfaceWithEnumKeyword.kt @@ -0,0 +1,7 @@ +enum interface class E1 { + D +} + +interface enum class E2 { + D +} \ No newline at end of file diff --git a/compiler/testData/psi/InterfaceWithEnumKeyword.txt b/compiler/testData/psi/InterfaceWithEnumKeyword.txt new file mode 100644 index 00000000000..68312e43b83 --- /dev/null +++ b/compiler/testData/psi/InterfaceWithEnumKeyword.txt @@ -0,0 +1,57 @@ +JetFile: InterfaceWithEnumKeyword.kt + PACKAGE_DIRECTIVE + + IMPORT_LIST + + CLASS + MODIFIER_LIST + PsiElement(enum)('enum') + PsiWhiteSpace(' ') + PsiElement(interface)('interface') + PsiErrorElement:Name expected + + PsiWhiteSpace(' ') + CLASS + PsiElement(class)('class') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('E1') + PsiWhiteSpace(' ') + CLASS_BODY + PsiElement(LBRACE)('{') + PsiWhiteSpace('\n ') + MODIFIER_LIST + ANNOTATION_ENTRY + CONSTRUCTOR_CALLEE + TYPE_REFERENCE + USER_TYPE + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('D') + PsiErrorElement:Expecting member declaration + + PsiWhiteSpace('\n') + PsiElement(RBRACE)('}') + PsiWhiteSpace('\n\n') + CLASS + PsiElement(interface)('interface') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('enum') + PsiWhiteSpace(' ') + CLASS + PsiElement(class)('class') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('E2') + PsiWhiteSpace(' ') + CLASS_BODY + PsiElement(LBRACE)('{') + PsiWhiteSpace('\n ') + MODIFIER_LIST + ANNOTATION_ENTRY + CONSTRUCTOR_CALLEE + TYPE_REFERENCE + USER_TYPE + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('D') + 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 c0dda0e8434..531d101c161 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java @@ -5133,6 +5133,12 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest { doTest(fileName); } + @TestMetadata("enumWithAnnotationKeyword.kt") + public void testEnumWithAnnotationKeyword() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/enum/enumWithAnnotationKeyword.kt"); + doTest(fileName); + } + @TestMetadata("enumWithEmptyName.kt") public void testEnumWithEmptyName() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/enum/enumWithEmptyName.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/parsing/JetParsingTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/parsing/JetParsingTestGenerated.java index c08487d9191..defaa843337 100644 --- a/compiler/tests/org/jetbrains/kotlin/parsing/JetParsingTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/parsing/JetParsingTestGenerated.java @@ -259,6 +259,12 @@ public class JetParsingTestGenerated extends AbstractJetParsingTest { doParsingTest(fileName); } + @TestMetadata("EnumWithAnnotationKeyword.kt") + public void testEnumWithAnnotationKeyword() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/psi/EnumWithAnnotationKeyword.kt"); + doParsingTest(fileName); + } + @TestMetadata("Enums.kt") public void testEnums() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/psi/Enums.kt"); @@ -415,6 +421,12 @@ public class JetParsingTestGenerated extends AbstractJetParsingTest { doParsingTest(fileName); } + @TestMetadata("InterfaceWithEnumKeyword.kt") + public void testInterfaceWithEnumKeyword() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/psi/InterfaceWithEnumKeyword.kt"); + doParsingTest(fileName); + } + @TestMetadata("Labels.kt") public void testLabels() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/psi/Labels.kt");