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/docs/codeql/reusables/supported-versions-compilers.rst b/docs/codeql/reusables/supported-versions-compilers.rst index b73c9d7e6e97..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.3.2\ *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/BUILD.bazel b/java/kotlin-extractor/BUILD.bazel index a4356af1835b..0eef645cba0c 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,32 @@ 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/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/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..9ec97ed24ccd 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 @@ -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, @@ -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) } @@ -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, @@ -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..e215b5ca31da 100644 --- a/java/kotlin-extractor/src/main/kotlin/MetaAnnotationSupport.kt +++ b/java/kotlin-extractor/src/main/kotlin/MetaAnnotationSupport.kt @@ -1,5 +1,10 @@ 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 +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 +100,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) @@ -117,12 +122,12 @@ class MetaAnnotationSupport( ) return null } else { - return IrConstructorCallImpl.fromSymbolOwner( + return codeQlAnnotationFromSymbolOwner( containerClass.defaultType, containerConstructor.symbol ) .apply { - putValueArgument( + codeQlPutValueArgument( 0, IrVarargImpl( UNDEFINED_OFFSET, @@ -144,7 +149,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()) } @@ -230,14 +235,14 @@ class MetaAnnotationSupport( ) } - return IrConstructorCallImpl.fromSymbolOwner( + return codeQlAnnotationFromSymbolOwner( UNDEFINED_OFFSET, UNDEFINED_OFFSET, targetConstructor.returnType, targetConstructor.symbol, 0 ) - .apply { putValueArgument(0, vararg) } + .apply { codeQlPutValueArgument(0, vararg) } } private val javaAnnotationRetention by lazy { @@ -263,7 +268,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()) @@ -283,7 +288,7 @@ class MetaAnnotationSupport( val targetConstructor = retentionType.declarations.firstIsInstanceOrNull() ?: return null - return IrConstructorCallImpl.fromSymbolOwner( + return codeQlAnnotationFromSymbolOwner( UNDEFINED_OFFSET, UNDEFINED_OFFSET, targetConstructor.returnType, @@ -291,7 +296,7 @@ class MetaAnnotationSupport( 0 ) .apply { - putValueArgument( + codeQlPutValueArgument( 0, IrGetEnumValueImpl( UNDEFINED_OFFSET, @@ -333,7 +338,7 @@ class MetaAnnotationSupport( return } val newParam = thisReceiever.copyTo(this) - dispatchReceiverParameter = newParam + codeQlSetDispatchReceiverParameter(newParam) body = factory .createBlockBody(UNDEFINED_OFFSET, UNDEFINED_OFFSET) @@ -406,7 +411,7 @@ class MetaAnnotationSupport( val repeatableContainerAnnotation = kotlinAnnotationRepeatableContainer?.constructors?.single() - containerClass.annotations = + codeQlSetAnnotations(containerClass, annotationClass.annotations .filter { it.isAnnotationWithEqualFqName(StandardNames.FqNames.retention) || @@ -415,7 +420,7 @@ class MetaAnnotationSupport( .map { it.deepCopyWithSymbols(containerClass) } + listOfNotNull( repeatableContainerAnnotation?.let { - IrConstructorCallImpl.fromSymbolOwner( + codeQlAnnotationFromSymbolOwner( UNDEFINED_OFFSET, UNDEFINED_OFFSET, it.returnType, @@ -424,6 +429,7 @@ class MetaAnnotationSupport( ) } ) + ) containerClass } @@ -462,14 +468,14 @@ class MetaAnnotationSupport( containerClass.symbol, containerClass.defaultType ) - return IrConstructorCallImpl.fromSymbolOwner( + return codeQlAnnotationFromSymbolOwner( UNDEFINED_OFFSET, UNDEFINED_OFFSET, repeatableConstructor.returnType, repeatableConstructor.symbol, 0 ) - .apply { putValueArgument(0, containerReference) } + .apply { codeQlPutValueArgument(0, containerReference) } } private val javaAnnotationDocumented by lazy { @@ -488,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/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..c990edc213f6 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 @@ -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) } } @@ -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..3ba3db2696f7 --- /dev/null +++ b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_8_0/IrCompat.kt @@ -0,0 +1,71 @@ +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.expressions.impl.* +import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol +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 +} + +// 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_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..d836eb3aecbe --- /dev/null +++ b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_2_4_0/IrCompat.kt @@ -0,0 +1,125 @@ +@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.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 + +/** + * 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 } + +// 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 - valueArgumentOffset() + +// IrMemberAccessExpression: getValueArgument +fun IrMemberAccessExpression<*>.codeQlGetValueArgument(index: Int): IrExpression? = arguments[index + valueArgumentOffset()] + +// IrMemberAccessExpression: putValueArgument +fun IrMemberAccessExpression<*>.codeQlPutValueArgument(index: Int, value: IrExpression?) { + arguments[index + valueArgumentOffset()] = value +} + +// 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 = extensionReceiverParameterIndex() ?: return null + return arguments[erp] + } + set(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 + +// IrMemberAccessExpression: getTypeArgument +fun IrMemberAccessExpression<*>.codeQlGetTypeArgument(index: Int): IrType? = typeArguments[index] + +// addAnnotations compat: in 2.4.0, addAnnotations expects List +// IrConstructorCall implements IrAnnotation in 2.4.0, so filterIsInstance is identity +fun IrType.codeQlAddAnnotations(annotations: List): IrType = + addAnnotations(annotations.filterIsInstance()) + +// IrMutableAnnotationContainer.annotations setter: in 2.4.0, expects List +fun codeQlSetAnnotations(container: org.jetbrains.kotlin.ir.declarations.IrMutableAnnotationContainer, annotations: List) { + container.annotations = annotations.filterIsInstance() +} + +// 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 +} + +// 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) + 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..0ee06810f8a6 --- /dev/null +++ b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_2_4_0/Kotlin2ComponentRegistrar.kt @@ -0,0 +1,46 @@ +@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(), 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 + 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 diff --git a/java/kotlin-extractor/versions.bzl b/java/kotlin-extractor/versions.bzl index cea5d6490255..de2d9e535a60 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): @@ -23,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/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/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", ] ) 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. 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..0d38db5b91ae --- /dev/null +++ b/java/ql/lib/change-notes/2026-06-08-kotlin-drop-1x.md @@ -0,0 +1,4 @@ +--- +category: deprecated +--- +* Kotlin versions below 2.0.0 are no longer supported for analysis. The minimum supported version is now Kotlin 2.0.0.