Add @JvmRecord annotation and relevant diagnostics

^KT-43677 In Progress
This commit is contained in:
Denis.Zharkov
2020-11-25 12:54:40 +03:00
parent 059e2aab7a
commit 4f5db241ea
13 changed files with 173 additions and 5 deletions
@@ -23,6 +23,7 @@ val JVM_OVERLOADS_FQ_NAME = FqName("kotlin.jvm.JvmOverloads")
@JvmField
val JVM_SYNTHETIC_ANNOTATION_FQ_NAME = FqName("kotlin.jvm.JvmSynthetic")
val JVM_RECORD_ANNOTATION_FQ_NAME = FqName("kotlin.jvm.JvmRecord")
@JvmField
val SYNCHRONIZED_ANNOTATION_FQ_NAME = FqName("kotlin.jvm.Synchronized")
@@ -92,3 +93,5 @@ fun DeclarationDescriptor.findStrictfpAnnotation(): AnnotationDescriptor? =
fun DeclarationDescriptor.findSynchronizedAnnotation(): AnnotationDescriptor? =
annotations.findAnnotation(SYNCHRONIZED_ANNOTATION_FQ_NAME)
fun ClassDescriptor.isJvmRecord(): Boolean = annotations.hasAnnotation(JVM_RECORD_ANNOTATION_FQ_NAME)
@@ -0,0 +1,67 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.resolve.jvm.checkers
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.checkers.DeclarationChecker
import org.jetbrains.kotlin.resolve.checkers.DeclarationCheckerContext
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
object JvmRecordApplicabilityChecker : DeclarationChecker {
override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) {
if (descriptor !is ClassDescriptor || declaration !is KtClassOrObject || !descriptor.isJvmRecord()) return
val reportOn =
declaration.annotationEntries.firstOrNull { it.shortName == JVM_RECORD_ANNOTATION_FQ_NAME.shortName() }
?: declaration
if (!context.languageVersionSettings.supportsFeature(LanguageFeature.JvmRecordSupport)) {
context.trace.report(
Errors.UNSUPPORTED_FEATURE.on(
reportOn,
LanguageFeature.JvmRecordSupport to context.languageVersionSettings
)
)
return
}
if (DescriptorUtils.isLocal(descriptor)) {
context.trace.report(ErrorsJvm.LOCAL_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
}
}
}
}
@@ -152,6 +152,11 @@ public class DefaultErrorMessagesJvm implements DefaultErrorMessages.Extension {
MAP.put(SUSPENSION_POINT_INSIDE_MONITOR, "A suspension point at {0} is inside a critical section", STRING);
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(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");
String MESSAGE_FOR_CONCURRENT_HASH_MAP_CONTAINS =
"Method 'contains' from ConcurrentHashMap may have unexpected semantics: it calls 'containsValue' instead of 'containsKey'. " +
"Use explicit form of the call to 'containsKey'/'containsValue'/'contains' or cast the value to kotlin.collections.Map instead. " +
@@ -115,6 +115,11 @@ public interface ErrorsJvm {
DiagnosticFactory0<KtDeclaration> JVM_DEFAULT_THROUGH_INHERITANCE = DiagnosticFactory0.create(ERROR, DECLARATION_SIGNATURE);
DiagnosticFactory0<PsiElement> USAGE_OF_JVM_DEFAULT_THROUGH_SUPER_CALL = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<PsiElement> LOCAL_JVM_RECORD = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<PsiElement> JVM_RECORD_WITHOUT_PRIMARY_CONSTRUCTOR_PARAMETERS = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<PsiElement> JVM_RECORD_NOT_VAL_PARAMETER = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<PsiElement> JVM_RECORD_NOT_LAST_VARARG_PARAMETER = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<KtDeclaration> NON_JVM_DEFAULT_OVERRIDES_JAVA_DEFAULT = DiagnosticFactory0.create(WARNING, DECLARATION_SIGNATURE);
DiagnosticFactory0<KtAnnotationEntry> EXPLICIT_METADATA_IS_DISALLOWED = DiagnosticFactory0.create(ERROR);
@@ -42,6 +42,7 @@ object JvmPlatformConfigurator : PlatformConfiguratorBase(
SynchronizedOnInlineMethodChecker,
DefaultCheckerInTailrec,
FunctionDelegateMemberNameClashChecker,
JvmRecordApplicabilityChecker
),
additionalCallCheckers = listOf(