K2: report PRE_RELEASE_CLASS on JVM

Note that 3 tests are still muted, but for another reason: for FIR
versions of the tests, we need to compile the "pre-release library" with
the next language version which is 2.1. But since currently
LanguageVersion.LATEST_STABLE is 1.9, the compiler refuses to read
metadata of version 2.1, regardless of its own language version. Which
is correct, but it leads to irrelevant errors in the test output -- the
ones about the incompatible metadata version, NOT about the
prereleaseness.

These 3 tests can be unmuted once the default language version is
switched to 2.0.

 #KT-60780 Fixed
This commit is contained in:
Alexander Udalov
2023-08-12 00:06:48 +02:00
committed by Space Team
parent 8738ffb84f
commit fec2d063c1
14 changed files with 96 additions and 10 deletions
@@ -5306,6 +5306,13 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
token,
)
}
add(FirJvmErrors.PRE_RELEASE_CLASS) { firDiagnostic ->
PreReleaseClassImpl(
firDiagnostic.a,
firDiagnostic as KtPsiDiagnostic,
token,
)
}
add(FirJsErrors.IMPLEMENTING_FUNCTION_INTERFACE) { firDiagnostic ->
ImplementingFunctionInterfaceImpl(
firDiagnostic as KtPsiDiagnostic,
@@ -3698,6 +3698,11 @@ sealed interface KtFirDiagnostic<PSI : PsiElement> : KtDiagnosticWithPsi<PSI> {
val incompatibility: IncompatibleVersionErrorData<*>
}
interface PreReleaseClass : KtFirDiagnostic<PsiElement> {
override val diagnosticClass get() = PreReleaseClass::class
val presentableString: String
}
interface ImplementingFunctionInterface : KtFirDiagnostic<KtClassOrObject> {
override val diagnosticClass get() = ImplementingFunctionInterface::class
}
@@ -4469,6 +4469,12 @@ internal class IncompatibleClassImpl(
token: KtLifetimeToken,
) : KtAbstractFirDiagnostic<PsiElement>(firDiagnostic, token), KtFirDiagnostic.IncompatibleClass
internal class PreReleaseClassImpl(
override val presentableString: String,
firDiagnostic: KtPsiDiagnostic,
token: KtLifetimeToken,
) : KtAbstractFirDiagnostic<PsiElement>(firDiagnostic, token), KtFirDiagnostic.PreReleaseClass
internal class ImplementingFunctionInterfaceImpl(
firDiagnostic: KtPsiDiagnostic,
token: KtLifetimeToken,
@@ -28,7 +28,7 @@ object FirDiagnosticsCompilerResultsReporter {
}.also {
AnalyzerWithCompilerReport.reportSpecialErrors(
diagnosticsCollector.diagnostics.any { it.factory == FirJvmErrors.INCOMPATIBLE_CLASS },
hasPrereleaseClasses = false, // TODO (KT-60780): missing PRE_RELEASE_CLASS
diagnosticsCollector.diagnostics.any { it.factory == FirJvmErrors.PRE_RELEASE_CLASS },
hasUnstableClasses = false, // TODO (KT-61598): report FIR_COMPILED_CLASS and IR_WITH_UNSTABLE_ABI_COMPILED_CLASS
hasFirUnstableClasses = false, // TODO (KT-61598): report FIR_COMPILED_CLASS and IR_WITH_UNSTABLE_ABI_COMPILED_CLASS
messageCollector,
@@ -181,5 +181,8 @@ object JVM_DIAGNOSTICS_LIST : DiagnosticList("FirJvmErrors") {
parameter<String>("presentableString")
parameter<IncompatibleVersionErrorData<*>>("incompatibility")
}
val PRE_RELEASE_CLASS by error<PsiElement> {
parameter<String>("presentableString")
}
}
}
@@ -122,6 +122,7 @@ object FirJvmErrors {
val JAVA_SAM_INTERFACE_CONSTRUCTOR_REFERENCE by error0<PsiElement>()
val NO_REFLECTION_IN_CLASS_PATH by warning0<PsiElement>()
val INCOMPATIBLE_CLASS by error2<PsiElement, String, IncompatibleVersionErrorData<*>>()
val PRE_RELEASE_CLASS by error1<PsiElement, String>()
init {
RootDiagnosticRendererFactory.registerFactory(FirJvmErrorsDefaultMessages)
@@ -64,6 +64,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.OVERLOADS_
import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.OVERLOADS_WITHOUT_DEFAULT_ARGUMENTS
import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.OVERRIDE_CANNOT_BE_STATIC
import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.POSITIONED_VALUE_ARGUMENT_FOR_JAVA_ANNOTATION
import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.PRE_RELEASE_CLASS
import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.REDUNDANT_REPEATABLE_ANNOTATION
import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.REPEATABLE_ANNOTATION_HAS_NESTED_CLASS_NAMED_CONTAINER
import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.REPEATABLE_CONTAINER_HAS_NON_DEFAULT_PARAMETER
@@ -279,5 +280,10 @@ object FirJvmErrorsDefaultMessages : BaseDiagnosticRendererFactory() {
"The class is loaded from ${FileUtil.toSystemIndependentName(incompatibility.filePath)}"
}
)
map.put(
PRE_RELEASE_CLASS,
"{0} is compiled by a pre-release version of Kotlin and cannot be loaded by this version of the compiler",
STRING
)
}
}
@@ -50,5 +50,8 @@ object FirIncompatibleClassExpressionChecker : FirQualifiedAccessExpressionCheck
if (incompatibility != null) {
reporter.reportOn(element.source, FirJvmErrors.INCOMPATIBLE_CLASS, source.presentableString, incompatibility, context)
}
if (source.isPreReleaseInvisible) {
reporter.reportOn(element.source, FirJvmErrors.PRE_RELEASE_CLASS, source.presentableString, context)
}
}
}
@@ -612,6 +612,7 @@ val FIR_NON_SUPPRESSIBLE_ERROR_NAMES: Set<String> = setOf(
"SUBCLASS_CANT_CALL_COMPANION_PROTECTED_NON_STATIC",
"JAVA_SAM_INTERFACE_CONSTRUCTOR_REFERENCE",
"INCOMPATIBLE_CLASS",
"PRE_RELEASE_CLASS",
"IMPLEMENTING_FUNCTION_INTERFACE",
"OVERRIDING_EXTERNAL_FUN_WITH_OPTIONAL_PARAMS",
"OVERRIDING_EXTERNAL_FUN_WITH_OPTIONAL_PARAMS_WITH_FAKE",
@@ -37,6 +37,7 @@ import org.jetbrains.kotlin.protobuf.InvalidProtocolBufferException
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
import org.jetbrains.kotlin.serialization.deserialization.IncompatibleVersionErrorData
import org.jetbrains.kotlin.serialization.deserialization.builtins.BuiltInSerializerProtocol
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedContainerAbiStability
import org.jetbrains.kotlin.utils.toMetadataVersion
import java.nio.file.Path
import java.nio.file.Paths
@@ -59,6 +60,9 @@ class JvmClassFileBasedSymbolProvider(
private val annotationsLoader = AnnotationsLoader(session, kotlinClassFinder)
private val ownMetadataVersion: JvmMetadataVersion = session.languageVersionSettings.languageVersion.toMetadataVersion()
private val reportErrorsOnPreReleaseDependencies =
!session.languageVersionSettings.getFlag(AnalysisFlags.skipPrereleaseCheck) && !session.languageVersionSettings.isPreRelease()
override fun computePackagePartsInfos(packageFqName: FqName): List<PackagePartsCacheData> =
packagePartProvider.findPackageParts(packageFqName.asString()).mapNotNull { partName ->
computePackagePartInfo(packageFqName, partName)
@@ -89,6 +93,7 @@ class JvmClassFileBasedSymbolProvider(
val source = JvmPackagePartSource(
kotlinClass, packageProto, nameResolver, kotlinClass.incompatibility, kotlinClass.isPreReleaseInvisible,
kotlinClass.abiStability,
)
return PackagePartsCacheData(
@@ -121,8 +126,20 @@ class JvmClassFileBasedSymbolProvider(
)
}
/**
* @return true if the class is invisible because it's compiled by a pre-release compiler, and this compiler is either released
* or is run with a released language version.
*/
private val KotlinJvmBinaryClass.isPreReleaseInvisible: Boolean
get() = classHeader.isPreRelease
get() = reportErrorsOnPreReleaseDependencies && classHeader.isPreRelease
private val KotlinJvmBinaryClass.abiStability: DeserializedContainerAbiStability
get() = when {
session.languageVersionSettings.getFlag(AnalysisFlags.allowUnstableDependencies) -> DeserializedContainerAbiStability.STABLE
classHeader.isUnstableFirBinary -> DeserializedContainerAbiStability.FIR_UNSTABLE
classHeader.isUnstableJvmIrBinary -> DeserializedContainerAbiStability.IR_UNSTABLE
else -> DeserializedContainerAbiStability.STABLE
}
override fun extractClassMetadata(classId: ClassId, parentContext: FirDeserializationContext?): ClassMetadataFindResult? {
// Kotlin classes are annotated Java classes, so this check also looks for them.
@@ -154,7 +171,9 @@ class JvmClassFileBasedSymbolProvider(
classProto,
JvmBinaryAnnotationDeserializer(session, kotlinClass, kotlinClassFinder, result.byteContent),
moduleDataProvider.getModuleData(kotlinClass.containingLibrary?.toPath()),
KotlinJvmBinarySourceElement(kotlinClass, kotlinClass.incompatibility),
KotlinJvmBinarySourceElement(
kotlinClass, kotlinClass.incompatibility, kotlinClass.isPreReleaseInvisible, kotlinClass.abiStability,
),
classPostProcessor = { loadAnnotationsFromClassFile(result, it) }
)
}
@@ -0,0 +1,32 @@
error: pre-release classes were found in dependencies. Remove them from the classpath, recompile with a release compiler or use '-Xskip-prerelease-check' to suppress errors
compiler/testData/compileKotlinAgainstCustomBinaries/releaseCompilerAgainstPreReleaseLibrary/source.kt:5:16: error: class 'a.A' is compiled by a pre-release version of Kotlin and cannot be loaded by this version of the compiler
fun baz(param: A, nested: A.Nested) {
^
compiler/testData/compileKotlinAgainstCustomBinaries/releaseCompilerAgainstPreReleaseLibrary/source.kt:5:27: error: class 'a.A.Nested' is compiled by a pre-release version of Kotlin and cannot be loaded by this version of the compiler
fun baz(param: A, nested: A.Nested) {
^
compiler/testData/compileKotlinAgainstCustomBinaries/releaseCompilerAgainstPreReleaseLibrary/source.kt:6:23: error: class 'a.A' is compiled by a pre-release version of Kotlin and cannot be loaded by this version of the compiler
val constructor = A()
^
compiler/testData/compileKotlinAgainstCustomBinaries/releaseCompilerAgainstPreReleaseLibrary/source.kt:7:18: error: class 'a.A.Nested' is compiled by a pre-release version of Kotlin and cannot be loaded by this version of the compiler
val nested = A.Nested()
^
compiler/testData/compileKotlinAgainstCustomBinaries/releaseCompilerAgainstPreReleaseLibrary/source.kt:8:22: error: class 'a.A' is compiled by a pre-release version of Kotlin and cannot be loaded by this version of the compiler
val methodCall = param.method()
^
compiler/testData/compileKotlinAgainstCustomBinaries/releaseCompilerAgainstPreReleaseLibrary/source.kt:8:28: error: unresolved reference 'method'.
val methodCall = param.method()
^
compiler/testData/compileKotlinAgainstCustomBinaries/releaseCompilerAgainstPreReleaseLibrary/source.kt:9:30: error: class 'a.A' is compiled by a pre-release version of Kotlin and cannot be loaded by this version of the compiler
val supertype = object : A() {}
^
compiler/testData/compileKotlinAgainstCustomBinaries/releaseCompilerAgainstPreReleaseLibrary/source.kt:11:13: error: class 'a.AKt' is compiled by a pre-release version of Kotlin and cannot be loaded by this version of the compiler
val x = foo()
^
compiler/testData/compileKotlinAgainstCustomBinaries/releaseCompilerAgainstPreReleaseLibrary/source.kt:12:13: error: class 'a.AKt' is compiled by a pre-release version of Kotlin and cannot be loaded by this version of the compiler
val y = bar
^
compiler/testData/compileKotlinAgainstCustomBinaries/releaseCompilerAgainstPreReleaseLibrary/source.kt:13:5: error: class 'a.AKt' is compiled by a pre-release version of Kotlin and cannot be loaded by this version of the compiler
bar = 239
^
COMPILATION_ERROR
@@ -106,7 +106,8 @@ abstract class AbstractCompileKotlinAgainstCustomBinariesTest : AbstractKotlinCo
) {
// Compiles the library with some non-stable language version, then compiles a usage of this library with stable LV.
// If there's no non-stable language version yet, the test does nothing.
val someNonStableVersion = LanguageVersion.entries.firstOrNull { it > LanguageVersion.LATEST_STABLE } ?: return
val someNonStableVersion =
LanguageVersion.entries.firstOrNull { it > languageVersion && it > LanguageVersion.LATEST_STABLE } ?: return
val libraryOptions = listOf(
"-language-version", someNonStableVersion.versionString,
@@ -244,17 +245,17 @@ abstract class AbstractCompileKotlinAgainstCustomBinariesTest : AbstractKotlinCo
doTestBrokenLibrary("library", "test/A\$Anno.class")
}
// KT-60780 K2: missing PRE_RELEASE_CLASS
// TODO: unmute this test after switching LanguageVersion.LATEST_STABLE to 2.0.
fun testReleaseCompilerAgainstPreReleaseLibrary() = muteForK2 {
doTestPreReleaseKotlinLibrary(K2JVMCompiler(), "library", tmpdir)
}
// KT-60780 K2: missing PRE_RELEASE_CLASS
// KT-61596 K2 JS: support reporting PRE_RELEASE_CLASS
fun testReleaseCompilerAgainstPreReleaseLibraryJs() = muteForK2 {
doTestPreReleaseKotlinLibrary(K2JSCompiler(), "library", File(tmpdir, "usage.js"))
}
// KT-60780 K2: missing PRE_RELEASE_CLASS
// TODO: unmute this test after switching LanguageVersion.LATEST_STABLE to 2.0.
fun testReleaseCompilerAgainstPreReleaseLibrarySkipPrereleaseCheck() = muteForK2 {
doTestPreReleaseKotlinLibrary(K2JVMCompiler(), "library", tmpdir, "-Xskip-prerelease-check")
}
@@ -263,12 +264,12 @@ abstract class AbstractCompileKotlinAgainstCustomBinariesTest : AbstractKotlinCo
doTestPreReleaseKotlinLibrary(K2JSCompiler(), "library", File(tmpdir, "usage.js"), "-Xskip-prerelease-check")
}
// KT-60780 K2: missing PRE_RELEASE_CLASS
fun testReleaseCompilerAgainstPreReleaseLibrarySkipMetadataVersionCheck() = muteForK2 {
fun testReleaseCompilerAgainstPreReleaseLibrarySkipMetadataVersionCheck() {
doTestPreReleaseKotlinLibrary(K2JVMCompiler(), "library", tmpdir, "-Xskip-metadata-version-check")
}
fun testReleaseCompilerAgainstPreReleaseLibrarySkipPrereleaseCheckAllowUnstableDependencies() {
// TODO: unmute this test after switching LanguageVersion.LATEST_STABLE to 2.0.
fun testReleaseCompilerAgainstPreReleaseLibrarySkipPrereleaseCheckAllowUnstableDependencies() = muteForK2 {
doTestPreReleaseKotlinLibrary(K2JVMCompiler(), "library", tmpdir, "-Xallow-unstable-dependencies", "-Xskip-prerelease-check")
}