From a872a51a193c7e7291920c05c442d27afc265fb2 Mon Sep 17 00:00:00 2001 From: Denis Zharkov Date: Wed, 25 Feb 2015 18:12:00 +0300 Subject: [PATCH] Prohibit explicit super call for enum ctrs --- .../jetbrains/kotlin/diagnostics/Errors.java | 1 + .../rendering/DefaultErrorMessages.java | 1 + ...structorDelegationReferenceExpression.java | 4 + .../kotlin/resolve/BodyResolver.java | 4 + .../kotlin/resolve/DeclarationsChecker.java | 3 +- .../kotlin/resolve/calls/CallResolver.java | 8 +- .../tests/secondaryConstructors/enums.kt | 40 ++++ .../tests/secondaryConstructors/enums.txt | 204 ++++++++++++++++++ .../checkers/JetDiagnosticsTestGenerated.java | 6 + 9 files changed, 266 insertions(+), 5 deletions(-) create mode 100644 compiler/testData/diagnostics/tests/secondaryConstructors/enums.kt create mode 100644 compiler/testData/diagnostics/tests/secondaryConstructors/enums.txt diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java index ba27a05c22d..d70e155a0bd 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java @@ -160,6 +160,7 @@ public interface Errors { DiagnosticFactory0 SUPERTYPE_INITIALIZED_WITHOUT_PRIMARY_CONSTRUCTOR = DiagnosticFactory0.create(ERROR); DiagnosticFactory0 PRIMARY_CONSTRUCTOR_DELEGATION_CALL_EXPECTED = DiagnosticFactory0.create(ERROR); DiagnosticFactory0 INIT_KEYWORD_BEFORE_CLASS_INITIALIZER_EXPECTED = DiagnosticFactory0.create(WARNING); + DiagnosticFactory0 DELEGATION_SUPER_CALL_IN_ENUM_CONSTRUCTOR = DiagnosticFactory0.create(ERROR); // Trait-specific diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java index c15cbd4f63a..0a16f813a79 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java @@ -417,6 +417,7 @@ public class DefaultErrorMessages { MAP.put(SECONDARY_CONSTRUCTOR_IN_OBJECT, "Constructors are not allowed for objects"); MAP.put(SUPERTYPE_INITIALIZED_WITHOUT_PRIMARY_CONSTRUCTOR, "Supertype initialization is impossible without primary constructor"); MAP.put(PRIMARY_CONSTRUCTOR_DELEGATION_CALL_EXPECTED, "Primary constructor call expected"); + MAP.put(DELEGATION_SUPER_CALL_IN_ENUM_CONSTRUCTOR, "Call to super is not allowed in enum constructor"); MAP.put(INIT_KEYWORD_BEFORE_CLASS_INITIALIZER_EXPECTED, "Expecting 'init' keyword before class initializer"); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/psi/JetConstructorDelegationReferenceExpression.java b/compiler/frontend/src/org/jetbrains/kotlin/psi/JetConstructorDelegationReferenceExpression.java index 0822cbd086d..2d8fe719fac 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/psi/JetConstructorDelegationReferenceExpression.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/psi/JetConstructorDelegationReferenceExpression.java @@ -28,4 +28,8 @@ public class JetConstructorDelegationReferenceExpression extends JetExpressionIm public boolean isThis() { return findChildByType(JetTokens.THIS_KEYWORD) != null; } + + public boolean isEmpty() { + return getFirstChild() == null; + } } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/BodyResolver.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/BodyResolver.java index dc7dbac13c5..454e6698e80 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/BodyResolver.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/BodyResolver.java @@ -171,6 +171,10 @@ public class BodyResolver { if (call == null || call.getCalleeExpression() == null) return; JetType superClassType = DescriptorUtils.getSuperClassType(descriptor.getContainingDeclaration()); + if (descriptor.getContainingDeclaration().getKind() == ClassKind.ENUM_CLASS && call.getCalleeExpression().isEmpty()) { + return; + } + OverloadResolutionResults results = callResolver.resolveFunctionCall( trace, scope, CallMaker.makeCall(ReceiverValue.NO_RECEIVER, null, call), diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/DeclarationsChecker.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/DeclarationsChecker.java index 36b56d992fb..1d1d5c6b3a9 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/DeclarationsChecker.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/DeclarationsChecker.java @@ -568,8 +568,7 @@ public class DeclarationsChecker { List delegationSpecifiers = enumEntry.getDelegationSpecifiers(); ConstructorDescriptor constructor = enumClass.getUnsubstitutedPrimaryConstructor(); - assert constructor != null; - if (!constructor.getValueParameters().isEmpty() && delegationSpecifiers.isEmpty()) { + if ((constructor == null || !constructor.getValueParameters().isEmpty()) && delegationSpecifiers.isEmpty()) { trace.report(ENUM_ENTRY_SHOULD_BE_INITIALIZED.on(enumEntry, enumClass)); } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallResolver.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallResolver.java index fb5f2fc02f5..5398d43efb2 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallResolver.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallResolver.java @@ -52,9 +52,7 @@ import java.util.Collection; import java.util.Collections; import java.util.List; -import static org.jetbrains.kotlin.diagnostics.Errors.EXPECTED_PRIMARY_CONSTRUCTOR_DELEGATION_CALL; -import static org.jetbrains.kotlin.diagnostics.Errors.NOT_A_CLASS; -import static org.jetbrains.kotlin.diagnostics.Errors.NO_CONSTRUCTOR; +import static org.jetbrains.kotlin.diagnostics.Errors.*; import static org.jetbrains.kotlin.resolve.bindingContextUtil.BindingContextUtilPackage.recordScopeAndDataFlowInfo; import static org.jetbrains.kotlin.resolve.calls.CallResolverUtil.ResolveArgumentsMode.RESOLVE_FUNCTION_ARGUMENTS; import static org.jetbrains.kotlin.resolve.calls.CallResolverUtil.ResolveArgumentsMode.SHAPE_FUNCTION_ARGUMENTS; @@ -297,6 +295,10 @@ public class CallResolver { @NotNull JetConstructorDelegationReferenceExpression calleeExpression ) { ClassDescriptor currentClassDescriptor = getClassDescriptorByConstructorContext(context); + if (currentClassDescriptor.getKind() == ClassKind.ENUM_CLASS && !calleeExpression.isThis()) { + context.trace.report(DELEGATION_SUPER_CALL_IN_ENUM_CONSTRUCTOR.on((JetConstructorDelegationCall) calleeExpression.getParent())); + return checkArgumentTypesAndFail(context); + } ClassDescriptor delegateClassDescriptor = calleeExpression.isThis() ? currentClassDescriptor : DescriptorUtilPackage.getSuperClassOrAny(currentClassDescriptor); diff --git a/compiler/testData/diagnostics/tests/secondaryConstructors/enums.kt b/compiler/testData/diagnostics/tests/secondaryConstructors/enums.kt new file mode 100644 index 00000000000..3a18ab057f7 --- /dev/null +++ b/compiler/testData/diagnostics/tests/secondaryConstructors/enums.kt @@ -0,0 +1,40 @@ +// !DIAGNOSTICS: -UNUSED_PARAMETER +enum class A { + W: A(1) X: A(1, 2) Y: A(3.0) Z: A("") E: A() + + constructor() {} + constructor(x: Int) {} + constructor(x: Int, y: Int): this(x+y) {} + constructor(x: Double): this(x.toInt(), 1) {} + constructor(x: String): super(x, 1) {} +} + +enum class B(x: Int) { + W: B(1) X: B(1, 2) Y: B(3.0) Z: B("") + + constructor(x: Int, y: Int): this(x+y) {} + constructor(x: Double): this(x.toInt(), 1) {} + constructor(x: String): super(x, 1) {} +} + +enum class C { + EMPTY: C() // may be we should avoid explicit call here + constructor() {} +} + +enum class D(val prop: Int) { + X: D(123) { + override fun f() = 1 + } + Y: D() { + override fun f() = prop + } + Z: D("abc") { + override fun f() = prop + } + + constructor(): this(1) {} + constructor(x: String): this(x.length()) {} + + abstract fun f(): Int +} diff --git a/compiler/testData/diagnostics/tests/secondaryConstructors/enums.txt b/compiler/testData/diagnostics/tests/secondaryConstructors/enums.txt new file mode 100644 index 00000000000..138979dfdff --- /dev/null +++ b/compiler/testData/diagnostics/tests/secondaryConstructors/enums.txt @@ -0,0 +1,204 @@ +package + +internal final enum class A : kotlin.Enum { + public enum entry W : A { + private constructor W() + public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: A): kotlin.Int + 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 + } + + public enum entry X : A { + private constructor X() + public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: A): kotlin.Int + 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 + } + + public enum entry Y : A { + private constructor Y() + public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: A): kotlin.Int + 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 + } + + public enum entry Z : A { + private constructor Z() + public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: A): kotlin.Int + 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 + } + + public enum entry E : A { + private constructor E() + public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: A): kotlin.Int + 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 A() + private constructor A(/*0*/ x: kotlin.Double) + private constructor A(/*0*/ x: kotlin.Int) + private constructor A(/*0*/ x: kotlin.Int, /*1*/ y: kotlin.Int) + private constructor A(/*0*/ x: kotlin.String) + public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: A): kotlin.Int + 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): A + public final /*synthesized*/ fun values(): kotlin.Array +} + +internal final enum class B : kotlin.Enum { + public enum entry W : B { + private constructor W() + public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: B): kotlin.Int + 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 + } + + public enum entry X : B { + private constructor X() + public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: B): kotlin.Int + 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 + } + + public enum entry Y : B { + private constructor Y() + public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: B): kotlin.Int + 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 + } + + public enum entry Z : B { + private constructor Z() + public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: B): kotlin.Int + 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 B(/*0*/ x: kotlin.Double) + private constructor B(/*0*/ x: kotlin.Int) + private constructor B(/*0*/ x: kotlin.Int, /*1*/ y: kotlin.Int) + private constructor B(/*0*/ x: kotlin.String) + public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: B): kotlin.Int + 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): B + public final /*synthesized*/ fun values(): kotlin.Array +} + +internal final enum class C : kotlin.Enum { + public enum entry EMPTY : C { + private constructor EMPTY() + public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: C): kotlin.Int + 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 C() + public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: C): kotlin.Int + 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): C + public final /*synthesized*/ fun values(): kotlin.Array +} + +internal final enum class D : kotlin.Enum { + public enum entry X : D { + private constructor X() + internal final override /*1*/ /*fake_override*/ val prop: kotlin.Int + public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: D): kotlin.Int + public final override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + internal open override /*1*/ fun f(): 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 Y : D { + private constructor Y() + internal final override /*1*/ /*fake_override*/ val prop: kotlin.Int + public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: D): kotlin.Int + public final override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + internal open override /*1*/ fun f(): 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 Z : D { + private constructor Z() + internal final override /*1*/ /*fake_override*/ val prop: kotlin.Int + public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: D): kotlin.Int + public final override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + internal open override /*1*/ fun f(): 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 D() + private constructor D(/*0*/ prop: kotlin.Int) + private constructor D(/*0*/ x: kotlin.String) + internal final val prop: kotlin.Int + public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: D): kotlin.Int + public final override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + internal abstract fun f(): 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): D + public final /*synthesized*/ fun values(): kotlin.Array +} diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java index 08dc179dbd4..fff96159308 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java @@ -10469,6 +10469,12 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest { doTest(fileName); } + @TestMetadata("enums.kt") + public void testEnums() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/secondaryConstructors/enums.kt"); + doTest(fileName); + } + @TestMetadata("expectedInitKeywordOnInitializer.kt") public void testExpectedInitKeywordOnInitializer() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/secondaryConstructors/expectedInitKeywordOnInitializer.kt");