Prohibit @JvmRecord for non-data classes
^KT-43677 In Progress
This commit is contained in:
+28
-22
@@ -83,28 +83,6 @@ object JvmRecordApplicabilityChecker : DeclarationChecker {
|
||||
return
|
||||
}
|
||||
|
||||
val primaryConstructor = declaration.primaryConstructor
|
||||
val parameters = primaryConstructor?.valueParameters ?: emptyList()
|
||||
if (parameters.isEmpty()) {
|
||||
(primaryConstructor?.valueParameterList ?: declaration.nameIdentifier)?.let {
|
||||
context.trace.report(ErrorsJvm.JVM_RECORD_WITHOUT_PRIMARY_CONSTRUCTOR_PARAMETERS.on(it))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
for (parameter in parameters) {
|
||||
if (!parameter.hasValOrVar() || parameter.isMutable) {
|
||||
context.trace.report(ErrorsJvm.JVM_RECORD_NOT_VAL_PARAMETER.on(parameter))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
for (parameter in parameters.dropLast(1)) {
|
||||
if (parameter.isVarArg) {
|
||||
context.trace.report(ErrorsJvm.JVM_RECORD_NOT_LAST_VARARG_PARAMETER.on(parameter))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
for (member in declaration.declarations) {
|
||||
if (member !is KtProperty) continue
|
||||
@@ -131,6 +109,34 @@ object JvmRecordApplicabilityChecker : DeclarationChecker {
|
||||
context.trace.report(ErrorsJvm.JVM_RECORD_EXTENDS_CLASS.on(reportSupertypeOn, supertype))
|
||||
return
|
||||
}
|
||||
|
||||
if (!descriptor.isData) {
|
||||
context.trace.report(ErrorsJvm.NON_DATA_CLASS_JVM_RECORD.on(reportOn))
|
||||
return
|
||||
}
|
||||
|
||||
val primaryConstructor = declaration.primaryConstructor
|
||||
val parameters = primaryConstructor?.valueParameters ?: emptyList()
|
||||
if (parameters.isEmpty()) {
|
||||
(primaryConstructor?.valueParameterList ?: declaration.nameIdentifier)?.let {
|
||||
context.trace.report(ErrorsJvm.JVM_RECORD_WITHOUT_PRIMARY_CONSTRUCTOR_PARAMETERS.on(it))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
for (parameter in parameters) {
|
||||
if (!parameter.hasValOrVar() || parameter.isMutable) {
|
||||
context.trace.report(ErrorsJvm.JVM_RECORD_NOT_VAL_PARAMETER.on(parameter))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
for (parameter in parameters.dropLast(1)) {
|
||||
if (parameter.isVarArg) {
|
||||
context.trace.report(ErrorsJvm.JVM_RECORD_NOT_LAST_VARARG_PARAMETER.on(parameter))
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
@@ -163,6 +163,7 @@ public class DefaultErrorMessagesJvm implements DefaultErrorMessages.Extension {
|
||||
MAP.put(INNER_JVM_RECORD, "@JvmRecord class should not be inner");
|
||||
MAP.put(FIELD_IN_JVM_RECORD, "It's not allowed to have non-constructor properties with backing filed in @JvmRecord class");
|
||||
MAP.put(DELEGATION_BY_IN_JVM_RECORD, "Delegation is not allowed for @JvmRecord classes");
|
||||
MAP.put(NON_DATA_CLASS_JVM_RECORD, "Only data classes are allowed to be marked as @JvmRecord");
|
||||
|
||||
String MESSAGE_FOR_CONCURRENT_HASH_MAP_CONTAINS =
|
||||
"Method 'contains' from ConcurrentHashMap may have unexpected semantics: it calls 'containsValue' instead of 'containsKey'. " +
|
||||
|
||||
@@ -126,6 +126,7 @@ public interface ErrorsJvm {
|
||||
DiagnosticFactory0<PsiElement> INNER_JVM_RECORD = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory0<PsiElement> FIELD_IN_JVM_RECORD = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory0<PsiElement> DELEGATION_BY_IN_JVM_RECORD = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory0<PsiElement> NON_DATA_CLASS_JVM_RECORD = DiagnosticFactory0.create(ERROR);
|
||||
|
||||
DiagnosticFactory0<KtDeclaration> NON_JVM_DEFAULT_OVERRIDES_JAVA_DEFAULT = DiagnosticFactory0.create(WARNING, DECLARATION_SIGNATURE);
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ public class JavaClass {
|
||||
// FILE: main.kt
|
||||
|
||||
@JvmRecord
|
||||
class MyRec<R>(val x: String, val y: R)
|
||||
data class MyRec<R>(val x: String, val y: R)
|
||||
|
||||
fun box(): String {
|
||||
val recordComponents = MyRec::class.java.recordComponents
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
// !LANGUAGE: +JvmRecordSupport
|
||||
// JVM_TARGET: 15_PREVIEW
|
||||
|
||||
@JvmRecord
|
||||
data class MyRec<R>(val x: String, val y: R)
|
||||
|
||||
fun box(): String {
|
||||
val m1 = MyRec("O", "K")
|
||||
val m2 = MyRec("O", "K")
|
||||
|
||||
if (m1 != m2) return "fail 1"
|
||||
if (m1.hashCode() != m2.hashCode()) return "fail 2"
|
||||
if (m1.toString() != "MyRec(x=O, y=K)") return "fail 3: $m1"
|
||||
|
||||
return "OK"
|
||||
}
|
||||
@@ -1,25 +1,25 @@
|
||||
// !LANGUAGE: +JvmRecordSupport
|
||||
// SKIP_TXT
|
||||
|
||||
@JvmRecord
|
||||
class <!JVM_RECORD_WITHOUT_PRIMARY_CONSTRUCTOR_PARAMETERS!>A0<!>
|
||||
<!NON_DATA_CLASS_JVM_RECORD!>@JvmRecord<!>
|
||||
class A0
|
||||
|
||||
@JvmRecord
|
||||
class <!JVM_RECORD_WITHOUT_PRIMARY_CONSTRUCTOR_PARAMETERS!>A1<!> {
|
||||
<!NON_DATA_CLASS_JVM_RECORD!>@JvmRecord<!>
|
||||
class A1 {
|
||||
constructor()
|
||||
}
|
||||
|
||||
@JvmRecord
|
||||
class A2<!JVM_RECORD_WITHOUT_PRIMARY_CONSTRUCTOR_PARAMETERS!>()<!>
|
||||
<!NON_DATA_CLASS_JVM_RECORD!>@JvmRecord<!>
|
||||
class A2()
|
||||
|
||||
@JvmRecord
|
||||
class A3(<!JVM_RECORD_NOT_VAL_PARAMETER!><!UNUSED_PARAMETER!>name<!>: String<!>)
|
||||
<!NON_DATA_CLASS_JVM_RECORD!>@JvmRecord<!>
|
||||
class A3(<!UNUSED_PARAMETER!>name<!>: String)
|
||||
|
||||
@JvmRecord
|
||||
class A4(<!JVM_RECORD_NOT_VAL_PARAMETER!>var name: String<!>)
|
||||
<!NON_DATA_CLASS_JVM_RECORD!>@JvmRecord<!>
|
||||
class A4(var name: String)
|
||||
|
||||
@JvmRecord
|
||||
class A5(vararg val name: String, <!JVM_RECORD_NOT_VAL_PARAMETER!><!UNUSED_PARAMETER!>y<!>: Int<!>)
|
||||
<!NON_DATA_CLASS_JVM_RECORD!>@JvmRecord<!>
|
||||
class A5(vararg val name: String, <!UNUSED_PARAMETER!>y<!>: Int)
|
||||
|
||||
@JvmRecord
|
||||
<!NON_FINAL_JVM_RECORD!>open<!> class A6(val x: String)
|
||||
@@ -35,7 +35,7 @@ class A5(vararg val name: String, <!JVM_RECORD_NOT_VAL_PARAMETER!><!UNUSED_PARAM
|
||||
X("");
|
||||
}
|
||||
|
||||
@JvmRecord
|
||||
<!NON_DATA_CLASS_JVM_RECORD!>@JvmRecord<!>
|
||||
class A10(
|
||||
val x: String,
|
||||
val y: Int,
|
||||
|
||||
+5
-5
@@ -6,15 +6,15 @@ interface I
|
||||
val i: I = object : I {}
|
||||
|
||||
@JvmRecord
|
||||
class MyRec1(val name: String) : <!DELEGATION_BY_IN_JVM_RECORD!>I by i<!>
|
||||
data class MyRec1(val name: String) : <!DELEGATION_BY_IN_JVM_RECORD!>I by i<!>
|
||||
|
||||
@JvmRecord
|
||||
class MyRec2(val name: String) {
|
||||
data class MyRec2(val name: String) {
|
||||
<!FIELD_IN_JVM_RECORD!>val x: Int = 0<!>
|
||||
}
|
||||
|
||||
@JvmRecord
|
||||
class MyRec3(val name: String) {
|
||||
data class MyRec3(val name: String) {
|
||||
<!FIELD_IN_JVM_RECORD!>val y: String
|
||||
get() = field + "1"<!>
|
||||
|
||||
@@ -24,12 +24,12 @@ class MyRec3(val name: String) {
|
||||
}
|
||||
|
||||
@JvmRecord
|
||||
class MyRec4(val name: String) {
|
||||
data class MyRec4(val name: String) {
|
||||
<!FIELD_IN_JVM_RECORD!>val z: Int by lazy { 1 }<!>
|
||||
}
|
||||
|
||||
@JvmRecord
|
||||
class MyRec5(val name: String) {
|
||||
data class MyRec5(val name: String) {
|
||||
val w: String get() = name + "1"
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -1,11 +1,11 @@
|
||||
// !LANGUAGE: +JvmRecordSupport
|
||||
|
||||
@JvmRecord
|
||||
<!NON_DATA_CLASS_JVM_RECORD!>@JvmRecord<!>
|
||||
class BasicRecord(val x: String)
|
||||
|
||||
@JvmRecord
|
||||
data class BasicDataRecord(val x: String)
|
||||
|
||||
@JvmRecord
|
||||
<!NON_DATA_CLASS_JVM_RECORD!>@JvmRecord<!>
|
||||
class BasicRecordWithSuperClass(val x: String) : Record()
|
||||
|
||||
|
||||
@@ -4,16 +4,16 @@ abstract class Abstract
|
||||
interface I
|
||||
|
||||
@JvmRecord
|
||||
class <!JVM_RECORD_EXTENDS_CLASS!>A1<!>(val x: String) : Abstract(), I
|
||||
data class <!JVM_RECORD_EXTENDS_CLASS!>A1<!>(val x: String) : Abstract(), I
|
||||
|
||||
@JvmRecord
|
||||
class <!JVM_RECORD_EXTENDS_CLASS!>A2<!>(val x: String) : Any(), I
|
||||
data class <!JVM_RECORD_EXTENDS_CLASS!>A2<!>(val x: String) : Any(), I
|
||||
|
||||
@JvmRecord
|
||||
class A3(val x: String) : Record(), I
|
||||
data class A3(val x: String) : Record(), I
|
||||
|
||||
@JvmRecord
|
||||
class A4(val x: String) : java.lang.Record(), I
|
||||
data class A4(val x: String) : java.lang.Record(), I
|
||||
|
||||
@JvmRecord
|
||||
class A5(val x: String) : I
|
||||
data class A5(val x: String) : I
|
||||
|
||||
+15
-5
@@ -1,40 +1,50 @@
|
||||
package
|
||||
|
||||
@kotlin.jvm.JvmRecord public final class A1 : Abstract, I, java.lang.Record {
|
||||
@kotlin.jvm.JvmRecord public final data class A1 : Abstract, I, java.lang.Record {
|
||||
public constructor A1(/*0*/ x: kotlin.String)
|
||||
public final val x: kotlin.String
|
||||
public final operator /*synthesized*/ fun component1(): kotlin.String
|
||||
public final /*synthesized*/ fun copy(/*0*/ x: kotlin.String = ...): A1
|
||||
public open override /*3*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*3*/ /*synthesized*/ fun hashCode(): kotlin.Int
|
||||
public open override /*3*/ /*synthesized*/ fun toString(): kotlin.String
|
||||
}
|
||||
|
||||
@kotlin.jvm.JvmRecord public final class A2 : kotlin.Any, I, java.lang.Record {
|
||||
@kotlin.jvm.JvmRecord public final data class A2 : kotlin.Any, I, java.lang.Record {
|
||||
public constructor A2(/*0*/ x: kotlin.String)
|
||||
public final val x: kotlin.String
|
||||
public final operator /*synthesized*/ fun component1(): kotlin.String
|
||||
public final /*synthesized*/ fun copy(/*0*/ x: kotlin.String = ...): A2
|
||||
public open override /*3*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*3*/ /*synthesized*/ fun hashCode(): kotlin.Int
|
||||
public open override /*3*/ /*synthesized*/ fun toString(): kotlin.String
|
||||
}
|
||||
|
||||
@kotlin.jvm.JvmRecord public final class A3 : java.lang.Record, I {
|
||||
@kotlin.jvm.JvmRecord public final data class A3 : java.lang.Record, I {
|
||||
public constructor A3(/*0*/ x: kotlin.String)
|
||||
public final val x: kotlin.String
|
||||
public final operator /*synthesized*/ fun component1(): kotlin.String
|
||||
public final /*synthesized*/ fun copy(/*0*/ x: kotlin.String = ...): A3
|
||||
public open override /*2*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*2*/ /*synthesized*/ fun hashCode(): kotlin.Int
|
||||
public open override /*2*/ /*synthesized*/ fun toString(): kotlin.String
|
||||
}
|
||||
|
||||
@kotlin.jvm.JvmRecord public final class A4 : java.lang.Record, I {
|
||||
@kotlin.jvm.JvmRecord public final data class A4 : java.lang.Record, I {
|
||||
public constructor A4(/*0*/ x: kotlin.String)
|
||||
public final val x: kotlin.String
|
||||
public final operator /*synthesized*/ fun component1(): kotlin.String
|
||||
public final /*synthesized*/ fun copy(/*0*/ x: kotlin.String = ...): A4
|
||||
public open override /*2*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*2*/ /*synthesized*/ fun hashCode(): kotlin.Int
|
||||
public open override /*2*/ /*synthesized*/ fun toString(): kotlin.String
|
||||
}
|
||||
|
||||
@kotlin.jvm.JvmRecord public final class A5 : I, java.lang.Record {
|
||||
@kotlin.jvm.JvmRecord public final data class A5 : I, java.lang.Record {
|
||||
public constructor A5(/*0*/ x: kotlin.String)
|
||||
public final val x: kotlin.String
|
||||
public final operator /*synthesized*/ fun component1(): kotlin.String
|
||||
public final /*synthesized*/ fun copy(/*0*/ x: kotlin.String = ...): A5
|
||||
public open override /*2*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*2*/ /*synthesized*/ fun hashCode(): kotlin.Int
|
||||
public open override /*2*/ /*synthesized*/ fun toString(): kotlin.String
|
||||
|
||||
+5
@@ -45,6 +45,11 @@ public class Jdk15BlackBoxCodegenTestGenerated extends AbstractJdk15BlackBoxCode
|
||||
runTest("compiler/testData/codegen/java15/box/records/bytecodeShapeForJava.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("dataJvmRecord.kt")
|
||||
public void testDataJvmRecord() throws Exception {
|
||||
runTest("compiler/testData/codegen/java15/box/records/dataJvmRecord.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("recordDifferentPropertyOverride.kt")
|
||||
public void testRecordDifferentPropertyOverride() throws Exception {
|
||||
runTest("compiler/testData/codegen/java15/box/records/recordDifferentPropertyOverride.kt");
|
||||
|
||||
Reference in New Issue
Block a user