diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDataClassConverters.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDataClassConverters.kt index 3ddced2d881..04d972f4253 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDataClassConverters.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDataClassConverters.kt @@ -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, diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt index 3743c59cfb5..a2aaa3c6477 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt @@ -3698,6 +3698,11 @@ sealed interface KtFirDiagnostic : KtDiagnosticWithPsi { val incompatibility: IncompatibleVersionErrorData<*> } + interface PreReleaseClass : KtFirDiagnostic { + override val diagnosticClass get() = PreReleaseClass::class + val presentableString: String + } + interface ImplementingFunctionInterface : KtFirDiagnostic { override val diagnosticClass get() = ImplementingFunctionInterface::class } diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt index 4ed88049bb2..2a9e382cf80 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt @@ -4469,6 +4469,12 @@ internal class IncompatibleClassImpl( token: KtLifetimeToken, ) : KtAbstractFirDiagnostic(firDiagnostic, token), KtFirDiagnostic.IncompatibleClass +internal class PreReleaseClassImpl( + override val presentableString: String, + firDiagnostic: KtPsiDiagnostic, + token: KtLifetimeToken, +) : KtAbstractFirDiagnostic(firDiagnostic, token), KtFirDiagnostic.PreReleaseClass + internal class ImplementingFunctionInterfaceImpl( firDiagnostic: KtPsiDiagnostic, token: KtLifetimeToken, diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/common/fir/FirDiagnosticsCompilerResultsReporter.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/common/fir/FirDiagnosticsCompilerResultsReporter.kt index 3b2530efd3e..cf94fdda807 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/common/fir/FirDiagnosticsCompilerResultsReporter.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/common/fir/FirDiagnosticsCompilerResultsReporter.kt @@ -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, diff --git a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirJvmDiagnosticsList.kt b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirJvmDiagnosticsList.kt index 6e04827d4b1..f83d19fb351 100644 --- a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirJvmDiagnosticsList.kt +++ b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirJvmDiagnosticsList.kt @@ -181,5 +181,8 @@ object JVM_DIAGNOSTICS_LIST : DiagnosticList("FirJvmErrors") { parameter("presentableString") parameter>("incompatibility") } + val PRE_RELEASE_CLASS by error { + parameter("presentableString") + } } } diff --git a/compiler/fir/checkers/checkers.jvm/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/jvm/FirJvmErrors.kt b/compiler/fir/checkers/checkers.jvm/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/jvm/FirJvmErrors.kt index f93595fa5c1..c8a921e24cf 100644 --- a/compiler/fir/checkers/checkers.jvm/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/jvm/FirJvmErrors.kt +++ b/compiler/fir/checkers/checkers.jvm/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/jvm/FirJvmErrors.kt @@ -122,6 +122,7 @@ object FirJvmErrors { val JAVA_SAM_INTERFACE_CONSTRUCTOR_REFERENCE by error0() val NO_REFLECTION_IN_CLASS_PATH by warning0() val INCOMPATIBLE_CLASS by error2>() + val PRE_RELEASE_CLASS by error1() init { RootDiagnosticRendererFactory.registerFactory(FirJvmErrorsDefaultMessages) diff --git a/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/diagnostics/jvm/FirJvmErrorsDefaultMessages.kt b/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/diagnostics/jvm/FirJvmErrorsDefaultMessages.kt index 7e4b0c93b48..6d03d000e19 100644 --- a/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/diagnostics/jvm/FirJvmErrorsDefaultMessages.kt +++ b/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/diagnostics/jvm/FirJvmErrorsDefaultMessages.kt @@ -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 + ) } } diff --git a/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/expression/FirIncompatibleClassExpressionChecker.kt b/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/expression/FirIncompatibleClassExpressionChecker.kt index a844c44875a..a1237cb4900 100644 --- a/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/expression/FirIncompatibleClassExpressionChecker.kt +++ b/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/expression/FirIncompatibleClassExpressionChecker.kt @@ -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) + } } } diff --git a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirNonSuppressibleErrorNames.kt b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirNonSuppressibleErrorNames.kt index 30537d72102..04a14dad1d7 100644 --- a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirNonSuppressibleErrorNames.kt +++ b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirNonSuppressibleErrorNames.kt @@ -612,6 +612,7 @@ val FIR_NON_SUPPRESSIBLE_ERROR_NAMES: Set = 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", diff --git a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/deserialization/JvmClassFileBasedSymbolProvider.kt b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/deserialization/JvmClassFileBasedSymbolProvider.kt index 7c4bc2c0393..712651feba2 100644 --- a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/deserialization/JvmClassFileBasedSymbolProvider.kt +++ b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/deserialization/JvmClassFileBasedSymbolProvider.kt @@ -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 = 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) } ) } diff --git a/compiler/testData/compileKotlinAgainstCustomBinaries/releaseCompilerAgainstPreReleaseLibrary/output.fir.txt b/compiler/testData/compileKotlinAgainstCustomBinaries/releaseCompilerAgainstPreReleaseLibrary/output.fir.txt new file mode 100644 index 00000000000..e3343537841 --- /dev/null +++ b/compiler/testData/compileKotlinAgainstCustomBinaries/releaseCompilerAgainstPreReleaseLibrary/output.fir.txt @@ -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 diff --git a/compiler/testData/compileKotlinAgainstCustomBinaries/releaseCompilerAgainstPreReleaseLibrarySkipMetadataVersionCheck/output.fir.txt b/compiler/testData/compileKotlinAgainstCustomBinaries/releaseCompilerAgainstPreReleaseLibrarySkipMetadataVersionCheck/output.fir.txt new file mode 100644 index 00000000000..d86bac9de59 --- /dev/null +++ b/compiler/testData/compileKotlinAgainstCustomBinaries/releaseCompilerAgainstPreReleaseLibrarySkipMetadataVersionCheck/output.fir.txt @@ -0,0 +1 @@ +OK diff --git a/compiler/testData/compileKotlinAgainstCustomBinaries/releaseCompilerAgainstPreReleaseLibrarySkipPrereleaseCheck/output.fir.txt b/compiler/testData/compileKotlinAgainstCustomBinaries/releaseCompilerAgainstPreReleaseLibrarySkipPrereleaseCheck/output.fir.txt new file mode 100644 index 00000000000..d86bac9de59 --- /dev/null +++ b/compiler/testData/compileKotlinAgainstCustomBinaries/releaseCompilerAgainstPreReleaseLibrarySkipPrereleaseCheck/output.fir.txt @@ -0,0 +1 @@ +OK diff --git a/compiler/tests/org/jetbrains/kotlin/jvm/compiler/AbstractCompileKotlinAgainstCustomBinariesTest.kt b/compiler/tests/org/jetbrains/kotlin/jvm/compiler/AbstractCompileKotlinAgainstCustomBinariesTest.kt index d9132f56774..6c22ceecbb8 100644 --- a/compiler/tests/org/jetbrains/kotlin/jvm/compiler/AbstractCompileKotlinAgainstCustomBinariesTest.kt +++ b/compiler/tests/org/jetbrains/kotlin/jvm/compiler/AbstractCompileKotlinAgainstCustomBinariesTest.kt @@ -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") }