Add custom diagnostic checker for @JvmDefault annotation
This commit is contained in:
@@ -129,4 +129,7 @@ public class JVMConfigurationKeys {
|
||||
|
||||
public static final CompilerConfigurationKey<List<String>> ADDITIONAL_JAVA_MODULES =
|
||||
CompilerConfigurationKey.create("additional Java modules");
|
||||
|
||||
public static final CompilerConfigurationKey<Boolean> ENABLE_JVM_DEFAULT =
|
||||
CompilerConfigurationKey.create("Allow to use '@JvmDefault'");
|
||||
}
|
||||
|
||||
+38
-4
@@ -5,26 +5,28 @@
|
||||
|
||||
package org.jetbrains.kotlin.resolve.jvm.checkers
|
||||
|
||||
import org.jetbrains.kotlin.config.AnalysisFlag
|
||||
import org.jetbrains.kotlin.config.JvmTarget
|
||||
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.PropertyAccessorDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.psi.KtDeclaration
|
||||
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
|
||||
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.descriptorUtil.getSuperInterfaces
|
||||
import org.jetbrains.kotlin.resolve.jvm.annotations.hasJvmDefaultAnnotation
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm
|
||||
|
||||
class JvmDefaultChecker(val jvmTarget: JvmTarget) : DeclarationChecker {
|
||||
|
||||
companion object {
|
||||
val JVM_DEFAULT_FQ_NAME = FqName("kotlin.jvm.JvmDefault")
|
||||
|
||||
}
|
||||
|
||||
|
||||
override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) {
|
||||
val enableJvmDefault = context.languageVersionSettings.getFlag(AnalysisFlag.enableJvmDefault)
|
||||
|
||||
descriptor.annotations.findAnnotation(JVM_DEFAULT_FQ_NAME)?.let { annotationDescriptor ->
|
||||
val reportOn = DescriptorToSourceUtils.getSourceFromAnnotation(annotationDescriptor) ?: declaration
|
||||
@@ -32,10 +34,23 @@ class JvmDefaultChecker(val jvmTarget: JvmTarget) : DeclarationChecker {
|
||||
context.trace.report(ErrorsJvm.JVM_DEFAULT_NOT_IN_INTERFACE.on(reportOn))
|
||||
} else if (jvmTarget == JvmTarget.JVM_1_6) {
|
||||
context.trace.report(ErrorsJvm.JVM_DEFAULT_IN_JVM6_TARGET.on(reportOn))
|
||||
} else if (!enableJvmDefault) {
|
||||
context.trace.report(ErrorsJvm.JVM_DEFAULT_IN_DECLARATION.on(declaration))
|
||||
}
|
||||
return@check
|
||||
}
|
||||
|
||||
if (descriptor is ClassDescriptor) {
|
||||
val hasDeclaredJvmDefaults =
|
||||
descriptor.unsubstitutedMemberScope.getContributedDescriptors().filterIsInstance<CallableMemberDescriptor>().any {
|
||||
it.kind.isReal && it.hasJvmDefaultAnnotation()
|
||||
}
|
||||
if (!hasDeclaredJvmDefaults && !checkJvmDefaultThroughInheritance(descriptor, enableJvmDefault)) {
|
||||
context.trace.report(ErrorsJvm.JVM_DEFAULT_THROUGH_INHERITANCE.on(declaration))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!DescriptorUtils.isInterface(descriptor.containingDeclaration)) return
|
||||
val memberDescriptor = descriptor as? CallableMemberDescriptor ?: return
|
||||
if (descriptor is PropertyAccessorDescriptor) return
|
||||
@@ -44,4 +59,23 @@ class JvmDefaultChecker(val jvmTarget: JvmTarget) : DeclarationChecker {
|
||||
context.trace.report(ErrorsJvm.JVM_DEFAULT_REQUIRED_FOR_OVERRIDE.on(declaration))
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkJvmDefaultThroughInheritance(descriptor: DeclarationDescriptor, enableJvmDefault: Boolean): Boolean {
|
||||
if (enableJvmDefault) return true
|
||||
|
||||
if (descriptor !is ClassDescriptor) return true
|
||||
|
||||
if (!DescriptorUtils.isInterface(descriptor) &&
|
||||
!DescriptorUtils.isAnnotationClass(descriptor)
|
||||
) {
|
||||
return descriptor.getSuperInterfaces().all {
|
||||
checkJvmDefaultThroughInheritance(it, enableJvmDefault)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return descriptor.unsubstitutedMemberScope.getContributedDescriptors().filterIsInstance<CallableMemberDescriptor>().all {
|
||||
!it.hasJvmDefaultAnnotation()
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
@@ -137,6 +137,9 @@ public class DefaultErrorMessagesJvm implements DefaultErrorMessages.Extension {
|
||||
MAP.put(JVM_DEFAULT_NOT_IN_INTERFACE,"'@JvmDefault' is only supported on interface members");
|
||||
MAP.put(JVM_DEFAULT_IN_JVM6_TARGET,"'@JvmDefault' is only supported since JVM target 1.8. Recompile with '-jvm-target 1.8'");
|
||||
MAP.put(JVM_DEFAULT_REQUIRED_FOR_OVERRIDE, "'@JvmDefault' is required for an override of a '@JvmDefault' member");
|
||||
MAP.put(JVM_DEFAULT_IN_DECLARATION, "Usage of '@JvmDefault' is only allowed if the flag -Xenable-jvm-default is enabled");
|
||||
MAP.put(JVM_DEFAULT_THROUGH_INHERITANCE, "Inheritance from an interface with '@JvmDefault' members is only allowed if the flag -Xenable-jvm-default is enabled");
|
||||
MAP.put(USAGE_OF_JVM_THROUGH_SUPER_CALL, "Super calls of '@JvmDefault' members are only allowed if the flag -Xenable-jvm-default is enabled");
|
||||
}
|
||||
|
||||
@NotNull
|
||||
|
||||
@@ -117,6 +117,9 @@ public interface ErrorsJvm {
|
||||
DiagnosticFactory0<PsiElement> JVM_DEFAULT_NOT_IN_INTERFACE = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory0<PsiElement> JVM_DEFAULT_IN_JVM6_TARGET = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory0<KtDeclaration> JVM_DEFAULT_REQUIRED_FOR_OVERRIDE = DiagnosticFactory0.create(ERROR, DECLARATION_SIGNATURE);
|
||||
DiagnosticFactory0<KtDeclaration> JVM_DEFAULT_IN_DECLARATION = DiagnosticFactory0.create(ERROR, DECLARATION_SIGNATURE);
|
||||
DiagnosticFactory0<KtDeclaration> JVM_DEFAULT_THROUGH_INHERITANCE = DiagnosticFactory0.create(ERROR, DECLARATION_SIGNATURE);
|
||||
DiagnosticFactory0<KtDeclaration> USAGE_OF_JVM_THROUGH_SUPER_CALL = DiagnosticFactory0.create(ERROR, DECLARATION_SIGNATURE);
|
||||
|
||||
enum NullabilityInformationSource {
|
||||
KOTLIN {
|
||||
|
||||
Reference in New Issue
Block a user