Add custom diagnostic checker for @JvmDefault annotation

This commit is contained in:
Mikhael Bogdanov
2018-03-29 16:41:47 +02:00
parent 89f22e69b4
commit 23e8adb793
21 changed files with 205 additions and 47 deletions
@@ -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'");
}
@@ -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()
}
}
}
@@ -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 {