Report missed INLINE_FROM_HIGHER_PLATFORM diagnostic for derived class

Inline function descriptor in derived class represented as FAKE_OVERRIDE.
 So we should find it in base class declaration
 (not interface cause inline function can't be virtual, but always final)
 and then check class version.

 #KT-29402 Fixed
This commit is contained in:
Mikhael Bogdanov
2019-01-21 14:00:44 +01:00
parent b03065b2ba
commit a020170a92
11 changed files with 219 additions and 45 deletions
@@ -18,6 +18,8 @@ package org.jetbrains.kotlin.resolve.jvm.checkers
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.config.JvmTarget
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
import org.jetbrains.kotlin.descriptors.ClassOrPackageFragmentDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
@@ -33,7 +35,10 @@ import org.jetbrains.kotlin.resolve.inline.InlineUtil
import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedCallableMemberDescriptor
class InlinePlatformCompatibilityChecker(val jvmTarget: JvmTarget) : CallChecker {
class InlinePlatformCompatibilityChecker(val jvmTarget: JvmTarget, languageVersionSettings: LanguageVersionSettings) : CallChecker {
private val properError = languageVersionSettings.supportsFeature(LanguageFeature.ProperInlineFromHigherPlatformDiagnostic)
private val doCheck = doCheck()
override fun check(resolvedCall: ResolvedCall<*>, reportOn: PsiElement, context: CallCheckerContext) {
@@ -56,27 +61,59 @@ class InlinePlatformCompatibilityChecker(val jvmTarget: JvmTarget) : CallChecker
}
val propertyOrFun = DescriptorUtils.getDirectMember(resultingDescriptor)
val inliningBytecodeVersion = getBytecodeVersionIfDeserializedDescriptor(propertyOrFun) ?: return
val compilingBytecodeVersion = jvmTarget.bytecodeVersion
if (compilingBytecodeVersion < inliningBytecodeVersion) {
context.trace.report(
ErrorsJvm.INLINE_FROM_HIGHER_PLATFORM.on(
reportOn,
JvmTarget.getDescription(inliningBytecodeVersion),
JvmTarget.getDescription(compilingBytecodeVersion)
if (!properError) {
val inliningBytecodeVersion = getBytecodeVersionIfDeserializedDescriptor(propertyOrFun, false)
if (inliningBytecodeVersion != null && compilingBytecodeVersion < inliningBytecodeVersion) {
context.trace.report(
ErrorsJvm.INLINE_FROM_HIGHER_PLATFORM.on(
reportOn,
JvmTarget.getDescription(inliningBytecodeVersion),
JvmTarget.getDescription(compilingBytecodeVersion)
)
)
)
return
}
}
val inliningBytecodeVersionProper = getBytecodeVersionIfDeserializedDescriptor(propertyOrFun, true) ?: return
if (compilingBytecodeVersion < inliningBytecodeVersionProper) {
if (properError) {
context.trace.report(
ErrorsJvm.INLINE_FROM_HIGHER_PLATFORM.on(
reportOn,
JvmTarget.getDescription(inliningBytecodeVersionProper),
JvmTarget.getDescription(compilingBytecodeVersion)
)
)
} else {
//INLINE_FROM_HIGHER_PLATFORM was checked in `if (!properError)`
context.trace.report(
ErrorsJvm.INLINE_FROM_HIGHER_PLATFORM_WARNING.on(
reportOn,
JvmTarget.getDescription(inliningBytecodeVersionProper),
JvmTarget.getDescription(compilingBytecodeVersion)
)
)
}
}
}
companion object {
fun doCheck() = "true" != System.getProperty("kotlin.skip.bytecode.version.check")
fun getBytecodeVersionIfDeserializedDescriptor(funOrProperty: DeclarationDescriptor): Int? {
fun getBytecodeVersionIfDeserializedDescriptor(
funOrProperty: DeclarationDescriptor,
useConcreteSuperImplementation: Boolean
): Int? {
if (funOrProperty !is DeserializedCallableMemberDescriptor) return null
val containingDeclaration = funOrProperty.containingDeclaration as ClassOrPackageFragmentDescriptor
val realDeclarationIfFound =
if (useConcreteSuperImplementation) funOrProperty.getConcreteDeclarationForInline() else funOrProperty
val containingDeclaration = realDeclarationIfFound.containingDeclaration as ClassOrPackageFragmentDescriptor
val source = containingDeclaration.source
val binaryClass =
@@ -88,5 +125,19 @@ class InlinePlatformCompatibilityChecker(val jvmTarget: JvmTarget) : CallChecker
return binaryClass.classVersion
}
private fun CallableMemberDescriptor.getConcreteDeclarationForInline(): CallableMemberDescriptor {
if (!this.kind.isReal) {
val superImplementation = overriddenDescriptors.firstOrNull {
val containingDeclaration = it.containingDeclaration
!DescriptorUtils.isInterface(containingDeclaration) && !DescriptorUtils.isAnnotationClass(containingDeclaration)
}
superImplementation?.let {
return it.getConcreteDeclarationForInline()
}
}
return this
}
}
}
@@ -122,6 +122,7 @@ public class DefaultErrorMessagesJvm implements DefaultErrorMessages.Extension {
MAP.put(INTERFACE_STATIC_METHOD_CALL_FROM_JAVA6_TARGET_ERROR, "Calls to static methods in Java interfaces are prohibited in JVM target 1.6. Recompile with '-jvm-target 1.8'");
MAP.put(INLINE_FROM_HIGHER_PLATFORM, "Cannot inline bytecode built with {0} into bytecode that is being built with {1}. Please specify proper ''-jvm-target'' option", STRING, STRING);
MAP.put(INLINE_FROM_HIGHER_PLATFORM_WARNING, "Cannot inline bytecode built with {0} into bytecode that is being built with {1}. Please specify proper ''-jvm-target'' option", STRING, STRING);
MAP.put(JAVA_MODULE_DOES_NOT_DEPEND_ON_MODULE, "Symbol is declared in module ''{0}'' which current module does not depend on", STRING);
MAP.put(JAVA_MODULE_DOES_NOT_READ_UNNAMED_MODULE, "Symbol is declared in unnamed module which is not read by current module");
@@ -99,6 +99,7 @@ public interface ErrorsJvm {
DiagnosticFactory0<PsiElement> INTERFACE_STATIC_METHOD_CALL_FROM_JAVA6_TARGET_ERROR = DiagnosticFactory0.create(ERROR);
DiagnosticFactory2<PsiElement, String, String> INLINE_FROM_HIGHER_PLATFORM = DiagnosticFactory2.create(ERROR);
DiagnosticFactory2<PsiElement, String, String> INLINE_FROM_HIGHER_PLATFORM_WARNING = DiagnosticFactory2.create(WARNING);
DiagnosticFactory1<PsiElement, String> JAVA_MODULE_DOES_NOT_DEPEND_ON_MODULE = DiagnosticFactory1.create(ERROR);
DiagnosticFactory0<PsiElement> JAVA_MODULE_DOES_NOT_READ_UNNAMED_MODULE = DiagnosticFactory0.create(ERROR);