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 9a84d7be9ad..af4c2442bae 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 @@ -7,17 +7,13 @@ 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.descriptors.* 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.psi.* +import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.checkers.DeclarationChecker import org.jetbrains.kotlin.resolve.checkers.DeclarationCheckerContext @@ -72,6 +68,16 @@ object JvmRecordApplicabilityChecker : DeclarationChecker { return } + if (descriptor.isInner) { + val modifierOrName = + declaration.modifierList?.getModifier(KtTokens.INNER_KEYWORD) + ?: declaration.nameIdentifier + ?: declaration + + context.trace.report(ErrorsJvm.INNER_JVM_RECORD.on(modifierOrName)) + return + } + if (DescriptorUtils.isLocal(descriptor)) { context.trace.report(ErrorsJvm.LOCAL_JVM_RECORD.on(reportOn)) return @@ -100,6 +106,23 @@ object JvmRecordApplicabilityChecker : DeclarationChecker { } } + for (member in declaration.declarations) { + if (member !is KtProperty) continue + + val propertyDescriptor = context.trace[BindingContext.DECLARATION_TO_DESCRIPTOR, member] as? PropertyDescriptor ?: continue + if (context.trace.bindingContext[BindingContext.BACKING_FIELD_REQUIRED, propertyDescriptor] != true && member.delegate == null) continue + + context.trace.report(ErrorsJvm.FIELD_IN_JVM_RECORD.on(member)) + return + } + + for (superTypeEntry in declaration.superTypeListEntries) { + if (superTypeEntry !is KtDelegatedSuperTypeEntry) continue + + context.trace.report(ErrorsJvm.DELEGATION_BY_IN_JVM_RECORD.on(superTypeEntry)) + return + } + for (supertype in descriptor.typeConstructor.supertypes) { val classDescriptor = supertype.constructor.declarationDescriptor as? ClassDescriptor ?: continue if (classDescriptor.kind == ClassKind.INTERFACE || classDescriptor.fqNameSafe == JAVA_LANG_RECORD_FQ_NAME) continue 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 1ccb32a1d24..ecae1cde86e 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 @@ -160,6 +160,9 @@ public class DefaultErrorMessagesJvm implements DefaultErrorMessages.Extension { MAP.put(JVM_RECORD_NOT_LAST_VARARG_PARAMETER, "Only the last constructor parameter of @JvmRecord may be a vararg"); MAP.put(JVM_RECORD_EXTENDS_CLASS, "Record cannot inherit an other class but java.lang.Record" , RENDER_TYPE); MAP.put(JVM_RECORD_REQUIRES_JDK15, "Using @JvmRecords requires at least JDK 15"); + 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"); String MESSAGE_FOR_CONCURRENT_HASH_MAP_CONTAINS = "Method 'contains' from ConcurrentHashMap may have unexpected semantics: it calls 'containsValue' instead of 'containsKey'. " + 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 999c15ac60c..9b2c0e9fc51 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 @@ -123,6 +123,9 @@ public interface ErrorsJvm { DiagnosticFactory0 JVM_RECORD_NOT_LAST_VARARG_PARAMETER = DiagnosticFactory0.create(ERROR); DiagnosticFactory1 JVM_RECORD_EXTENDS_CLASS = DiagnosticFactory1.create(ERROR); DiagnosticFactory0 JVM_RECORD_REQUIRES_JDK15 = DiagnosticFactory0.create(ERROR); + DiagnosticFactory0 INNER_JVM_RECORD = DiagnosticFactory0.create(ERROR); + DiagnosticFactory0 FIELD_IN_JVM_RECORD = DiagnosticFactory0.create(ERROR); + DiagnosticFactory0 DELEGATION_BY_IN_JVM_RECORD = DiagnosticFactory0.create(ERROR); DiagnosticFactory0 NON_JVM_DEFAULT_OVERRIDES_JAVA_DEFAULT = DiagnosticFactory0.create(WARNING, DECLARATION_SIGNATURE); diff --git a/compiler/testData/diagnostics/testsWithJava15/jvmRecord/diagnostics.kt b/compiler/testData/diagnostics/testsWithJava15/jvmRecord/diagnostics.kt index a43e8683aca..8aa985c3a64 100644 --- a/compiler/testData/diagnostics/testsWithJava15/jvmRecord/diagnostics.kt +++ b/compiler/testData/diagnostics/testsWithJava15/jvmRecord/diagnostics.kt @@ -46,3 +46,8 @@ fun main() { @JvmRecord class Local } + +class Outer { + @JvmRecord + inner class Inner(val name: String) +} diff --git a/compiler/testData/diagnostics/testsWithJava15/jvmRecord/irrelevantFields.kt b/compiler/testData/diagnostics/testsWithJava15/jvmRecord/irrelevantFields.kt new file mode 100644 index 00000000000..8733a6eafe1 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithJava15/jvmRecord/irrelevantFields.kt @@ -0,0 +1,40 @@ +// !LANGUAGE: +JvmRecordSupport +// SKIP_TXT + +interface I + +val i: I = object : I {} + +@JvmRecord +class MyRec1(val name: String) : I by i + +@JvmRecord +class MyRec2(val name: String) { + val x: Int = 0 +} + +@JvmRecord +class MyRec3(val name: String) { + val y: String + get() = field + "1" + + init { + y = "" + } +} + +@JvmRecord +class MyRec4(val name: String) { + val z: Int by lazy { 1 } +} + +@JvmRecord +class MyRec5(val name: String) { + val w: String get() = name + "1" +} + + + + + + diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsWithJdk15TestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsWithJdk15TestGenerated.java index 80854b70acc..8d5993043e9 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsWithJdk15TestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsWithJdk15TestGenerated.java @@ -50,6 +50,11 @@ public class DiagnosticsWithJdk15TestGenerated extends AbstractDiagnosticsWithJd runTest("compiler/testData/diagnostics/testsWithJava15/jvmRecord/disabledFeature.kt"); } + @TestMetadata("irrelevantFields.kt") + public void testIrrelevantFields() throws Exception { + runTest("compiler/testData/diagnostics/testsWithJava15/jvmRecord/irrelevantFields.kt"); + } + @TestMetadata("jvmRecordDescriptorStructure.kt") public void testJvmRecordDescriptorStructure() throws Exception { runTest("compiler/testData/diagnostics/testsWithJava15/jvmRecord/jvmRecordDescriptorStructure.kt");