Report error on missed specialization in compatibility mode

#KT-39603 Fixed
This commit is contained in:
Mikhail Bogdanov
2020-06-18 11:14:56 +02:00
parent a150e7b6e5
commit 9c0b96af71
32 changed files with 872 additions and 89 deletions
@@ -10,17 +10,17 @@ import org.jetbrains.kotlin.config.JvmDefaultMode
import org.jetbrains.kotlin.config.JvmTarget
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.load.java.descriptors.JavaMethodDescriptor
import org.jetbrains.kotlin.load.kotlin.computeJvmDescriptor
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.DescriptorUtils.isInterface
import org.jetbrains.kotlin.resolve.DescriptorUtils.*
import org.jetbrains.kotlin.resolve.OverridingUtil
import org.jetbrains.kotlin.resolve.checkers.DeclarationChecker
import org.jetbrains.kotlin.resolve.checkers.DeclarationCheckerContext
import org.jetbrains.kotlin.resolve.jvm.annotations.JVM_DEFAULT_FQ_NAME
import org.jetbrains.kotlin.resolve.jvm.annotations.JVM_DEFAULT_NO_COMPATIBILITY_FQ_NAME
import org.jetbrains.kotlin.resolve.jvm.annotations.isCompiledToJvmDefault
import org.jetbrains.kotlin.resolve.descriptorUtil.isEffectivelyPrivateApi
import org.jetbrains.kotlin.resolve.jvm.annotations.*
import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm
import org.jetbrains.kotlin.util.getNonPrivateTraitMembersForDelegation
class JvmDefaultChecker(val jvmTarget: JvmTarget) : DeclarationChecker {
@@ -29,7 +29,7 @@ class JvmDefaultChecker(val jvmTarget: JvmTarget) : DeclarationChecker {
descriptor.annotations.findAnnotation(JVM_DEFAULT_FQ_NAME)?.let { annotationDescriptor ->
val reportOn = DescriptorToSourceUtils.getSourceFromAnnotation(annotationDescriptor) ?: declaration
if (!DescriptorUtils.isInterface(descriptor.containingDeclaration)) {
if (!isInterface(descriptor.containingDeclaration)) {
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, "JvmDefault"))
@@ -60,23 +60,78 @@ class JvmDefaultChecker(val jvmTarget: JvmTarget) : DeclarationChecker {
}
if (!DescriptorUtils.isInterface(descriptor.containingDeclaration)) return
val memberDescriptor = descriptor as? CallableMemberDescriptor ?: return
if (descriptor is PropertyAccessorDescriptor) return
if (isInterface(descriptor.containingDeclaration)) {
val memberDescriptor = descriptor as? CallableMemberDescriptor ?: return
if (descriptor is PropertyAccessorDescriptor) return
if (memberDescriptor.overriddenDescriptors.any { it.annotations.hasAnnotation(JVM_DEFAULT_FQ_NAME) }) {
context.trace.report(ErrorsJvm.JVM_DEFAULT_REQUIRED_FOR_OVERRIDE.on(declaration))
} else if (jvmDefaultMode.isEnabled) {
descriptor.overriddenDescriptors.flatMap { OverridingUtil.getOverriddenDeclarations(it) }.toSet().let {
for (realDescriptor in OverridingUtil.filterOutOverridden(it)) {
if (realDescriptor is JavaMethodDescriptor && realDescriptor.modality != Modality.ABSTRACT) {
return context.trace.report(ErrorsJvm.NON_JVM_DEFAULT_OVERRIDES_JAVA_DEFAULT.on(declaration))
if (memberDescriptor.overriddenDescriptors.any { it.annotations.hasAnnotation(JVM_DEFAULT_FQ_NAME) }) {
context.trace.report(ErrorsJvm.JVM_DEFAULT_REQUIRED_FOR_OVERRIDE.on(declaration))
} else if (jvmDefaultMode.isEnabled) {
descriptor.overriddenDescriptors.flatMap { OverridingUtil.getOverriddenDeclarations(it) }.toSet().let {
for (realDescriptor in OverridingUtil.filterOutOverridden(it)) {
if (realDescriptor is JavaMethodDescriptor && realDescriptor.modality != Modality.ABSTRACT) {
return context.trace.report(ErrorsJvm.NON_JVM_DEFAULT_OVERRIDES_JAVA_DEFAULT.on(declaration))
}
}
}
}
} else if (jvmDefaultMode.isCompatibility &&
!isInterface(descriptor) &&
!isAnnotationClass(descriptor) &&
descriptor is ClassDescriptor &&
!descriptor.hasJvmDefaultNoCompatibilityAnnotation()
) {
val modality = descriptor.modality
//TODO: maybe remove this check for jvm compatibility
if (modality !== Modality.OPEN && modality !== Modality.ABSTRACT || descriptor.isEffectivelyPrivateApi) return
for ((inheritedMember, actualImplementation) in getNonPrivateTraitMembersForDelegation(
descriptor,
returnImplNotDelegate = true
)) {
if (actualImplementation.isCallableMemberCompiledToJvmDefault(jvmDefaultMode)) {
if (actualImplementation is FunctionDescriptor && inheritedMember is FunctionDescriptor) {
processMember(inheritedMember, actualImplementation, context, declaration)
} else if (actualImplementation is PropertyDescriptor && inheritedMember is PropertyDescriptor) {
val getterImpl = actualImplementation.getter
val getterInherited = inheritedMember.getter
if (getterImpl == null || getterInherited == null || processMember(getterImpl, getterImpl, context, declaration)) {
if (actualImplementation.isVar && inheritedMember.isVar) {
val setterImpl = actualImplementation.setter
val setterInherited = inheritedMember.setter
if (setterImpl != null && setterInherited != null) {
processMember(setterImpl, setterImpl, context, declaration)
}
}
}
}
}
}
}
}
private fun processMember(
inheritedFun: FunctionDescriptor,
actualImplementation: FunctionDescriptor,
context: DeclarationCheckerContext,
declaration: KtDeclaration
): Boolean {
val inheritedSignature = inheritedFun.computeJvmDescriptor(withReturnType = true, withName = false)
val originalImplementation = actualImplementation.original
val actualSignature = originalImplementation.computeJvmDescriptor(withReturnType = true, withName = false)
if (inheritedSignature != actualSignature) {
//NB: this diagnostics should be a bit tuned, see box/jvm8/defaults/allCompatibility/kt14243_2.kt for details
context.trace.report(
ErrorsJvm.EXPLICIT_OVERRIDE_REQUIRED_IN_COMPATIBILITY_MODE.on(
declaration,
getDirectMember(inheritedFun),
getDirectMember(originalImplementation)
)
)
return false
}
return true
}
private fun checkJvmDefaultsInHierarchy(descriptor: DeclarationDescriptor, jvmDefaultMode: JvmDefaultMode): Boolean {
if (jvmDefaultMode.isEnabled) return true
@@ -159,6 +159,12 @@ public class DefaultErrorMessagesJvm implements DefaultErrorMessages.Extension {
MAP.put(FUNCTION_DELEGATE_MEMBER_NAME_CLASH,
"Functional interface member cannot have this name on JVM because it clashes with an internal member getFunctionDelegate");
MAP.put(EXPLICIT_OVERRIDE_REQUIRED_IN_COMPATIBILITY_MODE,
"Compatibility mode requires to explicitly override ''{1}'' with specialization ''{0}'', " +
"or annotate the class with @JvmDefaultWithoutCompatibility. " +
"Please refer to KT-39603 for details",
COMPACT, SHORT_NAMES_IN_TYPES);
}
@NotNull
@@ -139,6 +139,8 @@ public interface ErrorsJvm {
DiagnosticFactory0<PsiElement> FUNCTION_DELEGATE_MEMBER_NAME_CLASH = DiagnosticFactory0.create(ERROR);
DiagnosticFactory2<KtDeclaration, CallableDescriptor, CallableDescriptor> EXPLICIT_OVERRIDE_REQUIRED_IN_COMPATIBILITY_MODE = DiagnosticFactory2.create(ERROR, DECLARATION_SIGNATURE_OR_DEFAULT);
@SuppressWarnings("UnusedDeclaration")
Object _initializer = new Object() {
{