Value classes: Forbid cloneable value classes

#KT-43741 Fixed
This commit is contained in:
Ilmir Usmanov
2020-12-04 11:02:52 +01:00
parent 25c228297a
commit 69be56d042
9 changed files with 315 additions and 0 deletions
@@ -364,6 +364,7 @@ public interface Errors {
DiagnosticFactory1<PsiElement, String> RESERVED_MEMBER_INSIDE_INLINE_CLASS = DiagnosticFactory1.create(ERROR);
DiagnosticFactory0<PsiElement> SECONDARY_CONSTRUCTOR_WITH_BODY_INSIDE_INLINE_CLASS = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<PsiElement> INNER_CLASS_INSIDE_INLINE_CLASS = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<PsiElement> VALUE_CLASS_CANNOT_BE_CLONEABLE = DiagnosticFactory0.create(ERROR);
// Result class
@@ -719,6 +719,7 @@ public class DefaultErrorMessages {
MAP.put(RESERVED_MEMBER_INSIDE_INLINE_CLASS, "Member with the name ''{0}'' is reserved for future releases", STRING);
MAP.put(SECONDARY_CONSTRUCTOR_WITH_BODY_INSIDE_INLINE_CLASS, "Secondary constructors with bodies are reserved for for future releases");
MAP.put(INNER_CLASS_INSIDE_INLINE_CLASS, "Inline class cannot have inner classes");
MAP.put(VALUE_CLASS_CANNOT_BE_CLONEABLE, "Value class cannot be Cloneable");
MAP.put(RESULT_CLASS_IN_RETURN_TYPE, "'kotlin.Result' cannot be used as a return type");
MAP.put(RESULT_CLASS_WITH_NULLABLE_OPERATOR, "Expression of type 'kotlin.Result' cannot be used as a left operand of ''{0}''", STRING);
@@ -6,18 +6,26 @@
package org.jetbrains.kotlin.resolve.checkers
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.builtins.StandardNames
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.name.FqNameUnsafe
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.modalityModifier
import org.jetbrains.kotlin.resolve.*
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
import org.jetbrains.kotlin.resolve.descriptorUtil.getAllSuperClassifiers
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperInterfaces
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.typeUtil.isNothing
import org.jetbrains.kotlin.types.typeUtil.isTypeParameter
import org.jetbrains.kotlin.types.typeUtil.isUnit
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
private val javaLangCloneable = FqNameUnsafe("java.lang.Cloneable")
object InlineClassDeclarationChecker : DeclarationChecker {
override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) {
if (declaration !is KtClass) return
@@ -87,6 +95,13 @@ object InlineClassDeclarationChecker : DeclarationChecker {
}
}
}
if (descriptor.getAllSuperClassifiers().any {
it.fqNameUnsafe == StandardNames.FqNames.cloneable || it.fqNameUnsafe == javaLangCloneable }
) {
trace.report(Errors.VALUE_CLASS_CANNOT_BE_CLONEABLE.on(inlineOrValueKeyword))
return
}
}
private fun KotlinType.isInapplicableParameterType() =
@@ -0,0 +1,31 @@
// !LANGUAGE: +InlineClasses
// !DIAGNOSTICS: -UNUSED_PARAMETER, -PLATFORM_CLASS_MAPPED_TO_KOTLIN
// WITH_RUNTIME
package kotlin.jvm
annotation class JvmInline
<!VALUE_CLASS_CANNOT_BE_CLONEABLE!>inline<!> class IC0(val a: Any): Cloneable
@JvmInline
<!VALUE_CLASS_CANNOT_BE_CLONEABLE!>value<!> class VC0(val a: Any): Cloneable
<!VALUE_CLASS_CANNOT_BE_CLONEABLE!>inline<!> class IC1(val a: Any): java.lang.Cloneable
@JvmInline
<!VALUE_CLASS_CANNOT_BE_CLONEABLE!>value<!> class VC1(val a: Any): java.lang.Cloneable
interface MyCloneable1: Cloneable
<!VALUE_CLASS_CANNOT_BE_CLONEABLE!>inline<!> class IC2(val a: Any): MyCloneable1
@JvmInline
<!VALUE_CLASS_CANNOT_BE_CLONEABLE!>value<!> class VC2(val a: Any): MyCloneable1
interface MyCloneable2: java.lang.Cloneable
<!VALUE_CLASS_CANNOT_BE_CLONEABLE!>inline<!> class IC3(val a: Any): MyCloneable2
@JvmInline
<!VALUE_CLASS_CANNOT_BE_CLONEABLE!>value<!> class VC3(val a: Any): MyCloneable2
@@ -0,0 +1,95 @@
package
package kotlin {
package kotlin.jvm {
public final inline class IC0 : kotlin.Cloneable {
public constructor IC0(/*0*/ a: kotlin.Any)
public final val a: kotlin.Any
protected open override /*1*/ /*fake_override*/ fun clone(): kotlin.Any
public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String
}
public final inline class IC1 : java.lang.Cloneable {
public constructor IC1(/*0*/ a: kotlin.Any)
public final val a: kotlin.Any
public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String
}
public final inline class IC2 : kotlin.jvm.MyCloneable1 {
public constructor IC2(/*0*/ a: kotlin.Any)
public final val a: kotlin.Any
protected open override /*1*/ /*fake_override*/ fun clone(): kotlin.Any
public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String
}
public final inline class IC3 : kotlin.jvm.MyCloneable2 {
public constructor IC3(/*0*/ a: kotlin.Any)
public final val a: kotlin.Any
public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String
}
public final annotation class JvmInline : kotlin.Annotation {
public constructor JvmInline()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public interface MyCloneable1 : kotlin.Cloneable {
protected open override /*1*/ /*fake_override*/ fun clone(): kotlin.Any
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public interface MyCloneable2 : java.lang.Cloneable {
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@kotlin.jvm.JvmInline public final value class VC0 : kotlin.Cloneable {
public constructor VC0(/*0*/ a: kotlin.Any)
public final val a: kotlin.Any
protected open override /*1*/ /*fake_override*/ fun clone(): kotlin.Any
public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String
}
@kotlin.jvm.JvmInline public final value class VC1 : java.lang.Cloneable {
public constructor VC1(/*0*/ a: kotlin.Any)
public final val a: kotlin.Any
public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String
}
@kotlin.jvm.JvmInline public final value class VC2 : kotlin.jvm.MyCloneable1 {
public constructor VC2(/*0*/ a: kotlin.Any)
public final val a: kotlin.Any
protected open override /*1*/ /*fake_override*/ fun clone(): kotlin.Any
public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String
}
@kotlin.jvm.JvmInline public final value class VC3 : kotlin.jvm.MyCloneable2 {
public constructor VC3(/*0*/ a: kotlin.Any)
public final val a: kotlin.Any
public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String
}
}
}
@@ -0,0 +1,31 @@
// !LANGUAGE: +InlineClasses
// !DIAGNOSTICS: -UNUSED_PARAMETER, -PLATFORM_CLASS_MAPPED_TO_KOTLIN
// WITH_RUNTIME
package kotlin.jvm
annotation class JvmInline
<!VALUE_CLASS_CANNOT_BE_CLONEABLE!>inline<!> class IC0(val a: Any): Cloneable
@JvmInline
<!VALUE_CLASS_CANNOT_BE_CLONEABLE!>value<!> class VC0(val a: Any): Cloneable
<!VALUE_CLASS_CANNOT_BE_CLONEABLE!>inline<!> class IC1(val a: Any): java.lang.Cloneable
@JvmInline
<!VALUE_CLASS_CANNOT_BE_CLONEABLE!>value<!> class VC1(val a: Any): java.lang.Cloneable
interface MyCloneable1: Cloneable
<!VALUE_CLASS_CANNOT_BE_CLONEABLE!>inline<!> class IC2(val a: Any): MyCloneable1
@JvmInline
<!VALUE_CLASS_CANNOT_BE_CLONEABLE!>value<!> class VC2(val a: Any): MyCloneable1
interface MyCloneable2: java.lang.Cloneable
<!VALUE_CLASS_CANNOT_BE_CLONEABLE!>inline<!> class IC3(val a: Any): MyCloneable2
@JvmInline
<!VALUE_CLASS_CANNOT_BE_CLONEABLE!>value<!> class VC3(val a: Any): MyCloneable2
@@ -0,0 +1,95 @@
package
package kotlin {
package kotlin.jvm {
public final inline class IC0 : kotlin.Cloneable {
public constructor IC0(/*0*/ a: kotlin.Any)
public final val a: kotlin.Any
protected open override /*1*/ /*fake_override*/ fun clone(): kotlin.Any
public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String
}
public final inline class IC1 : java.lang.Cloneable {
public constructor IC1(/*0*/ a: kotlin.Any)
public final val a: kotlin.Any
public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String
}
public final inline class IC2 : kotlin.jvm.MyCloneable1 {
public constructor IC2(/*0*/ a: kotlin.Any)
public final val a: kotlin.Any
protected open override /*1*/ /*fake_override*/ fun clone(): kotlin.Any
public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String
}
public final inline class IC3 : kotlin.jvm.MyCloneable2 {
public constructor IC3(/*0*/ a: kotlin.Any)
public final val a: kotlin.Any
public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String
}
public final annotation class JvmInline : kotlin.Annotation {
public constructor JvmInline()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public interface MyCloneable1 : kotlin.Cloneable {
protected open override /*1*/ /*fake_override*/ fun clone(): kotlin.Any
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public interface MyCloneable2 : java.lang.Cloneable {
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@kotlin.jvm.JvmInline public final value class VC0 : kotlin.Cloneable {
public constructor VC0(/*0*/ a: kotlin.Any)
public final val a: kotlin.Any
protected open override /*1*/ /*fake_override*/ fun clone(): kotlin.Any
public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String
}
@kotlin.jvm.JvmInline public final value class VC1 : java.lang.Cloneable {
public constructor VC1(/*0*/ a: kotlin.Any)
public final val a: kotlin.Any
public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String
}
@kotlin.jvm.JvmInline public final value class VC2 : kotlin.jvm.MyCloneable1 {
public constructor VC2(/*0*/ a: kotlin.Any)
public final val a: kotlin.Any
protected open override /*1*/ /*fake_override*/ fun clone(): kotlin.Any
public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String
}
@kotlin.jvm.JvmInline public final value class VC3 : kotlin.jvm.MyCloneable2 {
public constructor VC3(/*0*/ a: kotlin.Any)
public final val a: kotlin.Any
public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String
}
}
}
@@ -583,4 +583,27 @@ public class DiagnosticsTestWithJvmIrBackendGenerated extends AbstractDiagnostic
}
}
}
@TestMetadata("compiler/testData/diagnostics/testsWithJvmBackend/valueClasses")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class ValueClasses extends AbstractDiagnosticsTestWithJvmIrBackend {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM_IR, testDataFilePath);
}
public void testAllFilesPresentInValueClasses() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/testsWithJvmBackend/valueClasses"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true);
}
@TestMetadata("cloneable.kt")
public void testCloneable() throws Exception {
runTest("compiler/testData/diagnostics/testsWithJvmBackend/valueClasses/cloneable.kt");
}
@TestMetadata("cloneable.fir.kt")
public void testCloneable_fir() throws Exception {
runTest("compiler/testData/diagnostics/testsWithJvmBackend/valueClasses/cloneable.fir.kt");
}
}
}
@@ -573,4 +573,27 @@ public class DiagnosticsTestWithOldJvmBackendGenerated extends AbstractDiagnosti
}
}
}
@TestMetadata("compiler/testData/diagnostics/testsWithJvmBackend/valueClasses")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class ValueClasses extends AbstractDiagnosticsTestWithOldJvmBackend {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM_OLD, testDataFilePath);
}
public void testAllFilesPresentInValueClasses() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/testsWithJvmBackend/valueClasses"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_OLD, true);
}
@TestMetadata("cloneable.kt")
public void testCloneable() throws Exception {
runTest("compiler/testData/diagnostics/testsWithJvmBackend/valueClasses/cloneable.kt");
}
@TestMetadata("cloneable.fir.kt")
public void testCloneable_fir() throws Exception {
runTest("compiler/testData/diagnostics/testsWithJvmBackend/valueClasses/cloneable.fir.kt");
}
}
}