From 37fd9cea083d97ab91d25d6f710b0596f949a24f Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Mon, 17 Sep 2012 17:45:30 +0400 Subject: [PATCH] Place inner enum into its parent's class object #KT-2771 In Progress ClassifierCollector now collects all enum declarations inside non-static classes. After all declarations are done, it processes these enums: creates a class object if it's not present, reports errors on each enum if a class object is not allowed, and puts every enum into this class object otherwise, recording this fact to trace. --- .../jet/lang/diagnostics/Errors.java | 1 + .../rendering/DefaultErrorMessages.java | 1 + .../jet/lang/resolve/BindingContext.java | 2 + .../lang/resolve/TypeHierarchyResolver.java | 63 ++++++++++++++++ compiler/testData/codegen/enum/inner.kt | 7 ++ .../enum/innerWithExistingClassObject.kt | 8 +++ .../tests/enum/inner/existingClassObject.kt | 19 +++++ .../tests/enum/inner/insideClass.kt | 5 ++ .../tests/enum/inner/insideClassObject.kt | 7 ++ .../tests/enum/inner/insideEnum.kt | 7 ++ .../tests/enum/inner/insideEnumEntry.kt | 7 ++ .../enum/inner/insideInnerClassNotAllowed.kt | 7 ++ .../tests/enum/inner/insideObject.kt | 5 ++ .../tests/enum/inner/insideTrait.kt | 5 ++ .../enum/inner/redeclarationInClassObject.kt | 11 +++ .../diagnostics/tests/enum/inner/twoEnums.kt | 4 ++ .../twoEnumsInClassObjectAndInnerClass.kt | 9 +++ .../checkers/JetDiagnosticsTestGenerated.java | 72 ++++++++++++++++++- .../jetbrains/jet/codegen/EnumGenTest.java | 8 +++ 19 files changed, 247 insertions(+), 1 deletion(-) create mode 100644 compiler/testData/codegen/enum/inner.kt create mode 100644 compiler/testData/codegen/enum/innerWithExistingClassObject.kt create mode 100644 compiler/testData/diagnostics/tests/enum/inner/existingClassObject.kt create mode 100644 compiler/testData/diagnostics/tests/enum/inner/insideClass.kt create mode 100644 compiler/testData/diagnostics/tests/enum/inner/insideClassObject.kt create mode 100644 compiler/testData/diagnostics/tests/enum/inner/insideEnum.kt create mode 100644 compiler/testData/diagnostics/tests/enum/inner/insideEnumEntry.kt create mode 100644 compiler/testData/diagnostics/tests/enum/inner/insideInnerClassNotAllowed.kt create mode 100644 compiler/testData/diagnostics/tests/enum/inner/insideObject.kt create mode 100644 compiler/testData/diagnostics/tests/enum/inner/insideTrait.kt create mode 100644 compiler/testData/diagnostics/tests/enum/inner/redeclarationInClassObject.kt create mode 100644 compiler/testData/diagnostics/tests/enum/inner/twoEnums.kt create mode 100644 compiler/testData/diagnostics/tests/enum/inner/twoEnumsInClassObjectAndInnerClass.kt diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/Errors.java index 26b45a7268e..500918719f7 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/Errors.java @@ -159,6 +159,7 @@ public interface Errors { DiagnosticFactory1 ENUM_ENTRY_SHOULD_BE_INITIALIZED = DiagnosticFactory1.create(ERROR, NAME_IDENTIFIER); DiagnosticFactory1 ENUM_ENTRY_ILLEGAL_TYPE = DiagnosticFactory1.create(ERROR); + SimpleDiagnosticFactory ENUM_NOT_ALLOWED = SimpleDiagnosticFactory.create(ERROR); DiagnosticFactory1 UNINITIALIZED_VARIABLE = DiagnosticFactory1.create(ERROR); DiagnosticFactory1 UNINITIALIZED_PARAMETER = DiagnosticFactory1.create(ERROR); diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/rendering/DefaultErrorMessages.java b/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/rendering/DefaultErrorMessages.java index aa478e2bf33..adb4ec7b05b 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/rendering/DefaultErrorMessages.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/rendering/DefaultErrorMessages.java @@ -170,6 +170,7 @@ public class DefaultErrorMessages { MAP.put(ENUM_ENTRY_SHOULD_BE_INITIALIZED, "Missing delegation specifier ''{0}''", NAME); MAP.put(ENUM_ENTRY_ILLEGAL_TYPE, "The type constructor of enum entry should be ''{0}''", NAME); + MAP.put(ENUM_NOT_ALLOWED, "Enum class is not allowed here"); MAP.put(UNINITIALIZED_VARIABLE, "Variable ''{0}'' must be initialized", NAME); MAP.put(UNINITIALIZED_PARAMETER, "Parameter ''{0}'' is uninitialized here", NAME); diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BindingContext.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BindingContext.java index 4d2d57f4017..48fe2cb0e8e 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BindingContext.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BindingContext.java @@ -230,6 +230,8 @@ public interface BindingContext { ReadOnlySlice DECLARATION_TO_DESCRIPTOR = Slices.sliceBuilder() .setFurtherLookupSlices(DECLARATIONS_TO_DESCRIPTORS).build(); + WritableSlice IS_ENUM_MOVED_TO_CLASS_OBJECT = Slices.createSimpleSlice(); + WritableSlice LABEL_TARGET = Slices.sliceBuilder().build(); WritableSlice VALUE_PARAMETER_AS_PROPERTY = Slices.sliceBuilder().build(); diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/TypeHierarchyResolver.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/TypeHierarchyResolver.java index 1910ed6cf9d..09edbc218c6 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/TypeHierarchyResolver.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/TypeHierarchyResolver.java @@ -19,6 +19,7 @@ package org.jetbrains.jet.lang.resolve; import com.google.common.collect.Lists; import com.google.common.collect.Multimap; import com.google.common.collect.Sets; +import com.intellij.lang.ASTNode; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiNameIdentifierOwner; import com.intellij.util.containers.ContainerUtil; @@ -201,6 +202,8 @@ public class TypeHierarchyResolver { declaration.accept(collector); } + collector.finishProcessing(); + return forDeferredResolve; } @@ -473,6 +476,8 @@ public class TypeHierarchyResolver { private final NamespaceLikeBuilder owner; private final Collection forDeferredResolve; + private final List enumsToAddLater = new ArrayList(0); + public ClassifierCollector(@NotNull JetScope outerScope, @NotNull NamespaceLikeBuilder owner, @NotNull Collection forDeferredResolve @@ -502,12 +507,25 @@ public class TypeHierarchyResolver { @Override public void visitClass(JetClass klass) { + if (getClassKind(klass) == ClassKind.ENUM_CLASS && inClass()) { + // Enums inside non-static context are put not to owner, but rather into its class object. + // This is handled after visiting all declarations in this class, because we may or may not + // encounter class object to put this enum into. + enumsToAddLater.add(klass); + return; + } MutableClassDescriptor mutableClassDescriptor = createClassDescriptorForClass(klass, owner.getOwnerForChildren()); owner.addClassifierDescriptor(mutableClassDescriptor); } + private boolean inClass() { + if (!(owner.getOwnerForChildren() instanceof ClassDescriptor)) return false; + ClassKind kind = ((ClassDescriptor) owner.getOwnerForChildren()).getKind(); + return kind == ClassKind.CLASS || kind == ClassKind.ENUM_CLASS || kind == ClassKind.TRAIT; + } + @Override public void visitObjectDeclaration(JetObjectDeclaration declaration) { MutableClassDescriptor objectDescriptor = @@ -657,5 +675,50 @@ public class TypeHierarchyResolver { context.normalScope.put(container, outerScope); context.forDeferredResolver.put(container, withDeferredResolve); } + + @Nullable + private MutableClassDescriptor getOrCreateClassObjectDescriptor() { + ClassDescriptor ownerForChildren = (ClassDescriptor) owner.getOwnerForChildren(); + MutableClassDescriptor classObjectDescriptor = (MutableClassDescriptor) ownerForChildren.getClassObjectDescriptor(); + + if (classObjectDescriptor == null) { + classObjectDescriptor = createClassObjectDescriptor(ownerForChildren, Visibilities.PUBLIC); + NamespaceLikeBuilder.ClassObjectStatus status = owner.setClassObjectDescriptor(classObjectDescriptor); + assert status != NamespaceLikeBuilder.ClassObjectStatus.DUPLICATE : + "Attempting to create an artificial class object where the real one exists: " + ownerForChildren; + if (status == NamespaceLikeBuilder.ClassObjectStatus.NOT_ALLOWED) { + return null; + } + } + + return classObjectDescriptor; + } + + private void putEnumsIntoOuterClassObject() { + if (enumsToAddLater.isEmpty()) return; + + MutableClassDescriptor classObjectDescriptor = getOrCreateClassObjectDescriptor(); + if (classObjectDescriptor == null) { + // A class object is not allowed in outer class, so report an error on every declared enum + for (JetClass klass : enumsToAddLater) { + JetModifierList modifierList = klass.getModifierList(); + assert modifierList != null : "Enum class without modifier list: " + klass.getText(); + ASTNode node = modifierList.getModifierNode(JetTokens.ENUM_KEYWORD); + assert node != null : "Enum class without enum modifier: " + klass.getText(); + trace.report(ENUM_NOT_ALLOWED.on(node.getPsi())); + } + return; + } + + for (JetClass klass : enumsToAddLater) { + MutableClassDescriptor mutableClassDescriptor = createClassDescriptorForClass(klass, classObjectDescriptor); + trace.record(BindingContext.IS_ENUM_MOVED_TO_CLASS_OBJECT, mutableClassDescriptor); + classObjectDescriptor.getBuilder().addClassifierDescriptor(mutableClassDescriptor); + } + } + + private void finishProcessing() { + putEnumsIntoOuterClassObject(); + } } } diff --git a/compiler/testData/codegen/enum/inner.kt b/compiler/testData/codegen/enum/inner.kt new file mode 100644 index 00000000000..01ef380fbc3 --- /dev/null +++ b/compiler/testData/codegen/enum/inner.kt @@ -0,0 +1,7 @@ +class A { + enum class E { + OK + } +} + +fun box() = A.E.OK.toString() diff --git a/compiler/testData/codegen/enum/innerWithExistingClassObject.kt b/compiler/testData/codegen/enum/innerWithExistingClassObject.kt new file mode 100644 index 00000000000..74244024e98 --- /dev/null +++ b/compiler/testData/codegen/enum/innerWithExistingClassObject.kt @@ -0,0 +1,8 @@ +class A { + class object {} + enum class E { + OK + } +} + +fun box() = A.E.OK.toString() diff --git a/compiler/testData/diagnostics/tests/enum/inner/existingClassObject.kt b/compiler/testData/diagnostics/tests/enum/inner/existingClassObject.kt new file mode 100644 index 00000000000..3a0438c2483 --- /dev/null +++ b/compiler/testData/diagnostics/tests/enum/inner/existingClassObject.kt @@ -0,0 +1,19 @@ +class A { + enum class E { + ENTRY + } + + class object { + } +} + + + +class B { + class object { + } + + enum class E { + ENTRY + } +} diff --git a/compiler/testData/diagnostics/tests/enum/inner/insideClass.kt b/compiler/testData/diagnostics/tests/enum/inner/insideClass.kt new file mode 100644 index 00000000000..27369f8033f --- /dev/null +++ b/compiler/testData/diagnostics/tests/enum/inner/insideClass.kt @@ -0,0 +1,5 @@ +class A { + enum class E { + ENTRY + } +} diff --git a/compiler/testData/diagnostics/tests/enum/inner/insideClassObject.kt b/compiler/testData/diagnostics/tests/enum/inner/insideClassObject.kt new file mode 100644 index 00000000000..aaafb505abb --- /dev/null +++ b/compiler/testData/diagnostics/tests/enum/inner/insideClassObject.kt @@ -0,0 +1,7 @@ +class A { + class object { + enum class E { + ENTRY + } + } +} diff --git a/compiler/testData/diagnostics/tests/enum/inner/insideEnum.kt b/compiler/testData/diagnostics/tests/enum/inner/insideEnum.kt new file mode 100644 index 00000000000..f4a9d75cb88 --- /dev/null +++ b/compiler/testData/diagnostics/tests/enum/inner/insideEnum.kt @@ -0,0 +1,7 @@ +enum class E { + ABC + + enum class F { + DEF + } +} diff --git a/compiler/testData/diagnostics/tests/enum/inner/insideEnumEntry.kt b/compiler/testData/diagnostics/tests/enum/inner/insideEnumEntry.kt new file mode 100644 index 00000000000..7f302c53f19 --- /dev/null +++ b/compiler/testData/diagnostics/tests/enum/inner/insideEnumEntry.kt @@ -0,0 +1,7 @@ +enum class E { + ABC { + enum class F { + DEF + } + } +} diff --git a/compiler/testData/diagnostics/tests/enum/inner/insideInnerClassNotAllowed.kt b/compiler/testData/diagnostics/tests/enum/inner/insideInnerClassNotAllowed.kt new file mode 100644 index 00000000000..905f6e5f0df --- /dev/null +++ b/compiler/testData/diagnostics/tests/enum/inner/insideInnerClassNotAllowed.kt @@ -0,0 +1,7 @@ +class A { + class B { + enum class E { + ENTRY + } + } +} diff --git a/compiler/testData/diagnostics/tests/enum/inner/insideObject.kt b/compiler/testData/diagnostics/tests/enum/inner/insideObject.kt new file mode 100644 index 00000000000..80f679f8167 --- /dev/null +++ b/compiler/testData/diagnostics/tests/enum/inner/insideObject.kt @@ -0,0 +1,5 @@ +object A { + enum class E { + ENTRY + } +} diff --git a/compiler/testData/diagnostics/tests/enum/inner/insideTrait.kt b/compiler/testData/diagnostics/tests/enum/inner/insideTrait.kt new file mode 100644 index 00000000000..4138b24d5c7 --- /dev/null +++ b/compiler/testData/diagnostics/tests/enum/inner/insideTrait.kt @@ -0,0 +1,5 @@ +trait A { + enum class E { + ENTRY + } +} diff --git a/compiler/testData/diagnostics/tests/enum/inner/redeclarationInClassObject.kt b/compiler/testData/diagnostics/tests/enum/inner/redeclarationInClassObject.kt new file mode 100644 index 00000000000..75569dd004a --- /dev/null +++ b/compiler/testData/diagnostics/tests/enum/inner/redeclarationInClassObject.kt @@ -0,0 +1,11 @@ +class A { + enum class E { + ENTRY + } + + class object { + enum class E { + ENTRY2 + } + } +} diff --git a/compiler/testData/diagnostics/tests/enum/inner/twoEnums.kt b/compiler/testData/diagnostics/tests/enum/inner/twoEnums.kt new file mode 100644 index 00000000000..fbf22f500c5 --- /dev/null +++ b/compiler/testData/diagnostics/tests/enum/inner/twoEnums.kt @@ -0,0 +1,4 @@ +class A { + enum class E { ABC } + enum class F { DEF } +} diff --git a/compiler/testData/diagnostics/tests/enum/inner/twoEnumsInClassObjectAndInnerClass.kt b/compiler/testData/diagnostics/tests/enum/inner/twoEnumsInClassObjectAndInnerClass.kt new file mode 100644 index 00000000000..85e3e3708f3 --- /dev/null +++ b/compiler/testData/diagnostics/tests/enum/inner/twoEnumsInClassObjectAndInnerClass.kt @@ -0,0 +1,9 @@ +class A { + class object { + enum class E { ENTRY } // OK + } + + class B { + enum class E { ENTRY } + } +} diff --git a/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java index b3db96dcd88..07aca1ceca0 100644 --- a/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java @@ -1352,6 +1352,7 @@ public class JetDiagnosticsTestGenerated extends AbstractDiagnosticsTestWithEage } @TestMetadata("compiler/testData/diagnostics/tests/enum") + @InnerTestClasses({Enum.Inner.class}) public static class Enum extends AbstractDiagnosticsTestWithEagerResolve { public void testAllFilesPresentInEnum() throws Exception { JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.checkers.AbstractDiagnosticsTestWithEagerResolve", new File("compiler/testData/diagnostics/tests/enum"), "kt", true); @@ -1417,6 +1418,75 @@ public class JetDiagnosticsTestGenerated extends AbstractDiagnosticsTestWithEage doTest("compiler/testData/diagnostics/tests/enum/javaEnumWithProperty.kt"); } + @TestMetadata("compiler/testData/diagnostics/tests/enum/inner") + public static class Inner extends AbstractDiagnosticsTestWithEagerResolve { + public void testAllFilesPresentInInner() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.checkers.AbstractDiagnosticsTestWithEagerResolve", new File("compiler/testData/diagnostics/tests/enum/inner"), "kt", true); + } + + @TestMetadata("existingClassObject.kt") + public void testExistingClassObject() throws Exception { + doTest("compiler/testData/diagnostics/tests/enum/inner/existingClassObject.kt"); + } + + @TestMetadata("insideClass.kt") + public void testInsideClass() throws Exception { + doTest("compiler/testData/diagnostics/tests/enum/inner/insideClass.kt"); + } + + @TestMetadata("insideClassObject.kt") + public void testInsideClassObject() throws Exception { + doTest("compiler/testData/diagnostics/tests/enum/inner/insideClassObject.kt"); + } + + @TestMetadata("insideEnum.kt") + public void testInsideEnum() throws Exception { + doTest("compiler/testData/diagnostics/tests/enum/inner/insideEnum.kt"); + } + + @TestMetadata("insideEnumEntry.kt") + public void testInsideEnumEntry() throws Exception { + doTest("compiler/testData/diagnostics/tests/enum/inner/insideEnumEntry.kt"); + } + + @TestMetadata("insideInnerClassNotAllowed.kt") + public void testInsideInnerClassNotAllowed() throws Exception { + doTest("compiler/testData/diagnostics/tests/enum/inner/insideInnerClassNotAllowed.kt"); + } + + @TestMetadata("insideObject.kt") + public void testInsideObject() throws Exception { + doTest("compiler/testData/diagnostics/tests/enum/inner/insideObject.kt"); + } + + @TestMetadata("insideTrait.kt") + public void testInsideTrait() throws Exception { + doTest("compiler/testData/diagnostics/tests/enum/inner/insideTrait.kt"); + } + + @TestMetadata("redeclarationInClassObject.kt") + public void testRedeclarationInClassObject() throws Exception { + doTest("compiler/testData/diagnostics/tests/enum/inner/redeclarationInClassObject.kt"); + } + + @TestMetadata("twoEnums.kt") + public void testTwoEnums() throws Exception { + doTest("compiler/testData/diagnostics/tests/enum/inner/twoEnums.kt"); + } + + @TestMetadata("twoEnumsInClassObjectAndInnerClass.kt") + public void testTwoEnumsInClassObjectAndInnerClass() throws Exception { + doTest("compiler/testData/diagnostics/tests/enum/inner/twoEnumsInClassObjectAndInnerClass.kt"); + } + + } + + public static Test innerSuite() { + TestSuite suite = new TestSuite("Enum"); + suite.addTestSuite(Enum.class); + suite.addTestSuite(Inner.class); + return suite; + } } @TestMetadata("compiler/testData/diagnostics/tests/extensions") @@ -3359,7 +3429,7 @@ public class JetDiagnosticsTestGenerated extends AbstractDiagnosticsTestWithEage suite.addTestSuite(DataFlow.class); suite.addTestSuite(DataFlowInfoTraversal.class); suite.addTest(DeclarationChecks.innerSuite()); - suite.addTestSuite(Enum.class); + suite.addTest(Enum.innerSuite()); suite.addTestSuite(Extensions.class); suite.addTestSuite(Generics.class); suite.addTest(IncompleteCode.innerSuite()); diff --git a/compiler/tests/org/jetbrains/jet/codegen/EnumGenTest.java b/compiler/tests/org/jetbrains/jet/codegen/EnumGenTest.java index 531cd7c4799..028ab6e4fc4 100644 --- a/compiler/tests/org/jetbrains/jet/codegen/EnumGenTest.java +++ b/compiler/tests/org/jetbrains/jet/codegen/EnumGenTest.java @@ -155,4 +155,12 @@ public class EnumGenTest extends CodegenTestCase { public void testEntryWithInner() { blackBoxFile("enum/entrywithinner.kt"); } + + public void testInner() { + blackBoxFile("enum/inner.kt"); + } + + public void testInnerWithExistingClassObject() { + blackBoxFile("enum/innerWithExistingClassObject.kt"); + } }