From 93e6135e7d1981ae921b392734c4d123abf6a162 Mon Sep 17 00:00:00 2001 From: Anders Fugmann Date: Wed, 26 Aug 2026 12:42:19 +0200 Subject: [PATCH 01/26] Kotlin: add 2.4.20-RC artifacts Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- MODULE.bazel | 3 +++ java/kotlin-extractor/deps/kotlin-compiler-2.4.20-RC.jar | 3 +++ .../deps/kotlin-compiler-embeddable-2.4.20-RC.jar | 3 +++ java/kotlin-extractor/deps/kotlin-stdlib-2.4.20-RC.jar | 3 +++ 4 files changed, 12 insertions(+) create mode 100644 java/kotlin-extractor/deps/kotlin-compiler-2.4.20-RC.jar create mode 100644 java/kotlin-extractor/deps/kotlin-compiler-embeddable-2.4.20-RC.jar create mode 100644 java/kotlin-extractor/deps/kotlin-stdlib-2.4.20-RC.jar diff --git a/MODULE.bazel b/MODULE.bazel index 17a6936f892c..88b6cc0a17ce 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -276,6 +276,7 @@ use_repo( "kotlin-compiler-2.3.0", "kotlin-compiler-2.3.20", "kotlin-compiler-2.4.0", + "kotlin-compiler-2.4.20-RC", "kotlin-compiler-embeddable-1.8.0", "kotlin-compiler-embeddable-1.9.0-Beta", "kotlin-compiler-embeddable-1.9.20-Beta", @@ -288,6 +289,7 @@ use_repo( "kotlin-compiler-embeddable-2.3.0", "kotlin-compiler-embeddable-2.3.20", "kotlin-compiler-embeddable-2.4.0", + "kotlin-compiler-embeddable-2.4.20-RC", "kotlin-stdlib-1.8.0", "kotlin-stdlib-1.9.0-Beta", "kotlin-stdlib-1.9.20-Beta", @@ -300,6 +302,7 @@ use_repo( "kotlin-stdlib-2.3.0", "kotlin-stdlib-2.3.20", "kotlin-stdlib-2.4.0", + "kotlin-stdlib-2.4.20-RC", ) go_sdk = use_extension("@rules_go//go:extensions.bzl", "go_sdk") diff --git a/java/kotlin-extractor/deps/kotlin-compiler-2.4.20-RC.jar b/java/kotlin-extractor/deps/kotlin-compiler-2.4.20-RC.jar new file mode 100644 index 000000000000..8e5a4c945250 --- /dev/null +++ b/java/kotlin-extractor/deps/kotlin-compiler-2.4.20-RC.jar @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5e2e10f209ef5a63ed70d04ab5c64a8cf346c323f356eb4241ce34ee03cf2e53 +size 60184229 diff --git a/java/kotlin-extractor/deps/kotlin-compiler-embeddable-2.4.20-RC.jar b/java/kotlin-extractor/deps/kotlin-compiler-embeddable-2.4.20-RC.jar new file mode 100644 index 000000000000..4a7601447f96 --- /dev/null +++ b/java/kotlin-extractor/deps/kotlin-compiler-embeddable-2.4.20-RC.jar @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fac41bf816409462e189b41b87c4a8dfb48b995b4d8d6271968d4f638ceab255 +size 58593000 diff --git a/java/kotlin-extractor/deps/kotlin-stdlib-2.4.20-RC.jar b/java/kotlin-extractor/deps/kotlin-stdlib-2.4.20-RC.jar new file mode 100644 index 000000000000..2fb1da42a55a --- /dev/null +++ b/java/kotlin-extractor/deps/kotlin-stdlib-2.4.20-RC.jar @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:540bee6d6310863014877cb4df8f087c256b22d0925955d1d20943d706bba524 +size 1853317 From c1b4fe638360d5a8b938b4d7b7f61df481bdeb4d Mon Sep 17 00:00:00 2001 From: Anders Fugmann Date: Wed, 26 Aug 2026 12:42:19 +0200 Subject: [PATCH 02/26] Kotlin: address KT-85816 Kotlin 2.4.20-RC removes the remaining ComponentRegistrar API under KT-85816. Use an RC-specific registrar that only implements CompilerPluginRegistrar, and package the legacy ComponentRegistrar service only for older compilers. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- java/kotlin-extractor/BUILD.bazel | 31 ++++++++++++++++-- .../v_2_4_20-RC/Kotlin2ComponentRegistrar.kt | 32 +++++++++++++++++++ 2 files changed, 61 insertions(+), 2 deletions(-) create mode 100644 java/kotlin-extractor/src/main/kotlin/utils/versions/v_2_4_20-RC/Kotlin2ComponentRegistrar.kt diff --git a/java/kotlin-extractor/BUILD.bazel b/java/kotlin-extractor/BUILD.bazel index f33949f83914..1b9501e7c142 100644 --- a/java/kotlin-extractor/BUILD.bazel +++ b/java/kotlin-extractor/BUILD.bazel @@ -57,6 +57,10 @@ _compiler_plugin_registrar_service_source = "src/main/resources/META-INF/service _compiler_plugin_registrar_service_target = "META-INF/services/org.jetbrains.kotlin.compiler.plugin.CompilerPluginRegistrar" +_component_registrar_service_source = "src/main/resources/META-INF/services/org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar" + +_component_registrar_service_target = "META-INF/services/org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar" + py_binary( name = "generate_dbscheme", srcs = ["generate_dbscheme.py"], @@ -68,7 +72,10 @@ _resources = [ r[len("src/main/resources/"):], ) for r in glob(["src/main/resources/**"]) - if r != _compiler_plugin_registrar_service_source + if r not in ( + _compiler_plugin_registrar_service_source, + _component_registrar_service_source, + ) ] _compiler_plugin_registrar_service = ( @@ -76,6 +83,11 @@ _compiler_plugin_registrar_service = ( _compiler_plugin_registrar_service_target, ) +_component_registrar_service = ( + _component_registrar_service_source, + _component_registrar_service_target, +) + kt_javac_options( name = "javac-options", release = "8", @@ -93,7 +105,9 @@ kt_javac_options( "kotlin.RequiresOptIn", "org.jetbrains.kotlin.ir.symbols.%s" % ("IrSymbolInternals" if version_less(v, "2.0.0") else "UnsafeDuringIrConstructionAPI"), - ] + ([] if version_less(v, "2.2.20") else ["org.jetbrains.kotlin.DeprecatedForRemovalCompilerApi"]), + ] + ( + [] if version_less(v, "2.2.20") else ["org.jetbrains.kotlin.DeprecatedForRemovalCompilerApi"] + ), x_suppress_version_warnings = True, ), # * extractor.name is different for each version, so we need to put it in different output dirs @@ -103,6 +117,8 @@ kt_javac_options( name = "resources-%s" % v, srcs = [src for src, _ in _resources] + ( [_compiler_plugin_registrar_service[0]] if not version_less(v, "2.4.0") else [] + ) + ( + [_component_registrar_service[0]] if version_less(v, "2.4.20") else [] ), outs = [ "%s/com/github/codeql/extractor.name" % v, @@ -114,6 +130,11 @@ kt_javac_options( v, _compiler_plugin_registrar_service[1], )] if not version_less(v, "2.4.0") else [] + ) + ( + ["%s/%s" % ( + v, + _component_registrar_service[1], + )] if version_less(v, "2.4.20") else [] ), cmd = "\n".join([ "echo %s-%s > $(RULEDIR)/%s/com/github/codeql/extractor.name" % (_extractor_name_prefix, v, v), @@ -126,6 +147,12 @@ kt_javac_options( v, _compiler_plugin_registrar_service[1], )] if not version_less(v, "2.4.0") else [] + ) + ( + ["cp $(execpath %s) $(RULEDIR)/%s/%s" % ( + _component_registrar_service[0], + v, + _component_registrar_service[1], + )] if version_less(v, "2.4.20") else [] )), ), kt_jvm_library( diff --git a/java/kotlin-extractor/src/main/kotlin/utils/versions/v_2_4_20-RC/Kotlin2ComponentRegistrar.kt b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_2_4_20-RC/Kotlin2ComponentRegistrar.kt new file mode 100644 index 000000000000..f5091752633c --- /dev/null +++ b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_2_4_20-RC/Kotlin2ComponentRegistrar.kt @@ -0,0 +1,32 @@ +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 + +@OptIn(ExperimentalCompilerApi::class) +abstract class Kotlin2ComponentRegistrar : CompilerPluginRegistrar() { + override val supportsK2: Boolean + get() = true + + override val pluginId: String + get() = "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) + + protected fun registerExtractorExtension(extension: IrGenerationExtension) { + val storage = extensionStorage + ?: throw IllegalStateException("registerExtractorExtension called before registerExtensions") + with(storage) { + IrGenerationExtension.registerExtension(extension) + } + } +} From 0724cafd30ff38a42025a58bc12c0a3d9380b9ec Mon Sep 17 00:00:00 2001 From: Anders Fugmann Date: Wed, 26 Aug 2026 12:42:31 +0200 Subject: [PATCH 03/26] Kotlin: address KT-86046 Kotlin 2.4.20-RC marks public K1 APIs with K1Deprecation under KT-86046 and rejects the previous repeatable-annotation IR access path. Opt in to K1Deprecation for 2.4.20 and later, and use the existing annotationClass compatibility extension. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- java/kotlin-extractor/BUILD.bazel | 2 ++ java/kotlin-extractor/src/main/kotlin/MetaAnnotationSupport.kt | 3 +-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/java/kotlin-extractor/BUILD.bazel b/java/kotlin-extractor/BUILD.bazel index 1b9501e7c142..fae93f6a2cc4 100644 --- a/java/kotlin-extractor/BUILD.bazel +++ b/java/kotlin-extractor/BUILD.bazel @@ -107,6 +107,8 @@ kt_javac_options( ("IrSymbolInternals" if version_less(v, "2.0.0") else "UnsafeDuringIrConstructionAPI"), ] + ( [] if version_less(v, "2.2.20") else ["org.jetbrains.kotlin.DeprecatedForRemovalCompilerApi"] + ) + ( + [] if version_less(v, "2.4.20") else ["org.jetbrains.kotlin.K1Deprecation"] ), x_suppress_version_warnings = True, ), diff --git a/java/kotlin-extractor/src/main/kotlin/MetaAnnotationSupport.kt b/java/kotlin-extractor/src/main/kotlin/MetaAnnotationSupport.kt index e215b5ca31da..d5650389448d 100644 --- a/java/kotlin-extractor/src/main/kotlin/MetaAnnotationSupport.kt +++ b/java/kotlin-extractor/src/main/kotlin/MetaAnnotationSupport.kt @@ -96,8 +96,7 @@ class MetaAnnotationSupport( val metaAnnotations = annotationClass.annotations val jvmRepeatable = metaAnnotations.find { - it.symbol.owner.parentAsClass.fqNameWhenAvailable == - JvmAnnotationNames.REPEATABLE_ANNOTATION + it.annotationClass.fqNameWhenAvailable == JvmAnnotationNames.REPEATABLE_ANNOTATION } return if (jvmRepeatable != null) { ((jvmRepeatable.codeQlGetValueArgument(0) as? IrClassReference)?.symbol as? IrClassSymbol) From 96ac5ba6cbcf82b49a56385993a8586f203d4538 Mon Sep 17 00:00:00 2001 From: Anders Fugmann Date: Wed, 26 Aug 2026 12:42:32 +0200 Subject: [PATCH 04/26] Kotlin: preserve generated forwarder locations Kotlin 2.4.20-RC assigns synthetic source offsets to generated interface forwarders. Use the enclosing class location for each fabricated forwarder and its generated body and accesses, preserving existing database and query output. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../src/main/kotlin/KotlinFileExtractor.kt | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt b/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt index 0b975d9b829b..bef2b554d7ec 100644 --- a/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt +++ b/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt @@ -1645,8 +1645,9 @@ open class KotlinFileExtractor( extractMethodAndParameterTypeAccesses: Boolean, typeSubstitution: TypeSubstitution?, classTypeArgsIncludingOuterClasses: List? - ) : Label = - forceExtractFunction( + ) : Label { + val sourceLoc = tw.getLocation(f.parentClassOrNull ?: f) + return forceExtractFunction( f, parentId, extractBody = false, @@ -1656,6 +1657,7 @@ open class KotlinFileExtractor( classTypeArgsIncludingOuterClasses, overriddenAttributes = OverriddenFunctionAttributes( + sourceLoc = sourceLoc, visibility = DescriptorVisibilities.PUBLIC, modality = Modality.OPEN ) @@ -1666,7 +1668,6 @@ open class KotlinFileExtractor( CompilerGeneratedKinds.INTERFACE_FORWARDER.kind ) if (extractBody) { - val realFunctionLocId = tw.getLocation(f) val inheritedDefaultFunction = f.realOverrideTarget val directlyInheritedSymbol = when (f) { @@ -1686,10 +1687,10 @@ open class KotlinFileExtractor( (directlyInheritedSymbol.owner.parentClassOrNull ?: return functionId) .typeWith() - extractExpressionBody(functionId, realFunctionLocId).also { returnId -> + extractExpressionBody(functionId, sourceLoc).also { returnId -> extractRawMethodAccess( f, - realFunctionLocId, + sourceLoc, f.returnType, functionId, returnId, @@ -1702,7 +1703,7 @@ open class KotlinFileExtractor( extractVariableAccess( syntheticParamId, param.type, - realFunctionLocId, + sourceLoc, argParentId, idxOffset + idx, functionId, @@ -1718,7 +1719,7 @@ open class KotlinFileExtractor( callId, -1, returnId, - realFunctionLocId + sourceLoc ) }, null @@ -1726,6 +1727,7 @@ open class KotlinFileExtractor( } } } + } private fun extractFunction( f: IrFunction, From 8b8b96175ab27f94ca3209a3a4451db2718fdbbb Mon Sep 17 00:00:00 2001 From: Anders Fugmann Date: Wed, 26 Aug 2026 12:42:32 +0200 Subject: [PATCH 05/26] Kotlin: enable 2.4.20-RC support Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- java/kotlin-extractor/versions.bzl | 1 + .../diagnostics/kotlin-version-too-new/diagnostics.expected | 2 +- .../fake-kotlinc-source/kotlin/KotlinVersion.java | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/java/kotlin-extractor/versions.bzl b/java/kotlin-extractor/versions.bzl index f9642c96b788..f31d69b2a226 100644 --- a/java/kotlin-extractor/versions.bzl +++ b/java/kotlin-extractor/versions.bzl @@ -12,6 +12,7 @@ VERSIONS = [ "2.3.0", "2.3.20", "2.4.0", + "2.4.20-RC", ] def _version_to_tuple(v): 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 09429027c5d6..613709bfb152 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.4.20.", + "markdownMessage": "The Kotlin version installed (`2.4.20`) is too recent for this version of CodeQL. Install a version lower than 2.4.20.", "severity": "error", "source": { "extractorName": "java", diff --git a/java/ql/integration-tests/kotlin/all-platforms/diagnostics/kotlin-version-too-new/fake-kotlinc-source/kotlin/KotlinVersion.java b/java/ql/integration-tests/kotlin/all-platforms/diagnostics/kotlin-version-too-new/fake-kotlinc-source/kotlin/KotlinVersion.java index e4bd4ecb7e10..523d7e1d0d87 100644 --- a/java/ql/integration-tests/kotlin/all-platforms/diagnostics/kotlin-version-too-new/fake-kotlinc-source/kotlin/KotlinVersion.java +++ b/java/ql/integration-tests/kotlin/all-platforms/diagnostics/kotlin-version-too-new/fake-kotlinc-source/kotlin/KotlinVersion.java @@ -2,6 +2,6 @@ public class KotlinVersion { - public static String CURRENT = "999.999.999"; + public static String CURRENT = "2.4.20"; } From f146aa6601a99ca0a4e514c9ea83c2153dad911b Mon Sep 17 00:00:00 2001 From: Anders Fugmann Date: Wed, 26 Aug 2026 12:42:33 +0200 Subject: [PATCH 06/26] Kotlin: make 2.4.20-RC the test default Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- java/kotlin-extractor/dev/wrapper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/kotlin-extractor/dev/wrapper.py b/java/kotlin-extractor/dev/wrapper.py index 1b29de23f766..8c795418f67e 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.4.10" +DEFAULT_VERSION = "2.4.20-RC" def options(): From b9c81d5898aac5b2f2ac184caeff36a6a19f3922 Mon Sep 17 00:00:00 2001 From: Anders Fugmann Date: Wed, 26 Aug 2026 12:42:33 +0200 Subject: [PATCH 07/26] Kotlin: document preliminary 2.4.20-RC support Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- docs/codeql/reusables/supported-versions-compilers.rst | 3 ++- java/ql/lib/change-notes/2026-08-21-kotlin-2.4.20.md | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 java/ql/lib/change-notes/2026-08-21-kotlin-2.4.20.md diff --git a/docs/codeql/reusables/supported-versions-compilers.rst b/docs/codeql/reusables/supported-versions-compilers.rst index 8651fa91a269..4fc5c5919285 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 [5]_","javac (OpenJDK and Oracle JDK), Eclipse compiler for Java (ECJ) [6]_",``.java`` - Kotlin,"Kotlin 1.8.0 to 2.4.1\ *x*","kotlinc",``.kt`` + Kotlin [13]_,"Kotlin 1.8.0 to 2.4.20-RC","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`` [7]_" Python [8]_,"2.7, 3.5, 3.6, 3.7, 3.8, 3.9, 3.10, 3.11, 3.12, 3.13, 3.14",Not applicable,``.py`` Ruby,"up to 3.3",Not applicable,"``.rb``, ``.erb``, ``.gemspec``, ``Gemfile``" @@ -43,3 +43,4 @@ .. [10] Support for the analysis of Swift requires macOS. .. [11] Embedded Swift is not supported. .. [12] TypeScript analysis is performed by running the JavaScript extractor with TypeScript enabled. This is the default. + .. [13] Support for Kotlin 2.4.20-RC is preliminary. diff --git a/java/ql/lib/change-notes/2026-08-21-kotlin-2.4.20.md b/java/ql/lib/change-notes/2026-08-21-kotlin-2.4.20.md new file mode 100644 index 000000000000..15ae8bc3dbdd --- /dev/null +++ b/java/ql/lib/change-notes/2026-08-21-kotlin-2.4.20.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* Preliminary support for Kotlin 2.4.20-RC has been added. From 63553e31530ef2a5810fbe80626a1f4b61e8ffee Mon Sep 17 00:00:00 2001 From: Anders Fugmann Date: Fri, 28 Aug 2026 13:14:01 +0200 Subject: [PATCH 08/26] Kotlin: support 2.4.20-RC2 Replace the previous 2.4.20-RC dependency set and compatibility marker with RC2 while preserving existing extraction output. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- MODULE.bazel | 6 +++--- docs/codeql/reusables/supported-versions-compilers.rst | 4 ++-- java/kotlin-extractor/deps/kotlin-compiler-2.4.20-RC.jar | 3 --- java/kotlin-extractor/deps/kotlin-compiler-2.4.20-RC2.jar | 3 +++ .../deps/kotlin-compiler-embeddable-2.4.20-RC.jar | 3 --- .../deps/kotlin-compiler-embeddable-2.4.20-RC2.jar | 3 +++ java/kotlin-extractor/deps/kotlin-stdlib-2.4.20-RC.jar | 3 --- java/kotlin-extractor/deps/kotlin-stdlib-2.4.20-RC2.jar | 3 +++ java/kotlin-extractor/dev/wrapper.py | 2 +- .../Kotlin2ComponentRegistrar.kt | 0 java/kotlin-extractor/versions.bzl | 2 +- java/ql/lib/change-notes/2026-08-21-kotlin-2.4.20.md | 2 +- 12 files changed, 17 insertions(+), 17 deletions(-) delete mode 100644 java/kotlin-extractor/deps/kotlin-compiler-2.4.20-RC.jar create mode 100644 java/kotlin-extractor/deps/kotlin-compiler-2.4.20-RC2.jar delete mode 100644 java/kotlin-extractor/deps/kotlin-compiler-embeddable-2.4.20-RC.jar create mode 100644 java/kotlin-extractor/deps/kotlin-compiler-embeddable-2.4.20-RC2.jar delete mode 100644 java/kotlin-extractor/deps/kotlin-stdlib-2.4.20-RC.jar create mode 100644 java/kotlin-extractor/deps/kotlin-stdlib-2.4.20-RC2.jar rename java/kotlin-extractor/src/main/kotlin/utils/versions/{v_2_4_20-RC => v_2_4_20-RC2}/Kotlin2ComponentRegistrar.kt (100%) diff --git a/MODULE.bazel b/MODULE.bazel index 88b6cc0a17ce..2b62ecadd06f 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -276,7 +276,7 @@ use_repo( "kotlin-compiler-2.3.0", "kotlin-compiler-2.3.20", "kotlin-compiler-2.4.0", - "kotlin-compiler-2.4.20-RC", + "kotlin-compiler-2.4.20-RC2", "kotlin-compiler-embeddable-1.8.0", "kotlin-compiler-embeddable-1.9.0-Beta", "kotlin-compiler-embeddable-1.9.20-Beta", @@ -289,7 +289,7 @@ use_repo( "kotlin-compiler-embeddable-2.3.0", "kotlin-compiler-embeddable-2.3.20", "kotlin-compiler-embeddable-2.4.0", - "kotlin-compiler-embeddable-2.4.20-RC", + "kotlin-compiler-embeddable-2.4.20-RC2", "kotlin-stdlib-1.8.0", "kotlin-stdlib-1.9.0-Beta", "kotlin-stdlib-1.9.20-Beta", @@ -302,7 +302,7 @@ use_repo( "kotlin-stdlib-2.3.0", "kotlin-stdlib-2.3.20", "kotlin-stdlib-2.4.0", - "kotlin-stdlib-2.4.20-RC", + "kotlin-stdlib-2.4.20-RC2", ) go_sdk = use_extension("@rules_go//go:extensions.bzl", "go_sdk") diff --git a/docs/codeql/reusables/supported-versions-compilers.rst b/docs/codeql/reusables/supported-versions-compilers.rst index 4fc5c5919285..e4741032d0d4 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 [5]_","javac (OpenJDK and Oracle JDK), Eclipse compiler for Java (ECJ) [6]_",``.java`` - Kotlin [13]_,"Kotlin 1.8.0 to 2.4.20-RC","kotlinc",``.kt`` + Kotlin [13]_,"Kotlin 1.8.0 to 2.4.20-RC2","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`` [7]_" Python [8]_,"2.7, 3.5, 3.6, 3.7, 3.8, 3.9, 3.10, 3.11, 3.12, 3.13, 3.14",Not applicable,``.py`` Ruby,"up to 3.3",Not applicable,"``.rb``, ``.erb``, ``.gemspec``, ``Gemfile``" @@ -43,4 +43,4 @@ .. [10] Support for the analysis of Swift requires macOS. .. [11] Embedded Swift is not supported. .. [12] TypeScript analysis is performed by running the JavaScript extractor with TypeScript enabled. This is the default. - .. [13] Support for Kotlin 2.4.20-RC is preliminary. + .. [13] Support for Kotlin 2.4.20-RC2 is preliminary. diff --git a/java/kotlin-extractor/deps/kotlin-compiler-2.4.20-RC.jar b/java/kotlin-extractor/deps/kotlin-compiler-2.4.20-RC.jar deleted file mode 100644 index 8e5a4c945250..000000000000 --- a/java/kotlin-extractor/deps/kotlin-compiler-2.4.20-RC.jar +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:5e2e10f209ef5a63ed70d04ab5c64a8cf346c323f356eb4241ce34ee03cf2e53 -size 60184229 diff --git a/java/kotlin-extractor/deps/kotlin-compiler-2.4.20-RC2.jar b/java/kotlin-extractor/deps/kotlin-compiler-2.4.20-RC2.jar new file mode 100644 index 000000000000..61b97c2daab3 --- /dev/null +++ b/java/kotlin-extractor/deps/kotlin-compiler-2.4.20-RC2.jar @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ec34d6a2587d59cc0dfeb65b5588f2d631edf93d0f2543f3b026f902456094df +size 60184461 diff --git a/java/kotlin-extractor/deps/kotlin-compiler-embeddable-2.4.20-RC.jar b/java/kotlin-extractor/deps/kotlin-compiler-embeddable-2.4.20-RC.jar deleted file mode 100644 index 4a7601447f96..000000000000 --- a/java/kotlin-extractor/deps/kotlin-compiler-embeddable-2.4.20-RC.jar +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:fac41bf816409462e189b41b87c4a8dfb48b995b4d8d6271968d4f638ceab255 -size 58593000 diff --git a/java/kotlin-extractor/deps/kotlin-compiler-embeddable-2.4.20-RC2.jar b/java/kotlin-extractor/deps/kotlin-compiler-embeddable-2.4.20-RC2.jar new file mode 100644 index 000000000000..2c5e630eed5f --- /dev/null +++ b/java/kotlin-extractor/deps/kotlin-compiler-embeddable-2.4.20-RC2.jar @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2654e7834a9e3c12e4626e2d5ace62f38682f9cdd348dba48a6bbde3479d6db2 +size 58593232 diff --git a/java/kotlin-extractor/deps/kotlin-stdlib-2.4.20-RC.jar b/java/kotlin-extractor/deps/kotlin-stdlib-2.4.20-RC.jar deleted file mode 100644 index 2fb1da42a55a..000000000000 --- a/java/kotlin-extractor/deps/kotlin-stdlib-2.4.20-RC.jar +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:540bee6d6310863014877cb4df8f087c256b22d0925955d1d20943d706bba524 -size 1853317 diff --git a/java/kotlin-extractor/deps/kotlin-stdlib-2.4.20-RC2.jar b/java/kotlin-extractor/deps/kotlin-stdlib-2.4.20-RC2.jar new file mode 100644 index 000000000000..66a27b7faf12 --- /dev/null +++ b/java/kotlin-extractor/deps/kotlin-stdlib-2.4.20-RC2.jar @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1697a320cbceeee25123ce07aa8e3b356094601b1bff8a55ac8db39ffbbca0ef +size 1853317 diff --git a/java/kotlin-extractor/dev/wrapper.py b/java/kotlin-extractor/dev/wrapper.py index 8c795418f67e..fa1df5714e0b 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.4.20-RC" +DEFAULT_VERSION = "2.4.20-RC2" def options(): diff --git a/java/kotlin-extractor/src/main/kotlin/utils/versions/v_2_4_20-RC/Kotlin2ComponentRegistrar.kt b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_2_4_20-RC2/Kotlin2ComponentRegistrar.kt similarity index 100% rename from java/kotlin-extractor/src/main/kotlin/utils/versions/v_2_4_20-RC/Kotlin2ComponentRegistrar.kt rename to java/kotlin-extractor/src/main/kotlin/utils/versions/v_2_4_20-RC2/Kotlin2ComponentRegistrar.kt diff --git a/java/kotlin-extractor/versions.bzl b/java/kotlin-extractor/versions.bzl index f31d69b2a226..6fb456e9fa73 100644 --- a/java/kotlin-extractor/versions.bzl +++ b/java/kotlin-extractor/versions.bzl @@ -12,7 +12,7 @@ VERSIONS = [ "2.3.0", "2.3.20", "2.4.0", - "2.4.20-RC", + "2.4.20-RC2", ] def _version_to_tuple(v): diff --git a/java/ql/lib/change-notes/2026-08-21-kotlin-2.4.20.md b/java/ql/lib/change-notes/2026-08-21-kotlin-2.4.20.md index 15ae8bc3dbdd..08772e5635d1 100644 --- a/java/ql/lib/change-notes/2026-08-21-kotlin-2.4.20.md +++ b/java/ql/lib/change-notes/2026-08-21-kotlin-2.4.20.md @@ -1,4 +1,4 @@ --- category: minorAnalysis --- -* Preliminary support for Kotlin 2.4.20-RC has been added. +* Preliminary support for Kotlin 2.4.20-RC2 has been added. From 2f4571b083c7781ebcd008b86cc8f5912f7927bf Mon Sep 17 00:00:00 2001 From: Anders Fugmann Date: Fri, 28 Aug 2026 14:15:05 +0200 Subject: [PATCH 09/26] Kotlin: cover full value class extraction Add focused coverage for an abstract value base class and a concrete multi-field subclass with property overrides, inheritance, and a secondary constructor. The existing extractor represents these constructs correctly. This commit adds coverage only and requires no extractor change. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../full-value-classes/test.expected | 17 ++++++++ .../library-tests/full-value-classes/test.kt | 11 ++++++ .../library-tests/full-value-classes/test.ql | 39 +++++++++++++++++++ .../full-value-classes/test.qlref | 1 + 4 files changed, 68 insertions(+) create mode 100644 java/ql/test-kotlin2/library-tests/full-value-classes/test.expected create mode 100644 java/ql/test-kotlin2/library-tests/full-value-classes/test.kt create mode 100644 java/ql/test-kotlin2/library-tests/full-value-classes/test.ql create mode 100644 java/ql/test-kotlin2/library-tests/full-value-classes/test.qlref diff --git a/java/ql/test-kotlin2/library-tests/full-value-classes/test.expected b/java/ql/test-kotlin2/library-tests/full-value-classes/test.expected new file mode 100644 index 000000000000..5a4f96bdaee8 --- /dev/null +++ b/java/ql/test-kotlin2/library-tests/full-value-classes/test.expected @@ -0,0 +1,17 @@ +classes +| test.kt:3:1:5:1 | Base | abstract, public | +| test.kt:7:1:9:1 | PairValue | final, public | +supertypes +| test.kt:7:1:9:1 | PairValue | test.kt:3:1:5:1 | Base | +properties +| test.kt:4:14:4:27 | value | int | public | test.kt:4:14:4:27 | getValue | | +| test.kt:7:32:7:45 | value | int | public | test.kt:7:32:7:45 | getValue | value | +| test.kt:7:48:7:64 | label | String | public | test.kt:7:48:7:64 | getLabel | label | +constructors +| test.kt:3:1:5:1 | Base | Base() | +| test.kt:7:22:7:65 | PairValue | PairValue(int,java.lang.String) | +| test.kt:8:5:8:68 | PairValue | PairValue(long) | +constructorCalls +| test.kt:7:1:9:1 | super(...) | test.kt:3:1:5:1 | Base | +| test.kt:8:32:8:68 | this(...) | test.kt:7:22:7:65 | PairValue | +| test.kt:11:40:11:55 | new PairValue(...) | test.kt:8:5:8:68 | PairValue | diff --git a/java/ql/test-kotlin2/library-tests/full-value-classes/test.kt b/java/ql/test-kotlin2/library-tests/full-value-classes/test.kt new file mode 100644 index 000000000000..50f8209367a8 --- /dev/null +++ b/java/ql/test-kotlin2/library-tests/full-value-classes/test.kt @@ -0,0 +1,11 @@ +// codeql-extractor-kotlin-options: -XXLanguage:+FullValueClasses + +abstract value class Base { + abstract val value: Int +} + +value class PairValue(override val value: Int, val label: String) : Base() { + constructor(value: Long) : this(value.toInt(), value.toString()) +} + +fun makePairValue(value: Long): Base = PairValue(value) diff --git a/java/ql/test-kotlin2/library-tests/full-value-classes/test.ql b/java/ql/test-kotlin2/library-tests/full-value-classes/test.ql new file mode 100644 index 000000000000..c59845dba7e8 --- /dev/null +++ b/java/ql/test-kotlin2/library-tests/full-value-classes/test.ql @@ -0,0 +1,39 @@ +import java + +string backingField(Property p) { + if exists(p.getBackingField()) then result = p.getBackingField().toString() else result = "" +} + +query predicate classes(Class c, string classModifiers) { + c.fromSource() and + not c.isCompilerGenerated() and + c.getLocation().getStartLine() > 0 and + classModifiers = concat(string m | c.hasModifier(m) | m, ", ") +} + +query predicate supertypes(Class c, Class supertype) { + c.fromSource() and + supertype.fromSource() and + extendsReftype(c, supertype) +} + +query predicate properties( + Property p, string propertyType, string propertyModifiers, Method getter, string field +) { + p.fromSource() and + propertyType = p.getGetter().getReturnType().toString() and + propertyModifiers = concat(string m | p.hasModifier(m) | m, ", ") and + getter = p.getGetter() and + field = backingField(p) +} + +query predicate constructors(Constructor c, string signature) { + c.fromSource() and + signature = c.getSignature() +} + +query predicate constructorCalls(ConstructorCall call, Constructor target) { + call.getEnclosingCallable().fromSource() and + target = call.getConstructor() and + target.getSourceDeclaration().fromSource() +} diff --git a/java/ql/test-kotlin2/library-tests/full-value-classes/test.qlref b/java/ql/test-kotlin2/library-tests/full-value-classes/test.qlref new file mode 100644 index 000000000000..b79433a5b459 --- /dev/null +++ b/java/ql/test-kotlin2/library-tests/full-value-classes/test.qlref @@ -0,0 +1 @@ +test.ql From 06042b1fc322eb81aa0a7eb73aaf93fade2ca778 Mon Sep 17 00:00:00 2001 From: Anders Fugmann Date: Fri, 28 Aug 2026 14:19:17 +0200 Subject: [PATCH 10/26] Kotlin: cover name-based destructuring Add focused coverage for short-form name-based destructuring where the selected property is not the first declared field. Check the resolved getter and data flow from the selected field to the destructured local. The existing extractor selects the property by name correctly. This commit adds coverage only and requires no extractor change. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../name-based-destructuring/test.expected | 4 ++++ .../name-based-destructuring/test.kt | 14 ++++++++++++ .../name-based-destructuring/test.ql | 22 +++++++++++++++++++ .../name-based-destructuring/test.qlref | 1 + 4 files changed, 41 insertions(+) create mode 100644 java/ql/test-kotlin2/library-tests/name-based-destructuring/test.expected create mode 100644 java/ql/test-kotlin2/library-tests/name-based-destructuring/test.kt create mode 100644 java/ql/test-kotlin2/library-tests/name-based-destructuring/test.ql create mode 100644 java/ql/test-kotlin2/library-tests/name-based-destructuring/test.qlref diff --git a/java/ql/test-kotlin2/library-tests/name-based-destructuring/test.expected b/java/ql/test-kotlin2/library-tests/name-based-destructuring/test.expected new file mode 100644 index 000000000000..fd9267db21f4 --- /dev/null +++ b/java/ql/test-kotlin2/library-tests/name-based-destructuring/test.expected @@ -0,0 +1,4 @@ +selectedProperty +| test.kt:10:10:10:17 | currency | test.kt:10:10:10:17 | getCurrency(...) | test.kt:3:36:3:55 | getCurrency | +#select +| test.kt:14:28:14:35 | source(...) | test.kt:11:10:11:17 | currency | diff --git a/java/ql/test-kotlin2/library-tests/name-based-destructuring/test.kt b/java/ql/test-kotlin2/library-tests/name-based-destructuring/test.kt new file mode 100644 index 000000000000..34396bc753e8 --- /dev/null +++ b/java/ql/test-kotlin2/library-tests/name-based-destructuring/test.kt @@ -0,0 +1,14 @@ +// codeql-extractor-kotlin-options: -XXLanguage:+FullValueClasses -XXLanguage:+NameBasedDestructuring -XXLanguage:+EnableNameBasedDestructuringShortForm + +value class Money(val amount: Int, val currency: String) + +fun source(): String = "" + +fun sink(value: String) {} + +fun test(money: Money) { + val (currency) = money + sink(currency) +} + +fun flow() = test(Money(0, source())) diff --git a/java/ql/test-kotlin2/library-tests/name-based-destructuring/test.ql b/java/ql/test-kotlin2/library-tests/name-based-destructuring/test.ql new file mode 100644 index 000000000000..42dbe0643e19 --- /dev/null +++ b/java/ql/test-kotlin2/library-tests/name-based-destructuring/test.ql @@ -0,0 +1,22 @@ +import java +import semmle.code.java.dataflow.TaintTracking + +query predicate selectedProperty( + LocalVariableDeclExpr variable, MethodCall initializer, Method getter +) { + variable.getVariable().hasName("currency") and + initializer = variable.getInit() and + getter = initializer.getMethod() +} + +module Config implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node n) { n.asExpr().(MethodCall).getMethod().hasName("source") } + + predicate isSink(DataFlow::Node n) { n.asExpr().(Argument).getCall().getCallee().hasName("sink") } +} + +module Flow = TaintTracking::Global; + +from DataFlow::Node source, DataFlow::Node sink +where Flow::flow(source, sink) +select source, sink diff --git a/java/ql/test-kotlin2/library-tests/name-based-destructuring/test.qlref b/java/ql/test-kotlin2/library-tests/name-based-destructuring/test.qlref new file mode 100644 index 000000000000..b79433a5b459 --- /dev/null +++ b/java/ql/test-kotlin2/library-tests/name-based-destructuring/test.qlref @@ -0,0 +1 @@ +test.ql From 3d99697d7b4d0506ad378a5d521502f74a3efc34 Mon Sep 17 00:00:00 2001 From: Anders Fugmann Date: Fri, 28 Aug 2026 14:24:19 +0200 Subject: [PATCH 11/26] Kotlin: expose missing context parameters Add regression coverage for context parameters on a function and an extension-property getter. Record callable parameters and implicit call arguments. Without the follow-up fix, the test reports missing context parameters, missing arguments, and database consistency errors. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../context-parameters/DB-CHECK.expected | 0 .../context-parameters/test.expected | 13 ++++++++++ .../library-tests/context-parameters/test.kt | 16 ++++++++++++ .../library-tests/context-parameters/test.ql | 25 +++++++++++++++++++ .../context-parameters/test.qlref | 1 + 5 files changed, 55 insertions(+) create mode 100644 java/ql/test-kotlin2/library-tests/context-parameters/DB-CHECK.expected create mode 100644 java/ql/test-kotlin2/library-tests/context-parameters/test.expected create mode 100644 java/ql/test-kotlin2/library-tests/context-parameters/test.kt create mode 100644 java/ql/test-kotlin2/library-tests/context-parameters/test.ql create mode 100644 java/ql/test-kotlin2/library-tests/context-parameters/test.qlref diff --git a/java/ql/test-kotlin2/library-tests/context-parameters/DB-CHECK.expected b/java/ql/test-kotlin2/library-tests/context-parameters/DB-CHECK.expected new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/java/ql/test-kotlin2/library-tests/context-parameters/test.expected b/java/ql/test-kotlin2/library-tests/context-parameters/test.expected new file mode 100644 index 000000000000..2371a61f6f29 --- /dev/null +++ b/java/ql/test-kotlin2/library-tests/context-parameters/test.expected @@ -0,0 +1,13 @@ +parameters +| test.kt:8:1:8:45 | logged | test.kt:7:9:7:22 | logger | 0 | Logger | +| test.kt:8:1:8:45 | logged | test.kt:8:12:8:24 | value | 1 | String | +| test.kt:12:5:12:28 | getLogged | test.kt:10:9:10:22 | logger | 1 | Logger | +| test.kt:12:5:12:28 | getLogged | test.kt:11:5:11:10 | | 0 | String | +calls +| test.kt:15:5:15:21 | logged(...) | test.kt:14:43:16:1 | invoke | test.kt:8:1:8:45 | logged | test.kt:15:5:15:21 | TestKt | 2 | +| test.kt:15:5:15:28 | getLogged(...) | test.kt:14:43:16:1 | invoke | test.kt:12:5:12:28 | getLogged | test.kt:15:5:15:28 | TestKt | 2 | +arguments +| test.kt:15:5:15:21 | logged(...) | 0 | test.kt:0:0:0:0 | p0 | +| test.kt:15:5:15:21 | logged(...) | 1 | test.kt:15:12:15:20 | "message" | +| test.kt:15:5:15:28 | getLogged(...) | 0 | test.kt:15:5:15:21 | logged(...) | +| test.kt:15:5:15:28 | getLogged(...) | 1 | test.kt:0:0:0:0 | p0 | diff --git a/java/ql/test-kotlin2/library-tests/context-parameters/test.kt b/java/ql/test-kotlin2/library-tests/context-parameters/test.kt new file mode 100644 index 000000000000..fb1cb7e02d5b --- /dev/null +++ b/java/ql/test-kotlin2/library-tests/context-parameters/test.kt @@ -0,0 +1,16 @@ +// codeql-extractor-kotlin-options: -language-version 2.4 -Xcontext-parameters + +class Logger { + fun log(value: String) = value +} + +context(logger: Logger) +fun logged(value: String) = logger.log(value) + +context(logger: Logger) +val String.logged: String + get() = logger.log(this) + +fun use(logger: Logger) = context(logger) { + logged("message").logged +} diff --git a/java/ql/test-kotlin2/library-tests/context-parameters/test.ql b/java/ql/test-kotlin2/library-tests/context-parameters/test.ql new file mode 100644 index 000000000000..46d458a5cb5c --- /dev/null +++ b/java/ql/test-kotlin2/library-tests/context-parameters/test.ql @@ -0,0 +1,25 @@ +import java + +predicate isContextCallable(Callable c) { c.getName() = ["logged", "getLogged"] } + +query predicate parameters(Callable callable, Parameter parameter, int index, string parameterType) { + isContextCallable(callable) and + parameter = callable.getParameter(index) and + parameterType = parameter.getType().toString() +} + +query predicate calls( + MethodCall call, Callable caller, Method target, Expr qualifier, int argumentCount +) { + caller.fromSource() and + call.getEnclosingCallable() = caller and + target = call.getMethod() and + isContextCallable(target) and + qualifier = call.getQualifier() and + argumentCount = call.getNumArgument() +} + +query predicate arguments(MethodCall call, int index, Expr argument) { + isContextCallable(call.getMethod()) and + argument = call.getArgument(index) +} diff --git a/java/ql/test-kotlin2/library-tests/context-parameters/test.qlref b/java/ql/test-kotlin2/library-tests/context-parameters/test.qlref new file mode 100644 index 000000000000..b79433a5b459 --- /dev/null +++ b/java/ql/test-kotlin2/library-tests/context-parameters/test.qlref @@ -0,0 +1 @@ +test.ql From 2942d7051e04177b5870db7ad7b47ac38d9f58db Mon Sep 17 00:00:00 2001 From: Anders Fugmann Date: Fri, 28 Aug 2026 14:26:53 +0200 Subject: [PATCH 12/26] Kotlin: extract context parameters and arguments Treat Kotlin 2.4 IR context parameters as callable value parameters and map member-access arguments using their parameter kinds instead of assuming that all non-regular parameters form a prefix. This restores context parameters and implicit arguments while preserving dispatch and extension receiver handling. The change is confined to the Kotlin 2.4 compatibility source set. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../kotlin/utils/versions/v_2_4_0/IrCompat.kt | 27 ++++++++++--------- 1 file changed, 15 insertions(+), 12 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 2906b18c3140..cdb4e7203a4f 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 @@ -3,6 +3,7 @@ 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 import org.jetbrains.kotlin.ir.expressions.IrAnnotation import org.jetbrains.kotlin.ir.expressions.IrConstructorCall @@ -21,33 +22,35 @@ import org.jetbrains.kotlin.ir.types.addAnnotations * have been removed. This file provides the 2.4.0 implementations. */ -// IrFunction: valueParameters -> parameters filtered to Regular kind +private fun IrParameterKind.isCodeQlValueParameter() = + this == IrParameterKind.Context || this == IrParameterKind.Regular + +// IrFunction: valueParameters -> context and regular parameters val IrFunction.codeQlValueParameters: List - get() = parameters.filter { it.kind == org.jetbrains.kotlin.ir.declarations.IrParameterKind.Regular } + get() = parameters.filter { it.kind.isCodeQlValueParameter() } // IrFunction: extensionReceiverParameter 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 -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 } +private fun IrMemberAccessExpression<*>.valueArgumentIndices(): List { + val owner = symbol.owner as? IrFunction ?: return arguments.indices.toList() + return owner.parameters.mapIndexedNotNull { index, parameter -> + index.takeIf { parameter.kind.isCodeQlValueParameter() } + } } // IrMemberAccessExpression: valueArgumentsCount -// In 2.4.0, arguments[] includes dispatch/extension receivers before regular params val IrMemberAccessExpression<*>.codeQlValueArgumentsCount: Int - get() = arguments.size - valueArgumentOffset() + get() = valueArgumentIndices().size // IrMemberAccessExpression: getValueArgument -// In 2.4.0, arguments[] includes dispatch/extension receivers before regular params -fun IrMemberAccessExpression<*>.codeQlGetValueArgument(index: Int): IrExpression? = arguments[index + valueArgumentOffset()] +fun IrMemberAccessExpression<*>.codeQlGetValueArgument(index: Int): IrExpression? = + arguments[valueArgumentIndices()[index]] // IrMemberAccessExpression: putValueArgument -// In 2.4.0, arguments[] includes dispatch/extension receivers before regular params fun IrMemberAccessExpression<*>.codeQlPutValueArgument(index: Int, value: IrExpression?) { - arguments[index + valueArgumentOffset()] = value + arguments[valueArgumentIndices()[index]] = value } // Re-add accessor for the extensionReceiver property removed in Kotlin 2.4.0. From 05357c2981f862bb4a6b98d7e04f3d7fe73cffde Mon Sep 17 00:00:00 2001 From: Anders Fugmann Date: Fri, 28 Aug 2026 14:39:37 +0200 Subject: [PATCH 13/26] Kotlin: cover context parameter setters Extend the context-parameter regression test to an extension-property setter. Check the extension receiver, context argument, assigned value, and resolved setter call. The preceding extractor fix already handles this case correctly. This commit adds coverage only and requires no additional extractor change. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../context-parameters/test.expected | 19 +++++++++++++------ .../library-tests/context-parameters/test.kt | 9 +++++++-- .../library-tests/context-parameters/test.ql | 2 +- 3 files changed, 21 insertions(+), 9 deletions(-) diff --git a/java/ql/test-kotlin2/library-tests/context-parameters/test.expected b/java/ql/test-kotlin2/library-tests/context-parameters/test.expected index 2371a61f6f29..789d9f2505f7 100644 --- a/java/ql/test-kotlin2/library-tests/context-parameters/test.expected +++ b/java/ql/test-kotlin2/library-tests/context-parameters/test.expected @@ -3,11 +3,18 @@ parameters | test.kt:8:1:8:45 | logged | test.kt:8:12:8:24 | value | 1 | String | | test.kt:12:5:12:28 | getLogged | test.kt:10:9:10:22 | logger | 1 | Logger | | test.kt:12:5:12:28 | getLogged | test.kt:11:5:11:10 | | 0 | String | +| test.kt:13:5:15:5 | setLogged | test.kt:10:9:10:22 | logger | 1 | Logger | +| test.kt:13:5:15:5 | setLogged | test.kt:11:5:11:10 | | 0 | String | +| test.kt:13:5:15:5 | setLogged | test.kt:13:9:13:13 | value | 2 | String | calls -| test.kt:15:5:15:21 | logged(...) | test.kt:14:43:16:1 | invoke | test.kt:8:1:8:45 | logged | test.kt:15:5:15:21 | TestKt | 2 | -| test.kt:15:5:15:28 | getLogged(...) | test.kt:14:43:16:1 | invoke | test.kt:12:5:12:28 | getLogged | test.kt:15:5:15:28 | TestKt | 2 | +| test.kt:18:17:18:33 | logged(...) | test.kt:17:43:21:1 | invoke | test.kt:8:1:8:45 | logged | test.kt:18:17:18:33 | TestKt | 2 | +| test.kt:18:17:18:40 | getLogged(...) | test.kt:17:43:21:1 | invoke | test.kt:12:5:12:28 | getLogged | test.kt:18:17:18:40 | TestKt | 2 | +| test.kt:19:5:19:27 | setLogged(...) | test.kt:17:43:21:1 | invoke | test.kt:13:5:15:5 | setLogged | test.kt:19:5:19:27 | TestKt | 3 | arguments -| test.kt:15:5:15:21 | logged(...) | 0 | test.kt:0:0:0:0 | p0 | -| test.kt:15:5:15:21 | logged(...) | 1 | test.kt:15:12:15:20 | "message" | -| test.kt:15:5:15:28 | getLogged(...) | 0 | test.kt:15:5:15:21 | logged(...) | -| test.kt:15:5:15:28 | getLogged(...) | 1 | test.kt:0:0:0:0 | p0 | +| test.kt:18:17:18:33 | logged(...) | 0 | test.kt:0:0:0:0 | p0 | +| test.kt:18:17:18:33 | logged(...) | 1 | test.kt:18:24:18:32 | "message" | +| test.kt:18:17:18:40 | getLogged(...) | 0 | test.kt:18:17:18:33 | logged(...) | +| test.kt:18:17:18:40 | getLogged(...) | 1 | test.kt:0:0:0:0 | p0 | +| test.kt:19:5:19:27 | setLogged(...) | 0 | test.kt:19:5:19:12 | "target" | +| test.kt:19:5:19:27 | setLogged(...) | 1 | test.kt:0:0:0:0 | p0 | +| test.kt:19:5:19:27 | setLogged(...) | 2 | test.kt:19:23:19:27 | value | diff --git a/java/ql/test-kotlin2/library-tests/context-parameters/test.kt b/java/ql/test-kotlin2/library-tests/context-parameters/test.kt index fb1cb7e02d5b..640ec53f3c25 100644 --- a/java/ql/test-kotlin2/library-tests/context-parameters/test.kt +++ b/java/ql/test-kotlin2/library-tests/context-parameters/test.kt @@ -8,9 +8,14 @@ context(logger: Logger) fun logged(value: String) = logger.log(value) context(logger: Logger) -val String.logged: String +var String.logged: String get() = logger.log(this) + set(value) { + logger.log(value) + } fun use(logger: Logger) = context(logger) { - logged("message").logged + val value = logged("message").logged + "target".logged = value + value } diff --git a/java/ql/test-kotlin2/library-tests/context-parameters/test.ql b/java/ql/test-kotlin2/library-tests/context-parameters/test.ql index 46d458a5cb5c..d522564ed90f 100644 --- a/java/ql/test-kotlin2/library-tests/context-parameters/test.ql +++ b/java/ql/test-kotlin2/library-tests/context-parameters/test.ql @@ -1,6 +1,6 @@ import java -predicate isContextCallable(Callable c) { c.getName() = ["logged", "getLogged"] } +predicate isContextCallable(Callable c) { c.getName() = ["logged", "getLogged", "setLogged"] } query predicate parameters(Callable callable, Parameter parameter, int index, string parameterType) { isContextCallable(callable) and From 8595a36f4c541a3fa25c0eb964417e374f89ed15 Mon Sep 17 00:00:00 2001 From: Anders Fugmann Date: Fri, 28 Aug 2026 14:47:24 +0200 Subject: [PATCH 14/26] Kotlin: cover collection literal extraction Add focused coverage for a collection literal resolved through a companion operator fun of. Check the call target, arguments, result type, locations, and element data flow through the resulting collection. The existing extractor handles the lowered call correctly. This commit adds coverage only and requires no extractor change. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../test.expected | 7 +++++ .../collection-literal-operators/test.kt | 16 ++++++++++ .../collection-literal-operators/test.ql | 30 +++++++++++++++++++ .../collection-literal-operators/test.qlref | 1 + 4 files changed, 54 insertions(+) create mode 100644 java/ql/test-kotlin2/library-tests/collection-literal-operators/test.expected create mode 100644 java/ql/test-kotlin2/library-tests/collection-literal-operators/test.kt create mode 100644 java/ql/test-kotlin2/library-tests/collection-literal-operators/test.ql create mode 100644 java/ql/test-kotlin2/library-tests/collection-literal-operators/test.qlref diff --git a/java/ql/test-kotlin2/library-tests/collection-literal-operators/test.expected b/java/ql/test-kotlin2/library-tests/collection-literal-operators/test.expected new file mode 100644 index 000000000000..e26a10b424d6 --- /dev/null +++ b/java/ql/test-kotlin2/library-tests/collection-literal-operators/test.expected @@ -0,0 +1,7 @@ +literalCall +| test.kt:14:24:14:40 | of(...) | test.kt:5:18:5:62 | of | Words | test.kt:14:24:14:40 | Companion | 2 | +literalArguments +| test.kt:14:24:14:40 | of(...) | 0 | test.kt:14:25:14:32 | source(...) | +| test.kt:14:24:14:40 | of(...) | 1 | test.kt:14:35:14:39 | "two" | +#select +| test.kt:14:25:14:32 | source(...) | test.kt:15:10:15:24 | ...[...] | diff --git a/java/ql/test-kotlin2/library-tests/collection-literal-operators/test.kt b/java/ql/test-kotlin2/library-tests/collection-literal-operators/test.kt new file mode 100644 index 000000000000..521c49b3858e --- /dev/null +++ b/java/ql/test-kotlin2/library-tests/collection-literal-operators/test.kt @@ -0,0 +1,16 @@ +// codeql-extractor-kotlin-options: -language-version 2.4 -XXLanguage:+CollectionLiterals + +class Words private constructor(val values: Array) { + companion object { + operator fun of(vararg values: String) = Words(values) + } +} + +fun source(): String = "" + +fun sink(value: String) {} + +fun test() { + val words: Words = [source(), "two"] + sink(words.values[0]) +} diff --git a/java/ql/test-kotlin2/library-tests/collection-literal-operators/test.ql b/java/ql/test-kotlin2/library-tests/collection-literal-operators/test.ql new file mode 100644 index 000000000000..e81cc5a9888a --- /dev/null +++ b/java/ql/test-kotlin2/library-tests/collection-literal-operators/test.ql @@ -0,0 +1,30 @@ +import java +import semmle.code.java.dataflow.TaintTracking + +query predicate literalCall( + MethodCall call, Method target, string resultType, Expr qualifier, int argumentCount +) { + target = call.getMethod() and + target.hasName("of") and + call.getEnclosingCallable().fromSource() and + resultType = call.getType().toString() and + qualifier = call.getQualifier() and + argumentCount = call.getNumArgument() +} + +query predicate literalArguments(MethodCall call, int index, Expr argument) { + call.getMethod().hasName("of") and + argument = call.getArgument(index) +} + +module Config implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node n) { n.asExpr().(MethodCall).getMethod().hasName("source") } + + predicate isSink(DataFlow::Node n) { n.asExpr().(Argument).getCall().getCallee().hasName("sink") } +} + +module Flow = TaintTracking::Global; + +from DataFlow::Node source, DataFlow::Node sink +where Flow::flow(source, sink) +select source, sink diff --git a/java/ql/test-kotlin2/library-tests/collection-literal-operators/test.qlref b/java/ql/test-kotlin2/library-tests/collection-literal-operators/test.qlref new file mode 100644 index 000000000000..b79433a5b459 --- /dev/null +++ b/java/ql/test-kotlin2/library-tests/collection-literal-operators/test.qlref @@ -0,0 +1 @@ +test.ql From 7015661ed7b4f267861a89d3f04163f1395141fb Mon Sep 17 00:00:00 2001 From: Anders Fugmann Date: Fri, 28 Aug 2026 14:53:25 +0200 Subject: [PATCH 15/26] Kotlin: cover companion blocks and extensions Add focused coverage for a companion-block function and companion extension function and property. Check declaration ownership, property accessors, resolved calls, and data flow. The existing extractor handles the lowered declarations and calls correctly. This commit adds coverage only and requires no extractor change. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../companion-extensions/test.expected | 11 +++++ .../companion-extensions/test.kt | 21 +++++++++ .../companion-extensions/test.ql | 43 +++++++++++++++++++ .../companion-extensions/test.qlref | 1 + 4 files changed, 76 insertions(+) create mode 100644 java/ql/test-kotlin2/library-tests/companion-extensions/test.expected create mode 100644 java/ql/test-kotlin2/library-tests/companion-extensions/test.kt create mode 100644 java/ql/test-kotlin2/library-tests/companion-extensions/test.ql create mode 100644 java/ql/test-kotlin2/library-tests/companion-extensions/test.qlref diff --git a/java/ql/test-kotlin2/library-tests/companion-extensions/test.expected b/java/ql/test-kotlin2/library-tests/companion-extensions/test.expected new file mode 100644 index 000000000000..0eed1f5dced2 --- /dev/null +++ b/java/ql/test-kotlin2/library-tests/companion-extensions/test.expected @@ -0,0 +1,11 @@ +declarations +| test.kt:5:9:5:29 | empty | test.kt:3:1:7:1 | Box | Method | empty() | +| test.kt:9:11:9:52 | create | test.kt:0:0:0:0 | TestKt | Method | create(java.lang.String) | +| test.kt:12:5:12:19 | getDefault | test.kt:0:0:0:0 | TestKt | Method | getDefault() | +calls +| test.kt:19:14:19:29 | create(...) | test.kt:18:1:21:1 | test | test.kt:9:11:9:52 | create | test.kt:19:14:19:29 | TestKt | +| test.kt:20:14:20:20 | getDefault(...) | test.kt:18:1:21:1 | test | test.kt:12:5:12:19 | getDefault | test.kt:20:14:20:20 | TestKt | +properties +| test.kt:11:11:12:19 | default | test.kt:0:0:0:0 | TestKt | test.kt:12:5:12:19 | getDefault | +#select +| test.kt:19:21:19:28 | source(...) | test.kt:19:14:19:35 | getValue(...) | diff --git a/java/ql/test-kotlin2/library-tests/companion-extensions/test.kt b/java/ql/test-kotlin2/library-tests/companion-extensions/test.kt new file mode 100644 index 000000000000..db79f5f9d000 --- /dev/null +++ b/java/ql/test-kotlin2/library-tests/companion-extensions/test.kt @@ -0,0 +1,21 @@ +// codeql-extractor-kotlin-options: -language-version 2.4 -Xcompanion-blocks-and-extensions + +class Box(val value: String) { + companion { + fun empty() = Box("") + } +} + +companion fun Box.create(value: String) = Box(value) + +companion val Box.default: Box + get() = Box("") + +fun source(): String = "" + +fun sink(value: String) {} + +fun test() { + sink(Box.create(source()).value) + sink(Box.default.value) +} diff --git a/java/ql/test-kotlin2/library-tests/companion-extensions/test.ql b/java/ql/test-kotlin2/library-tests/companion-extensions/test.ql new file mode 100644 index 000000000000..f28b76761c02 --- /dev/null +++ b/java/ql/test-kotlin2/library-tests/companion-extensions/test.ql @@ -0,0 +1,43 @@ +import java +import semmle.code.java.dataflow.TaintTracking + +predicate isCompanionCallable(Callable callable) { + callable.getName() = ["empty", "create", "getDefault"] +} + +query predicate declarations( + Callable callable, RefType declaringType, string primaryClass, string signature +) { + isCompanionCallable(callable) and + callable.fromSource() and + declaringType = callable.getDeclaringType() and + primaryClass = callable.getAPrimaryQlClass() and + signature = callable.getSignature() +} + +query predicate calls(MethodCall call, Callable caller, Method target, Expr qualifier) { + caller.fromSource() and + call.getEnclosingCallable() = caller and + target = call.getMethod() and + isCompanionCallable(target) and + qualifier = call.getQualifier() +} + +query predicate properties(Property property, RefType declaringType, Method getter) { + property.hasName("default") and + property.fromSource() and + getter = property.getGetter() and + declaringType = getter.getDeclaringType() +} + +module Config implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node n) { n.asExpr().(MethodCall).getMethod().hasName("source") } + + predicate isSink(DataFlow::Node n) { n.asExpr().(Argument).getCall().getCallee().hasName("sink") } +} + +module Flow = TaintTracking::Global; + +from DataFlow::Node source, DataFlow::Node sink +where Flow::flow(source, sink) +select source, sink diff --git a/java/ql/test-kotlin2/library-tests/companion-extensions/test.qlref b/java/ql/test-kotlin2/library-tests/companion-extensions/test.qlref new file mode 100644 index 000000000000..b79433a5b459 --- /dev/null +++ b/java/ql/test-kotlin2/library-tests/companion-extensions/test.qlref @@ -0,0 +1 @@ +test.ql From dedba973801b7abf8c7a15d2bf5be33a73e7057d Mon Sep 17 00:00:00 2001 From: Anders Fugmann Date: Fri, 28 Aug 2026 14:55:51 +0200 Subject: [PATCH 16/26] Kotlin: cover invokedynamic when extraction Add a JVM 21 regression test for when generation using invokedynamic. The same expected source AST also passes with inline when generation. The backend choice does not change the IR observed by the extractor. This commit adds coverage only and requires no extractor change. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../library-tests/when-indy/PrintAst.expected | 38 +++++++++++++++++++ .../library-tests/when-indy/PrintAst.qlref | 1 + .../library-tests/when-indy/test.kt | 8 ++++ 3 files changed, 47 insertions(+) create mode 100644 java/ql/test-kotlin2/library-tests/when-indy/PrintAst.expected create mode 100644 java/ql/test-kotlin2/library-tests/when-indy/PrintAst.qlref create mode 100644 java/ql/test-kotlin2/library-tests/when-indy/test.kt diff --git a/java/ql/test-kotlin2/library-tests/when-indy/PrintAst.expected b/java/ql/test-kotlin2/library-tests/when-indy/PrintAst.expected new file mode 100644 index 000000000000..9ba7f92457e4 --- /dev/null +++ b/java/ql/test-kotlin2/library-tests/when-indy/PrintAst.expected @@ -0,0 +1,38 @@ +test.kt: +# 0| [CompilationUnit] test +# 0| 1: [Class] TestKt +# 3| 1: [Method] classify +# 3| 3: [TypeAccess] int +#-----| 4: (Parameters) +# 3| 0: [Parameter] value +# 3| 0: [TypeAccess] Object +# 4| 5: [BlockStmt] { ... } +# 8| 0: [ReturnStmt] return ... +# 4| 0: [StmtExpr] +# 4| 0: [BlockStmt] { ... } +# 4| 0: [LocalVariableDeclStmt] var ...; +# 4| 1: [LocalVariableDeclExpr] tmp0_subject +# 4| 0: [VarAccess] value +# 4| 1: [ExprStmt] ; +# 4| 0: [WhenExpr] when ... +# 5| 0: [WhenBranch] ... -> ... +# 5| 0: [InstanceOfExpr] ...instanceof... +# 5| 0: [VarAccess] tmp0_subject +# 5| 1: [TypeAccess] String +# 5| 1: [ExprStmt] ; +# 5| 0: [MethodCall] length(...) +# 5| -1: [ImplicitCastExpr] +# 5| 0: [TypeAccess] String +# 5| 1: [VarAccess] value +# 6| 1: [WhenBranch] ... -> ... +# 6| 0: [InstanceOfExpr] ...instanceof... +# 6| 0: [VarAccess] tmp0_subject +# 6| 1: [TypeAccess] int +# 6| 1: [ExprStmt] ; +# 6| 0: [ImplicitCastExpr] +# 6| 0: [TypeAccess] int +# 6| 1: [VarAccess] value +# 7| 2: [WhenBranch] ... -> ... +# 7| 0: [BooleanLiteral] true +# 7| 1: [ExprStmt] ; +# 7| 0: [IntegerLiteral] -1 diff --git a/java/ql/test-kotlin2/library-tests/when-indy/PrintAst.qlref b/java/ql/test-kotlin2/library-tests/when-indy/PrintAst.qlref new file mode 100644 index 000000000000..f391eb5e4636 --- /dev/null +++ b/java/ql/test-kotlin2/library-tests/when-indy/PrintAst.qlref @@ -0,0 +1 @@ +semmle/code/java/PrintAst.ql diff --git a/java/ql/test-kotlin2/library-tests/when-indy/test.kt b/java/ql/test-kotlin2/library-tests/when-indy/test.kt new file mode 100644 index 000000000000..4ca9734be110 --- /dev/null +++ b/java/ql/test-kotlin2/library-tests/when-indy/test.kt @@ -0,0 +1,8 @@ +// codeql-extractor-kotlin-options: -language-version 2.4 -jvm-target 21 -Xwhen-expressions=indy + +fun classify(value: Any): Int = + when (value) { + is String -> value.length + is Int -> value + else -> -1 + } From aa583f8cd4c2bd930692ef485c3d9296701e2f93 Mon Sep 17 00:00:00 2001 From: Anders Fugmann Date: Fri, 28 Aug 2026 15:09:10 +0200 Subject: [PATCH 17/26] Kotlin: keep full value class coverage focused Replace the secondary constructor's Long conversion with a String overload. This preserves coverage for non-trivial value-class construction without exercising an unrelated primitive-conversion diagnostic. This is a test-only refinement and does not change extractor behaviour. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../library-tests/full-value-classes/test.expected | 6 +++--- .../test-kotlin2/library-tests/full-value-classes/test.kt | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/java/ql/test-kotlin2/library-tests/full-value-classes/test.expected b/java/ql/test-kotlin2/library-tests/full-value-classes/test.expected index 5a4f96bdaee8..b72a61ccbe92 100644 --- a/java/ql/test-kotlin2/library-tests/full-value-classes/test.expected +++ b/java/ql/test-kotlin2/library-tests/full-value-classes/test.expected @@ -10,8 +10,8 @@ properties constructors | test.kt:3:1:5:1 | Base | Base() | | test.kt:7:22:7:65 | PairValue | PairValue(int,java.lang.String) | -| test.kt:8:5:8:68 | PairValue | PairValue(long) | +| test.kt:8:5:8:58 | PairValue | PairValue(java.lang.String) | constructorCalls | test.kt:7:1:9:1 | super(...) | test.kt:3:1:5:1 | Base | -| test.kt:8:32:8:68 | this(...) | test.kt:7:22:7:65 | PairValue | -| test.kt:11:40:11:55 | new PairValue(...) | test.kt:8:5:8:68 | PairValue | +| test.kt:8:34:8:58 | this(...) | test.kt:7:22:7:65 | PairValue | +| test.kt:11:42:11:57 | new PairValue(...) | test.kt:8:5:8:58 | PairValue | diff --git a/java/ql/test-kotlin2/library-tests/full-value-classes/test.kt b/java/ql/test-kotlin2/library-tests/full-value-classes/test.kt index 50f8209367a8..52c8163da134 100644 --- a/java/ql/test-kotlin2/library-tests/full-value-classes/test.kt +++ b/java/ql/test-kotlin2/library-tests/full-value-classes/test.kt @@ -5,7 +5,7 @@ abstract value class Base { } value class PairValue(override val value: Int, val label: String) : Base() { - constructor(value: Long) : this(value.toInt(), value.toString()) + constructor(value: String) : this(value.length, value) } -fun makePairValue(value: Long): Base = PairValue(value) +fun makePairValue(value: String): Base = PairValue(value) From 701a005d0d81b92fe586e269c4d266f0d0466e8a Mon Sep 17 00:00:00 2001 From: Anders Fugmann Date: Fri, 28 Aug 2026 15:09:10 +0200 Subject: [PATCH 18/26] Kotlin: assign stable context parameter indices Derive Kotlin 2.4 parameter indices from preceding context and regular parameters while reserving the Java parameter slot for an extension receiver. This removes negative context-parameter indices and prevents setter parameter label collisions found by the consistency checks. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../v_2_4_0/parameterIndexExcludingReceivers.kt | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) 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 index 5e9b384b47e5..60d899742158 100644 --- 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 @@ -5,9 +5,15 @@ 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 + if ( + vp.kind == IrParameterKind.DispatchReceiver || + vp.kind == IrParameterKind.ExtensionReceiver + ) { + return -1 + } + return (vp.parent as? IrFunction) + ?.parameters + ?.take(vp.indexInParameters) + ?.count { it.kind == IrParameterKind.Context || it.kind == IrParameterKind.Regular } + ?: vp.indexInParameters } From 01235a25ece3dce7a731e86ee8e4a63aaf2e54aa Mon Sep 17 00:00:00 2001 From: Anders Fugmann Date: Mon, 31 Aug 2026 09:32:31 +0200 Subject: [PATCH 19/26] Kotlin: gate full value class coverage Run the full multi-field value class fixture only with Kotlin 2.4.20-RC2 or later in K2 mode. Earlier supported compilers reject the tested syntax even with the FullValueClasses language feature enabled.\n\nThis is coverage metadata only. Existing extraction remains correct and no extractor change is required.\n\nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../library-tests/full-value-classes/codeql-test.yml | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 java/ql/test-kotlin2/library-tests/full-value-classes/codeql-test.yml diff --git a/java/ql/test-kotlin2/library-tests/full-value-classes/codeql-test.yml b/java/ql/test-kotlin2/library-tests/full-value-classes/codeql-test.yml new file mode 100644 index 000000000000..4e0b2727282d --- /dev/null +++ b/java/ql/test-kotlin2/library-tests/full-value-classes/codeql-test.yml @@ -0,0 +1,3 @@ +kotlin: + minimumVersion: 2.4.20-RC2 + languageMode: K2 From b514ee0d6147841d86f022c10b284f756460f6d4 Mon Sep 17 00:00:00 2001 From: Anders Fugmann Date: Mon, 31 Aug 2026 09:32:39 +0200 Subject: [PATCH 20/26] Kotlin: gate name-based destructuring coverage Run the short-form name-based destructuring fixture only with Kotlin 2.4.20-RC2 or later in K2 mode. Earlier supported compilers reject the tested combination of full value classes and name-based destructuring options.\n\nThis is coverage metadata only. Existing extraction remains correct and no extractor change is required.\n\nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../library-tests/name-based-destructuring/codeql-test.yml | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 java/ql/test-kotlin2/library-tests/name-based-destructuring/codeql-test.yml diff --git a/java/ql/test-kotlin2/library-tests/name-based-destructuring/codeql-test.yml b/java/ql/test-kotlin2/library-tests/name-based-destructuring/codeql-test.yml new file mode 100644 index 000000000000..4e0b2727282d --- /dev/null +++ b/java/ql/test-kotlin2/library-tests/name-based-destructuring/codeql-test.yml @@ -0,0 +1,3 @@ +kotlin: + minimumVersion: 2.4.20-RC2 + languageMode: K2 From 07783714e0838438aeac92d0fdd35233146aad9c Mon Sep 17 00:00:00 2001 From: Anders Fugmann Date: Mon, 31 Aug 2026 09:32:46 +0200 Subject: [PATCH 21/26] Kotlin: gate context parameter coverage Run the function, getter, and setter context-parameter fixture from Kotlin 2.2.20-Beta2 onwards in K2 mode. This is the earliest supported compiler that accepts the exact fixture with -Xcontext-parameters.\n\nThe wider gate makes the existing regression test applicable to earlier compilers so their extraction behaviour can be assessed rather than assuming the fix is specific to Kotlin 2.4.\n\nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../library-tests/context-parameters/codeql-test.yml | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 java/ql/test-kotlin2/library-tests/context-parameters/codeql-test.yml diff --git a/java/ql/test-kotlin2/library-tests/context-parameters/codeql-test.yml b/java/ql/test-kotlin2/library-tests/context-parameters/codeql-test.yml new file mode 100644 index 000000000000..a087b5d33b7d --- /dev/null +++ b/java/ql/test-kotlin2/library-tests/context-parameters/codeql-test.yml @@ -0,0 +1,3 @@ +kotlin: + minimumVersion: 2.2.20-Beta2 + languageMode: K2 From a0cf379cdf9bb483876893c83f01db2ad7d094cc Mon Sep 17 00:00:00 2001 From: Anders Fugmann Date: Mon, 31 Aug 2026 09:32:56 +0200 Subject: [PATCH 22/26] Kotlin: gate collection literal coverage Run the custom collection literal fixture from Kotlin 2.4.0 onwards in K2 mode. Earlier supported compilers reject the tested CollectionLiterals language feature.\n\nThis is coverage metadata only. Existing extraction remains correct and no extractor change is required.\n\nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../library-tests/collection-literal-operators/codeql-test.yml | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 java/ql/test-kotlin2/library-tests/collection-literal-operators/codeql-test.yml diff --git a/java/ql/test-kotlin2/library-tests/collection-literal-operators/codeql-test.yml b/java/ql/test-kotlin2/library-tests/collection-literal-operators/codeql-test.yml new file mode 100644 index 000000000000..d9dcb71c2be8 --- /dev/null +++ b/java/ql/test-kotlin2/library-tests/collection-literal-operators/codeql-test.yml @@ -0,0 +1,3 @@ +kotlin: + minimumVersion: 2.4.0 + languageMode: K2 From 131ef8eb74d523c46ec28701653f0b16707624c0 Mon Sep 17 00:00:00 2001 From: Anders Fugmann Date: Mon, 31 Aug 2026 09:33:05 +0200 Subject: [PATCH 23/26] Kotlin: gate companion extension coverage Run the companion block and companion extension fixture only with Kotlin 2.4.20-RC2 or later in K2 mode. Earlier supported compilers reject -Xcompanion-blocks-and-extensions.\n\nThis is coverage metadata only. Existing extraction remains correct and no extractor change is required.\n\nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../library-tests/companion-extensions/codeql-test.yml | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 java/ql/test-kotlin2/library-tests/companion-extensions/codeql-test.yml diff --git a/java/ql/test-kotlin2/library-tests/companion-extensions/codeql-test.yml b/java/ql/test-kotlin2/library-tests/companion-extensions/codeql-test.yml new file mode 100644 index 000000000000..4e0b2727282d --- /dev/null +++ b/java/ql/test-kotlin2/library-tests/companion-extensions/codeql-test.yml @@ -0,0 +1,3 @@ +kotlin: + minimumVersion: 2.4.20-RC2 + languageMode: K2 From 8630d5af4d9d763e7540aeed239a8c505d56e85b Mon Sep 17 00:00:00 2001 From: Anders Fugmann Date: Mon, 31 Aug 2026 09:33:13 +0200 Subject: [PATCH 24/26] Kotlin: gate invokedynamic when coverage Run the invokedynamic when-expression fixture from Kotlin 2.2.20-Beta2 onwards in K2 mode, and require Java 21 for its configured JVM target. Earlier supported compilers reject the tested backend option.\n\nThis is coverage metadata only. Existing source-level extraction remains correct and no extractor change is required.\n\nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- java/ql/test-kotlin2/library-tests/when-indy/codeql-test.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 java/ql/test-kotlin2/library-tests/when-indy/codeql-test.yml diff --git a/java/ql/test-kotlin2/library-tests/when-indy/codeql-test.yml b/java/ql/test-kotlin2/library-tests/when-indy/codeql-test.yml new file mode 100644 index 000000000000..027fb12c8523 --- /dev/null +++ b/java/ql/test-kotlin2/library-tests/when-indy/codeql-test.yml @@ -0,0 +1,4 @@ +kotlin: + minimumVersion: 2.2.20-Beta2 + languageMode: K2 + minimumJavaVersion: 21 From b0fab01ec33a10c8e262df779bbb558b786e1092 Mon Sep 17 00:00:00 2001 From: Anders Fugmann Date: Mon, 31 Aug 2026 09:44:01 +0200 Subject: [PATCH 25/26] Kotlin: extract context parameters from 2.2.20 Use the unified parameter and argument layout introduced in Kotlin 2.2.20-Beta2 so context parameters and their implicit call arguments are included before the old accessors disappear in Kotlin 2.4.\n\nAssign parameter indices after excluding receiver slots, and give compiler-generated context arguments a stable whole-file location when the earlier compiler supplies inconsistent offsets. This fixes the version-gated function, getter, and setter regression test without changing its expected output. Earlier compilers retain the legacy compatibility implementation because they do not accept the tested context-parameter syntax.\n\nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../src/main/kotlin/KotlinFileExtractor.kt | 87 ++++++++++++++----- .../v_1_8_0/isCodeQlContextParameter.kt | 5 ++ .../utils/versions/v_2_2_20-Beta2/IrCompat.kt | 82 +++++++++++++++++ .../isCodeQlContextParameter.kt | 6 ++ .../parameterIndexExcludingReceivers.kt | 19 ++++ 5 files changed, 178 insertions(+), 21 deletions(-) create mode 100644 java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_8_0/isCodeQlContextParameter.kt create mode 100644 java/kotlin-extractor/src/main/kotlin/utils/versions/v_2_2_20-Beta2/IrCompat.kt create mode 100644 java/kotlin-extractor/src/main/kotlin/utils/versions/v_2_2_20-Beta2/isCodeQlContextParameter.kt create mode 100644 java/kotlin-extractor/src/main/kotlin/utils/versions/v_2_2_20-Beta2/parameterIndexExcludingReceivers.kt diff --git a/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt b/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt index bef2b554d7ec..a6c66523e89c 100644 --- a/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt +++ b/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt @@ -3590,7 +3590,9 @@ open class KotlinFileExtractor( valueArguments, enclosingStmt, enclosingCallable, - idxOffset + idxOffset, + valueParameters = syntacticCallTarget.codeQlValueParameters, + invalidArgumentLocation = tw.getWholeFileLocation() ) }, dispatchReceiver?.type, @@ -3804,14 +3806,17 @@ open class KotlinFileExtractor( enclosingStmt: Label, enclosingCallable: Label, idxOffset: Int - ) = + ) { extractCallValueArguments( callId, (0 until call.codeQlValueArgumentsCount).map { call.codeQlGetValueArgument(it) }, enclosingStmt, enclosingCallable, - idxOffset + idxOffset, + valueParameters = call.symbol.owner.codeQlValueParameters, + invalidArgumentLocation = tw.getWholeFileLocation() ) + } private fun extractCallValueArguments( callId: Label, @@ -3819,35 +3824,75 @@ open class KotlinFileExtractor( enclosingStmt: Label, enclosingCallable: Label, idxOffset: Int, - extractVarargAsArray: Boolean = false + extractVarargAsArray: Boolean = false, + valueParameters: List? = null, + invalidArgumentLocation: Label? = null ) { var i = 0 - valueArguments.forEach { arg -> + valueArguments.forEachIndexed { argumentIndex, arg -> if (arg != null) { - if (arg is IrVararg && !extractVarargAsArray) { - arg.elements.forEachIndexed { varargNo, vararg -> - extractVarargElement( - vararg, - enclosingCallable, - callId, - i + idxOffset + varargNo, - enclosingStmt - ) - } - i += arg.elements.size - } else { - extractExpressionExpr( - arg, - enclosingCallable, + val parameter = valueParameters?.getOrNull(argumentIndex) + if ( + parameter?.isCodeQlContextParameter() == true && + arg is IrGetValue && + (arg.startOffset < 0 || arg.endOffset < 0) && + invalidArgumentLocation != null + ) { + extractVariableAccess( + useValueDeclaration(arg.symbol.owner), + arg.type, + invalidArgumentLocation, callId, - (i++) + idxOffset, + i++ + idxOffset, + enclosingCallable, enclosingStmt ) + } else { + i += + extractCallValueArgument( + callId, + arg, + enclosingStmt, + enclosingCallable, + i + idxOffset, + extractVarargAsArray + ) } } } } + private fun extractCallValueArgument( + callId: Label, + argument: IrExpression, + enclosingStmt: Label, + enclosingCallable: Label, + outputIndex: Int, + extractVarargAsArray: Boolean = false + ): Int { + if (argument is IrVararg && !extractVarargAsArray) { + argument.elements.forEachIndexed { varargIndex, element -> + extractVarargElement( + element, + enclosingCallable, + callId, + outputIndex + varargIndex, + enclosingStmt + ) + } + return argument.elements.size + } + + extractExpressionExpr( + argument, + enclosingCallable, + callId, + outputIndex, + enclosingStmt + ) + return 1 + } + private fun findFunction(cls: IrClass, name: String): IrFunction? = cls.declarations.findSubType { it.name.asString() == name } diff --git a/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_8_0/isCodeQlContextParameter.kt b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_8_0/isCodeQlContextParameter.kt new file mode 100644 index 000000000000..fb2e82ede34b --- /dev/null +++ b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_8_0/isCodeQlContextParameter.kt @@ -0,0 +1,5 @@ +package com.github.codeql.utils.versions + +import org.jetbrains.kotlin.ir.declarations.IrValueParameter + +fun IrValueParameter.isCodeQlContextParameter() = false diff --git a/java/kotlin-extractor/src/main/kotlin/utils/versions/v_2_2_20-Beta2/IrCompat.kt b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_2_2_20-Beta2/IrCompat.kt new file mode 100644 index 000000000000..7f157bcb5e14 --- /dev/null +++ b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_2_2_20-Beta2/IrCompat.kt @@ -0,0 +1,82 @@ +package com.github.codeql.utils.versions + +import org.jetbrains.kotlin.ir.declarations.IrFunction +import org.jetbrains.kotlin.ir.declarations.IrMutableAnnotationContainer +import org.jetbrains.kotlin.ir.declarations.IrParameterKind +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.IrConstructorCallImpl +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 + +private fun IrParameterKind.isCodeQlValueParameter() = + this == IrParameterKind.Context || this == IrParameterKind.Regular + +val IrFunction.codeQlValueParameters: List + get() = parameters.filter { it.kind.isCodeQlValueParameter() } + +val IrFunction.codeQlExtensionReceiverParameter: IrValueParameter? + get() = extensionReceiverParameter + +private fun IrMemberAccessExpression<*>.valueArgumentIndices(): List { + val owner = symbol.owner as? IrFunction ?: return arguments.indices.toList() + return owner.parameters.mapIndexedNotNull { index, parameter -> + index.takeIf { parameter.kind.isCodeQlValueParameter() } + } +} + +val IrMemberAccessExpression<*>.codeQlValueArgumentsCount: Int + get() = valueArgumentIndices().size + +fun IrMemberAccessExpression<*>.codeQlGetValueArgument(index: Int): IrExpression? = + arguments[valueArgumentIndices()[index]] + +fun IrMemberAccessExpression<*>.codeQlPutValueArgument(index: Int, value: IrExpression?) { + arguments[valueArgumentIndices()[index]] = value +} + +val IrMemberAccessExpression<*>.codeQlExtensionReceiver: IrExpression? + get() = extensionReceiver + +val IrMemberAccessExpression<*>.codeQlTypeArgumentsCount: Int + get() = typeArgumentsCount + +fun IrMemberAccessExpression<*>.codeQlGetTypeArgument(index: Int): IrType? = getTypeArgument(index) + +fun IrType.codeQlAddAnnotations(annotations: List): IrType = + addAnnotations(annotations) + +fun codeQlSetAnnotations( + container: IrMutableAnnotationContainer, + annotations: List +) { + container.annotations = annotations +} + +fun IrFunction.codeQlSetDispatchReceiverParameter(param: IrValueParameter?) { + dispatchReceiverParameter = param +} + +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_2_20-Beta2/isCodeQlContextParameter.kt b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_2_2_20-Beta2/isCodeQlContextParameter.kt new file mode 100644 index 000000000000..ae4900d722e5 --- /dev/null +++ b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_2_2_20-Beta2/isCodeQlContextParameter.kt @@ -0,0 +1,6 @@ +package com.github.codeql.utils.versions + +import org.jetbrains.kotlin.ir.declarations.IrParameterKind +import org.jetbrains.kotlin.ir.declarations.IrValueParameter + +fun IrValueParameter.isCodeQlContextParameter() = kind == IrParameterKind.Context diff --git a/java/kotlin-extractor/src/main/kotlin/utils/versions/v_2_2_20-Beta2/parameterIndexExcludingReceivers.kt b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_2_2_20-Beta2/parameterIndexExcludingReceivers.kt new file mode 100644 index 000000000000..60d899742158 --- /dev/null +++ b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_2_2_20-Beta2/parameterIndexExcludingReceivers.kt @@ -0,0 +1,19 @@ +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 { + if ( + vp.kind == IrParameterKind.DispatchReceiver || + vp.kind == IrParameterKind.ExtensionReceiver + ) { + return -1 + } + return (vp.parent as? IrFunction) + ?.parameters + ?.take(vp.indexInParameters) + ?.count { it.kind == IrParameterKind.Context || it.kind == IrParameterKind.Regular } + ?: vp.indexInParameters +} From 44f0616d07c6966c40e2c765552fad2502241f0e Mon Sep 17 00:00:00 2001 From: Anders Fugmann Date: Mon, 31 Aug 2026 09:48:06 +0200 Subject: [PATCH 26/26] Kotlin: stabilise implicit context argument locations Use the shared whole-file location for implicit context arguments across Kotlin 2.2.20-Beta2 through 2.4.20-RC2. Kotlin 2.3.20 reports broad source ranges for these generated reads while other versions report synthetic offsets, although the extracted argument semantics are identical.\n\nNormalising only implicit arguments to context parameters keeps one focused expected result across supported compilers without weakening the assertions or changing unrelated expression locations.\n\nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../src/main/kotlin/KotlinFileExtractor.kt | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt b/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt index a6c66523e89c..808278a55031 100644 --- a/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt +++ b/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt @@ -3592,7 +3592,7 @@ open class KotlinFileExtractor( enclosingCallable, idxOffset, valueParameters = syntacticCallTarget.codeQlValueParameters, - invalidArgumentLocation = tw.getWholeFileLocation() + contextArgumentLocation = tw.getWholeFileLocation() ) }, dispatchReceiver?.type, @@ -3814,7 +3814,7 @@ open class KotlinFileExtractor( enclosingCallable, idxOffset, valueParameters = call.symbol.owner.codeQlValueParameters, - invalidArgumentLocation = tw.getWholeFileLocation() + contextArgumentLocation = tw.getWholeFileLocation() ) } @@ -3826,7 +3826,7 @@ open class KotlinFileExtractor( idxOffset: Int, extractVarargAsArray: Boolean = false, valueParameters: List? = null, - invalidArgumentLocation: Label? = null + contextArgumentLocation: Label? = null ) { var i = 0 valueArguments.forEachIndexed { argumentIndex, arg -> @@ -3835,13 +3835,12 @@ open class KotlinFileExtractor( if ( parameter?.isCodeQlContextParameter() == true && arg is IrGetValue && - (arg.startOffset < 0 || arg.endOffset < 0) && - invalidArgumentLocation != null + contextArgumentLocation != null ) { extractVariableAccess( useValueDeclaration(arg.symbol.owner), arg.type, - invalidArgumentLocation, + contextArgumentLocation, callId, i++ + idxOffset, enclosingCallable,