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.
This commit is contained in:
@@ -159,6 +159,7 @@ public interface Errors {
|
|||||||
|
|
||||||
DiagnosticFactory1<JetClass, ClassDescriptor> ENUM_ENTRY_SHOULD_BE_INITIALIZED = DiagnosticFactory1.create(ERROR, NAME_IDENTIFIER);
|
DiagnosticFactory1<JetClass, ClassDescriptor> ENUM_ENTRY_SHOULD_BE_INITIALIZED = DiagnosticFactory1.create(ERROR, NAME_IDENTIFIER);
|
||||||
DiagnosticFactory1<JetTypeReference, ClassDescriptor> ENUM_ENTRY_ILLEGAL_TYPE = DiagnosticFactory1.create(ERROR);
|
DiagnosticFactory1<JetTypeReference, ClassDescriptor> ENUM_ENTRY_ILLEGAL_TYPE = DiagnosticFactory1.create(ERROR);
|
||||||
|
SimpleDiagnosticFactory<PsiElement> ENUM_NOT_ALLOWED = SimpleDiagnosticFactory.create(ERROR);
|
||||||
|
|
||||||
DiagnosticFactory1<JetSimpleNameExpression, VariableDescriptor> UNINITIALIZED_VARIABLE = DiagnosticFactory1.create(ERROR);
|
DiagnosticFactory1<JetSimpleNameExpression, VariableDescriptor> UNINITIALIZED_VARIABLE = DiagnosticFactory1.create(ERROR);
|
||||||
DiagnosticFactory1<JetSimpleNameExpression, ValueParameterDescriptor> UNINITIALIZED_PARAMETER = DiagnosticFactory1.create(ERROR);
|
DiagnosticFactory1<JetSimpleNameExpression, ValueParameterDescriptor> UNINITIALIZED_PARAMETER = DiagnosticFactory1.create(ERROR);
|
||||||
|
|||||||
+1
@@ -170,6 +170,7 @@ public class DefaultErrorMessages {
|
|||||||
|
|
||||||
MAP.put(ENUM_ENTRY_SHOULD_BE_INITIALIZED, "Missing delegation specifier ''{0}''", NAME);
|
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_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_VARIABLE, "Variable ''{0}'' must be initialized", NAME);
|
||||||
MAP.put(UNINITIALIZED_PARAMETER, "Parameter ''{0}'' is uninitialized here", NAME);
|
MAP.put(UNINITIALIZED_PARAMETER, "Parameter ''{0}'' is uninitialized here", NAME);
|
||||||
|
|||||||
@@ -230,6 +230,8 @@ public interface BindingContext {
|
|||||||
ReadOnlySlice<PsiElement, DeclarationDescriptor> DECLARATION_TO_DESCRIPTOR = Slices.<PsiElement, DeclarationDescriptor>sliceBuilder()
|
ReadOnlySlice<PsiElement, DeclarationDescriptor> DECLARATION_TO_DESCRIPTOR = Slices.<PsiElement, DeclarationDescriptor>sliceBuilder()
|
||||||
.setFurtherLookupSlices(DECLARATIONS_TO_DESCRIPTORS).build();
|
.setFurtherLookupSlices(DECLARATIONS_TO_DESCRIPTORS).build();
|
||||||
|
|
||||||
|
WritableSlice<ClassDescriptor, Boolean> IS_ENUM_MOVED_TO_CLASS_OBJECT = Slices.createSimpleSlice();
|
||||||
|
|
||||||
WritableSlice<JetReferenceExpression, PsiElement> LABEL_TARGET = Slices.<JetReferenceExpression, PsiElement>sliceBuilder().build();
|
WritableSlice<JetReferenceExpression, PsiElement> LABEL_TARGET = Slices.<JetReferenceExpression, PsiElement>sliceBuilder().build();
|
||||||
WritableSlice<ValueParameterDescriptor, PropertyDescriptor> VALUE_PARAMETER_AS_PROPERTY =
|
WritableSlice<ValueParameterDescriptor, PropertyDescriptor> VALUE_PARAMETER_AS_PROPERTY =
|
||||||
Slices.<ValueParameterDescriptor, PropertyDescriptor>sliceBuilder().build();
|
Slices.<ValueParameterDescriptor, PropertyDescriptor>sliceBuilder().build();
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ package org.jetbrains.jet.lang.resolve;
|
|||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
import com.google.common.collect.Multimap;
|
import com.google.common.collect.Multimap;
|
||||||
import com.google.common.collect.Sets;
|
import com.google.common.collect.Sets;
|
||||||
|
import com.intellij.lang.ASTNode;
|
||||||
import com.intellij.psi.PsiElement;
|
import com.intellij.psi.PsiElement;
|
||||||
import com.intellij.psi.PsiNameIdentifierOwner;
|
import com.intellij.psi.PsiNameIdentifierOwner;
|
||||||
import com.intellij.util.containers.ContainerUtil;
|
import com.intellij.util.containers.ContainerUtil;
|
||||||
@@ -201,6 +202,8 @@ public class TypeHierarchyResolver {
|
|||||||
declaration.accept(collector);
|
declaration.accept(collector);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
collector.finishProcessing();
|
||||||
|
|
||||||
return forDeferredResolve;
|
return forDeferredResolve;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -473,6 +476,8 @@ public class TypeHierarchyResolver {
|
|||||||
private final NamespaceLikeBuilder owner;
|
private final NamespaceLikeBuilder owner;
|
||||||
private final Collection<JetDeclarationContainer> forDeferredResolve;
|
private final Collection<JetDeclarationContainer> forDeferredResolve;
|
||||||
|
|
||||||
|
private final List<JetClass> enumsToAddLater = new ArrayList<JetClass>(0);
|
||||||
|
|
||||||
public ClassifierCollector(@NotNull JetScope outerScope,
|
public ClassifierCollector(@NotNull JetScope outerScope,
|
||||||
@NotNull NamespaceLikeBuilder owner,
|
@NotNull NamespaceLikeBuilder owner,
|
||||||
@NotNull Collection<JetDeclarationContainer> forDeferredResolve
|
@NotNull Collection<JetDeclarationContainer> forDeferredResolve
|
||||||
@@ -502,12 +507,25 @@ public class TypeHierarchyResolver {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visitClass(JetClass klass) {
|
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());
|
MutableClassDescriptor mutableClassDescriptor = createClassDescriptorForClass(klass, owner.getOwnerForChildren());
|
||||||
|
|
||||||
owner.addClassifierDescriptor(mutableClassDescriptor);
|
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
|
@Override
|
||||||
public void visitObjectDeclaration(JetObjectDeclaration declaration) {
|
public void visitObjectDeclaration(JetObjectDeclaration declaration) {
|
||||||
MutableClassDescriptor objectDescriptor =
|
MutableClassDescriptor objectDescriptor =
|
||||||
@@ -657,5 +675,50 @@ public class TypeHierarchyResolver {
|
|||||||
context.normalScope.put(container, outerScope);
|
context.normalScope.put(container, outerScope);
|
||||||
context.forDeferredResolver.put(container, withDeferredResolve);
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
class A {
|
||||||
|
enum class E {
|
||||||
|
OK
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun box() = A.E.OK.toString()
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
class A {
|
||||||
|
class object {}
|
||||||
|
enum class E {
|
||||||
|
OK
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun box() = A.E.OK.toString()
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
class A {
|
||||||
|
enum class E {
|
||||||
|
ENTRY
|
||||||
|
}
|
||||||
|
|
||||||
|
class object {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class B {
|
||||||
|
class object {
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class E {
|
||||||
|
ENTRY
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
class A {
|
||||||
|
enum class E {
|
||||||
|
ENTRY
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
class A {
|
||||||
|
class object {
|
||||||
|
enum class E {
|
||||||
|
ENTRY
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
enum class E {
|
||||||
|
ABC
|
||||||
|
|
||||||
|
enum class F {
|
||||||
|
DEF
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
enum class E {
|
||||||
|
ABC {
|
||||||
|
enum class F {
|
||||||
|
DEF
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
class A {
|
||||||
|
class B {
|
||||||
|
<!ENUM_NOT_ALLOWED!>enum<!> class E {
|
||||||
|
ENTRY
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
object A {
|
||||||
|
enum class E {
|
||||||
|
ENTRY
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
trait A {
|
||||||
|
enum class E {
|
||||||
|
ENTRY
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
class A {
|
||||||
|
enum class <!REDECLARATION!>E<!> {
|
||||||
|
ENTRY
|
||||||
|
}
|
||||||
|
|
||||||
|
class object {
|
||||||
|
enum class <!REDECLARATION!>E<!> {
|
||||||
|
ENTRY2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
class A {
|
||||||
|
enum class E { ABC }
|
||||||
|
enum class F { DEF }
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
class A {
|
||||||
|
class object {
|
||||||
|
enum class E { ENTRY } // OK
|
||||||
|
}
|
||||||
|
|
||||||
|
class B {
|
||||||
|
<!ENUM_NOT_ALLOWED!>enum<!> class E { ENTRY }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1352,6 +1352,7 @@ public class JetDiagnosticsTestGenerated extends AbstractDiagnosticsTestWithEage
|
|||||||
}
|
}
|
||||||
|
|
||||||
@TestMetadata("compiler/testData/diagnostics/tests/enum")
|
@TestMetadata("compiler/testData/diagnostics/tests/enum")
|
||||||
|
@InnerTestClasses({Enum.Inner.class})
|
||||||
public static class Enum extends AbstractDiagnosticsTestWithEagerResolve {
|
public static class Enum extends AbstractDiagnosticsTestWithEagerResolve {
|
||||||
public void testAllFilesPresentInEnum() throws Exception {
|
public void testAllFilesPresentInEnum() throws Exception {
|
||||||
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.checkers.AbstractDiagnosticsTestWithEagerResolve", new File("compiler/testData/diagnostics/tests/enum"), "kt", true);
|
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");
|
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")
|
@TestMetadata("compiler/testData/diagnostics/tests/extensions")
|
||||||
@@ -3359,7 +3429,7 @@ public class JetDiagnosticsTestGenerated extends AbstractDiagnosticsTestWithEage
|
|||||||
suite.addTestSuite(DataFlow.class);
|
suite.addTestSuite(DataFlow.class);
|
||||||
suite.addTestSuite(DataFlowInfoTraversal.class);
|
suite.addTestSuite(DataFlowInfoTraversal.class);
|
||||||
suite.addTest(DeclarationChecks.innerSuite());
|
suite.addTest(DeclarationChecks.innerSuite());
|
||||||
suite.addTestSuite(Enum.class);
|
suite.addTest(Enum.innerSuite());
|
||||||
suite.addTestSuite(Extensions.class);
|
suite.addTestSuite(Extensions.class);
|
||||||
suite.addTestSuite(Generics.class);
|
suite.addTestSuite(Generics.class);
|
||||||
suite.addTest(IncompleteCode.innerSuite());
|
suite.addTest(IncompleteCode.innerSuite());
|
||||||
|
|||||||
@@ -155,4 +155,12 @@ public class EnumGenTest extends CodegenTestCase {
|
|||||||
public void testEntryWithInner() {
|
public void testEntryWithInner() {
|
||||||
blackBoxFile("enum/entrywithinner.kt");
|
blackBoxFile("enum/entrywithinner.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testInner() {
|
||||||
|
blackBoxFile("enum/inner.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testInnerWithExistingClassObject() {
|
||||||
|
blackBoxFile("enum/innerWithExistingClassObject.kt");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user