diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/checkers/JvmRecordApplicabilityChecker.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/checkers/JvmRecordApplicabilityChecker.kt index 26283c25c3b..9a84d7be9ad 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/checkers/JvmRecordApplicabilityChecker.kt +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/checkers/JvmRecordApplicabilityChecker.kt @@ -5,14 +5,19 @@ package org.jetbrains.kotlin.resolve.jvm.checkers +import com.intellij.psi.PsiElement import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.descriptors.isFinalClass import org.jetbrains.kotlin.diagnostics.Errors import org.jetbrains.kotlin.incremental.components.NoLookupLocation +import org.jetbrains.kotlin.lexer.KtModifierKeywordToken +import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.psi.KtClassOrObject import org.jetbrains.kotlin.psi.KtDeclaration +import org.jetbrains.kotlin.psi.KtModifierList import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.checkers.DeclarationChecker import org.jetbrains.kotlin.resolve.checkers.DeclarationCheckerContext @@ -22,6 +27,7 @@ import org.jetbrains.kotlin.resolve.jvm.JAVA_LANG_RECORD_FQ_NAME import org.jetbrains.kotlin.resolve.jvm.annotations.JVM_RECORD_ANNOTATION_FQ_NAME import org.jetbrains.kotlin.resolve.jvm.annotations.isJvmRecord import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm +import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult object JvmRecordApplicabilityChecker : DeclarationChecker { override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) { @@ -46,6 +52,26 @@ object JvmRecordApplicabilityChecker : DeclarationChecker { return } + if (descriptor.kind == ClassKind.ENUM_CLASS) { + val modifierOrName = + declaration.modifierList?.getModifier(KtTokens.ENUM_KEYWORD) + ?: declaration.nameIdentifier + ?: declaration + + context.trace.report(ErrorsJvm.ENUM_JVM_RECORD.on(modifierOrName)) + return + } + + if (!descriptor.isFinalClass) { + val modifierOrName = + declaration.modifierList?.findOneOfModifiers(KtTokens.ABSTRACT_KEYWORD, KtTokens.OPEN_KEYWORD, KtTokens.SEALED_KEYWORD) + ?: declaration.nameIdentifier + ?: declaration + + context.trace.report(ErrorsJvm.NON_FINAL_JVM_RECORD.on(modifierOrName)) + return + } + if (DescriptorUtils.isLocal(descriptor)) { context.trace.report(ErrorsJvm.LOCAL_JVM_RECORD.on(reportOn)) return @@ -84,3 +110,6 @@ object JvmRecordApplicabilityChecker : DeclarationChecker { } } } + +private fun KtModifierList.findOneOfModifiers(vararg modifierTokens: KtModifierKeywordToken): PsiElement? = + modifierTokens.firstNotNullResult(this::getModifier) diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/DefaultErrorMessagesJvm.java b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/DefaultErrorMessagesJvm.java index 55a5378d8c1..1ccb32a1d24 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/DefaultErrorMessagesJvm.java +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/DefaultErrorMessagesJvm.java @@ -153,6 +153,8 @@ public class DefaultErrorMessagesJvm implements DefaultErrorMessages.Extension { MAP.put(SUSPENSION_POINT_INSIDE_CRITICAL_SECTION, "The ''{0}'' suspension point is inside a critical section", NAME); MAP.put(LOCAL_JVM_RECORD, "Local @JvmRecord classes are not allowed"); + MAP.put(NON_FINAL_JVM_RECORD, "@JvmRecord class should be final"); + MAP.put(ENUM_JVM_RECORD, "@JvmRecord class should not be an enum"); MAP.put(JVM_RECORD_WITHOUT_PRIMARY_CONSTRUCTOR_PARAMETERS, "Primary constructor with parameters is required for @JvmRecord class"); MAP.put(JVM_RECORD_NOT_VAL_PARAMETER, "Constructor parameter of @JvmRecord class should be a val"); MAP.put(JVM_RECORD_NOT_LAST_VARARG_PARAMETER, "Only the last constructor parameter of @JvmRecord may be a vararg"); diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/ErrorsJvm.java b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/ErrorsJvm.java index 13b4606b1e5..999c15ac60c 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/ErrorsJvm.java +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/ErrorsJvm.java @@ -116,6 +116,8 @@ public interface ErrorsJvm { DiagnosticFactory0 USAGE_OF_JVM_DEFAULT_THROUGH_SUPER_CALL = DiagnosticFactory0.create(ERROR); DiagnosticFactory0 LOCAL_JVM_RECORD = DiagnosticFactory0.create(ERROR); + DiagnosticFactory0 NON_FINAL_JVM_RECORD = DiagnosticFactory0.create(ERROR); + DiagnosticFactory0 ENUM_JVM_RECORD = DiagnosticFactory0.create(ERROR); DiagnosticFactory0 JVM_RECORD_WITHOUT_PRIMARY_CONSTRUCTOR_PARAMETERS = DiagnosticFactory0.create(ERROR); DiagnosticFactory0 JVM_RECORD_NOT_VAL_PARAMETER = DiagnosticFactory0.create(ERROR); DiagnosticFactory0 JVM_RECORD_NOT_LAST_VARARG_PARAMETER = DiagnosticFactory0.create(ERROR); diff --git a/compiler/testData/diagnostics/testsWithJava15/jvmRecord/diagnostics.kt b/compiler/testData/diagnostics/testsWithJava15/jvmRecord/diagnostics.kt index 11d96263680..a43e8683aca 100644 --- a/compiler/testData/diagnostics/testsWithJava15/jvmRecord/diagnostics.kt +++ b/compiler/testData/diagnostics/testsWithJava15/jvmRecord/diagnostics.kt @@ -22,7 +22,21 @@ class A4(var name: String) class A5(vararg val name: String, y: Int) @JvmRecord -class A6( +open class A6(val x: String) + +@JvmRecord +abstract class A7(val x: String) + +@JvmRecord +sealed class A8(val x: String) + +@JvmRecord +enum class A9(val x: String) { + X(""); +} + +@JvmRecord +class A10( val x: String, val y: Int, vararg val z: Double,