diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 1cd1fbb7d..d66f975c0 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -4,7 +4,7 @@ gradle-api = "8.11.1" junit-jupiter = "5.11.4" kctfork = "0.13.0" kotest = "6.2.5" -kotlin = "2.4.10" +kotlin = "2.4.20" kotlinx-serialization = "1.11.0" lombok = "1.18.48" maven-plugin-annotations = "3.16.0" diff --git a/scip-kotlinc/build.gradle.kts b/scip-kotlinc/build.gradle.kts index aad935acf..f6a40ba6b 100644 --- a/scip-kotlinc/build.gradle.kts +++ b/scip-kotlinc/build.gradle.kts @@ -26,6 +26,20 @@ dependencies { tasks.named("test") { maxHeapSize = "2g" + dependsOn("testKotlin240") +} + +// The plugin runs inside the indexed project's compiler, which can predate our build compiler. +val kotlin240TestRuntime = configurations.create("kotlin240TestRuntime") { + extendsFrom(configurations.testRuntimeClasspath.get()) + resolutionStrategy.force("org.jetbrains.kotlin:kotlin-compiler-embeddable:2.4.0") +} + +tasks.register("testKotlin240") { + useJUnitPlatform() + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().output + sourceSets.main.get().output + kotlin240TestRuntime + maxHeapSize = "2g" } tasks.named("shadowJar") { diff --git a/scip-kotlinc/src/main/kotlin/org/scip_code/scip_java/kotlinc/AnalyzerCheckers.kt b/scip-kotlinc/src/main/kotlin/org/scip_code/scip_java/kotlinc/AnalyzerCheckers.kt index 1251f3a8f..366752b21 100644 --- a/scip-kotlinc/src/main/kotlin/org/scip_code/scip_java/kotlinc/AnalyzerCheckers.kt +++ b/scip-kotlinc/src/main/kotlin/org/scip_code/scip_java/kotlinc/AnalyzerCheckers.kt @@ -26,6 +26,7 @@ import org.jetbrains.kotlin.fir.resolve.getContainingClassSymbol import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider import org.jetbrains.kotlin.fir.resolve.toClassLikeSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirAnonymousObjectSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol import org.jetbrains.kotlin.fir.types.FirTypeRef import org.jetbrains.kotlin.lexer.KtTokens @@ -74,7 +75,8 @@ open class AnalyzerCheckers(session: FirSession) : FirAdditionalCheckersExtensio override val classLikeCheckers: Set = setOf(SemanticClassLikeChecker()) override val constructorCheckers: Set = setOf(SemanticConstructorChecker()) - override val simpleFunctionCheckers: Set = + // The specialized checker property was renamed in Kotlin 2.4.20. + override val functionCheckers: Set = setOf(SemanticSimpleFunctionChecker()) override val anonymousFunctionCheckers: Set = setOf(SemanticAnonymousFunctionChecker()) @@ -278,9 +280,10 @@ open class AnalyzerCheckers(session: FirSession) : FirAdditionalCheckersExtensio } } - private class SemanticSimpleFunctionChecker : FirSimpleFunctionChecker(MppCheckerKind.Common) { + private class SemanticSimpleFunctionChecker : FirFunctionChecker(MppCheckerKind.Common) { context(context: CheckerContext, reporter: DiagnosticReporter) - override fun check(declaration: FirNamedFunction) { + override fun check(declaration: FirFunction) { + if (declaration !is FirNamedFunction) return val source = declaration.source ?: return val ktFile = context.containingFileSymbol?.sourceFile ?: return val visitor = visitors[ktFile] @@ -388,9 +391,19 @@ open class AnalyzerCheckers(session: FirSession) : FirAdditionalCheckersExtensio private class SemanticResolvedQualifierChecker : FirResolvedQualifierChecker(MppCheckerKind.Common) { + companion object { + // Resolve once against the host compiler: symbol became qualifierSymbol in 2.4.20. + private val symbolGetter = + try { + FirResolvedQualifier::class.java.getMethod("getQualifierSymbol") + } catch (_: NoSuchMethodException) { + FirResolvedQualifier::class.java.getMethod("getSymbol") + } + } + context(context: CheckerContext, reporter: DiagnosticReporter) override fun check(expression: FirResolvedQualifier) { - val symbol = expression.symbol ?: return + val symbol = symbolGetter.invoke(expression) as FirClassLikeSymbol<*>? ?: return val source = expression.source ?: return if (source.kind is KtFakeSourceElementKind) return val ktFile = context.containingFileSymbol?.sourceFile ?: return diff --git a/scip-kotlinc/src/main/kotlin/org/scip_code/scip_java/kotlinc/PostAnalysisExtension.kt b/scip-kotlinc/src/main/kotlin/org/scip_code/scip_java/kotlinc/PostAnalysisExtension.kt index e814439c5..211ca221e 100644 --- a/scip-kotlinc/src/main/kotlin/org/scip_code/scip_java/kotlinc/PostAnalysisExtension.kt +++ b/scip-kotlinc/src/main/kotlin/org/scip_code/scip_java/kotlinc/PostAnalysisExtension.kt @@ -12,6 +12,7 @@ import org.jetbrains.kotlin.cli.common.messages.MessageRenderer import org.jetbrains.kotlin.cli.common.messages.PrintingMessageCollector import org.jetbrains.kotlin.config.CommonConfigurationKeys import org.jetbrains.kotlin.config.CompilerConfiguration +import org.jetbrains.kotlin.config.MessageCollectorAccess import org.jetbrains.kotlin.ir.declarations.IrModuleFragment import org.scip_code.scip.Document import org.scip_code.scip_java.shared.ScipShardPaths @@ -61,6 +62,7 @@ class PostAnalysisExtension( return null } + @OptIn(MessageCollectorAccess::class) private val messageCollector = configuration.get( CommonConfigurationKeys.MESSAGE_COLLECTOR_KEY,