diff --git a/analysis/analysis-api-fir/analysis-api-fir-generator/src/org/jetbrains/kotlin/analysis/api/fir/generator/HLDiagnosticConverter.kt b/analysis/analysis-api-fir/analysis-api-fir-generator/src/org/jetbrains/kotlin/analysis/api/fir/generator/HLDiagnosticConverter.kt index 95e45cdbbb7..5d90cbcfa9c 100644 --- a/analysis/analysis-api-fir/analysis-api-fir-generator/src/org/jetbrains/kotlin/analysis/api/fir/generator/HLDiagnosticConverter.kt +++ b/analysis/analysis-api-fir/analysis-api-fir-generator/src/org/jetbrains/kotlin/analysis/api/fir/generator/HLDiagnosticConverter.kt @@ -40,6 +40,7 @@ import org.jetbrains.kotlin.resolve.ForbiddenNamedArgumentsTarget import org.jetbrains.kotlin.resolve.deprecation.DeprecationInfo import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualAnnotationsIncompatibilityType import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility +import org.jetbrains.kotlin.serialization.deserialization.IncompatibleVersionErrorData import org.jetbrains.kotlin.types.Variance import kotlin.reflect.KClass import kotlin.reflect.KType @@ -372,6 +373,7 @@ internal object FirToKtConversionCreator { ClassKind::class, FunctionTypeKind::class, VersionRequirement.Version::class, + IncompatibleVersionErrorData::class, ) private val KType.kClass: KClass<*> 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 e734b0d0f91..3ddced2d881 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 @@ -5298,6 +5298,14 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert token, ) } + add(FirJvmErrors.INCOMPATIBLE_CLASS) { firDiagnostic -> + IncompatibleClassImpl( + firDiagnostic.a, + firDiagnostic.b, + 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 35802340cc1..3743c59cfb5 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 @@ -86,6 +86,7 @@ import org.jetbrains.kotlin.resolve.deprecation.DeprecationInfo import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualAnnotationsIncompatibilityType import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility.Incompatible +import org.jetbrains.kotlin.serialization.deserialization.IncompatibleVersionErrorData import org.jetbrains.kotlin.types.Variance /* @@ -3691,6 +3692,12 @@ sealed interface KtFirDiagnostic : KtDiagnosticWithPsi { override val diagnosticClass get() = NoReflectionInClassPath::class } + interface IncompatibleClass : KtFirDiagnostic { + override val diagnosticClass get() = IncompatibleClass::class + val presentableString: String + val incompatibility: IncompatibleVersionErrorData<*> + } + 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 79f12c50bb7..4ed88049bb2 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 @@ -87,6 +87,7 @@ import org.jetbrains.kotlin.resolve.deprecation.DeprecationInfo import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualAnnotationsIncompatibilityType import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility.Incompatible +import org.jetbrains.kotlin.serialization.deserialization.IncompatibleVersionErrorData import org.jetbrains.kotlin.types.Variance /* @@ -4461,6 +4462,13 @@ internal class NoReflectionInClassPathImpl( token: KtLifetimeToken, ) : KtAbstractFirDiagnostic(firDiagnostic, token), KtFirDiagnostic.NoReflectionInClassPath +internal class IncompatibleClassImpl( + override val presentableString: String, + override val incompatibility: IncompatibleVersionErrorData<*>, + firDiagnostic: KtPsiDiagnostic, + token: KtLifetimeToken, +) : KtAbstractFirDiagnostic(firDiagnostic, token), KtFirDiagnostic.IncompatibleClass + 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 2274c0d0d86..3b2530efd3e 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 @@ -12,6 +12,7 @@ import org.jetbrains.kotlin.diagnostics.KtPsiDiagnostic import org.jetbrains.kotlin.diagnostics.Severity import org.jetbrains.kotlin.diagnostics.impl.BaseDiagnosticsCollector import org.jetbrains.kotlin.diagnostics.rendering.RootDiagnosticRendererFactory +import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors import java.io.Closeable import java.io.File import java.io.InputStreamReader @@ -24,6 +25,14 @@ object FirDiagnosticsCompilerResultsReporter { ): Boolean { return reportByFile(diagnosticsCollector) { diagnostic, location -> reportDiagnosticToMessageCollector(diagnostic, location, messageCollector, renderDiagnosticName) + }.also { + AnalyzerWithCompilerReport.reportSpecialErrors( + diagnosticsCollector.diagnostics.any { it.factory == FirJvmErrors.INCOMPATIBLE_CLASS }, + hasPrereleaseClasses = false, // TODO (KT-60780): missing 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, + ) } } @@ -77,22 +86,9 @@ object FirDiagnosticsCompilerResultsReporter { } } } - // TODO: for uncommenting, see comment in reportSpecialErrors -// reportSpecialErrors(diagnostics) return hasErrors } - @Suppress("UNUSED_PARAMETER", "unused") - private fun reportSpecialErrors(diagnostics: Collection) { - /* - * TODO: handle next diagnostics when they will be supported in FIR: - * - INCOMPATIBLE_CLASS - * - PRE_RELEASE_CLASS - * - IR_WITH_UNSTABLE_ABI_COMPILED_CLASS - * - FIR_COMPILED_CLASS - */ - } - private fun reportDiagnosticToMessageCollector( diagnostic: KtDiagnostic, location: CompilerMessageSourceLocation, diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/common/messages/AnalyzerWithCompilerReport.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/common/messages/AnalyzerWithCompilerReport.kt index 9eac07f9aec..725fee45912 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/common/messages/AnalyzerWithCompilerReport.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/common/messages/AnalyzerWithCompilerReport.kt @@ -180,9 +180,25 @@ class AnalyzerWithCompilerReport( messageCollector: MessageCollector, renderInternalDiagnosticName: Boolean ): Boolean { - val hasErrors = reportDiagnostics(diagnostics, DefaultDiagnosticReporter(messageCollector), renderInternalDiagnosticName) + return reportDiagnostics(diagnostics, DefaultDiagnosticReporter(messageCollector), renderInternalDiagnosticName).also { + reportSpecialErrors( + diagnostics.any { it.factory == Errors.INCOMPATIBLE_CLASS }, + diagnostics.any { it.factory == Errors.PRE_RELEASE_CLASS }, + diagnostics.any { it.factory == Errors.IR_WITH_UNSTABLE_ABI_COMPILED_CLASS }, + diagnostics.any { it.factory == Errors.FIR_COMPILED_CLASS }, + messageCollector, + ) + } + } - if (diagnostics.any { it.factory == Errors.INCOMPATIBLE_CLASS }) { + fun reportSpecialErrors( + hasIncompatibleClasses: Boolean, + hasPrereleaseClasses: Boolean, + hasUnstableClasses: Boolean, + hasFirUnstableClasses: Boolean, + messageCollector: MessageCollector, + ) { + if (hasIncompatibleClasses) { messageCollector.report( ERROR, "Incompatible classes were found in dependencies. " + @@ -190,7 +206,7 @@ class AnalyzerWithCompilerReport( ) } - if (diagnostics.any { it.factory == Errors.PRE_RELEASE_CLASS }) { + if (hasPrereleaseClasses) { messageCollector.report( ERROR, "Pre-release classes were found in dependencies. " + @@ -199,7 +215,7 @@ class AnalyzerWithCompilerReport( ) } - if (diagnostics.any { it.factory == Errors.IR_WITH_UNSTABLE_ABI_COMPILED_CLASS }) { + if (hasUnstableClasses) { messageCollector.report( ERROR, "Classes compiled by an unstable version of the Kotlin compiler were found in dependencies. " + @@ -207,15 +223,13 @@ class AnalyzerWithCompilerReport( ) } - if (diagnostics.any { it.factory == Errors.FIR_COMPILED_CLASS }) { + if (hasFirUnstableClasses) { messageCollector.report( ERROR, "Classes compiled by the new Kotlin compiler frontend were found in dependencies. " + "Remove them from the classpath or use '-Xallow-unstable-dependencies' to suppress errors" ) } - - return hasErrors } fun reportSyntaxErrors(file: PsiElement, reporter: DiagnosticMessageReporter): SyntaxErrorReport { 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 fb73a56edea..6e04827d4b1 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 @@ -6,7 +6,9 @@ package org.jetbrains.kotlin.fir.checkers.generator.diagnostics import com.intellij.psi.PsiElement +import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.config.LanguageFeature.* +import org.jetbrains.kotlin.config.LanguageVersionSettings import org.jetbrains.kotlin.util.PrivateForInline import org.jetbrains.kotlin.fir.checkers.generator.diagnostics.model.DiagnosticList import org.jetbrains.kotlin.fir.checkers.generator.diagnostics.model.PositioningStrategy @@ -19,6 +21,7 @@ import org.jetbrains.kotlin.psi.KtAnnotationEntry import org.jetbrains.kotlin.psi.KtDeclaration import org.jetbrains.kotlin.psi.KtElement import org.jetbrains.kotlin.psi.KtExpression +import org.jetbrains.kotlin.serialization.deserialization.IncompatibleVersionErrorData @Suppress("ClassName", "unused") @OptIn(PrivateForInline::class) @@ -174,5 +177,9 @@ object JVM_DIAGNOSTICS_LIST : DiagnosticList("FirJvmErrors") { ) val JAVA_SAM_INTERFACE_CONSTRUCTOR_REFERENCE by error() val NO_REFLECTION_IN_CLASS_PATH by warning() + val INCOMPATIBLE_CLASS by error { + parameter("presentableString") + parameter>("incompatibility") + } } } 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 4654f69f900..f93595fa5c1 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 @@ -24,6 +24,7 @@ import org.jetbrains.kotlin.psi.KtAnnotationEntry import org.jetbrains.kotlin.psi.KtDeclaration import org.jetbrains.kotlin.psi.KtElement import org.jetbrains.kotlin.psi.KtExpression +import org.jetbrains.kotlin.serialization.deserialization.IncompatibleVersionErrorData /* * This file was generated automatically @@ -120,6 +121,7 @@ object FirJvmErrors { val SPREAD_ON_SIGNATURE_POLYMORPHIC_CALL by deprecationError0(ProhibitSpreadOnSignaturePolymorphicCall, SourceElementPositioningStrategies.SPREAD_OPERATOR) val JAVA_SAM_INTERFACE_CONSTRUCTOR_REFERENCE by error0() val NO_REFLECTION_IN_CLASS_PATH by warning0() + val INCOMPATIBLE_CLASS by error2>() 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 bab9352bbba..7e4b0c93b48 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 @@ -5,10 +5,12 @@ package org.jetbrains.kotlin.fir.analysis.diagnostics.jvm +import com.intellij.openapi.util.io.FileUtil import org.jetbrains.kotlin.diagnostics.KtDiagnosticFactoryToRendererMap import org.jetbrains.kotlin.diagnostics.KtDiagnosticRenderers.TO_STRING import org.jetbrains.kotlin.diagnostics.rendering.BaseDiagnosticRendererFactory import org.jetbrains.kotlin.diagnostics.rendering.CommonRenderers.STRING +import org.jetbrains.kotlin.diagnostics.rendering.Renderer import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.RENDER_TYPE import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.SYMBOL import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.CONCURRENT_HASH_MAP_CONTAINS_OPERATOR @@ -27,6 +29,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.ILLEGAL_JV import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.INAPPLICABLE_JVM_FIELD import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.INAPPLICABLE_JVM_FIELD_WARNING import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.INAPPLICABLE_JVM_NAME +import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.INCOMPATIBLE_CLASS import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.INNER_JVM_RECORD import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.INTERFACE_CANT_CALL_DEFAULT_METHOD_VIA_SUPER import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.JAVA_SAM_INTERFACE_CONSTRUCTOR_REFERENCE @@ -79,6 +82,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.SYNCHRONIZ import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.SYNCHRONIZED_ON_SUSPEND import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.UPPER_BOUND_CANNOT_BE_ARRAY import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.VALUE_CLASS_WITHOUT_JVM_INLINE_ANNOTATION +import org.jetbrains.kotlin.serialization.deserialization.IncompatibleVersionErrorData object FirJvmErrorsDefaultMessages : BaseDiagnosticRendererFactory() { @@ -263,5 +267,17 @@ object FirJvmErrorsDefaultMessages : BaseDiagnosticRendererFactory() { "Call uses reflection API which is not found in compilation classpath. " + "Make sure you have kotlin-reflect.jar in the classpath." ) + map.put( + INCOMPATIBLE_CLASS, + "{0} was compiled with an incompatible version of Kotlin. {1}", + STRING, + Renderer { incompatibility: IncompatibleVersionErrorData<*> -> + "The actual metadata version is ${incompatibility.actualVersion}, " + + "but the compiler version ${incompatibility.compilerVersion}" + + (if (incompatibility.languageVersion == incompatibility.compilerVersion) "" else " [with language version " + incompatibility.languageVersion + "]") + + " can read versions up to ${incompatibility.expectedVersion}.\n" + + "The class is loaded from ${FileUtil.toSystemIndependentName(incompatibility.filePath)}" + } + ) } } diff --git a/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/JvmExpressionCheckers.kt b/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/JvmExpressionCheckers.kt index f527bd5156e..72ce2af3bf2 100644 --- a/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/JvmExpressionCheckers.kt +++ b/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/JvmExpressionCheckers.kt @@ -19,6 +19,7 @@ object JvmExpressionCheckers : ExpressionCheckers() { get() = setOf( FirInterfaceDefaultMethodCallChecker, FirJavaSamInterfaceConstructorReferenceChecker, + FirIncompatibleClassExpressionChecker, ) override val callableReferenceAccessCheckers: Set diff --git a/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/JvmTypeCheckers.kt b/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/JvmTypeCheckers.kt index 040490dda38..7840003b72f 100644 --- a/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/JvmTypeCheckers.kt +++ b/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/JvmTypeCheckers.kt @@ -8,10 +8,12 @@ package org.jetbrains.kotlin.fir.analysis.jvm.checkers import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirDynamicUnsupportedChecker import org.jetbrains.kotlin.fir.analysis.checkers.type.* import org.jetbrains.kotlin.fir.analysis.jvm.checkers.type.FirFunctionalTypeParameterNameChecker +import org.jetbrains.kotlin.fir.analysis.jvm.checkers.type.FirJvmIncompatibleClassTypeChecker object JvmTypeCheckers : TypeCheckers() { override val typeRefCheckers: Set = setOf( FirDynamicUnsupportedChecker, FirFunctionalTypeParameterNameChecker, + FirJvmIncompatibleClassTypeChecker, ) } 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 new file mode 100644 index 00000000000..a844c44875a --- /dev/null +++ b/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/expression/FirIncompatibleClassExpressionChecker.kt @@ -0,0 +1,54 @@ +/* + * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.fir.analysis.jvm.checkers.expression + +import org.jetbrains.kotlin.descriptors.SourceElement +import org.jetbrains.kotlin.diagnostics.DiagnosticReporter +import org.jetbrains.kotlin.diagnostics.reportOn +import org.jetbrains.kotlin.fir.FirElement +import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext +import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirQualifiedAccessExpressionChecker +import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors +import org.jetbrains.kotlin.fir.declarations.utils.sourceElement +import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression +import org.jetbrains.kotlin.fir.references.toResolvedCallableSymbol +import org.jetbrains.kotlin.fir.symbols.SymbolInternals +import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol +import org.jetbrains.kotlin.fir.types.ConeKotlinType +import org.jetbrains.kotlin.fir.types.coneType +import org.jetbrains.kotlin.fir.types.toRegularClassSymbol +import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedContainerSource + +object FirIncompatibleClassExpressionChecker : FirQualifiedAccessExpressionChecker() { + override fun check(expression: FirQualifiedAccessExpression, context: CheckerContext, reporter: DiagnosticReporter) { + val symbol = expression.calleeReference.toResolvedCallableSymbol() ?: return + + checkType(symbol.resolvedReturnType, expression, context, reporter) + checkType(symbol.receiverParameter?.typeRef?.coneType, expression, context, reporter) + if (symbol is FirFunctionSymbol) { + for (parameter in symbol.valueParameterSymbols) { + checkType(parameter.resolvedReturnTypeRef.type, expression, context, reporter) + } + } + + @OptIn(SymbolInternals::class) + checkSourceElement(symbol.fir.containerSource, expression, context, reporter) + } + + internal fun checkType(type: ConeKotlinType?, element: FirElement, context: CheckerContext, reporter: DiagnosticReporter) { + val classSymbol = type?.toRegularClassSymbol(context.session) + checkSourceElement(classSymbol?.sourceElement, element, context, reporter) + } + + private fun checkSourceElement(source: SourceElement?, element: FirElement, context: CheckerContext, reporter: DiagnosticReporter) { + if (source !is DeserializedContainerSource) return + + val incompatibility = source.incompatibility + if (incompatibility != null) { + reporter.reportOn(element.source, FirJvmErrors.INCOMPATIBLE_CLASS, source.presentableString, incompatibility, context) + } + } +} diff --git a/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/type/FirJvmIncompatibleClassTypeChecker.kt b/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/type/FirJvmIncompatibleClassTypeChecker.kt new file mode 100644 index 00000000000..556667636d0 --- /dev/null +++ b/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/type/FirJvmIncompatibleClassTypeChecker.kt @@ -0,0 +1,19 @@ +/* + * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.fir.analysis.jvm.checkers.type + +import org.jetbrains.kotlin.diagnostics.DiagnosticReporter +import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext +import org.jetbrains.kotlin.fir.analysis.checkers.type.FirTypeRefChecker +import org.jetbrains.kotlin.fir.analysis.jvm.checkers.expression.FirIncompatibleClassExpressionChecker +import org.jetbrains.kotlin.fir.types.FirTypeRef +import org.jetbrains.kotlin.fir.types.coneTypeOrNull + +object FirJvmIncompatibleClassTypeChecker : FirTypeRefChecker() { + override fun check(typeRef: FirTypeRef, context: CheckerContext, reporter: DiagnosticReporter) { + FirIncompatibleClassExpressionChecker.checkType(typeRef.coneTypeOrNull, typeRef, context, reporter) + } +} 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 7c781a67bb1..30537d72102 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 @@ -611,6 +611,7 @@ val FIR_NON_SUPPRESSIBLE_ERROR_NAMES: Set = setOf( "JVM_SYNTHETIC_ON_DELEGATE", "SUBCLASS_CANT_CALL_COMPANION_PROTECTED_NON_STATIC", "JAVA_SAM_INTERFACE_CONSTRUCTOR_REFERENCE", + "INCOMPATIBLE_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 ceb4b06d5c6..e10c07af359 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 @@ -135,7 +135,7 @@ class JvmClassFileBasedSymbolProvider( val kotlinClass = result.kotlinJvmBinaryClass if (kotlinClass.classHeader.kind != KotlinClassHeader.Kind.CLASS || kotlinClass.classId != classId) return null - val data = kotlinClass.classHeader.data ?: return null + val data = kotlinClass.classHeader.data ?: kotlinClass.classHeader.incompatibleData ?: return null val strings = kotlinClass.classHeader.strings ?: return null val (nameResolver, classProto) = JvmProtoBufUtil.readClassDataFrom(data, strings) @@ -144,7 +144,7 @@ class JvmClassFileBasedSymbolProvider( classProto, JvmBinaryAnnotationDeserializer(session, kotlinClass, kotlinClassFinder, result.byteContent), moduleDataProvider.getModuleData(kotlinClass.containingLibrary?.toPath()), - KotlinJvmBinarySourceElement(kotlinClass), + KotlinJvmBinarySourceElement(kotlinClass, kotlinClass.incompatibility), classPostProcessor = { loadAnnotationsFromClassFile(result, it) } ) } diff --git a/compiler/testData/compileKotlinAgainstCustomBinaries/strictMetadataVersionSemanticsOldVersion/output.fir.txt b/compiler/testData/compileKotlinAgainstCustomBinaries/strictMetadataVersionSemanticsOldVersion/output.fir.txt new file mode 100644 index 00000000000..fc546bd10ba --- /dev/null +++ b/compiler/testData/compileKotlinAgainstCustomBinaries/strictMetadataVersionSemanticsOldVersion/output.fir.txt @@ -0,0 +1,21 @@ +error: incompatible classes were found in dependencies. Remove them from the classpath or use '-Xskip-metadata-version-check' to suppress errors +$TMP_DIR$/library.jar!/META-INF/main.kotlin_module: error: module was compiled with an incompatible version of Kotlin. The binary version of its metadata is 2.1.0, expected version is $ABI_VERSION$. +compiler/testData/compileKotlinAgainstCustomBinaries/strictMetadataVersionSemanticsOldVersion/source.kt:3:13: error: class 'a.C' was compiled with an incompatible version of Kotlin. The actual metadata version is 2.1.0, but the compiler version $ABI_VERSION$ [with language version $ABI_VERSION_NEXT$] can read versions up to $ABI_VERSION_NEXT$. +The class is loaded from $TMP_DIR$/library.jar!/a/C.class +fun test(c: C) { + ^ +compiler/testData/compileKotlinAgainstCustomBinaries/strictMetadataVersionSemanticsOldVersion/source.kt:4:5: error: unresolved reference 'f'. + f() + ^ +compiler/testData/compileKotlinAgainstCustomBinaries/strictMetadataVersionSemanticsOldVersion/source.kt:5:5: error: unresolved reference 'v'. + v + ^ +compiler/testData/compileKotlinAgainstCustomBinaries/strictMetadataVersionSemanticsOldVersion/source.kt:6:5: error: class 'a.C' was compiled with an incompatible version of Kotlin. The actual metadata version is 2.1.0, but the compiler version $ABI_VERSION$ [with language version $ABI_VERSION_NEXT$] can read versions up to $ABI_VERSION_NEXT$. +The class is loaded from $TMP_DIR$/library.jar!/a/C.class + c.let { C() } + ^ +compiler/testData/compileKotlinAgainstCustomBinaries/strictMetadataVersionSemanticsOldVersion/source.kt:6:13: error: class 'a.C' was compiled with an incompatible version of Kotlin. The actual metadata version is 2.1.0, but the compiler version $ABI_VERSION$ [with language version $ABI_VERSION_NEXT$] can read versions up to $ABI_VERSION_NEXT$. +The class is loaded from $TMP_DIR$/library.jar!/a/C.class + c.let { C() } + ^ +COMPILATION_ERROR diff --git a/compiler/testData/compileKotlinAgainstCustomBinaries/wrongMetadataVersion/output.fir.txt b/compiler/testData/compileKotlinAgainstCustomBinaries/wrongMetadataVersion/output.fir.txt new file mode 100644 index 00000000000..f026bd17d86 --- /dev/null +++ b/compiler/testData/compileKotlinAgainstCustomBinaries/wrongMetadataVersion/output.fir.txt @@ -0,0 +1,39 @@ +error: incompatible classes were found in dependencies. Remove them from the classpath or use '-Xskip-metadata-version-check' to suppress errors +$TMP_DIR$/library-after.jar!/META-INF/main.kotlin_module: error: module was compiled with an incompatible version of Kotlin. The binary version of its metadata is 42.0.0, expected version is $ABI_VERSION$. +compiler/testData/compileKotlinAgainstCustomBinaries/wrongMetadataVersion/source.kt:5:16: error: class 'a.A' was compiled with an incompatible version of Kotlin. The actual metadata version is 42.0.0, but the compiler version $ABI_VERSION$ [with language version $ABI_VERSION_NEXT$] can read versions up to $ABI_VERSION_NEXT$. +The class is loaded from $TMP_DIR$/library-after.jar!/a/A.class +fun baz(param: A, nested: A.Nested) { + ^ +compiler/testData/compileKotlinAgainstCustomBinaries/wrongMetadataVersion/source.kt:5:27: error: class 'a.A.Nested' was compiled with an incompatible version of Kotlin. The actual metadata version is 42.0.0, but the compiler version $ABI_VERSION$ [with language version $ABI_VERSION_NEXT$] can read versions up to $ABI_VERSION_NEXT$. +The class is loaded from $TMP_DIR$/library-after.jar!/a/A$Nested.class +fun baz(param: A, nested: A.Nested) { + ^ +compiler/testData/compileKotlinAgainstCustomBinaries/wrongMetadataVersion/source.kt:6:23: error: class 'a.A' was compiled with an incompatible version of Kotlin. The actual metadata version is 42.0.0, but the compiler version $ABI_VERSION$ [with language version $ABI_VERSION_NEXT$] can read versions up to $ABI_VERSION_NEXT$. +The class is loaded from $TMP_DIR$/library-after.jar!/a/A.class + val constructor = A() + ^ +compiler/testData/compileKotlinAgainstCustomBinaries/wrongMetadataVersion/source.kt:7:18: error: class 'a.A.Nested' was compiled with an incompatible version of Kotlin. The actual metadata version is 42.0.0, but the compiler version $ABI_VERSION$ [with language version $ABI_VERSION_NEXT$] can read versions up to $ABI_VERSION_NEXT$. +The class is loaded from $TMP_DIR$/library-after.jar!/a/A$Nested.class + val nested = A.Nested() + ^ +compiler/testData/compileKotlinAgainstCustomBinaries/wrongMetadataVersion/source.kt:8:22: error: class 'a.A' was compiled with an incompatible version of Kotlin. The actual metadata version is 42.0.0, but the compiler version $ABI_VERSION$ [with language version $ABI_VERSION_NEXT$] can read versions up to $ABI_VERSION_NEXT$. +The class is loaded from $TMP_DIR$/library-after.jar!/a/A.class + val methodCall = param.method() + ^ +compiler/testData/compileKotlinAgainstCustomBinaries/wrongMetadataVersion/source.kt:9:30: error: class 'a.A' was compiled with an incompatible version of Kotlin. The actual metadata version is 42.0.0, but the compiler version $ABI_VERSION$ [with language version $ABI_VERSION_NEXT$] can read versions up to $ABI_VERSION_NEXT$. +The class is loaded from $TMP_DIR$/library-after.jar!/a/A.class + val supertype = object : A() {} + ^ +compiler/testData/compileKotlinAgainstCustomBinaries/wrongMetadataVersion/source.kt:11:13: error: unresolved reference 'foo'. + val x = foo() + ^ +compiler/testData/compileKotlinAgainstCustomBinaries/wrongMetadataVersion/source.kt:12:13: error: unresolved reference 'bar'. + val y = bar + ^ +compiler/testData/compileKotlinAgainstCustomBinaries/wrongMetadataVersion/source.kt:13:5: error: unresolved reference 'bar'. + bar = 239 + ^ +compiler/testData/compileKotlinAgainstCustomBinaries/wrongMetadataVersion/source.kt:14:12: error: unresolved reference 'TA'. + val z: TA = "" + ^ +COMPILATION_ERROR diff --git a/compiler/testData/compileKotlinAgainstCustomBinaries/wrongMetadataVersionSkipPrereleaseCheckHasNoEffect/output.fir.txt b/compiler/testData/compileKotlinAgainstCustomBinaries/wrongMetadataVersionSkipPrereleaseCheckHasNoEffect/output.fir.txt new file mode 100644 index 00000000000..60111a8972d --- /dev/null +++ b/compiler/testData/compileKotlinAgainstCustomBinaries/wrongMetadataVersionSkipPrereleaseCheckHasNoEffect/output.fir.txt @@ -0,0 +1,39 @@ +error: incompatible classes were found in dependencies. Remove them from the classpath or use '-Xskip-metadata-version-check' to suppress errors +$TMP_DIR$/library-after.jar!/META-INF/main.kotlin_module: error: module was compiled with an incompatible version of Kotlin. The binary version of its metadata is 42.0.0, expected version is $ABI_VERSION$. +compiler/testData/compileKotlinAgainstCustomBinaries/wrongMetadataVersionSkipPrereleaseCheckHasNoEffect/source.kt:5:16: error: class 'a.A' was compiled with an incompatible version of Kotlin. The actual metadata version is 42.0.0, but the compiler version $ABI_VERSION$ [with language version $ABI_VERSION_NEXT$] can read versions up to $ABI_VERSION_NEXT$. +The class is loaded from $TMP_DIR$/library-after.jar!/a/A.class +fun baz(param: A, nested: A.Nested) { + ^ +compiler/testData/compileKotlinAgainstCustomBinaries/wrongMetadataVersionSkipPrereleaseCheckHasNoEffect/source.kt:5:27: error: class 'a.A.Nested' was compiled with an incompatible version of Kotlin. The actual metadata version is 42.0.0, but the compiler version $ABI_VERSION$ [with language version $ABI_VERSION_NEXT$] can read versions up to $ABI_VERSION_NEXT$. +The class is loaded from $TMP_DIR$/library-after.jar!/a/A$Nested.class +fun baz(param: A, nested: A.Nested) { + ^ +compiler/testData/compileKotlinAgainstCustomBinaries/wrongMetadataVersionSkipPrereleaseCheckHasNoEffect/source.kt:6:23: error: class 'a.A' was compiled with an incompatible version of Kotlin. The actual metadata version is 42.0.0, but the compiler version $ABI_VERSION$ [with language version $ABI_VERSION_NEXT$] can read versions up to $ABI_VERSION_NEXT$. +The class is loaded from $TMP_DIR$/library-after.jar!/a/A.class + val constructor = A() + ^ +compiler/testData/compileKotlinAgainstCustomBinaries/wrongMetadataVersionSkipPrereleaseCheckHasNoEffect/source.kt:7:18: error: class 'a.A.Nested' was compiled with an incompatible version of Kotlin. The actual metadata version is 42.0.0, but the compiler version $ABI_VERSION$ [with language version $ABI_VERSION_NEXT$] can read versions up to $ABI_VERSION_NEXT$. +The class is loaded from $TMP_DIR$/library-after.jar!/a/A$Nested.class + val nested = A.Nested() + ^ +compiler/testData/compileKotlinAgainstCustomBinaries/wrongMetadataVersionSkipPrereleaseCheckHasNoEffect/source.kt:8:22: error: class 'a.A' was compiled with an incompatible version of Kotlin. The actual metadata version is 42.0.0, but the compiler version $ABI_VERSION$ [with language version $ABI_VERSION_NEXT$] can read versions up to $ABI_VERSION_NEXT$. +The class is loaded from $TMP_DIR$/library-after.jar!/a/A.class + val methodCall = param.method() + ^ +compiler/testData/compileKotlinAgainstCustomBinaries/wrongMetadataVersionSkipPrereleaseCheckHasNoEffect/source.kt:9:30: error: class 'a.A' was compiled with an incompatible version of Kotlin. The actual metadata version is 42.0.0, but the compiler version $ABI_VERSION$ [with language version $ABI_VERSION_NEXT$] can read versions up to $ABI_VERSION_NEXT$. +The class is loaded from $TMP_DIR$/library-after.jar!/a/A.class + val supertype = object : A() {} + ^ +compiler/testData/compileKotlinAgainstCustomBinaries/wrongMetadataVersionSkipPrereleaseCheckHasNoEffect/source.kt:11:13: error: unresolved reference 'foo'. + val x = foo() + ^ +compiler/testData/compileKotlinAgainstCustomBinaries/wrongMetadataVersionSkipPrereleaseCheckHasNoEffect/source.kt:12:13: error: unresolved reference 'bar'. + val y = bar + ^ +compiler/testData/compileKotlinAgainstCustomBinaries/wrongMetadataVersionSkipPrereleaseCheckHasNoEffect/source.kt:13:5: error: unresolved reference 'bar'. + bar = 239 + ^ +compiler/testData/compileKotlinAgainstCustomBinaries/wrongMetadataVersionSkipPrereleaseCheckHasNoEffect/source.kt:14:12: error: unresolved reference 'TA'. + val z: TA = "" + ^ +COMPILATION_ERROR diff --git a/compiler/tests/org/jetbrains/kotlin/jvm/compiler/AbstractCompileKotlinAgainstCustomBinariesTest.kt b/compiler/tests/org/jetbrains/kotlin/jvm/compiler/AbstractCompileKotlinAgainstCustomBinariesTest.kt index 82da3e96755..573b04275da 100644 --- a/compiler/tests/org/jetbrains/kotlin/jvm/compiler/AbstractCompileKotlinAgainstCustomBinariesTest.kt +++ b/compiler/tests/org/jetbrains/kotlin/jvm/compiler/AbstractCompileKotlinAgainstCustomBinariesTest.kt @@ -282,8 +282,7 @@ abstract class AbstractCompileKotlinAgainstCustomBinariesTest : AbstractKotlinCo ) } - // KT-60795 K2: missing INCOMPATIBLE_CLASS and corresponding CLI error - fun testWrongMetadataVersion() = muteForK2 { + fun testWrongMetadataVersion() { doTestKotlinLibraryWithWrongMetadataVersion("library", null) } @@ -312,8 +311,7 @@ abstract class AbstractCompileKotlinAgainstCustomBinariesTest : AbstractKotlinCo doTestKotlinLibraryWithWrongMetadataVersion("library", null, "-Xskip-metadata-version-check") } - // KT-60795 K2: missing INCOMPATIBLE_CLASS and corresponding CLI error - fun testWrongMetadataVersionSkipPrereleaseCheckHasNoEffect() = muteForK2 { + fun testWrongMetadataVersionSkipPrereleaseCheckHasNoEffect() { doTestKotlinLibraryWithWrongMetadataVersion("library", null, "-Xskip-prerelease-check") } @@ -346,9 +344,8 @@ abstract class AbstractCompileKotlinAgainstCustomBinariesTest : AbstractKotlinCo compileKotlin("source.kt", tmpdir, listOf(library)) } - // KT-60795 K2: missing INCOMPATIBLE_CLASS and corresponding CLI error - fun testStrictMetadataVersionSemanticsOldVersion() = muteForK2 { - val nextMetadataVersion = JvmMetadataVersion.INSTANCE.next() + fun testStrictMetadataVersionSemanticsOldVersion() { + val nextMetadataVersion = languageVersion.toMetadataVersion().next() val library = compileLibrary( "library", additionalOptions = listOf("-Xgenerate-strict-metadata-version", "-Xmetadata-version=$nextMetadataVersion") )