From ed996ae48bb8ecb92f8facba676c234eab3b8c59 Mon Sep 17 00:00:00 2001 From: Anders Fugmann Date: Thu, 4 Jun 2026 12:58:52 +0200 Subject: [PATCH 01/10] Kotlin: Add support for Kotlin 2.4.0 Raise the acceptable version limit to 2.4.10 and update documentation to reflect Kotlin 2.4.x support. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- docs/codeql/reusables/supported-versions-compilers.rst | 2 +- .../diagnostics/kotlin-version-too-new/diagnostics.expected | 2 +- java/ql/lib/change-notes/2026-06-04-kotlin-2.4.0.md | 4 ++++ 3 files changed, 6 insertions(+), 2 deletions(-) create mode 100644 java/ql/lib/change-notes/2026-06-04-kotlin-2.4.0.md diff --git a/docs/codeql/reusables/supported-versions-compilers.rst b/docs/codeql/reusables/supported-versions-compilers.rst index b73c9d7e6e97..aacb188eb20b 100644 --- a/docs/codeql/reusables/supported-versions-compilers.rst +++ b/docs/codeql/reusables/supported-versions-compilers.rst @@ -21,7 +21,7 @@ Java,"Java 7 to 26 [6]_","javac (OpenJDK and Oracle JDK), Eclipse compiler for Java (ECJ) [7]_",``.java`` - Kotlin,"Kotlin 1.8.0 to 2.3.2\ *x*","kotlinc",``.kt`` + Kotlin,"Kotlin 1.8.0 to 2.4.\ *x*","kotlinc",``.kt`` JavaScript,ECMAScript 2022 or lower,Not applicable,"``.js``, ``.jsx``, ``.mjs``, ``.es``, ``.es6``, ``.htm``, ``.html``, ``.xhtm``, ``.xhtml``, ``.vue``, ``.hbs``, ``.ejs``, ``.njk``, ``.json``, ``.yaml``, ``.yml``, ``.raml``, ``.xml`` [8]_" Python [9]_,"2.7, 3.5, 3.6, 3.7, 3.8, 3.9, 3.10, 3.11, 3.12, 3.13",Not applicable,``.py`` Ruby [10]_,"up to 3.3",Not applicable,"``.rb``, ``.erb``, ``.gemspec``, ``Gemfile``" diff --git a/java/ql/integration-tests/kotlin/all-platforms/diagnostics/kotlin-version-too-new/diagnostics.expected b/java/ql/integration-tests/kotlin/all-platforms/diagnostics/kotlin-version-too-new/diagnostics.expected index 2720daff0b22..33ef093cb9a2 100644 --- a/java/ql/integration-tests/kotlin/all-platforms/diagnostics/kotlin-version-too-new/diagnostics.expected +++ b/java/ql/integration-tests/kotlin/all-platforms/diagnostics/kotlin-version-too-new/diagnostics.expected @@ -1,5 +1,5 @@ { - "markdownMessage": "The Kotlin version installed (`999.999.999`) is too recent for this version of CodeQL. Install a version lower than 2.3.30.", + "markdownMessage": "The Kotlin version installed (`999.999.999`) is too recent for this version of CodeQL. Install a version lower than 2.4.10.", "severity": "error", "source": { "extractorName": "java", diff --git a/java/ql/lib/change-notes/2026-06-04-kotlin-2.4.0.md b/java/ql/lib/change-notes/2026-06-04-kotlin-2.4.0.md new file mode 100644 index 000000000000..b778a48148a4 --- /dev/null +++ b/java/ql/lib/change-notes/2026-06-04-kotlin-2.4.0.md @@ -0,0 +1,4 @@ +--- +category: feature +--- +* Kotlin 2.4.0 can now be analysed. From ed9e160e89a6ea0e4899b02d7ce1f1d72554ffeb Mon Sep 17 00:00:00 2001 From: Anders Fugmann Date: Thu, 4 Jun 2026 15:02:27 +0200 Subject: [PATCH 02/10] Kotlin: Add 2.4.0 compiler jars and register version Download kotlin-compiler-2.4.0.jar, kotlin-compiler-embeddable-2.4.0.jar, and kotlin-stdlib-2.4.0.jar from Maven Central. Add 2.4.0 to the VERSIONS list and update MODULE.bazel via bazel mod tidy. The extractor does not yet compile against 2.4.0 due to removed APIs (valueParameters, extensionReceiverParameter, getValueArgument, etc.). Version-specific compatibility shims are needed. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- MODULE.bazel | 3 +++ java/kotlin-extractor/deps/kotlin-compiler-2.4.0.jar | 3 +++ .../kotlin-extractor/deps/kotlin-compiler-embeddable-2.4.0.jar | 3 +++ java/kotlin-extractor/deps/kotlin-stdlib-2.4.0.jar | 3 +++ java/kotlin-extractor/dev/wrapper.py | 2 +- java/kotlin-extractor/versions.bzl | 1 + 6 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 java/kotlin-extractor/deps/kotlin-compiler-2.4.0.jar create mode 100644 java/kotlin-extractor/deps/kotlin-compiler-embeddable-2.4.0.jar create mode 100644 java/kotlin-extractor/deps/kotlin-stdlib-2.4.0.jar diff --git a/MODULE.bazel b/MODULE.bazel index fd923a32e62b..13cc086ba28d 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -248,6 +248,7 @@ use_repo( "kotlin-compiler-2.2.20-Beta2", "kotlin-compiler-2.3.0", "kotlin-compiler-2.3.20", + "kotlin-compiler-2.4.0", "kotlin-compiler-embeddable-1.8.0", "kotlin-compiler-embeddable-1.9.0-Beta", "kotlin-compiler-embeddable-1.9.20-Beta", @@ -259,6 +260,7 @@ use_repo( "kotlin-compiler-embeddable-2.2.20-Beta2", "kotlin-compiler-embeddable-2.3.0", "kotlin-compiler-embeddable-2.3.20", + "kotlin-compiler-embeddable-2.4.0", "kotlin-stdlib-1.8.0", "kotlin-stdlib-1.9.0-Beta", "kotlin-stdlib-1.9.20-Beta", @@ -270,6 +272,7 @@ use_repo( "kotlin-stdlib-2.2.20-Beta2", "kotlin-stdlib-2.3.0", "kotlin-stdlib-2.3.20", + "kotlin-stdlib-2.4.0", ) go_sdk = use_extension("@rules_go//go:extensions.bzl", "go_sdk") diff --git a/java/kotlin-extractor/deps/kotlin-compiler-2.4.0.jar b/java/kotlin-extractor/deps/kotlin-compiler-2.4.0.jar new file mode 100644 index 000000000000..39d9779c219b --- /dev/null +++ b/java/kotlin-extractor/deps/kotlin-compiler-2.4.0.jar @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:66e73eacd619c9beb7c22042117af36b443529c4d80237ee82cc4b2acb6f3d0b +size 61902486 diff --git a/java/kotlin-extractor/deps/kotlin-compiler-embeddable-2.4.0.jar b/java/kotlin-extractor/deps/kotlin-compiler-embeddable-2.4.0.jar new file mode 100644 index 000000000000..bda62390cc7a --- /dev/null +++ b/java/kotlin-extractor/deps/kotlin-compiler-embeddable-2.4.0.jar @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c2b25e8c1c93ec416ba4327f5e31eaec0f0c8847241b9353e294b8db9dce564f +size 60351320 diff --git a/java/kotlin-extractor/deps/kotlin-stdlib-2.4.0.jar b/java/kotlin-extractor/deps/kotlin-stdlib-2.4.0.jar new file mode 100644 index 000000000000..7733acaf0aaa --- /dev/null +++ b/java/kotlin-extractor/deps/kotlin-stdlib-2.4.0.jar @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ccb14ff83fabcb11458b798dbc9824748ccdffeec79c9aba789e6ed1cda86b1c +size 1841929 diff --git a/java/kotlin-extractor/dev/wrapper.py b/java/kotlin-extractor/dev/wrapper.py index 6fc51aded798..34b9d6b9425e 100755 --- a/java/kotlin-extractor/dev/wrapper.py +++ b/java/kotlin-extractor/dev/wrapper.py @@ -27,7 +27,7 @@ import io import os -DEFAULT_VERSION = "2.3.20" +DEFAULT_VERSION = "2.4.0" def options(): diff --git a/java/kotlin-extractor/versions.bzl b/java/kotlin-extractor/versions.bzl index cea5d6490255..f9642c96b788 100644 --- a/java/kotlin-extractor/versions.bzl +++ b/java/kotlin-extractor/versions.bzl @@ -11,6 +11,7 @@ VERSIONS = [ "2.2.20-Beta2", "2.3.0", "2.3.20", + "2.4.0", ] def _version_to_tuple(v): From 4a2f244ffa50a2d723e7b96bd749f0d57e0f7000 Mon Sep 17 00:00:00 2001 From: Anders Fugmann Date: Thu, 4 Jun 2026 15:25:35 +0200 Subject: [PATCH 03/10] Kotlin: Add 2.4.0 API compatibility layer and plugin registration - Add version-specific compatibility wrappers (v_2_4_0/IrCompat.kt) for removed APIs: valueParameters, extensionReceiverParameter, extensionReceiver, getValueArgument, putValueArgument, valueArgumentsCount, typeArgumentsCount, getTypeArgument, addAnnotations, setAnnotations, setDispatchReceiverParameter - Add pre-2.4.0 pass-through implementations (v_1_8_0/IrCompat.kt) - Migrate plugin registration from ComponentRegistrar to CompilerPluginRegistrar for 2.4.0 (v_2_4_0/Kotlin2ComponentRegistrar.kt) - Add META-INF service file for CompilerPluginRegistrar - Update all extractor source files to use codeQl* compat functions - All versions (1.8.0 through 2.4.0) build successfully Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../KotlinExtractorComponentRegistrar.kt | 17 +- .../src/main/kotlin/KotlinFileExtractor.kt | 216 +++++++++--------- .../src/main/kotlin/KotlinUsesExtractor.kt | 38 +-- .../src/main/kotlin/MetaAnnotationSupport.kt | 23 +- .../src/main/kotlin/TrapWriter.kt | 3 +- .../main/kotlin/comments/CommentExtractor.kt | 3 +- .../src/main/kotlin/utils/JvmNames.kt | 6 +- .../src/main/kotlin/utils/TypeSubstitution.kt | 6 +- .../kotlin/utils/versions/v_1_8_0/IrCompat.kt | 60 +++++ .../v_1_8_0/Kotlin2ComponentRegistrar.kt | 22 ++ .../v_1_9_0-Beta/Kotlin2ComponentRegistrar.kt | 24 ++ .../kotlin/utils/versions/v_2_4_0/IrCompat.kt | 88 +++++++ .../v_2_4_0/Kotlin2ComponentRegistrar.kt | 33 +++ .../parameterIndexExcludingReceivers.kt | 13 ++ ...in.compiler.plugin.CompilerPluginRegistrar | 1 + 15 files changed, 396 insertions(+), 157 deletions(-) create mode 100644 java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_8_0/IrCompat.kt create mode 100644 java/kotlin-extractor/src/main/kotlin/utils/versions/v_2_4_0/IrCompat.kt create mode 100644 java/kotlin-extractor/src/main/kotlin/utils/versions/v_2_4_0/Kotlin2ComponentRegistrar.kt create mode 100644 java/kotlin-extractor/src/main/kotlin/utils/versions/v_2_4_0/parameterIndexExcludingReceivers.kt create mode 100644 java/kotlin-extractor/src/main/resources/META-INF/services/org.jetbrains.kotlin.compiler.plugin.CompilerPluginRegistrar diff --git a/java/kotlin-extractor/src/main/kotlin/KotlinExtractorComponentRegistrar.kt b/java/kotlin-extractor/src/main/kotlin/KotlinExtractorComponentRegistrar.kt index 81e3c2bba360..c2ca017cbce3 100644 --- a/java/kotlin-extractor/src/main/kotlin/KotlinExtractorComponentRegistrar.kt +++ b/java/kotlin-extractor/src/main/kotlin/KotlinExtractorComponentRegistrar.kt @@ -3,32 +3,21 @@ package com.github.codeql -import com.intellij.mock.MockProject -import com.intellij.openapi.extensions.LoadingOrder -import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension import org.jetbrains.kotlin.config.CompilerConfiguration class KotlinExtractorComponentRegistrar : Kotlin2ComponentRegistrar() { - override fun registerProjectComponents( - project: MockProject, - configuration: CompilerConfiguration - ) { + override fun doRegisterExtensions(configuration: CompilerConfiguration) { val invocationTrapFile = configuration[KEY_INVOCATION_TRAP_FILE] if (invocationTrapFile == null) { throw Exception("Required argument for TRAP invocation file not given") } - // Register with LoadingOrder.LAST to ensure the extractor runs after other - // IR generation plugins (like kotlinx.serialization) have generated their code. - val extensionPoint = project.extensionArea.getExtensionPoint(IrGenerationExtension.extensionPointName) - extensionPoint.registerExtension( + registerExtractorExtension( KotlinExtractorExtension( invocationTrapFile, configuration[KEY_CHECK_TRAP_IDENTICAL] ?: false, configuration[KEY_COMPILATION_STARTTIME], configuration[KEY_EXIT_AFTER_EXTRACTION] ?: false - ), - LoadingOrder.LAST, - project + ) ) } } diff --git a/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt b/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt index 1c2ed959caf2..efafbeab3bfd 100644 --- a/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt +++ b/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt @@ -173,9 +173,9 @@ open class KotlinFileExtractor( when (d) { is IrFunction -> when (d.name.asString()) { - "toString" -> d.valueParameters.isEmpty() - "hashCode" -> d.valueParameters.isEmpty() - "equals" -> d.valueParameters.singleOrNull()?.type?.isNullableAny() ?: false + "toString" -> d.codeQlValueParameters.isEmpty() + "hashCode" -> d.codeQlValueParameters.isEmpty() + "equals" -> d.codeQlValueParameters.singleOrNull()?.type?.isNullableAny() ?: false else -> false } && isJavaBinaryDeclaration(d) else -> false @@ -781,13 +781,13 @@ open class KotlinFileExtractor( val locId = tw.getLocation(constructorCall) tw.writeHasLocation(id, locId) - for (i in 0 until constructorCall.valueArgumentsCount) { - val param = constructorCall.symbol.owner.valueParameters[i] + for (i in 0 until constructorCall.codeQlValueArgumentsCount) { + val param = constructorCall.symbol.owner.codeQlValueParameters[i] val prop = constructorCall.symbol.owner.parentAsClass.declarations .filterIsInstance() .first { it.name == param.name } - val v = constructorCall.getValueArgument(i) ?: param.defaultValue?.expression + val v = constructorCall.codeQlGetValueArgument(i) ?: param.defaultValue?.expression val getter = prop.getter if (getter == null) { logger.warnElement("Expected annotation property to define a getter", prop) @@ -1115,9 +1115,9 @@ open class KotlinFileExtractor( returnId, 0, returnId, - f.valueParameters.size, + f.codeQlValueParameters.size, { argParent, idxOffset -> - f.valueParameters.forEachIndexed { idx, param -> + f.codeQlValueParameters.forEachIndexed { idx, param -> val syntheticParamId = useValueParameter(param, proxyFunctionId) extractVariableAccess( syntheticParamId, @@ -1695,9 +1695,9 @@ open class KotlinFileExtractor( returnId, 0, returnId, - f.valueParameters.size, + f.codeQlValueParameters.size, { argParentId, idxOffset -> - f.valueParameters.mapIndexed { idx, param -> + f.codeQlValueParameters.mapIndexed { idx, param -> val syntheticParamId = useValueParameter(param, functionId) extractVariableAccess( syntheticParamId, @@ -1792,7 +1792,7 @@ open class KotlinFileExtractor( extractBody: Boolean, extractMethodAndParameterTypeAccesses: Boolean ) { - if (f.valueParameters.none { it.defaultValue != null }) return + if (f.codeQlValueParameters.none { it.defaultValue != null }) return val id = getDefaultsMethodLabel(f) if (id == null) { @@ -1800,7 +1800,7 @@ open class KotlinFileExtractor( return } val locId = getLocation(f, null) - val extReceiver = f.extensionReceiverParameter + val extReceiver = f.codeQlExtensionReceiverParameter val dispatchReceiver = if (f.shouldExtractAsStatic) null else f.dispatchReceiverParameter val parameterTypes = getDefaultsMethodArgTypes(f) val allParamTypeResults = @@ -1869,7 +1869,7 @@ open class KotlinFileExtractor( tw.writeCompiler_generated(id, CompilerGeneratedKinds.DEFAULT_ARGUMENTS_METHOD.kind) if (extractBody) { - val nonSyntheticParams = listOfNotNull(dispatchReceiver) + f.valueParameters + val nonSyntheticParams = listOfNotNull(dispatchReceiver) + f.codeQlValueParameters // This stack entry represents as if we're extracting the 'real' function `f`, giving // the indices of its non-synthetic parameters // such that when we extract the default expressions below, any reference to f's nth @@ -1895,12 +1895,12 @@ open class KotlinFileExtractor( val realParamsVarId = getValueParameterLabel(id, parameterTypes.size - 2) val intType = pluginContext.irBuiltIns.intType val paramIdxOffset = - listOf(dispatchReceiver, f.extensionReceiverParameter).count { it != null } + listOf(dispatchReceiver, f.codeQlExtensionReceiverParameter).count { it != null } extractBlockBody(id, locId).also { blockId -> var nextStmt = 0 // For each parameter with a default, sub in the default value if the caller // hasn't supplied a value: - f.valueParameters.forEachIndexed { paramIdx, param -> + f.codeQlValueParameters.forEachIndexed { paramIdx, param -> val defaultVal = param.defaultValue if (defaultVal != null) { extractIfStmt(locId, blockId, nextStmt++, id).also { ifId -> @@ -1975,7 +1975,7 @@ open class KotlinFileExtractor( id ) tw.writeHasLocation(thisCallId, locId) - f.valueParameters.forEachIndexed { idx, param -> + f.codeQlValueParameters.forEachIndexed { idx, param -> extractVariableAccess( tw.getLabelFor(getValueParameterLabel(id, idx)), param.type, @@ -2003,9 +2003,9 @@ open class KotlinFileExtractor( ) .also { thisCallId -> val realFnIdxOffset = - if (f.extensionReceiverParameter != null) 1 else 0 + if (f.codeQlExtensionReceiverParameter != null) 1 else 0 val paramMappings = - f.valueParameters.mapIndexed { idx, param -> + f.codeQlValueParameters.mapIndexed { idx, param -> Triple( param.type, idx + paramIdxOffset, @@ -2156,7 +2156,7 @@ open class KotlinFileExtractor( val dispatchReceiver = f.dispatchReceiverParameter?.let { IrGetValueImpl(-1, -1, it.symbol) } val extensionReceiver = - f.extensionReceiverParameter?.let { IrGetValueImpl(-1, -1, it.symbol) } + f.codeQlExtensionReceiverParameter?.let { IrGetValueImpl(-1, -1, it.symbol) } extractExpressionBody(overloadId, realFunctionLocId).also { returnId -> extractsDefaultsCall( @@ -2180,28 +2180,28 @@ open class KotlinFileExtractor( if (!f.hasAnnotation(jvmOverloadsFqName)) { if ( f is IrConstructor && - f.valueParameters.isNotEmpty() && - f.valueParameters.all { it.defaultValue != null } && + f.codeQlValueParameters.isNotEmpty() && + f.codeQlValueParameters.all { it.defaultValue != null } && f.parentClassOrNull?.let { // Don't create a default constructor for an annotation class, or a class // that explicitly declares a no-arg constructor. !it.isAnnotationClass && it.declarations.none { d -> - d is IrConstructor && d.valueParameters.isEmpty() + d is IrConstructor && d.codeQlValueParameters.isEmpty() } } == true ) { // Per https://kotlinlang.org/docs/classes.html#creating-instances-of-classes, a // single default overload gets created specifically // when we have all default parameters, regardless of `@JvmOverloads`. - extractGeneratedOverload(f.valueParameters.map { _ -> null }) + extractGeneratedOverload(f.codeQlValueParameters.map { _ -> null }) } return } - val paramList: MutableList = f.valueParameters.toMutableList() - for (n in (f.valueParameters.size - 1) downTo 0) { - if (f.valueParameters[n].defaultValue != null) { + val paramList: MutableList = f.codeQlValueParameters.toMutableList() + for (n in (f.codeQlValueParameters.size - 1) downTo 0) { + if (f.codeQlValueParameters[n].defaultValue != null) { paramList[n] = null // Remove this parameter, to be replaced by a default value extractGeneratedOverload(paramList) } @@ -2388,13 +2388,13 @@ open class KotlinFileExtractor( id } - val extReceiver = f.extensionReceiverParameter + val extReceiver = f.codeQlExtensionReceiverParameter // The following parameter order is correct, because member $default methods (where // the order would be [dispatchParam], [extensionParam], normalParams) are not // extracted here val fParameters = listOfNotNull(extReceiver) + - (overriddenAttributes?.valueParameters ?: f.valueParameters) + (overriddenAttributes?.valueParameters ?: f.codeQlValueParameters) val paramTypes = fParameters.mapIndexed { i, vp -> extractValueParameter( @@ -3069,14 +3069,14 @@ open class KotlinFileExtractor( logger.errorElement("Unexpected dispatch receiver found", c) } - if (c.valueArgumentsCount < 1) { + if (c.codeQlValueArgumentsCount < 1) { logger.errorElement("No arguments found", c) return } extractArgument(id, c, callable, enclosingStmt, 0, "Operand null") - if (c.valueArgumentsCount > 1) { + if (c.codeQlValueArgumentsCount > 1) { logger.errorElement("Extra arguments found", c) } } @@ -3095,21 +3095,21 @@ open class KotlinFileExtractor( logger.errorElement("Unexpected dispatch receiver found", c) } - if (c.valueArgumentsCount < 1) { + if (c.codeQlValueArgumentsCount < 1) { logger.errorElement("No arguments found", c) return } extractArgument(id, c, callable, enclosingStmt, 0, "LHS null") - if (c.valueArgumentsCount < 2) { + if (c.codeQlValueArgumentsCount < 2) { logger.errorElement("No RHS found", c) return } extractArgument(id, c, callable, enclosingStmt, 1, "RHS null") - if (c.valueArgumentsCount > 2) { + if (c.codeQlValueArgumentsCount > 2) { logger.errorElement("Extra arguments found", c) } } @@ -3122,7 +3122,7 @@ open class KotlinFileExtractor( idx: Int, msg: String ) { - val op = c.getValueArgument(idx) + val op = c.codeQlGetValueArgument(idx) if (op == null) { logger.errorElement(msg, c) } else { @@ -3267,8 +3267,8 @@ open class KotlinFileExtractor( // and which should be replaced by defaults. The final Object parameter is apparently always // null. (listOfNotNull(if (f.shouldExtractAsStatic) null else f.dispatchReceiverParameter?.type) + - listOfNotNull(f.extensionReceiverParameter?.type) + - f.valueParameters.map { it.type } + + listOfNotNull(f.codeQlExtensionReceiverParameter?.type) + + f.codeQlValueParameters.map { it.type } + listOf(pluginContext.irBuiltIns.intType, getDefaultsMethodLastArgType(f))) .map { erase(it) } @@ -3345,7 +3345,7 @@ open class KotlinFileExtractor( val overriddenCallTarget = (callTarget as? IrSimpleFunction)?.allOverridden(includeSelf = true)?.firstOrNull { it.overriddenSymbols.isEmpty() && - it.valueParameters.any { p -> p.defaultValue != null } + it.codeQlValueParameters.any { p -> p.defaultValue != null } } ?: callTarget if (isExternalDeclaration(overriddenCallTarget)) { // Likewise, ensure the overridden target gets extracted. @@ -3419,7 +3419,7 @@ open class KotlinFileExtractor( } val valueArgsWithDummies = - valueArguments.zip(callTarget.valueParameters).map { (expr, param) -> + valueArguments.zip(callTarget.codeQlValueParameters).map { (expr, param) -> expr ?: IrConstImpl.defaultValueForType(0, 0, param.type) } @@ -3529,7 +3529,7 @@ open class KotlinFileExtractor( callTarget: IrFunction, valueArguments: List ): Boolean { - val varargParam = callTarget.valueParameters.withIndex().find { it.value.isVararg } + val varargParam = callTarget.codeQlValueParameters.withIndex().find { it.value.isVararg } // If the vararg param is the only one not specified, and it has no default value, then we // don't need to call a $default method, // as omitting it already implies passing an empty vararg array. @@ -3805,7 +3805,7 @@ open class KotlinFileExtractor( ) = extractCallValueArguments( callId, - (0 until call.valueArgumentsCount).map { call.getValueArgument(it) }, + (0 until call.codeQlValueArgumentsCount).map { call.codeQlGetValueArgument(it) }, enclosingStmt, enclosingCallable, idxOffset @@ -3874,7 +3874,7 @@ open class KotlinFileExtractor( (owner.parentClassOrNull?.fqNameWhenAvailable?.asString() == type || (owner.parent is IrExternalPackageFragment && getFileClassFqName(owner)?.asString() == type)) && - owner.valueParameters + owner.codeQlValueParameters .map { it.type.classFqName?.asString() } .toTypedArray() contentEquals parameterTypes } @@ -3926,8 +3926,8 @@ open class KotlinFileExtractor( val result = javaLangString?.declarations?.findSubType { it.name.asString() == "valueOf" && - it.valueParameters.size == 1 && - it.valueParameters[0].type == pluginContext.irBuiltIns.anyNType + it.codeQlValueParameters.size == 1 && + it.codeQlValueParameters[0].type == pluginContext.irBuiltIns.anyNType } if (result == null) { logger.error("Couldn't find declaration java.lang.String.valueOf(Object)") @@ -3951,7 +3951,7 @@ open class KotlinFileExtractor( val kotlinNoWhenBranchMatchedConstructor by lazy { val result = kotlinNoWhenBranchMatchedExn?.declarations?.findSubType { - it.valueParameters.isEmpty() + it.codeQlValueParameters.isEmpty() } if (result == null) { logger.error("Couldn't find no-arg constructor for kotlin.NoWhenBranchMatchedException") @@ -3990,7 +3990,7 @@ open class KotlinFileExtractor( verboseln("No match as function name is ${target.name.asString()} not $fName") return false } - val extensionReceiverParameter = target.extensionReceiverParameter + val extensionReceiverParameter = target.codeQlExtensionReceiverParameter val targetClass = if (extensionReceiverParameter == null) { if (isNullable == true) { @@ -4098,8 +4098,8 @@ open class KotlinFileExtractor( ) { val typeArgs = if (extractMethodTypeArguments) - (0 until c.typeArgumentsCount) - .map { c.getTypeArgument(it) } + (0 until c.codeQlTypeArgumentsCount) + .map { c.codeQlGetTypeArgument(it) } .requireNoNullsOrNull() else listOf() @@ -4116,9 +4116,9 @@ open class KotlinFileExtractor( parent, idx, enclosingStmt, - (0 until c.valueArgumentsCount).map { c.getValueArgument(it) }, + (0 until c.codeQlValueArgumentsCount).map { c.codeQlGetValueArgument(it) }, c.dispatchReceiver, - c.extensionReceiver, + c.codeQlExtensionReceiver, typeArgs, extractClassTypeArguments, c.superQualifierSymbol @@ -4126,12 +4126,12 @@ open class KotlinFileExtractor( } fun extractSpecialEnumFunction(fnName: String) { - if (c.typeArgumentsCount != 1) { + if (c.codeQlTypeArgumentsCount != 1) { logger.errorElement("Expected to find exactly one type argument", c) return } - val enumType = (c.getTypeArgument(0) as? IrSimpleType)?.classifier?.owner + val enumType = (c.codeQlGetTypeArgument(0) as? IrSimpleType)?.classifier?.owner if (enumType == null) { logger.errorElement("Couldn't find type of enum type", c) return @@ -4178,13 +4178,13 @@ open class KotlinFileExtractor( } else { extractExpressionExpr(receiver, callable, id, 0, enclosingStmt) } - if (c.valueArgumentsCount < 1) { + if (c.codeQlValueArgumentsCount < 1) { logger.errorElement("No RHS found", c) } else { - if (c.valueArgumentsCount > 1) { + if (c.codeQlValueArgumentsCount > 1) { logger.errorElement("Extra arguments found", c) } - val arg = c.getValueArgument(0) + val arg = c.codeQlGetValueArgument(0) if (arg == null) { logger.errorElement("RHS null", c) } else { @@ -4205,7 +4205,7 @@ open class KotlinFileExtractor( } else { extractExpressionExpr(receiver, callable, id, 0, enclosingStmt) } - if (c.valueArgumentsCount > 0) { + if (c.codeQlValueArgumentsCount > 0) { logger.errorElement("Extra arguments found", c) } } @@ -4219,7 +4219,7 @@ open class KotlinFileExtractor( } fun binopExt(id: Label) { - binopReceiver(id, c.extensionReceiver, "Extension receiver") + binopReceiver(id, c.codeQlExtensionReceiver, "Extension receiver") } fun unaryopDisp(id: Label) { @@ -4227,7 +4227,7 @@ open class KotlinFileExtractor( } fun unaryopExt(id: Label) { - unaryopReceiver(id, c.extensionReceiver, "Extension receiver") + unaryopReceiver(id, c.codeQlExtensionReceiver, "Extension receiver") } val dr = c.dispatchReceiver @@ -4249,7 +4249,7 @@ open class KotlinFileExtractor( parent, idx, enclosingStmt, - listOf(c.extensionReceiver, c.getValueArgument(0)), + listOf(c.codeQlExtensionReceiver, c.codeQlGetValueArgument(0)), null, null ) @@ -4350,7 +4350,7 @@ open class KotlinFileExtractor( // != gets desugared into not and ==. Here we resugar it. c.origin == IrStatementOrigin.EXCLEQ && isFunction(target, "kotlin", "Boolean", "not") && - c.valueArgumentsCount == 0 && + c.codeQlValueArgumentsCount == 0 && dr != null && dr is IrCall && isBuiltinCallInternal(dr, "EQEQ") -> { @@ -4362,7 +4362,7 @@ open class KotlinFileExtractor( } c.origin == IrStatementOrigin.EXCLEQEQ && isFunction(target, "kotlin", "Boolean", "not") && - c.valueArgumentsCount == 0 && + c.codeQlValueArgumentsCount == 0 && dr != null && dr is IrCall && isBuiltinCallInternal(dr, "EQEQEQ") -> { @@ -4374,7 +4374,7 @@ open class KotlinFileExtractor( } c.origin == IrStatementOrigin.EXCLEQ && isFunction(target, "kotlin", "Boolean", "not") && - c.valueArgumentsCount == 0 && + c.codeQlValueArgumentsCount == 0 && dr != null && dr is IrCall && isBuiltinCallInternal(dr, "ieee754equals") -> { @@ -4576,7 +4576,7 @@ open class KotlinFileExtractor( parent, idx, enclosingStmt, - listOf(c.extensionReceiver), + listOf(c.codeQlExtensionReceiver), null, null ) @@ -4596,8 +4596,8 @@ open class KotlinFileExtractor( val locId = tw.getLocation(c) extractExprContext(id, locId, callable, enclosingStmt) - if (c.typeArgumentsCount == 1) { - val typeArgument = c.getTypeArgument(0) + if (c.codeQlTypeArgumentsCount == 1) { + val typeArgument = c.codeQlGetTypeArgument(0) if (typeArgument == null) { logger.errorElement("Type argument missing in an arrayOfNulls call", c) } else { @@ -4618,8 +4618,8 @@ open class KotlinFileExtractor( ) } - if (c.valueArgumentsCount == 1) { - val dim = c.getValueArgument(0) + if (c.codeQlValueArgumentsCount == 1) { + val dim = c.codeQlGetValueArgument(0) if (dim != null) { extractExpressionExpr(dim, callable, id, 0, enclosingStmt) } else { @@ -4651,8 +4651,8 @@ open class KotlinFileExtractor( c.type.getArrayElementTypeCodeQL(pluginContext.irBuiltIns) } else { // TODO: is there any reason not to always use getArrayElementTypeCodeQL? - if (c.typeArgumentsCount == 1) { - c.getTypeArgument(0).also { + if (c.codeQlTypeArgumentsCount == 1) { + c.codeQlGetTypeArgument(0).also { if (it == null) { logger.errorElement( "Type argument missing in an arrayOf call", @@ -4670,7 +4670,7 @@ open class KotlinFileExtractor( } val arg = - if (c.valueArgumentsCount == 1) c.getValueArgument(0) + if (c.codeQlValueArgumentsCount == 1) c.codeQlGetValueArgument(0) else { logger.errorElement( "Expected to find only one (vararg) argument in ${c.symbol.owner.name.asString()} call", @@ -4719,7 +4719,7 @@ open class KotlinFileExtractor( return } - val ext = c.extensionReceiver + val ext = c.codeQlExtensionReceiver if (ext == null) { logger.errorElement( "No extension receiver found for `KClass::java` call", @@ -4826,8 +4826,8 @@ open class KotlinFileExtractor( c.origin == IrStatementOrigin.EQ && c.dispatchReceiver != null -> { val array = c.dispatchReceiver - val arrayIdx = c.getValueArgument(0) - val assignedValue = c.getValueArgument(1) + val arrayIdx = c.codeQlGetValueArgument(0) + val assignedValue = c.codeQlGetValueArgument(1) if (array != null && arrayIdx != null && assignedValue != null) { @@ -4882,22 +4882,22 @@ open class KotlinFileExtractor( } isBuiltinCall(c, "", "kotlin.jvm.internal") -> { - if (c.valueArgumentsCount != 1) { + if (c.codeQlValueArgumentsCount != 1) { logger.errorElement( - "Expected to find one argument for a kotlin.jvm.internal.() call, but found ${c.valueArgumentsCount}", + "Expected to find one argument for a kotlin.jvm.internal.() call, but found ${c.codeQlValueArgumentsCount}", c ) return } - if (c.typeArgumentsCount != 2) { + if (c.codeQlTypeArgumentsCount != 2) { logger.errorElement( - "Expected to find two type arguments for a kotlin.jvm.internal.() call, but found ${c.typeArgumentsCount}", + "Expected to find two type arguments for a kotlin.jvm.internal.() call, but found ${c.codeQlTypeArgumentsCount}", c ) return } - val valueArg = c.getValueArgument(0) + val valueArg = c.codeQlGetValueArgument(0) if (valueArg == null) { logger.errorElement( "Cannot find value argument for a kotlin.jvm.internal.() call", @@ -4905,7 +4905,7 @@ open class KotlinFileExtractor( ) return } - val typeArg = c.getTypeArgument(1) + val typeArg = c.codeQlGetTypeArgument(1) if (typeArg == null) { logger.errorElement( "Cannot find type argument for a kotlin.jvm.internal.() call", @@ -4924,7 +4924,7 @@ open class KotlinFileExtractor( extractExpressionExpr(valueArg, callable, id, 1, enclosingStmt) } isBuiltinCallInternal(c, "dataClassArrayMemberToString") -> { - val arrayArg = c.getValueArgument(0) + val arrayArg = c.codeQlGetValueArgument(0) val realArrayClass = arrayArg?.type?.classOrNull if (realArrayClass == null) { logger.errorElement( @@ -4936,8 +4936,8 @@ open class KotlinFileExtractor( val realCallee = javaUtilArrays?.declarations?.findSubType { decl -> decl.name.asString() == "toString" && - decl.valueParameters.size == 1 && - decl.valueParameters[0].type.classOrNull?.let { + decl.codeQlValueParameters.size == 1 && + decl.codeQlValueParameters[0].type.classOrNull?.let { it == realArrayClass } == true } @@ -4962,7 +4962,7 @@ open class KotlinFileExtractor( } } isBuiltinCallInternal(c, "dataClassArrayMemberHashCode") -> { - val arrayArg = c.getValueArgument(0) + val arrayArg = c.codeQlGetValueArgument(0) val realArrayClass = arrayArg?.type?.classOrNull if (realArrayClass == null) { logger.errorElement( @@ -4974,8 +4974,8 @@ open class KotlinFileExtractor( val realCallee = javaUtilArrays?.declarations?.findSubType { decl -> decl.name.asString() == "hashCode" && - decl.valueParameters.size == 1 && - decl.valueParameters[0].type.classOrNull?.let { + decl.codeQlValueParameters.size == 1 && + decl.codeQlValueParameters[0].type.classOrNull?.let { it == realArrayClass } == true } @@ -5155,7 +5155,7 @@ open class KotlinFileExtractor( val type = useType(eType) val isAnonymous = eType.isAnonymous val locId = tw.getLocation(e) - val valueArgs = (0 until e.valueArgumentsCount).map { e.getValueArgument(it) } + val valueArgs = (0 until e.codeQlValueArgumentsCount).map { e.codeQlGetValueArgument(it) } val id = if ( @@ -5211,10 +5211,10 @@ open class KotlinFileExtractor( realCallTarget is IrConstructor && realCallTarget.parentClassOrNull?.fqNameWhenAvailable?.asString() == "kotlin.Enum" && - realCallTarget.valueParameters.size == 2 && - realCallTarget.valueParameters[0].type == + realCallTarget.codeQlValueParameters.size == 2 && + realCallTarget.codeQlValueParameters[0].type == pluginContext.irBuiltIns.stringType && - realCallTarget.valueParameters[1].type == pluginContext.irBuiltIns.intType + realCallTarget.codeQlValueParameters[1].type == pluginContext.irBuiltIns.intType ) { val id0 = @@ -5287,7 +5287,7 @@ open class KotlinFileExtractor( } val args = - (0 until e.typeArgumentsCount).map { e.getTypeArgument(it) }.requireNoNullsOrNull() + (0 until e.codeQlTypeArgumentsCount).map { e.codeQlGetTypeArgument(it) }.requireNoNullsOrNull() if (args == null) { logger.warnElement("Found null type argument in enum constructor call", e) return @@ -5365,7 +5365,7 @@ open class KotlinFileExtractor( // Check for an expression like x = get(x).op(e): val opReceiver = updateRhs.dispatchReceiver if (isExpectedLhs(opReceiver)) { - updateRhs.getValueArgument(0) + updateRhs.codeQlGetValueArgument(0) } else null } else null } @@ -5560,7 +5560,7 @@ open class KotlinFileExtractor( "set" ) ) { - val updateRhs0 = arraySetCall.getValueArgument(1) + val updateRhs0 = arraySetCall.codeQlGetValueArgument(1) if (updateRhs0 == null) { logger.errorElement("Update RHS not found", e) return false @@ -6403,12 +6403,12 @@ open class KotlinFileExtractor( val ids = getLocallyVisibleFunctionLabels(e.function) val locId = tw.getLocation(e) - val ext = e.function.extensionReceiverParameter + val ext = e.function.codeQlExtensionReceiverParameter val parameters = if (ext != null) { - listOf(ext) + e.function.valueParameters + listOf(ext) + e.function.codeQlValueParameters } else { - e.function.valueParameters + e.function.codeQlValueParameters } var types = parameters.map { it.type } @@ -6670,7 +6670,7 @@ open class KotlinFileExtractor( is IrFunction -> { if ( ownerParent.dispatchReceiverParameter == owner && - ownerParent.extensionReceiverParameter != null + ownerParent.codeQlExtensionReceiverParameter != null ) { val ownerParent2 = ownerParent.parent @@ -7089,7 +7089,7 @@ open class KotlinFileExtractor( makeReceiverInfo(callableReferenceExpr.dispatchReceiver, 0) private val extensionReceiverInfo = makeReceiverInfo( - callableReferenceExpr.extensionReceiver, + callableReferenceExpr.codeQlExtensionReceiver, if (dispatchReceiverInfo == null) 0 else 1 ) @@ -7627,8 +7627,8 @@ open class KotlinFileExtractor( } val expressionTypeArguments = - (0 until propertyReferenceExpr.typeArgumentsCount).mapNotNull { - propertyReferenceExpr.getTypeArgument(it) + (0 until propertyReferenceExpr.codeQlTypeArgumentsCount).mapNotNull { + propertyReferenceExpr.codeQlGetTypeArgument(it) } val idPropertyRef = tw.getFreshIdLabel() @@ -7808,7 +7808,7 @@ open class KotlinFileExtractor( * constructor(dispatchReceiver: TD, extensionReceiver: TE) { * super() * this.dispatchReceiver = dispatchReceiver - * this.extensionReceiver = extensionReceiver + * this.codeQlExtensionReceiver = extensionReceiver * } * fun invoke(a0:T0, a1:T1, ... aI: TI): R { return this.dispatchReceiver.FN(a0,a1,...,aI) } OR * fun invoke( a1:T1, ... aI: TI): R { return this.dispatchReceiver.FN(this.dispatchReceiver,a1,...,aI) } OR @@ -7829,7 +7829,7 @@ open class KotlinFileExtractor( if ( functionReferenceExpr.dispatchReceiver != null && - functionReferenceExpr.extensionReceiver != null + functionReferenceExpr.codeQlExtensionReceiver != null ) { logger.errorElement( "Unexpected: dispatchReceiver and extensionReceiver are both non-null", @@ -7840,7 +7840,7 @@ open class KotlinFileExtractor( if ( target.owner.dispatchReceiverParameter != null && - target.owner.extensionReceiverParameter != null + target.owner.codeQlExtensionReceiverParameter != null ) { logger.errorElement( "Unexpected: dispatch and extension parameters are both non-null", @@ -7899,8 +7899,8 @@ open class KotlinFileExtractor( null } expressionTypeArguments = - (0 until functionReferenceExpr.typeArgumentsCount).mapNotNull { - functionReferenceExpr.getTypeArgument(it) + (0 until functionReferenceExpr.codeQlTypeArgumentsCount).mapNotNull { + functionReferenceExpr.codeQlGetTypeArgument(it) } dispatchReceiverIdx = -1 } @@ -7965,7 +7965,7 @@ open class KotlinFileExtractor( functionReferenceExpr, declarationParent, null, - { it.valueParameters.size == 1 } + { it.codeQlValueParameters.size == 1 } ) { // The argument to FunctionReference's constructor is the function arity. extractConstantInteger( @@ -8572,7 +8572,7 @@ open class KotlinFileExtractor( reverse: Boolean = false ) { val typeArguments = - (0 until c.typeArgumentsCount).map { c.getTypeArgument(it) }.requireNoNullsOrNull() + (0 until c.codeQlTypeArgumentsCount).map { c.codeQlGetTypeArgument(it) }.requireNoNullsOrNull() if (typeArguments == null) { logger.errorElement("Found a null type argument for a member access expression", c) } else { @@ -8923,11 +8923,11 @@ open class KotlinFileExtractor( tw.writeVariableBinding(lhsId, fieldId) val parameters = mutableListOf() - val extParam = samMember.extensionReceiverParameter + val extParam = samMember.codeQlExtensionReceiverParameter if (extParam != null) { parameters.add(extParam) } - parameters.addAll(samMember.valueParameters) + parameters.addAll(samMember.codeQlValueParameters) fun extractArgument( p: IrValueParameter, @@ -9032,7 +9032,7 @@ open class KotlinFileExtractor( elementToReportOn: IrElement, declarationParent: IrDeclarationParent, compilerGeneratedKindOverride: CompilerGeneratedKinds? = null, - superConstructorSelector: (IrFunction) -> Boolean = { it.valueParameters.isEmpty() }, + superConstructorSelector: (IrFunction) -> Boolean = { it.codeQlValueParameters.isEmpty() }, extractSuperconstructorArgs: (Label) -> Unit = {}, ): Label { // Write class diff --git a/java/kotlin-extractor/src/main/kotlin/KotlinUsesExtractor.kt b/java/kotlin-extractor/src/main/kotlin/KotlinUsesExtractor.kt index 93e032a05413..b3577858f99c 100644 --- a/java/kotlin-extractor/src/main/kotlin/KotlinUsesExtractor.kt +++ b/java/kotlin-extractor/src/main/kotlin/KotlinUsesExtractor.kt @@ -12,7 +12,7 @@ import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.symbols.* -import org.jetbrains.kotlin.ir.types.addAnnotations +import com.github.codeql.utils.versions.codeQlAddAnnotations import org.jetbrains.kotlin.ir.types.classFqName import org.jetbrains.kotlin.ir.types.classifierOrNull import org.jetbrains.kotlin.ir.types.classOrNull @@ -355,7 +355,7 @@ open class KotlinUsesExtractor( } private fun propertySignature(p: IrProperty) = - ((p.getter ?: p.setter)?.extensionReceiverParameter?.let { + ((p.getter ?: p.setter)?.codeQlExtensionReceiverParameter?.let { useType(erase(it.type)).javaResult.signature } ?: "") @@ -368,7 +368,7 @@ open class KotlinUsesExtractor( // useDeclarationParent -> useFunction // -> extractFunctionLaterIfExternalFileMember, which would result for `fun f(t: // T) { ... }` for example. - (listOfNotNull(d.extensionReceiverParameter) + d.valueParameters) + (listOfNotNull(d.codeQlExtensionReceiverParameter) + d.codeQlValueParameters) .map { useType(erase(it.type)).javaResult.signature } .joinToString(separator = ",", prefix = "(", postfix = ")") is IrProperty -> propertySignature(d) + externalClassExtractor.propertySignature @@ -488,8 +488,8 @@ open class KotlinUsesExtractor( val result = replacementClass.declarations.findSubType { replacementDecl -> replacementDecl.name == f.name && - replacementDecl.valueParameters.size == f.valueParameters.size && - replacementDecl.valueParameters.zip(f.valueParameters).all { + replacementDecl.codeQlValueParameters.size == f.codeQlValueParameters.size && + replacementDecl.codeQlValueParameters.zip(f.codeQlValueParameters).all { erase(it.first.type) == erase(it.second.type) } } @@ -1265,7 +1265,7 @@ open class KotlinUsesExtractor( private fun getWildcardSuppressionDirective(t: IrAnnotationContainer): Boolean? = t.getAnnotation(jvmWildcardSuppressionAnnotation)?.let { @Suppress("USELESS_CAST") // `as? Boolean` is not needed for Kotlin < 2.1 - (it.getValueArgument(0) as? CodeQLIrConst)?.value as? Boolean ?: true + (it.codeQlGetValueArgument(0) as? CodeQLIrConst)?.value as? Boolean ?: true } private fun addJavaLoweringArgumentWildcards( @@ -1376,9 +1376,9 @@ open class KotlinUsesExtractor( f.parent, parentId, getFunctionShortName(f).nameInDB, - (maybeParameterList ?: f.valueParameters).map { it.type }, + (maybeParameterList ?: f.codeQlValueParameters).map { it.type }, getAdjustedReturnType(f), - f.extensionReceiverParameter?.type, + f.codeQlExtensionReceiverParameter?.type, getFunctionTypeParameters(f), classTypeArgsIncludingOuterClasses, overridesCollectionsMethodWithAlteredParameterTypes(f), @@ -1401,12 +1401,12 @@ open class KotlinUsesExtractor( // The name of the function; normally f.name.asString(). name: String, // The types of the value parameters that the functions takes; normally - // f.valueParameters.map { it.type }. + // f.codeQlValueParameters.map { it.type }. parameterTypes: List, // The return type of the function; normally f.returnType. returnType: IrType, // The extension receiver of the function, if any; normally - // f.extensionReceiverParameter?.type. + // f.codeQlExtensionReceiverParameter?.type. extensionParamType: IrType?, // The type parameters of the function. This does not include type parameters of enclosing // classes. @@ -1579,7 +1579,7 @@ open class KotlinUsesExtractor( parentClass.fqNameWhenAvailable?.asString() != "java.util.concurrent.ConcurrentHashMap" || getFunctionShortName(f).nameInDB != "keySet" || - f.valueParameters.isNotEmpty() || + f.codeQlValueParameters.isNotEmpty() || f.returnType.classFqName?.asString() != "kotlin.collections.MutableSet" ) { return f.returnType @@ -1587,7 +1587,7 @@ open class KotlinUsesExtractor( val otherKeySet = parentClass.declarations.findSubType { - it.name.asString() == "keySet" && it.valueParameters.size == 1 + it.name.asString() == "keySet" && it.codeQlValueParameters.size == 1 } ?: return f.returnType return otherKeySet.returnType.codeQlWithHasQuestionMark(false) @@ -1695,8 +1695,8 @@ open class KotlinUsesExtractor( javaClass.declarations.findSubType { decl -> !decl.isFakeOverride && decl.name.asString() == jvmName && - decl.valueParameters.size == f.valueParameters.size && - decl.valueParameters.zip(f.valueParameters).all { p -> + decl.codeQlValueParameters.size == f.codeQlValueParameters.size && + decl.codeQlValueParameters.zip(f.codeQlValueParameters).all { p -> erase(p.first.type).classifierOrNull == erase(p.second.type).classifierOrNull } @@ -2125,7 +2125,7 @@ open class KotlinUsesExtractor( } return if (t.arguments.isNotEmpty()) - t.addAnnotations(listOf(RawTypeAnnotation.annotationConstructor)) + t.codeQlAddAnnotations(listOf(RawTypeAnnotation.annotationConstructor)) else t } } @@ -2153,7 +2153,7 @@ open class KotlinUsesExtractor( val idxOffset = if ( declarationParent is IrFunction && - declarationParent.extensionReceiverParameter != null + declarationParent.codeQlExtensionReceiverParameter != null ) // For extension functions increase the index to match what the java extractor sees: 1 @@ -2187,7 +2187,7 @@ open class KotlinUsesExtractor( // Gets a field's corresponding property's extension receiver type, if any fun getExtensionReceiverType(f: IrField) = f.correspondingPropertySymbol?.owner?.let { - (it.getter ?: it.setter)?.extensionReceiverParameter?.type + (it.getter ?: it.setter)?.codeQlExtensionReceiverParameter?.type } fun getFieldLabel(f: IrField): String { @@ -2222,14 +2222,14 @@ open class KotlinUsesExtractor( val setter = p.setter val func = getter ?: setter - val ext = func?.extensionReceiverParameter + val ext = func?.codeQlExtensionReceiverParameter return if (ext == null) { "@\"property;{$parentId};${p.name.asString()}\"" } else { val returnType = getter?.returnType - ?: setter?.valueParameters?.singleOrNull()?.type + ?: setter?.codeQlValueParameters?.singleOrNull()?.type ?: pluginContext.irBuiltIns.unitType val typeParams = getFunctionTypeParameters(func) diff --git a/java/kotlin-extractor/src/main/kotlin/MetaAnnotationSupport.kt b/java/kotlin-extractor/src/main/kotlin/MetaAnnotationSupport.kt index 96d5dd8bbbdd..ccebbafc7ceb 100644 --- a/java/kotlin-extractor/src/main/kotlin/MetaAnnotationSupport.kt +++ b/java/kotlin-extractor/src/main/kotlin/MetaAnnotationSupport.kt @@ -1,5 +1,9 @@ package com.github.codeql +import com.github.codeql.utils.versions.codeQlGetValueArgument +import com.github.codeql.utils.versions.codeQlPutValueArgument +import com.github.codeql.utils.versions.codeQlSetAnnotations +import com.github.codeql.utils.versions.codeQlSetDispatchReceiverParameter import com.github.codeql.utils.versions.createImplicitParameterDeclarationWithWrappedDescriptor import java.lang.annotation.ElementType import java.util.HashSet @@ -95,7 +99,7 @@ class MetaAnnotationSupport( JvmAnnotationNames.REPEATABLE_ANNOTATION } return if (jvmRepeatable != null) { - ((jvmRepeatable.getValueArgument(0) as? IrClassReference)?.symbol as? IrClassSymbol) + ((jvmRepeatable.codeQlGetValueArgument(0) as? IrClassReference)?.symbol as? IrClassSymbol) ?.owner } else { getOrCreateSyntheticRepeatableAnnotationContainer(annotationClass) @@ -122,7 +126,7 @@ class MetaAnnotationSupport( containerConstructor.symbol ) .apply { - putValueArgument( + codeQlPutValueArgument( 0, IrVarargImpl( UNDEFINED_OFFSET, @@ -144,7 +148,7 @@ class MetaAnnotationSupport( // Taken from AdditionalClassAnnotationLowering.kt private fun loadAnnotationTargets(targetEntry: IrConstructorCall): Set? { - val valueArgument = targetEntry.getValueArgument(0) as? IrVararg ?: return null + val valueArgument = targetEntry.codeQlGetValueArgument(0) as? IrVararg ?: return null return valueArgument.elements .filterIsInstance() .mapNotNull { KotlinTarget.valueOrNull(it.symbol.owner.name.asString()) } @@ -237,7 +241,7 @@ class MetaAnnotationSupport( targetConstructor.symbol, 0 ) - .apply { putValueArgument(0, vararg) } + .apply { codeQlPutValueArgument(0, vararg) } } private val javaAnnotationRetention by lazy { @@ -263,7 +267,7 @@ class MetaAnnotationSupport( // Taken from AnnotationCodegen.kt (not available in Kotlin < 1.6.20) private fun IrClass.getAnnotationRetention(): KotlinRetention? { val retentionArgument = - getAnnotation(StandardNames.FqNames.retention)?.getValueArgument(0) as? IrGetEnumValue + getAnnotation(StandardNames.FqNames.retention)?.codeQlGetValueArgument(0) as? IrGetEnumValue ?: return null val retentionArgumentValue = retentionArgument.symbol.owner return KotlinRetention.valueOf(retentionArgumentValue.name.asString()) @@ -291,7 +295,7 @@ class MetaAnnotationSupport( 0 ) .apply { - putValueArgument( + codeQlPutValueArgument( 0, IrGetEnumValueImpl( UNDEFINED_OFFSET, @@ -333,7 +337,7 @@ class MetaAnnotationSupport( return } val newParam = thisReceiever.copyTo(this) - dispatchReceiverParameter = newParam + codeQlSetDispatchReceiverParameter(newParam) body = factory .createBlockBody(UNDEFINED_OFFSET, UNDEFINED_OFFSET) @@ -406,7 +410,7 @@ class MetaAnnotationSupport( val repeatableContainerAnnotation = kotlinAnnotationRepeatableContainer?.constructors?.single() - containerClass.annotations = + codeQlSetAnnotations(containerClass, annotationClass.annotations .filter { it.isAnnotationWithEqualFqName(StandardNames.FqNames.retention) || @@ -424,6 +428,7 @@ class MetaAnnotationSupport( ) } ) + ) containerClass } @@ -469,7 +474,7 @@ class MetaAnnotationSupport( repeatableConstructor.symbol, 0 ) - .apply { putValueArgument(0, containerReference) } + .apply { codeQlPutValueArgument(0, containerReference) } } private val javaAnnotationDocumented by lazy { diff --git a/java/kotlin-extractor/src/main/kotlin/TrapWriter.kt b/java/kotlin-extractor/src/main/kotlin/TrapWriter.kt index da04893b4d00..3ff4adb2eeed 100644 --- a/java/kotlin-extractor/src/main/kotlin/TrapWriter.kt +++ b/java/kotlin-extractor/src/main/kotlin/TrapWriter.kt @@ -1,6 +1,7 @@ package com.github.codeql import com.github.codeql.KotlinUsesExtractor.LocallyVisibleFunctionLabels +import com.github.codeql.utils.versions.codeQlExtensionReceiver import com.semmle.extractor.java.PopulateFile import com.semmle.util.unicode.UTF8Util import java.io.BufferedWriter @@ -331,7 +332,7 @@ open class FileTrapWriter( is IrCall -> { // Calls have incorrect startOffset, so we adjust them: val dr = e.dispatchReceiver?.let { getStartOffset(it) } - val er = e.extensionReceiver?.let { getStartOffset(it) } + val er = e.codeQlExtensionReceiver?.let { getStartOffset(it) } offsetMinOf(e.startOffset, dr, er) } else -> e.startOffset diff --git a/java/kotlin-extractor/src/main/kotlin/comments/CommentExtractor.kt b/java/kotlin-extractor/src/main/kotlin/comments/CommentExtractor.kt index 322cffc87f32..a27af84bb702 100644 --- a/java/kotlin-extractor/src/main/kotlin/comments/CommentExtractor.kt +++ b/java/kotlin-extractor/src/main/kotlin/comments/CommentExtractor.kt @@ -2,6 +2,7 @@ package com.github.codeql.comments import com.github.codeql.* import com.github.codeql.utils.isLocalFunction +import com.github.codeql.utils.versions.codeQlExtensionReceiverParameter import com.github.codeql.utils.versions.isDispatchReceiver import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.declarations.* @@ -11,7 +12,7 @@ import org.jetbrains.kotlin.ir.util.parentClassOrNull private fun IrValueParameter.isExtensionReceiver(): Boolean { val parentFun = parent as? IrFunction ?: return false - return parentFun.extensionReceiverParameter == this + return parentFun.codeQlExtensionReceiverParameter == this } open class CommentExtractor( diff --git a/java/kotlin-extractor/src/main/kotlin/utils/JvmNames.kt b/java/kotlin-extractor/src/main/kotlin/utils/JvmNames.kt index 02059b3db649..cfefb69c111c 100644 --- a/java/kotlin-extractor/src/main/kotlin/utils/JvmNames.kt +++ b/java/kotlin-extractor/src/main/kotlin/utils/JvmNames.kt @@ -1,6 +1,8 @@ package com.github.codeql.utils import com.github.codeql.utils.versions.CodeQLIrConst +import com.github.codeql.utils.versions.codeQlGetValueArgument +import com.github.codeql.utils.versions.codeQlValueArgumentsCount import org.jetbrains.kotlin.builtins.StandardNames import org.jetbrains.kotlin.ir.declarations.IrAnnotationContainer import org.jetbrains.kotlin.ir.declarations.IrClass @@ -76,9 +78,9 @@ private fun getSpecialJvmName(f: IrFunction): String? { fun getJvmName(container: IrAnnotationContainer): String? { for (a: IrConstructorCall in container.annotations) { val t = a.type - if (t is IrSimpleType && a.valueArgumentsCount == 1) { + if (t is IrSimpleType && a.codeQlValueArgumentsCount == 1) { val owner = t.classifier.owner - val v = a.getValueArgument(0) + val v = a.codeQlGetValueArgument(0) if (owner is IrClass) { val aPkg = owner.packageFqName?.asString() val name = owner.name.asString() diff --git a/java/kotlin-extractor/src/main/kotlin/utils/TypeSubstitution.kt b/java/kotlin-extractor/src/main/kotlin/utils/TypeSubstitution.kt index 10f0dbde887a..b9f0b6e301ac 100644 --- a/java/kotlin-extractor/src/main/kotlin/utils/TypeSubstitution.kt +++ b/java/kotlin-extractor/src/main/kotlin/utils/TypeSubstitution.kt @@ -18,7 +18,7 @@ import org.jetbrains.kotlin.ir.expressions.IrConstructorCall import org.jetbrains.kotlin.ir.expressions.impl.* import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol import org.jetbrains.kotlin.ir.symbols.impl.DescriptorlessExternalPackageFragmentSymbol -import org.jetbrains.kotlin.ir.types.addAnnotations +import com.github.codeql.utils.versions.codeQlAddAnnotations import org.jetbrains.kotlin.ir.types.classifierOrNull import org.jetbrains.kotlin.ir.types.makeNotNull import org.jetbrains.kotlin.ir.types.makeNullable @@ -202,7 +202,7 @@ fun IrType.toRawType(): IrType = when (val owner = this.classifier.owner) { is IrClass -> { if (this.arguments.isNotEmpty()) - this.addAnnotations(listOf(RawTypeAnnotation.annotationConstructor)) + this.codeQlAddAnnotations(listOf(RawTypeAnnotation.annotationConstructor)) else this } is IrTypeParameter -> owner.superTypes[0].toRawType() @@ -215,7 +215,7 @@ fun IrType.toRawType(): IrType = fun IrClass.toRawType(): IrType { val result = this.typeWith(listOf()) return if (this.typeParameters.isNotEmpty()) - result.addAnnotations(listOf(RawTypeAnnotation.annotationConstructor)) + result.codeQlAddAnnotations(listOf(RawTypeAnnotation.annotationConstructor)) else result } diff --git a/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_8_0/IrCompat.kt b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_8_0/IrCompat.kt new file mode 100644 index 000000000000..837b7dfdae4e --- /dev/null +++ b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_8_0/IrCompat.kt @@ -0,0 +1,60 @@ +package com.github.codeql.utils.versions + +import org.jetbrains.kotlin.ir.declarations.IrFunction +import org.jetbrains.kotlin.ir.declarations.IrValueParameter +import org.jetbrains.kotlin.ir.expressions.IrConstructorCall +import org.jetbrains.kotlin.ir.expressions.IrExpression +import org.jetbrains.kotlin.ir.expressions.IrMemberAccessExpression +import org.jetbrains.kotlin.ir.types.IrType +import org.jetbrains.kotlin.ir.types.addAnnotations + +/** + * Compatibility accessors for pre-2.4.0 API patterns. + * In pre-2.4.0 versions, these delegate directly to the existing APIs. + */ + +// IrFunction: valueParameters +val IrFunction.codeQlValueParameters: List + get() = valueParameters + +// IrFunction: extensionReceiverParameter +val IrFunction.codeQlExtensionReceiverParameter: IrValueParameter? + get() = extensionReceiverParameter + +// IrMemberAccessExpression: valueArgumentsCount +val IrMemberAccessExpression<*>.codeQlValueArgumentsCount: Int + get() = valueArgumentsCount + +// IrMemberAccessExpression: getValueArgument +fun IrMemberAccessExpression<*>.codeQlGetValueArgument(index: Int): IrExpression? = getValueArgument(index) + +// IrMemberAccessExpression: putValueArgument +fun IrMemberAccessExpression<*>.codeQlPutValueArgument(index: Int, value: IrExpression?) { + putValueArgument(index, value) +} + +// IrMemberAccessExpression: extensionReceiver +var IrMemberAccessExpression<*>.codeQlExtensionReceiver: IrExpression? + get() = extensionReceiver + set(value) { extensionReceiver = value } + +// IrMemberAccessExpression: typeArgumentsCount +val IrMemberAccessExpression<*>.codeQlTypeArgumentsCount: Int + get() = typeArgumentsCount + +// IrMemberAccessExpression: getTypeArgument +fun IrMemberAccessExpression<*>.codeQlGetTypeArgument(index: Int): IrType? = getTypeArgument(index) + +// addAnnotations compat: in pre-2.4.0, addAnnotations expects List +fun IrType.codeQlAddAnnotations(annotations: List): IrType = + addAnnotations(annotations) + +// IrMutableAnnotationContainer.annotations setter: in pre-2.4.0, annotations is var with List +fun codeQlSetAnnotations(container: org.jetbrains.kotlin.ir.declarations.IrMutableAnnotationContainer, annotations: List) { + container.annotations = annotations +} + +// IrFunction: set dispatch receiver parameter (pre-2.4.0 it's a var) +fun IrFunction.codeQlSetDispatchReceiverParameter(param: IrValueParameter?) { + dispatchReceiverParameter = param +} diff --git a/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_8_0/Kotlin2ComponentRegistrar.kt b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_8_0/Kotlin2ComponentRegistrar.kt index 84c5fc3bfb6e..3aef6a7dc7ae 100644 --- a/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_8_0/Kotlin2ComponentRegistrar.kt +++ b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_8_0/Kotlin2ComponentRegistrar.kt @@ -3,10 +3,32 @@ package com.github.codeql +import com.intellij.mock.MockProject +import com.intellij.openapi.extensions.LoadingOrder +import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension import org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar import org.jetbrains.kotlin.compiler.plugin.ExperimentalCompilerApi +import org.jetbrains.kotlin.config.CompilerConfiguration @OptIn(ExperimentalCompilerApi::class) abstract class Kotlin2ComponentRegistrar : ComponentRegistrar { /* Nothing to do; supportsK2 doesn't exist yet. */ + + private var project: MockProject? = null + + override fun registerProjectComponents( + project: MockProject, + configuration: CompilerConfiguration + ) { + this.project = project + doRegisterExtensions(configuration) + } + + abstract fun doRegisterExtensions(configuration: CompilerConfiguration) + + fun registerExtractorExtension(extension: IrGenerationExtension) { + val p = project ?: throw IllegalStateException("registerExtractorExtension called before registerProjectComponents") + val extensionPoint = p.extensionArea.getExtensionPoint(IrGenerationExtension.extensionPointName) + extensionPoint.registerExtension(extension, LoadingOrder.LAST, p) + } } diff --git a/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_9_0-Beta/Kotlin2ComponentRegistrar.kt b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_9_0-Beta/Kotlin2ComponentRegistrar.kt index e20c45ddc4d4..1225339ed40b 100644 --- a/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_9_0-Beta/Kotlin2ComponentRegistrar.kt +++ b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_9_0-Beta/Kotlin2ComponentRegistrar.kt @@ -3,11 +3,35 @@ package com.github.codeql +import com.intellij.mock.MockProject +import com.intellij.openapi.extensions.LoadingOrder +import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension import org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar import org.jetbrains.kotlin.compiler.plugin.ExperimentalCompilerApi +import org.jetbrains.kotlin.config.CompilerConfiguration @OptIn(ExperimentalCompilerApi::class) abstract class Kotlin2ComponentRegistrar : ComponentRegistrar { override val supportsK2: Boolean get() = true + + private var project: MockProject? = null + + override fun registerProjectComponents( + project: MockProject, + configuration: CompilerConfiguration + ) { + this.project = project + doRegisterExtensions(configuration) + } + + abstract fun doRegisterExtensions(configuration: CompilerConfiguration) + + fun registerExtractorExtension(extension: IrGenerationExtension) { + val p = project ?: throw IllegalStateException("registerExtractorExtension called before registerProjectComponents") + // Register with LoadingOrder.LAST to ensure the extractor runs after other + // IR generation plugins (like kotlinx.serialization) have generated their code. + val extensionPoint = p.extensionArea.getExtensionPoint(IrGenerationExtension.extensionPointName) + extensionPoint.registerExtension(extension, LoadingOrder.LAST, p) + } } diff --git a/java/kotlin-extractor/src/main/kotlin/utils/versions/v_2_4_0/IrCompat.kt b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_2_4_0/IrCompat.kt new file mode 100644 index 000000000000..87fe52b3c293 --- /dev/null +++ b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_2_4_0/IrCompat.kt @@ -0,0 +1,88 @@ +@file:Suppress("DEPRECATION") + +package com.github.codeql.utils.versions + +import org.jetbrains.kotlin.ir.declarations.IrFunction +import org.jetbrains.kotlin.ir.declarations.IrValueParameter +import org.jetbrains.kotlin.ir.expressions.IrAnnotation +import org.jetbrains.kotlin.ir.expressions.IrConstructorCall +import org.jetbrains.kotlin.ir.expressions.IrExpression +import org.jetbrains.kotlin.ir.expressions.IrMemberAccessExpression +import org.jetbrains.kotlin.ir.types.IrType +import org.jetbrains.kotlin.ir.types.addAnnotations + +/** + * Compatibility accessors for pre-2.4.0 API patterns. + * In 2.4.0, valueParameters/extensionReceiverParameter/extensionReceiver/ + * getValueArgument/putValueArgument/valueArgumentsCount/typeArgumentsCount/getTypeArgument + * have been removed. This file provides the 2.4.0 implementations. + */ + +// IrFunction: valueParameters -> parameters filtered to Regular kind +val IrFunction.codeQlValueParameters: List + get() = parameters.filter { it.kind == org.jetbrains.kotlin.ir.declarations.IrParameterKind.Regular } + +// IrFunction: extensionReceiverParameter +val IrFunction.codeQlExtensionReceiverParameter: IrValueParameter? + get() = parameters.firstOrNull { it.kind == org.jetbrains.kotlin.ir.declarations.IrParameterKind.ExtensionReceiver } + +// IrMemberAccessExpression: valueArgumentsCount +val IrMemberAccessExpression<*>.codeQlValueArgumentsCount: Int + get() = arguments.size + +// IrMemberAccessExpression: getValueArgument +fun IrMemberAccessExpression<*>.codeQlGetValueArgument(index: Int): IrExpression? = arguments[index] + +// IrMemberAccessExpression: putValueArgument +fun IrMemberAccessExpression<*>.codeQlPutValueArgument(index: Int, value: IrExpression?) { + arguments[index] = value +} + +// IrMemberAccessExpression: extensionReceiver +var IrMemberAccessExpression<*>.codeQlExtensionReceiver: IrExpression? + get() { + val erp = symbol.owner.let { it as? IrFunction }?.codeQlExtensionReceiverParameter ?: return null + return arguments[erp.indexInParameters] + } + set(value) { + val erp = symbol.owner.let { it as? IrFunction }?.codeQlExtensionReceiverParameter ?: return + arguments[erp.indexInParameters] = value + } + +// IrMemberAccessExpression: typeArgumentsCount +val IrMemberAccessExpression<*>.codeQlTypeArgumentsCount: Int + get() = typeArguments.size + +// IrMemberAccessExpression: getTypeArgument +fun IrMemberAccessExpression<*>.codeQlGetTypeArgument(index: Int): IrType? = typeArguments[index] + +// addAnnotations compat: in 2.4.0, addAnnotations expects List +// IrAnnotation extends IrConstructorCall, so we cast +@Suppress("UNCHECKED_CAST") +fun IrType.codeQlAddAnnotations(annotations: List): IrType = + addAnnotations(annotations as List) + +// IrMutableAnnotationContainer.annotations setter: in 2.4.0, expects List +@Suppress("UNCHECKED_CAST") +fun codeQlSetAnnotations(container: org.jetbrains.kotlin.ir.declarations.IrMutableAnnotationContainer, annotations: List) { + container.annotations = annotations as List +} + +// IrFunction: set dispatch receiver parameter +// In 2.4.0, dispatchReceiverParameter is val; modify the parameters list directly. +fun IrFunction.codeQlSetDispatchReceiverParameter(param: IrValueParameter?) { + val existing = parameters.indexOfFirst { it.kind == org.jetbrains.kotlin.ir.declarations.IrParameterKind.DispatchReceiver } + val mutableParams = parameters.toMutableList() + if (existing >= 0) { + if (param != null) { + mutableParams[existing] = param + } else { + mutableParams.removeAt(existing) + } + } else if (param != null) { + param.kind = org.jetbrains.kotlin.ir.declarations.IrParameterKind.DispatchReceiver + mutableParams.add(0, param) + } + parameters = mutableParams +} + diff --git a/java/kotlin-extractor/src/main/kotlin/utils/versions/v_2_4_0/Kotlin2ComponentRegistrar.kt b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_2_4_0/Kotlin2ComponentRegistrar.kt new file mode 100644 index 000000000000..3dcd1231fede --- /dev/null +++ b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_2_4_0/Kotlin2ComponentRegistrar.kt @@ -0,0 +1,33 @@ +@file:Suppress("DEPRECATION") +@file:OptIn(ExperimentalCompilerApi::class) + +package com.github.codeql + +import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension +import org.jetbrains.kotlin.compiler.plugin.CompilerPluginRegistrar +import org.jetbrains.kotlin.compiler.plugin.ExperimentalCompilerApi +import org.jetbrains.kotlin.config.CompilerConfiguration + +abstract class Kotlin2ComponentRegistrar : CompilerPluginRegistrar() { + override val supportsK2: Boolean + get() = true + + override val pluginId: String + get() = "com.github.codeql.kotlin-extractor" + + private var extensionStorage: CompilerPluginRegistrar.ExtensionStorage? = null + + override fun ExtensionStorage.registerExtensions(configuration: CompilerConfiguration) { + this@Kotlin2ComponentRegistrar.extensionStorage = this + doRegisterExtensions(configuration) + } + + abstract fun doRegisterExtensions(configuration: CompilerConfiguration) + + fun registerExtractorExtension(extension: IrGenerationExtension) { + val storage = extensionStorage ?: throw IllegalStateException("registerExtractorExtension called before registerExtensions") + with(storage) { + IrGenerationExtension.registerExtension(extension) + } + } +} diff --git a/java/kotlin-extractor/src/main/kotlin/utils/versions/v_2_4_0/parameterIndexExcludingReceivers.kt b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_2_4_0/parameterIndexExcludingReceivers.kt new file mode 100644 index 000000000000..5e9b384b47e5 --- /dev/null +++ b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_2_4_0/parameterIndexExcludingReceivers.kt @@ -0,0 +1,13 @@ +package com.github.codeql.utils.versions + +import org.jetbrains.kotlin.ir.declarations.IrFunction +import org.jetbrains.kotlin.ir.declarations.IrParameterKind +import org.jetbrains.kotlin.ir.declarations.IrValueParameter + +fun parameterIndexExcludingReceivers(vp: IrValueParameter): Int { + val offset = + (vp.parent as? IrFunction)?.let { f -> + f.parameters.count { it.kind == IrParameterKind.DispatchReceiver || it.kind == IrParameterKind.ExtensionReceiver || it.kind == IrParameterKind.Context } + } ?: 0 + return vp.indexInParameters - offset +} diff --git a/java/kotlin-extractor/src/main/resources/META-INF/services/org.jetbrains.kotlin.compiler.plugin.CompilerPluginRegistrar b/java/kotlin-extractor/src/main/resources/META-INF/services/org.jetbrains.kotlin.compiler.plugin.CompilerPluginRegistrar new file mode 100644 index 000000000000..564ed6bfe253 --- /dev/null +++ b/java/kotlin-extractor/src/main/resources/META-INF/services/org.jetbrains.kotlin.compiler.plugin.CompilerPluginRegistrar @@ -0,0 +1 @@ +com.github.codeql.KotlinExtractorComponentRegistrar From e21bc4da05264fda59e3fc13f5ba4c4f2dbe8ac8 Mon Sep 17 00:00:00 2001 From: Anders Fugmann Date: Thu, 4 Jun 2026 16:54:50 +0200 Subject: [PATCH 04/10] Kotlin 2.4.0: Fix plugin service file to be version-conditional The CompilerPluginRegistrar service file must only be included in the 2.4.0 jar. Older Kotlin versions (2.3.x and below) read this service file and try to cast the class to CompilerPluginRegistrar, but the older version extractor only implements ComponentRegistrar, causing a ClassCastException at runtime. For 2.4.0, the registrar implements both ComponentRegistrar (no-op, as extensionArea was removed) and CompilerPluginRegistrar (actual registration via ExtensionStorage). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- java/kotlin-extractor/BUILD.bazel | 19 ++++++++++++++++--- .../v_2_4_0/Kotlin2ComponentRegistrar.kt | 17 +++++++++++++++-- 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/java/kotlin-extractor/BUILD.bazel b/java/kotlin-extractor/BUILD.bazel index a4356af1835b..21ba2bea1aa0 100644 --- a/java/kotlin-extractor/BUILD.bazel +++ b/java/kotlin-extractor/BUILD.bazel @@ -64,8 +64,14 @@ _resources = [ r[len("src/main/resources/"):], ) for r in glob(["src/main/resources/**"]) + if r != "src/main/resources/META-INF/services/org.jetbrains.kotlin.compiler.plugin.CompilerPluginRegistrar" ] +_compiler_plugin_registrar_service = ( + "src/main/resources/META-INF/services/org.jetbrains.kotlin.compiler.plugin.CompilerPluginRegistrar", + "META-INF/services/org.jetbrains.kotlin.compiler.plugin.CompilerPluginRegistrar", +) + kt_javac_options( name = "javac-options", release = "8", @@ -91,19 +97,26 @@ kt_javac_options( # * `resource_strip_prefix` is unique per jar, so we must also put other resources under the same version prefix genrule( name = "resources-%s" % v, - srcs = [src for src, _ in _resources], + srcs = [src for src, _ in _resources] + ( + [_compiler_plugin_registrar_service[0]] if not version_less(v, "2.4.0") else [] + ), outs = [ "%s/com/github/codeql/extractor.name" % v, ] + [ "%s/%s" % (v, target) for _, target in _resources - ], + ] + ( + ["%s/%s" % (v, _compiler_plugin_registrar_service[1])] if not version_less(v, "2.4.0") else [] + ), cmd = "\n".join([ "echo %s-%s > $(RULEDIR)/%s/com/github/codeql/extractor.name" % (_extractor_name_prefix, v, v), ] + [ "cp $(execpath %s) $(RULEDIR)/%s/%s" % (source, v, target) for source, target in _resources - ]), + ] + ( + ["cp $(execpath %s) $(RULEDIR)/%s/%s" % (_compiler_plugin_registrar_service[0], v, _compiler_plugin_registrar_service[1])] + if not version_less(v, "2.4.0") else [] + )), ), kt_jvm_library( name = "%s-%s" % (_extractor_name_prefix, v), diff --git a/java/kotlin-extractor/src/main/kotlin/utils/versions/v_2_4_0/Kotlin2ComponentRegistrar.kt b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_2_4_0/Kotlin2ComponentRegistrar.kt index 3dcd1231fede..0ee06810f8a6 100644 --- a/java/kotlin-extractor/src/main/kotlin/utils/versions/v_2_4_0/Kotlin2ComponentRegistrar.kt +++ b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_2_4_0/Kotlin2ComponentRegistrar.kt @@ -1,21 +1,34 @@ -@file:Suppress("DEPRECATION") +@file:Suppress("DEPRECATION", "DEPRECATION_ERROR") @file:OptIn(ExperimentalCompilerApi::class) package com.github.codeql +import com.intellij.mock.MockProject import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension +import org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar import org.jetbrains.kotlin.compiler.plugin.CompilerPluginRegistrar import org.jetbrains.kotlin.compiler.plugin.ExperimentalCompilerApi import org.jetbrains.kotlin.config.CompilerConfiguration -abstract class Kotlin2ComponentRegistrar : CompilerPluginRegistrar() { +abstract class Kotlin2ComponentRegistrar : CompilerPluginRegistrar(), ComponentRegistrar { override val supportsK2: Boolean get() = true override val pluginId: String get() = "com.github.codeql.kotlin-extractor" + // ComponentRegistrar implementation (legacy path, still called by Kotlin compiler) + override fun registerProjectComponents( + project: MockProject, + configuration: CompilerConfiguration + ) { + // In 2.4.0, we use CompilerPluginRegistrar path instead. + // This is only called if the compiler uses the ComponentRegistrar service file. + // We do nothing here since registerExtensions will be called separately. + } + private var extensionStorage: CompilerPluginRegistrar.ExtensionStorage? = null + private var registeredExtension: IrGenerationExtension? = null override fun ExtensionStorage.registerExtensions(configuration: CompilerConfiguration) { this@Kotlin2ComponentRegistrar.extensionStorage = this From 972304434231f71052ac44ec3ccc9ddbeeaeb592 Mon Sep 17 00:00:00 2001 From: Anders Fugmann Date: Thu, 4 Jun 2026 17:30:40 +0200 Subject: [PATCH 05/10] Kotlin 2.4.0: Fix value argument indexing in compat layer In 2.4.0, IrMemberAccessExpression.arguments includes all parameters (dispatch receiver, extension receiver, and regular value args). The old getValueArgument/putValueArgument/valueArgumentsCount APIs indexed only value arguments. Fix the compat layer to apply the correct offset when translating between the old index-based API and the new unified arguments list. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../main/kotlin/utils/versions/v_2_4_0/IrCompat.kt | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/java/kotlin-extractor/src/main/kotlin/utils/versions/v_2_4_0/IrCompat.kt b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_2_4_0/IrCompat.kt index 87fe52b3c293..1d010b0996cf 100644 --- a/java/kotlin-extractor/src/main/kotlin/utils/versions/v_2_4_0/IrCompat.kt +++ b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_2_4_0/IrCompat.kt @@ -26,16 +26,23 @@ val IrFunction.codeQlValueParameters: List val IrFunction.codeQlExtensionReceiverParameter: IrValueParameter? get() = parameters.firstOrNull { it.kind == org.jetbrains.kotlin.ir.declarations.IrParameterKind.ExtensionReceiver } +// Helper: get the offset of value arguments in the arguments list +// In 2.4.0, arguments[] includes dispatch/extension receivers before regular params +private fun IrMemberAccessExpression<*>.valueArgumentOffset(): Int { + val owner = symbol.owner as? IrFunction ?: return 0 + return owner.parameters.count { it.kind != org.jetbrains.kotlin.ir.declarations.IrParameterKind.Regular } +} + // IrMemberAccessExpression: valueArgumentsCount val IrMemberAccessExpression<*>.codeQlValueArgumentsCount: Int - get() = arguments.size + get() = arguments.size - valueArgumentOffset() // IrMemberAccessExpression: getValueArgument -fun IrMemberAccessExpression<*>.codeQlGetValueArgument(index: Int): IrExpression? = arguments[index] +fun IrMemberAccessExpression<*>.codeQlGetValueArgument(index: Int): IrExpression? = arguments[index + valueArgumentOffset()] // IrMemberAccessExpression: putValueArgument fun IrMemberAccessExpression<*>.codeQlPutValueArgument(index: Int, value: IrExpression?) { - arguments[index] = value + arguments[index + valueArgumentOffset()] = value } // IrMemberAccessExpression: extensionReceiver From b89b6e432a471f962969194bbc8dbcde53995a81 Mon Sep 17 00:00:00 2001 From: Anders Fugmann Date: Thu, 4 Jun 2026 18:12:45 +0200 Subject: [PATCH 06/10] Kotlin 2.4.0: Use IrAnnotationImpl for annotation creation In 2.4.0, annotation lists are typed as List and IrConstructorCallImpl does not extend IrAnnotation. Replace all IrConstructorCallImpl.fromSymbolOwner() calls that create annotations with a compat wrapper codeQlAnnotationFromSymbolOwner() that uses IrAnnotationImpl.fromSymbolOwner() in 2.4.0 (returning proper IrAnnotation instances) and IrConstructorCallImpl.fromSymbolOwner() in pre-2.4.0 versions. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../src/main/kotlin/KotlinFileExtractor.kt | 4 ++-- .../src/main/kotlin/MetaAnnotationSupport.kt | 13 +++++++------ .../src/main/kotlin/utils/TypeSubstitution.kt | 2 +- .../main/kotlin/utils/versions/v_1_8_0/IrCompat.kt | 11 +++++++++++ .../main/kotlin/utils/versions/v_2_4_0/IrCompat.kt | 13 +++++++++++++ 5 files changed, 34 insertions(+), 9 deletions(-) diff --git a/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt b/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt index efafbeab3bfd..9ec97ed24ccd 100644 --- a/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt +++ b/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt @@ -721,7 +721,7 @@ open class KotlinFileExtractor( (it.type as? IrSimpleType)?.classFqName?.asString() != "kotlin.Deprecated" } + // Note we lose any arguments to @java.lang.Deprecated that were written in source. - IrConstructorCallImpl.fromSymbolOwner( + codeQlAnnotationFromSymbolOwner( UNDEFINED_OFFSET, UNDEFINED_OFFSET, jldConstructor.returnType, @@ -2327,7 +2327,7 @@ open class KotlinFileExtractor( getClassByFqName(pluginContext, it)?.let { annotationClass -> annotationClass.owner.declarations.firstIsInstanceOrNull()?.let { annotationConstructor -> - IrConstructorCallImpl.fromSymbolOwner( + codeQlAnnotationFromSymbolOwner( UNDEFINED_OFFSET, UNDEFINED_OFFSET, annotationConstructor.returnType, diff --git a/java/kotlin-extractor/src/main/kotlin/MetaAnnotationSupport.kt b/java/kotlin-extractor/src/main/kotlin/MetaAnnotationSupport.kt index ccebbafc7ceb..e215b5ca31da 100644 --- a/java/kotlin-extractor/src/main/kotlin/MetaAnnotationSupport.kt +++ b/java/kotlin-extractor/src/main/kotlin/MetaAnnotationSupport.kt @@ -1,5 +1,6 @@ package com.github.codeql +import com.github.codeql.utils.versions.codeQlAnnotationFromSymbolOwner import com.github.codeql.utils.versions.codeQlGetValueArgument import com.github.codeql.utils.versions.codeQlPutValueArgument import com.github.codeql.utils.versions.codeQlSetAnnotations @@ -121,7 +122,7 @@ class MetaAnnotationSupport( ) return null } else { - return IrConstructorCallImpl.fromSymbolOwner( + return codeQlAnnotationFromSymbolOwner( containerClass.defaultType, containerConstructor.symbol ) @@ -234,7 +235,7 @@ class MetaAnnotationSupport( ) } - return IrConstructorCallImpl.fromSymbolOwner( + return codeQlAnnotationFromSymbolOwner( UNDEFINED_OFFSET, UNDEFINED_OFFSET, targetConstructor.returnType, @@ -287,7 +288,7 @@ class MetaAnnotationSupport( val targetConstructor = retentionType.declarations.firstIsInstanceOrNull() ?: return null - return IrConstructorCallImpl.fromSymbolOwner( + return codeQlAnnotationFromSymbolOwner( UNDEFINED_OFFSET, UNDEFINED_OFFSET, targetConstructor.returnType, @@ -419,7 +420,7 @@ class MetaAnnotationSupport( .map { it.deepCopyWithSymbols(containerClass) } + listOfNotNull( repeatableContainerAnnotation?.let { - IrConstructorCallImpl.fromSymbolOwner( + codeQlAnnotationFromSymbolOwner( UNDEFINED_OFFSET, UNDEFINED_OFFSET, it.returnType, @@ -467,7 +468,7 @@ class MetaAnnotationSupport( containerClass.symbol, containerClass.defaultType ) - return IrConstructorCallImpl.fromSymbolOwner( + return codeQlAnnotationFromSymbolOwner( UNDEFINED_OFFSET, UNDEFINED_OFFSET, repeatableConstructor.returnType, @@ -493,7 +494,7 @@ class MetaAnnotationSupport( javaAnnotationDocumented?.declarations?.firstIsInstanceOrNull() ?: return null - return IrConstructorCallImpl.fromSymbolOwner( + return codeQlAnnotationFromSymbolOwner( UNDEFINED_OFFSET, UNDEFINED_OFFSET, documentedConstructor.returnType, diff --git a/java/kotlin-extractor/src/main/kotlin/utils/TypeSubstitution.kt b/java/kotlin-extractor/src/main/kotlin/utils/TypeSubstitution.kt index b9f0b6e301ac..c990edc213f6 100644 --- a/java/kotlin-extractor/src/main/kotlin/utils/TypeSubstitution.kt +++ b/java/kotlin-extractor/src/main/kotlin/utils/TypeSubstitution.kt @@ -192,7 +192,7 @@ object RawTypeAnnotation { addConstructor { isPrimary = true } } val constructor = annoClass.constructors.single() - IrConstructorCallImpl.fromSymbolOwner(constructor.constructedClassType, constructor.symbol) + codeQlAnnotationFromSymbolOwner(constructor.constructedClassType, constructor.symbol) } } diff --git a/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_8_0/IrCompat.kt b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_8_0/IrCompat.kt index 837b7dfdae4e..3ba3db2696f7 100644 --- a/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_8_0/IrCompat.kt +++ b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_8_0/IrCompat.kt @@ -5,6 +5,8 @@ import org.jetbrains.kotlin.ir.declarations.IrValueParameter import org.jetbrains.kotlin.ir.expressions.IrConstructorCall import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.expressions.IrMemberAccessExpression +import org.jetbrains.kotlin.ir.expressions.impl.* +import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.kotlin.ir.types.addAnnotations @@ -58,3 +60,12 @@ fun codeQlSetAnnotations(container: org.jetbrains.kotlin.ir.declarations.IrMutab fun IrFunction.codeQlSetDispatchReceiverParameter(param: IrValueParameter?) { dispatchReceiverParameter = param } + +// In pre-2.4.0, annotations are List so IrConstructorCallImpl works directly. +fun codeQlAnnotationFromSymbolOwner( + startOffset: Int, endOffset: Int, type: IrType, symbol: IrConstructorSymbol, typeArgumentsCount: Int +): IrConstructorCall = + IrConstructorCallImpl.fromSymbolOwner(startOffset, endOffset, type, symbol, typeArgumentsCount) + +fun codeQlAnnotationFromSymbolOwner(type: IrType, symbol: IrConstructorSymbol): IrConstructorCall = + IrConstructorCallImpl.fromSymbolOwner(type, symbol) diff --git a/java/kotlin-extractor/src/main/kotlin/utils/versions/v_2_4_0/IrCompat.kt b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_2_4_0/IrCompat.kt index 1d010b0996cf..e388a09c37a1 100644 --- a/java/kotlin-extractor/src/main/kotlin/utils/versions/v_2_4_0/IrCompat.kt +++ b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_2_4_0/IrCompat.kt @@ -8,6 +8,9 @@ import org.jetbrains.kotlin.ir.expressions.IrAnnotation import org.jetbrains.kotlin.ir.expressions.IrConstructorCall import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.expressions.IrMemberAccessExpression +import org.jetbrains.kotlin.ir.expressions.impl.IrAnnotationImpl +import org.jetbrains.kotlin.ir.expressions.impl.fromSymbolOwner +import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.kotlin.ir.types.addAnnotations @@ -93,3 +96,13 @@ fun IrFunction.codeQlSetDispatchReceiverParameter(param: IrValueParameter?) { parameters = mutableParams } +// In 2.4.0, annotation lists require IrAnnotation instances. +// Use IrAnnotationImpl.fromSymbolOwner instead of IrConstructorCallImpl.fromSymbolOwner. +fun codeQlAnnotationFromSymbolOwner( + startOffset: Int, endOffset: Int, type: IrType, symbol: IrConstructorSymbol, typeArgumentsCount: Int +): IrConstructorCall = + IrAnnotationImpl.fromSymbolOwner(startOffset, endOffset, type, symbol, typeArgumentsCount) + +fun codeQlAnnotationFromSymbolOwner(type: IrType, symbol: IrConstructorSymbol): IrConstructorCall = + IrAnnotationImpl.fromSymbolOwner(type, symbol) + From 5732a99f9d3924459723a3c82625cfb9adf5b512 Mon Sep 17 00:00:00 2001 From: Anders Fugmann Date: Mon, 8 Jun 2026 11:50:56 +0200 Subject: [PATCH 07/10] Kotlin 2.4.0: Fix extension receiver on property references In 2.4.0's unified parameters model, IrPropertyReference stores the bound extension receiver in the arguments list (indexed via the getter's extension receiver parameter). Our codeQlExtensionReceiver compat function only handled the case where symbol.owner is an IrFunction, returning null for IrPropertyReference (where symbol.owner is IrProperty). Fix: when the direct cast to IrFunction fails, look at the getter/setter function's parameters to find the extension receiver index. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../kotlin/utils/versions/v_2_4_0/IrCompat.kt | 27 ++++++++++++++++--- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/java/kotlin-extractor/src/main/kotlin/utils/versions/v_2_4_0/IrCompat.kt b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_2_4_0/IrCompat.kt index e388a09c37a1..7e461e6da623 100644 --- a/java/kotlin-extractor/src/main/kotlin/utils/versions/v_2_4_0/IrCompat.kt +++ b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_2_4_0/IrCompat.kt @@ -49,16 +49,35 @@ fun IrMemberAccessExpression<*>.codeQlPutValueArgument(index: Int, value: IrExpr } // IrMemberAccessExpression: extensionReceiver +// For IrCall/IrFunctionReference, look at symbol.owner (IrFunction) directly. +// For IrPropertyReference, symbol.owner is IrProperty; use the getter's parameters instead. var IrMemberAccessExpression<*>.codeQlExtensionReceiver: IrExpression? get() { - val erp = symbol.owner.let { it as? IrFunction }?.codeQlExtensionReceiverParameter ?: return null - return arguments[erp.indexInParameters] + val erp = extensionReceiverParameterIndex() ?: return null + return arguments[erp] } set(value) { - val erp = symbol.owner.let { it as? IrFunction }?.codeQlExtensionReceiverParameter ?: return - arguments[erp.indexInParameters] = value + val erp = extensionReceiverParameterIndex() ?: return + arguments[erp] = value } +private fun IrMemberAccessExpression<*>.extensionReceiverParameterIndex(): Int? { + // Direct function owner (IrCall, IrFunctionReference, etc.) + (symbol.owner as? IrFunction)?.codeQlExtensionReceiverParameter?.let { + return it.indexInParameters + } + // Property reference: look at getter or setter function + (this as? org.jetbrains.kotlin.ir.expressions.IrPropertyReference)?.let { propRef -> + propRef.getter?.owner?.codeQlExtensionReceiverParameter?.let { + return it.indexInParameters + } + propRef.setter?.owner?.codeQlExtensionReceiverParameter?.let { + return it.indexInParameters + } + } + return null +} + // IrMemberAccessExpression: typeArgumentsCount val IrMemberAccessExpression<*>.codeQlTypeArgumentsCount: Int get() = typeArguments.size From 557f904e22bdd33764db6071313b75d9a801378b Mon Sep 17 00:00:00 2001 From: Anders Fugmann Date: Mon, 8 Jun 2026 15:02:03 +0200 Subject: [PATCH 08/10] Kotlin: Drop support for Kotlin 1.x language versions Kotlin 2.4.0 no longer supports -language-version 1.9 (the last 1.x version). Clamp get_language_version() in versions.bzl to minimum 2.0 for extractor builds, and update the supported versions documentation. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- docs/codeql/reusables/supported-versions-compilers.rst | 2 +- java/kotlin-extractor/versions.bzl | 2 ++ java/ql/lib/change-notes/2026-06-08-kotlin-drop-1x.md | 4 ++++ 3 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 java/ql/lib/change-notes/2026-06-08-kotlin-drop-1x.md diff --git a/docs/codeql/reusables/supported-versions-compilers.rst b/docs/codeql/reusables/supported-versions-compilers.rst index aacb188eb20b..050de9180492 100644 --- a/docs/codeql/reusables/supported-versions-compilers.rst +++ b/docs/codeql/reusables/supported-versions-compilers.rst @@ -21,7 +21,7 @@ Java,"Java 7 to 26 [6]_","javac (OpenJDK and Oracle JDK), Eclipse compiler for Java (ECJ) [7]_",``.java`` - Kotlin,"Kotlin 1.8.0 to 2.4.\ *x*","kotlinc",``.kt`` + Kotlin,"Kotlin 2.0.0 to 2.4.\ *x*","kotlinc",``.kt`` JavaScript,ECMAScript 2022 or lower,Not applicable,"``.js``, ``.jsx``, ``.mjs``, ``.es``, ``.es6``, ``.htm``, ``.html``, ``.xhtm``, ``.xhtml``, ``.vue``, ``.hbs``, ``.ejs``, ``.njk``, ``.json``, ``.yaml``, ``.yml``, ``.raml``, ``.xml`` [8]_" Python [9]_,"2.7, 3.5, 3.6, 3.7, 3.8, 3.9, 3.10, 3.11, 3.12, 3.13",Not applicable,``.py`` Ruby [10]_,"up to 3.3",Not applicable,"``.rb``, ``.erb``, ``.gemspec``, ``Gemfile``" diff --git a/java/kotlin-extractor/versions.bzl b/java/kotlin-extractor/versions.bzl index f9642c96b788..de2d9e535a60 100644 --- a/java/kotlin-extractor/versions.bzl +++ b/java/kotlin-extractor/versions.bzl @@ -24,6 +24,8 @@ def version_less(lhs, rhs): def get_language_version(version): major, minor, _ = _version_to_tuple(version) + if major == 1: + return "2.0" return "%s.%s" % (major, minor) def _basename(path): diff --git a/java/ql/lib/change-notes/2026-06-08-kotlin-drop-1x.md b/java/ql/lib/change-notes/2026-06-08-kotlin-drop-1x.md new file mode 100644 index 000000000000..19a75b1df326 --- /dev/null +++ b/java/ql/lib/change-notes/2026-06-08-kotlin-drop-1x.md @@ -0,0 +1,4 @@ +--- +category: deprecation +--- +* Kotlin versions below 2.0.0 are no longer supported for analysis. The minimum supported version is now Kotlin 2.0.0. From c11d82737ab44c21965ce9e6f29f542c3bd24468 Mon Sep 17 00:00:00 2001 From: Anders Fugmann Date: Mon, 8 Jun 2026 16:19:10 +0200 Subject: [PATCH 09/10] Kotlin 2.4.0: Fix bazel formatting, change-note category, and internal query style - Fix BUILD.bazel formatting (buildifier line wrapping) - Fix change-note category: 'deprecation' -> 'deprecated' - Replace unsafe list casts with filterIsInstance to satisfy the possiblyThrowingExpressions internal query Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- java/kotlin-extractor/BUILD.bazel | 7 +++++-- .../src/main/kotlin/utils/versions/v_2_4_0/IrCompat.kt | 8 +++----- java/ql/lib/change-notes/2026-06-08-kotlin-drop-1x.md | 2 +- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/java/kotlin-extractor/BUILD.bazel b/java/kotlin-extractor/BUILD.bazel index 21ba2bea1aa0..9882b173c072 100644 --- a/java/kotlin-extractor/BUILD.bazel +++ b/java/kotlin-extractor/BUILD.bazel @@ -114,8 +114,11 @@ kt_javac_options( "cp $(execpath %s) $(RULEDIR)/%s/%s" % (source, v, target) for source, target in _resources ] + ( - ["cp $(execpath %s) $(RULEDIR)/%s/%s" % (_compiler_plugin_registrar_service[0], v, _compiler_plugin_registrar_service[1])] - if not version_less(v, "2.4.0") else [] + ["cp $(execpath %s) $(RULEDIR)/%s/%s" % ( + _compiler_plugin_registrar_service[0], + v, + _compiler_plugin_registrar_service[1], + )] if not version_less(v, "2.4.0") else [] )), ), kt_jvm_library( diff --git a/java/kotlin-extractor/src/main/kotlin/utils/versions/v_2_4_0/IrCompat.kt b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_2_4_0/IrCompat.kt index 7e461e6da623..d836eb3aecbe 100644 --- a/java/kotlin-extractor/src/main/kotlin/utils/versions/v_2_4_0/IrCompat.kt +++ b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_2_4_0/IrCompat.kt @@ -86,15 +86,13 @@ val IrMemberAccessExpression<*>.codeQlTypeArgumentsCount: Int fun IrMemberAccessExpression<*>.codeQlGetTypeArgument(index: Int): IrType? = typeArguments[index] // addAnnotations compat: in 2.4.0, addAnnotations expects List -// IrAnnotation extends IrConstructorCall, so we cast -@Suppress("UNCHECKED_CAST") +// IrConstructorCall implements IrAnnotation in 2.4.0, so filterIsInstance is identity fun IrType.codeQlAddAnnotations(annotations: List): IrType = - addAnnotations(annotations as List) + addAnnotations(annotations.filterIsInstance()) // IrMutableAnnotationContainer.annotations setter: in 2.4.0, expects List -@Suppress("UNCHECKED_CAST") fun codeQlSetAnnotations(container: org.jetbrains.kotlin.ir.declarations.IrMutableAnnotationContainer, annotations: List) { - container.annotations = annotations as List + container.annotations = annotations.filterIsInstance() } // IrFunction: set dispatch receiver parameter diff --git a/java/ql/lib/change-notes/2026-06-08-kotlin-drop-1x.md b/java/ql/lib/change-notes/2026-06-08-kotlin-drop-1x.md index 19a75b1df326..0d38db5b91ae 100644 --- a/java/ql/lib/change-notes/2026-06-08-kotlin-drop-1x.md +++ b/java/ql/lib/change-notes/2026-06-08-kotlin-drop-1x.md @@ -1,4 +1,4 @@ --- -category: deprecation +category: deprecated --- * Kotlin versions below 2.0.0 are no longer supported for analysis. The minimum supported version is now Kotlin 2.0.0. From f3037d68ca662bd05bf02a11e9285f650a027f58 Mon Sep 17 00:00:00 2001 From: Anders Fugmann Date: Mon, 8 Jun 2026 16:22:38 +0200 Subject: [PATCH 10/10] Kotlin: Update integration tests from -language-version 1.9 to 2.0 Kotlin 2.4.0 no longer supports -language-version 1.9. Update all integration tests that explicitly pass this flag to use 2.0 instead. Also update the extractor_information_kotlin1 expected output since -language-version 2.0 causes the extractor to report 'Uses Kotlin 2: true'. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- java/kotlin-extractor/BUILD.bazel | 5 ++++- .../kotlin/all-platforms/enhanced-nullability/test.py | 2 +- .../kotlin/all-platforms/external-property-overloads/test.py | 4 ++-- .../ExtractorInformation.expected | 2 +- .../all-platforms/extractor_information_kotlin1/test.py | 2 +- .../kotlin/all-platforms/file_classes/test.py | 4 ++-- .../all-platforms/java-interface-redeclares-tostring/test.py | 2 +- .../all-platforms/kotlin_java_lowering_wildcards/test.py | 2 +- 8 files changed, 13 insertions(+), 10 deletions(-) diff --git a/java/kotlin-extractor/BUILD.bazel b/java/kotlin-extractor/BUILD.bazel index 9882b173c072..0eef645cba0c 100644 --- a/java/kotlin-extractor/BUILD.bazel +++ b/java/kotlin-extractor/BUILD.bazel @@ -106,7 +106,10 @@ kt_javac_options( "%s/%s" % (v, target) for _, target in _resources ] + ( - ["%s/%s" % (v, _compiler_plugin_registrar_service[1])] if not version_less(v, "2.4.0") else [] + ["%s/%s" % ( + v, + _compiler_plugin_registrar_service[1], + )] if not version_less(v, "2.4.0") else [] ), cmd = "\n".join([ "echo %s-%s > $(RULEDIR)/%s/com/github/codeql/extractor.name" % (_extractor_name_prefix, v, v), diff --git a/java/ql/integration-tests/kotlin/all-platforms/enhanced-nullability/test.py b/java/ql/integration-tests/kotlin/all-platforms/enhanced-nullability/test.py index e030b51db8f9..72ff061aa821 100644 --- a/java/ql/integration-tests/kotlin/all-platforms/enhanced-nullability/test.py +++ b/java/ql/integration-tests/kotlin/all-platforms/enhanced-nullability/test.py @@ -6,6 +6,6 @@ def test(codeql, java_full): codeql.database.create( command=[ f"javac {java_srcs} -d build", - "kotlinc -language-version 1.9 user.kt -cp build", + "kotlinc -language-version 2.0 user.kt -cp build", ] ) diff --git a/java/ql/integration-tests/kotlin/all-platforms/external-property-overloads/test.py b/java/ql/integration-tests/kotlin/all-platforms/external-property-overloads/test.py index 403f3729d06b..338ae19a9aa0 100644 --- a/java/ql/integration-tests/kotlin/all-platforms/external-property-overloads/test.py +++ b/java/ql/integration-tests/kotlin/all-platforms/external-property-overloads/test.py @@ -2,5 +2,5 @@ def test(codeql, java_full): - commands.run("kotlinc -language-version 1.9 test.kt -d lib") - codeql.database.create(command="kotlinc -language-version 1.9 user.kt -cp lib") + commands.run("kotlinc -language-version 2.0 test.kt -d lib") + codeql.database.create(command="kotlinc -language-version 2.0 user.kt -cp lib") diff --git a/java/ql/integration-tests/kotlin/all-platforms/extractor_information_kotlin1/ExtractorInformation.expected b/java/ql/integration-tests/kotlin/all-platforms/extractor_information_kotlin1/ExtractorInformation.expected index 5e32cd7786e8..d00ef5f0ec68 100644 --- a/java/ql/integration-tests/kotlin/all-platforms/extractor_information_kotlin1/ExtractorInformation.expected +++ b/java/ql/integration-tests/kotlin/all-platforms/extractor_information_kotlin1/ExtractorInformation.expected @@ -9,4 +9,4 @@ | Percentage of calls with call target | 100 | | Total number of lines | 3 | | Total number of lines with extension kt | 3 | -| Uses Kotlin 2: false | 1 | +| Uses Kotlin 2: true | 1 | diff --git a/java/ql/integration-tests/kotlin/all-platforms/extractor_information_kotlin1/test.py b/java/ql/integration-tests/kotlin/all-platforms/extractor_information_kotlin1/test.py index 5a0dc9e072bc..b5bb5378a230 100755 --- a/java/ql/integration-tests/kotlin/all-platforms/extractor_information_kotlin1/test.py +++ b/java/ql/integration-tests/kotlin/all-platforms/extractor_information_kotlin1/test.py @@ -1,2 +1,2 @@ def test(codeql, java_full): - codeql.database.create(command="kotlinc -J-Xmx2G -language-version 1.9 SomeClass.kt") + codeql.database.create(command="kotlinc -J-Xmx2G -language-version 2.0 SomeClass.kt") diff --git a/java/ql/integration-tests/kotlin/all-platforms/file_classes/test.py b/java/ql/integration-tests/kotlin/all-platforms/file_classes/test.py index 99c21ceb0b81..1d0913490f1d 100644 --- a/java/ql/integration-tests/kotlin/all-platforms/file_classes/test.py +++ b/java/ql/integration-tests/kotlin/all-platforms/file_classes/test.py @@ -2,5 +2,5 @@ def test(codeql, java_full): - commands.run("kotlinc -language-version 1.9 A.kt") - codeql.database.create(command="kotlinc -cp . -language-version 1.9 B.kt C.kt") + commands.run("kotlinc -language-version 2.0 A.kt") + codeql.database.create(command="kotlinc -cp . -language-version 2.0 B.kt C.kt") diff --git a/java/ql/integration-tests/kotlin/all-platforms/java-interface-redeclares-tostring/test.py b/java/ql/integration-tests/kotlin/all-platforms/java-interface-redeclares-tostring/test.py index 3db804c83bee..1f24c2be7906 100644 --- a/java/ql/integration-tests/kotlin/all-platforms/java-interface-redeclares-tostring/test.py +++ b/java/ql/integration-tests/kotlin/all-platforms/java-interface-redeclares-tostring/test.py @@ -3,4 +3,4 @@ def test(codeql, java_full): commands.run(["javac", "Test.java", "-d", "bin"]) - codeql.database.create(command="kotlinc -language-version 1.9 user.kt -cp bin") + codeql.database.create(command="kotlinc -language-version 2.0 user.kt -cp bin") diff --git a/java/ql/integration-tests/kotlin/all-platforms/kotlin_java_lowering_wildcards/test.py b/java/ql/integration-tests/kotlin/all-platforms/kotlin_java_lowering_wildcards/test.py index d1c4948dfe71..b259450e7b95 100644 --- a/java/ql/integration-tests/kotlin/all-platforms/kotlin_java_lowering_wildcards/test.py +++ b/java/ql/integration-tests/kotlin/all-platforms/kotlin_java_lowering_wildcards/test.py @@ -8,6 +8,6 @@ def test(codeql, java_full): command=[ "kotlinc kotlindefns.kt", "javac JavaUser.java JavaDefns.java -cp .", - "kotlinc -language-version 1.9 -cp . kotlinuser.kt", + "kotlinc -language-version 2.0 -cp . kotlinuser.kt", ] )