Compare commits

..

17 Commits

Author SHA1 Message Date
idrissrio
e3d0cebe81 C/C++ overlay: avoid massive cartesian product in overlay discardElement 2026-06-04 13:00:45 +02:00
Sotiris Dragonas
d6892eaf0d Merge pull request #21900 from github/bazookamusic/range-analysis-bound-move-to-shared
Bound.qll - Replace utility for range analysis duplicate across java and cs with shared file
2026-06-04 12:45:11 +02:00
BazookaMusic
d2972cb53f Add back alias for module 2026-06-04 11:08:49 +02:00
BazookaMusic
f34275636c No duplicate Ssa and remove release changenot 2026-06-03 11:54:24 +02:00
BazookaMusic
0a801440b9 review comments 2026-06-03 10:48:50 +02:00
Sotiris Dragonas
61a5cece56 Merge branch 'main' into bazookamusic/range-analysis-bound-move-to-shared 2026-06-02 10:41:49 +02:00
BazookaMusic
566a92e555 formatting again 2026-06-02 10:41:10 +02:00
BazookaMusic
2a3cff382c more specific comment 2026-06-01 18:20:50 +02:00
BazookaMusic
c610af88d3 fix comment and add overlay[local?] 2026-06-01 18:18:37 +02:00
BazookaMusic
fa63dad1d1 change note 2026-06-01 18:16:51 +02:00
Sotiris Dragonas
019a5c01ad Merge branch 'main' into bazookamusic/range-analysis-bound-move-to-shared 2026-06-01 18:10:02 +02:00
BazookaMusic
c1c9287535 restore file header 2026-06-01 15:48:26 +02:00
BazookaMusic
d1226b71de formatting 2026-06-01 15:46:52 +02:00
BazookaMusic
71a363545a formatting 2026-06-01 15:24:06 +02:00
BazookaMusic
cc12740c0e remove check for files in sync 2026-05-27 17:41:44 +02:00
BazookaMusic
acb5c0e70f missed changes 2026-05-27 17:23:45 +02:00
BazookaMusic
6042adebae move identical java and cs bound.qll to shared library 2026-05-27 17:23:28 +02:00
52 changed files with 366 additions and 760 deletions

View File

@@ -237,6 +237,9 @@ use_repo(
kotlin_extractor_deps,
"codeql_kotlin_defaults",
"codeql_kotlin_embeddable",
"kotlin-compiler-1.8.0",
"kotlin-compiler-1.9.0-Beta",
"kotlin-compiler-1.9.20-Beta",
"kotlin-compiler-2.0.0-RC1",
"kotlin-compiler-2.0.20-Beta2",
"kotlin-compiler-2.1.0-Beta1",
@@ -245,7 +248,9 @@ 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",
"kotlin-compiler-embeddable-2.0.0-RC1",
"kotlin-compiler-embeddable-2.0.20-Beta2",
"kotlin-compiler-embeddable-2.1.0-Beta1",
@@ -254,7 +259,9 @@ 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",
"kotlin-stdlib-2.0.0-RC1",
"kotlin-stdlib-2.0.20-Beta2",
"kotlin-stdlib-2.1.0-Beta1",
@@ -263,7 +270,6 @@ 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")

View File

@@ -11,10 +11,6 @@
"java/ql/lib/semmle/code/java/dataflow/internal/rangeanalysis/SignAnalysisCommon.qll",
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/SignAnalysisCommon.qll"
],
"Bound Java/C#": [
"java/ql/lib/semmle/code/java/dataflow/Bound.qll",
"csharp/ql/lib/semmle/code/csharp/dataflow/Bound.qll"
],
"ModulusAnalysis Java/C#": [
"java/ql/lib/semmle/code/java/dataflow/ModulusAnalysis.qll",
"csharp/ql/lib/semmle/code/csharp/dataflow/ModulusAnalysis.qll"

View File

@@ -61,12 +61,13 @@ private predicate discardElement(@element e) {
// particular, been deleted), or the overlay has redefined the TRAP
// file or tag it is in, or the overlay runner has re-extracted the same
// source file (e.g. because a header it includes has changed).
forall(@trap_or_tag t, string sourceFile |
not exists(@trap_or_tag t |
locallyInTrapOrTag(false, e, t) and
locallyReachableTrapOrTag(false, sourceFile, t)
|
overlayChangedFiles(sourceFile) or
locallyReachableTrapOrTag(true, _, t) or
locallyReachableTrapOrTag(true, sourceFile, _)
not locallyReachableTrapOrTag(true, _, t) and
exists(string sourceFile |
locallyReachableTrapOrTag(false, sourceFile, t) and
not overlayChangedFiles(sourceFile) and
not locallyReachableTrapOrTag(true, sourceFile, _)
)
)
}

View File

@@ -9,6 +9,7 @@ dependencies:
codeql/controlflow: ${workspace}
codeql/dataflow: ${workspace}
codeql/mad: ${workspace}
codeql/rangeanalysis: ${workspace}
codeql/ssa: ${workspace}
codeql/threat-models: ${workspace}
codeql/tutorial: ${workspace}

View File

@@ -4,67 +4,31 @@
overlay[local?]
module;
private import internal.rangeanalysis.BoundSpecific
private import csharp as CS
private import semmle.code.csharp.dataflow.SSA::Ssa
private import semmle.code.csharp.dataflow.internal.rangeanalysis.ConstantUtils as CU
private import semmle.code.csharp.dataflow.internal.rangeanalysis.RangeUtils as RU
private import semmle.code.csharp.dataflow.internal.rangeanalysis.SsaUtils as SU
private import codeql.rangeanalysis.Bound as SharedBound
private newtype TBound =
TBoundZero() or
TBoundSsa(SsaVariable v) { v.getSourceVariable().getType() instanceof IntegralType } or
TBoundExpr(Expr e) {
interestingExprBound(e) and
not exists(SsaVariable v | e = v.getAUse())
}
/** Provides C#-specific definitions for bounds. */
private module BoundDefs implements SharedBound::BoundDefinitions<CS::Location> {
class Type = CS::Type;
/**
* A bound that may be inferred for an expression plus/minus an integer delta.
*/
abstract class Bound extends TBound {
/** Gets a textual representation of this bound. */
abstract string toString();
class SsaVariable = SU::SsaVariable;
/** Gets an expression that equals this bound plus `delta`. */
abstract Expr getExpr(int delta);
class SsaSourceVariable = SourceVariable;
/** Gets an expression that equals this bound. */
Expr getExpr() { result = this.getExpr(0) }
class Expr = CS::ControlFlowNodes::ExprNode;
/** Gets the location of this bound. */
abstract Location getLocation();
class IntegralType = CS::IntegralType;
class ConstantIntegerExpr = CU::ConstantIntegerExpr;
/** Holds if `e` is a bound expression and it is not an SSA variable read. */
predicate interestingExprBound(Expr e) { CU::systemArrayLengthAccess(e.getExpr()) }
}
/**
* The bound that corresponds to the integer 0. This is used to represent all
* integer bounds as bounds are always accompanied by an added integer delta.
*/
class ZeroBound extends Bound, TBoundZero {
override string toString() { result = "0" }
module BoundImpl = SharedBound::Bound<CS::Location, BoundDefs>;
override Expr getExpr(int delta) { result.(ConstantIntegerExpr).getIntValue() = delta }
override Location getLocation() { result.hasLocationInfo("", 0, 0, 0, 0) }
}
/**
* A bound corresponding to the value of an SSA variable.
*/
class SsaBound extends Bound, TBoundSsa {
/** Gets the SSA variable that equals this bound. */
SsaVariable getSsa() { this = TBoundSsa(result) }
override string toString() { result = this.getSsa().toString() }
override Expr getExpr(int delta) { result = this.getSsa().getAUse() and delta = 0 }
override Location getLocation() { result = this.getSsa().getLocation() }
}
/**
* A bound that corresponds to the value of a specific expression that might be
* interesting, but isn't otherwise represented by the value of an SSA variable.
*/
class ExprBound extends Bound, TBoundExpr {
override string toString() { result = this.getExpr().toString() }
override Expr getExpr(int delta) { this = TBoundExpr(result) and delta = 0 }
override Location getLocation() { result = this.getExpr().getLocation() }
}
import BoundImpl

View File

@@ -1,22 +0,0 @@
/**
* Provides C#-specific definitions for bounds.
*/
private import csharp as CS
private import semmle.code.csharp.dataflow.SSA::Ssa as Ssa
private import semmle.code.csharp.dataflow.internal.rangeanalysis.ConstantUtils as CU
private import semmle.code.csharp.dataflow.internal.rangeanalysis.RangeUtils as RU
private import semmle.code.csharp.dataflow.internal.rangeanalysis.SsaUtils as SU
class SsaVariable = SU::SsaVariable;
class Expr = CS::ControlFlowNodes::ExprNode;
class Location = CS::Location;
class IntegralType = CS::IntegralType;
class ConstantIntegerExpr = CU::ConstantIntegerExpr;
/** Holds if `e` is a bound expression and it is not an SSA variable read. */
predicate interestingExprBound(Expr e) { CU::systemArrayLengthAccess(e.getExpr()) }

View File

@@ -21,7 +21,7 @@
Java,"Java 7 to 26 [6]_","javac (OpenJDK and Oracle JDK),
Eclipse compiler for Java (ECJ) [7]_",``.java``
Kotlin,"Kotlin 2.0.0 to 2.4.\ *x*","kotlinc",``.kt``
Kotlin,"Kotlin 1.8.0 to 2.3.2\ *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``"

View File

@@ -64,14 +64,8 @@ _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",
@@ -97,32 +91,19 @@ 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] + (
[_compiler_plugin_registrar_service[0]] if not version_less(v, "2.4.0") else []
),
srcs = [src for src, _ in _resources],
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),

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -27,7 +27,7 @@ import shutil
import io
import os
DEFAULT_VERSION = "2.4.0"
DEFAULT_VERSION = "2.3.20"
def options():

View File

@@ -3,21 +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.config.CompilerConfiguration
class KotlinExtractorComponentRegistrar : Kotlin2ComponentRegistrar() {
override fun doRegisterExtensions(configuration: CompilerConfiguration) {
override fun registerProjectComponents(
project: MockProject,
configuration: CompilerConfiguration
) {
val invocationTrapFile = configuration[KEY_INVOCATION_TRAP_FILE]
if (invocationTrapFile == null) {
throw Exception("Required argument for TRAP invocation file not given")
}
registerExtractorExtension(
// 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(
KotlinExtractorExtension(
invocationTrapFile,
configuration[KEY_CHECK_TRAP_IDENTICAL] ?: false,
configuration[KEY_COMPILATION_STARTTIME],
configuration[KEY_EXIT_AFTER_EXTRACTION] ?: false
)
),
LoadingOrder.LAST,
project
)
}
}

View File

@@ -173,9 +173,9 @@ open class KotlinFileExtractor(
when (d) {
is IrFunction ->
when (d.name.asString()) {
"toString" -> d.codeQlValueParameters.isEmpty()
"hashCode" -> d.codeQlValueParameters.isEmpty()
"equals" -> d.codeQlValueParameters.singleOrNull()?.type?.isNullableAny() ?: false
"toString" -> d.valueParameters.isEmpty()
"hashCode" -> d.valueParameters.isEmpty()
"equals" -> d.valueParameters.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.
codeQlAnnotationFromSymbolOwner(
IrConstructorCallImpl.fromSymbolOwner(
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.codeQlValueArgumentsCount) {
val param = constructorCall.symbol.owner.codeQlValueParameters[i]
for (i in 0 until constructorCall.valueArgumentsCount) {
val param = constructorCall.symbol.owner.valueParameters[i]
val prop =
constructorCall.symbol.owner.parentAsClass.declarations
.filterIsInstance<IrProperty>()
.first { it.name == param.name }
val v = constructorCall.codeQlGetValueArgument(i) ?: param.defaultValue?.expression
val v = constructorCall.getValueArgument(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.codeQlValueParameters.size,
f.valueParameters.size,
{ argParent, idxOffset ->
f.codeQlValueParameters.forEachIndexed { idx, param ->
f.valueParameters.forEachIndexed { idx, param ->
val syntheticParamId = useValueParameter(param, proxyFunctionId)
extractVariableAccess(
syntheticParamId,
@@ -1695,9 +1695,9 @@ open class KotlinFileExtractor(
returnId,
0,
returnId,
f.codeQlValueParameters.size,
f.valueParameters.size,
{ argParentId, idxOffset ->
f.codeQlValueParameters.mapIndexed { idx, param ->
f.valueParameters.mapIndexed { idx, param ->
val syntheticParamId = useValueParameter(param, functionId)
extractVariableAccess(
syntheticParamId,
@@ -1792,7 +1792,7 @@ open class KotlinFileExtractor(
extractBody: Boolean,
extractMethodAndParameterTypeAccesses: Boolean
) {
if (f.codeQlValueParameters.none { it.defaultValue != null }) return
if (f.valueParameters.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.codeQlExtensionReceiverParameter
val extReceiver = f.extensionReceiverParameter
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.codeQlValueParameters
val nonSyntheticParams = listOfNotNull(dispatchReceiver) + f.valueParameters
// 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.codeQlExtensionReceiverParameter).count { it != null }
listOf(dispatchReceiver, f.extensionReceiverParameter).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.codeQlValueParameters.forEachIndexed { paramIdx, param ->
f.valueParameters.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.codeQlValueParameters.forEachIndexed { idx, param ->
f.valueParameters.forEachIndexed { idx, param ->
extractVariableAccess(
tw.getLabelFor<DbParam>(getValueParameterLabel(id, idx)),
param.type,
@@ -2003,9 +2003,9 @@ open class KotlinFileExtractor(
)
.also { thisCallId ->
val realFnIdxOffset =
if (f.codeQlExtensionReceiverParameter != null) 1 else 0
if (f.extensionReceiverParameter != null) 1 else 0
val paramMappings =
f.codeQlValueParameters.mapIndexed { idx, param ->
f.valueParameters.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.codeQlExtensionReceiverParameter?.let { IrGetValueImpl(-1, -1, it.symbol) }
f.extensionReceiverParameter?.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.codeQlValueParameters.isNotEmpty() &&
f.codeQlValueParameters.all { it.defaultValue != null } &&
f.valueParameters.isNotEmpty() &&
f.valueParameters.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.codeQlValueParameters.isEmpty()
d is IrConstructor && d.valueParameters.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.codeQlValueParameters.map { _ -> null })
extractGeneratedOverload(f.valueParameters.map { _ -> null })
}
return
}
val paramList: MutableList<IrValueParameter?> = f.codeQlValueParameters.toMutableList()
for (n in (f.codeQlValueParameters.size - 1) downTo 0) {
if (f.codeQlValueParameters[n].defaultValue != null) {
val paramList: MutableList<IrValueParameter?> = f.valueParameters.toMutableList()
for (n in (f.valueParameters.size - 1) downTo 0) {
if (f.valueParameters[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<IrConstructor>()?.let {
annotationConstructor ->
codeQlAnnotationFromSymbolOwner(
IrConstructorCallImpl.fromSymbolOwner(
UNDEFINED_OFFSET,
UNDEFINED_OFFSET,
annotationConstructor.returnType,
@@ -2388,13 +2388,13 @@ open class KotlinFileExtractor(
id
}
val extReceiver = f.codeQlExtensionReceiverParameter
val extReceiver = f.extensionReceiverParameter
// 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.codeQlValueParameters)
(overriddenAttributes?.valueParameters ?: f.valueParameters)
val paramTypes =
fParameters.mapIndexed { i, vp ->
extractValueParameter(
@@ -3069,14 +3069,14 @@ open class KotlinFileExtractor(
logger.errorElement("Unexpected dispatch receiver found", c)
}
if (c.codeQlValueArgumentsCount < 1) {
if (c.valueArgumentsCount < 1) {
logger.errorElement("No arguments found", c)
return
}
extractArgument(id, c, callable, enclosingStmt, 0, "Operand null")
if (c.codeQlValueArgumentsCount > 1) {
if (c.valueArgumentsCount > 1) {
logger.errorElement("Extra arguments found", c)
}
}
@@ -3095,21 +3095,21 @@ open class KotlinFileExtractor(
logger.errorElement("Unexpected dispatch receiver found", c)
}
if (c.codeQlValueArgumentsCount < 1) {
if (c.valueArgumentsCount < 1) {
logger.errorElement("No arguments found", c)
return
}
extractArgument(id, c, callable, enclosingStmt, 0, "LHS null")
if (c.codeQlValueArgumentsCount < 2) {
if (c.valueArgumentsCount < 2) {
logger.errorElement("No RHS found", c)
return
}
extractArgument(id, c, callable, enclosingStmt, 1, "RHS null")
if (c.codeQlValueArgumentsCount > 2) {
if (c.valueArgumentsCount > 2) {
logger.errorElement("Extra arguments found", c)
}
}
@@ -3122,7 +3122,7 @@ open class KotlinFileExtractor(
idx: Int,
msg: String
) {
val op = c.codeQlGetValueArgument(idx)
val op = c.getValueArgument(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.codeQlExtensionReceiverParameter?.type) +
f.codeQlValueParameters.map { it.type } +
listOfNotNull(f.extensionReceiverParameter?.type) +
f.valueParameters.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.codeQlValueParameters.any { p -> p.defaultValue != null }
it.valueParameters.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.codeQlValueParameters).map { (expr, param) ->
valueArguments.zip(callTarget.valueParameters).map { (expr, param) ->
expr ?: IrConstImpl.defaultValueForType(0, 0, param.type)
}
@@ -3529,7 +3529,7 @@ open class KotlinFileExtractor(
callTarget: IrFunction,
valueArguments: List<IrExpression?>
): Boolean {
val varargParam = callTarget.codeQlValueParameters.withIndex().find { it.value.isVararg }
val varargParam = callTarget.valueParameters.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.codeQlValueArgumentsCount).map { call.codeQlGetValueArgument(it) },
(0 until call.valueArgumentsCount).map { call.getValueArgument(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.codeQlValueParameters
owner.valueParameters
.map { it.type.classFqName?.asString() }
.toTypedArray() contentEquals parameterTypes
}
@@ -3926,8 +3926,8 @@ open class KotlinFileExtractor(
val result =
javaLangString?.declarations?.findSubType<IrFunction> {
it.name.asString() == "valueOf" &&
it.codeQlValueParameters.size == 1 &&
it.codeQlValueParameters[0].type == pluginContext.irBuiltIns.anyNType
it.valueParameters.size == 1 &&
it.valueParameters[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<IrConstructor> {
it.codeQlValueParameters.isEmpty()
it.valueParameters.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.codeQlExtensionReceiverParameter
val extensionReceiverParameter = target.extensionReceiverParameter
val targetClass =
if (extensionReceiverParameter == null) {
if (isNullable == true) {
@@ -4098,8 +4098,8 @@ open class KotlinFileExtractor(
) {
val typeArgs =
if (extractMethodTypeArguments)
(0 until c.codeQlTypeArgumentsCount)
.map { c.codeQlGetTypeArgument(it) }
(0 until c.typeArgumentsCount)
.map { c.getTypeArgument(it) }
.requireNoNullsOrNull()
else listOf()
@@ -4116,9 +4116,9 @@ open class KotlinFileExtractor(
parent,
idx,
enclosingStmt,
(0 until c.codeQlValueArgumentsCount).map { c.codeQlGetValueArgument(it) },
(0 until c.valueArgumentsCount).map { c.getValueArgument(it) },
c.dispatchReceiver,
c.codeQlExtensionReceiver,
c.extensionReceiver,
typeArgs,
extractClassTypeArguments,
c.superQualifierSymbol
@@ -4126,12 +4126,12 @@ open class KotlinFileExtractor(
}
fun extractSpecialEnumFunction(fnName: String) {
if (c.codeQlTypeArgumentsCount != 1) {
if (c.typeArgumentsCount != 1) {
logger.errorElement("Expected to find exactly one type argument", c)
return
}
val enumType = (c.codeQlGetTypeArgument(0) as? IrSimpleType)?.classifier?.owner
val enumType = (c.getTypeArgument(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.codeQlValueArgumentsCount < 1) {
if (c.valueArgumentsCount < 1) {
logger.errorElement("No RHS found", c)
} else {
if (c.codeQlValueArgumentsCount > 1) {
if (c.valueArgumentsCount > 1) {
logger.errorElement("Extra arguments found", c)
}
val arg = c.codeQlGetValueArgument(0)
val arg = c.getValueArgument(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.codeQlValueArgumentsCount > 0) {
if (c.valueArgumentsCount > 0) {
logger.errorElement("Extra arguments found", c)
}
}
@@ -4219,7 +4219,7 @@ open class KotlinFileExtractor(
}
fun binopExt(id: Label<out DbExpr>) {
binopReceiver(id, c.codeQlExtensionReceiver, "Extension receiver")
binopReceiver(id, c.extensionReceiver, "Extension receiver")
}
fun unaryopDisp(id: Label<out DbExpr>) {
@@ -4227,7 +4227,7 @@ open class KotlinFileExtractor(
}
fun unaryopExt(id: Label<out DbExpr>) {
unaryopReceiver(id, c.codeQlExtensionReceiver, "Extension receiver")
unaryopReceiver(id, c.extensionReceiver, "Extension receiver")
}
val dr = c.dispatchReceiver
@@ -4249,7 +4249,7 @@ open class KotlinFileExtractor(
parent,
idx,
enclosingStmt,
listOf(c.codeQlExtensionReceiver, c.codeQlGetValueArgument(0)),
listOf(c.extensionReceiver, c.getValueArgument(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.codeQlValueArgumentsCount == 0 &&
c.valueArgumentsCount == 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.codeQlValueArgumentsCount == 0 &&
c.valueArgumentsCount == 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.codeQlValueArgumentsCount == 0 &&
c.valueArgumentsCount == 0 &&
dr != null &&
dr is IrCall &&
isBuiltinCallInternal(dr, "ieee754equals") -> {
@@ -4576,7 +4576,7 @@ open class KotlinFileExtractor(
parent,
idx,
enclosingStmt,
listOf(c.codeQlExtensionReceiver),
listOf(c.extensionReceiver),
null,
null
)
@@ -4596,8 +4596,8 @@ open class KotlinFileExtractor(
val locId = tw.getLocation(c)
extractExprContext(id, locId, callable, enclosingStmt)
if (c.codeQlTypeArgumentsCount == 1) {
val typeArgument = c.codeQlGetTypeArgument(0)
if (c.typeArgumentsCount == 1) {
val typeArgument = c.getTypeArgument(0)
if (typeArgument == null) {
logger.errorElement("Type argument missing in an arrayOfNulls call", c)
} else {
@@ -4618,8 +4618,8 @@ open class KotlinFileExtractor(
)
}
if (c.codeQlValueArgumentsCount == 1) {
val dim = c.codeQlGetValueArgument(0)
if (c.valueArgumentsCount == 1) {
val dim = c.getValueArgument(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.codeQlTypeArgumentsCount == 1) {
c.codeQlGetTypeArgument(0).also {
if (c.typeArgumentsCount == 1) {
c.getTypeArgument(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.codeQlValueArgumentsCount == 1) c.codeQlGetValueArgument(0)
if (c.valueArgumentsCount == 1) c.getValueArgument(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.codeQlExtensionReceiver
val ext = c.extensionReceiver
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.codeQlGetValueArgument(0)
val assignedValue = c.codeQlGetValueArgument(1)
val arrayIdx = c.getValueArgument(0)
val assignedValue = c.getValueArgument(1)
if (array != null && arrayIdx != null && assignedValue != null) {
@@ -4882,22 +4882,22 @@ open class KotlinFileExtractor(
}
isBuiltinCall(c, "<unsafe-coerce>", "kotlin.jvm.internal") -> {
if (c.codeQlValueArgumentsCount != 1) {
if (c.valueArgumentsCount != 1) {
logger.errorElement(
"Expected to find one argument for a kotlin.jvm.internal.<unsafe-coerce>() call, but found ${c.codeQlValueArgumentsCount}",
"Expected to find one argument for a kotlin.jvm.internal.<unsafe-coerce>() call, but found ${c.valueArgumentsCount}",
c
)
return
}
if (c.codeQlTypeArgumentsCount != 2) {
if (c.typeArgumentsCount != 2) {
logger.errorElement(
"Expected to find two type arguments for a kotlin.jvm.internal.<unsafe-coerce>() call, but found ${c.codeQlTypeArgumentsCount}",
"Expected to find two type arguments for a kotlin.jvm.internal.<unsafe-coerce>() call, but found ${c.typeArgumentsCount}",
c
)
return
}
val valueArg = c.codeQlGetValueArgument(0)
val valueArg = c.getValueArgument(0)
if (valueArg == null) {
logger.errorElement(
"Cannot find value argument for a kotlin.jvm.internal.<unsafe-coerce>() call",
@@ -4905,7 +4905,7 @@ open class KotlinFileExtractor(
)
return
}
val typeArg = c.codeQlGetTypeArgument(1)
val typeArg = c.getTypeArgument(1)
if (typeArg == null) {
logger.errorElement(
"Cannot find type argument for a kotlin.jvm.internal.<unsafe-coerce>() call",
@@ -4924,7 +4924,7 @@ open class KotlinFileExtractor(
extractExpressionExpr(valueArg, callable, id, 1, enclosingStmt)
}
isBuiltinCallInternal(c, "dataClassArrayMemberToString") -> {
val arrayArg = c.codeQlGetValueArgument(0)
val arrayArg = c.getValueArgument(0)
val realArrayClass = arrayArg?.type?.classOrNull
if (realArrayClass == null) {
logger.errorElement(
@@ -4936,8 +4936,8 @@ open class KotlinFileExtractor(
val realCallee =
javaUtilArrays?.declarations?.findSubType<IrFunction> { decl ->
decl.name.asString() == "toString" &&
decl.codeQlValueParameters.size == 1 &&
decl.codeQlValueParameters[0].type.classOrNull?.let {
decl.valueParameters.size == 1 &&
decl.valueParameters[0].type.classOrNull?.let {
it == realArrayClass
} == true
}
@@ -4962,7 +4962,7 @@ open class KotlinFileExtractor(
}
}
isBuiltinCallInternal(c, "dataClassArrayMemberHashCode") -> {
val arrayArg = c.codeQlGetValueArgument(0)
val arrayArg = c.getValueArgument(0)
val realArrayClass = arrayArg?.type?.classOrNull
if (realArrayClass == null) {
logger.errorElement(
@@ -4974,8 +4974,8 @@ open class KotlinFileExtractor(
val realCallee =
javaUtilArrays?.declarations?.findSubType<IrFunction> { decl ->
decl.name.asString() == "hashCode" &&
decl.codeQlValueParameters.size == 1 &&
decl.codeQlValueParameters[0].type.classOrNull?.let {
decl.valueParameters.size == 1 &&
decl.valueParameters[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.codeQlValueArgumentsCount).map { e.codeQlGetValueArgument(it) }
val valueArgs = (0 until e.valueArgumentsCount).map { e.getValueArgument(it) }
val id =
if (
@@ -5211,10 +5211,10 @@ open class KotlinFileExtractor(
realCallTarget is IrConstructor &&
realCallTarget.parentClassOrNull?.fqNameWhenAvailable?.asString() ==
"kotlin.Enum" &&
realCallTarget.codeQlValueParameters.size == 2 &&
realCallTarget.codeQlValueParameters[0].type ==
realCallTarget.valueParameters.size == 2 &&
realCallTarget.valueParameters[0].type ==
pluginContext.irBuiltIns.stringType &&
realCallTarget.codeQlValueParameters[1].type == pluginContext.irBuiltIns.intType
realCallTarget.valueParameters[1].type == pluginContext.irBuiltIns.intType
) {
val id0 =
@@ -5287,7 +5287,7 @@ open class KotlinFileExtractor(
}
val args =
(0 until e.codeQlTypeArgumentsCount).map { e.codeQlGetTypeArgument(it) }.requireNoNullsOrNull()
(0 until e.typeArgumentsCount).map { e.getTypeArgument(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.codeQlGetValueArgument(0)
updateRhs.getValueArgument(0)
} else null
} else null
}
@@ -5560,7 +5560,7 @@ open class KotlinFileExtractor(
"set"
)
) {
val updateRhs0 = arraySetCall.codeQlGetValueArgument(1)
val updateRhs0 = arraySetCall.getValueArgument(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.codeQlExtensionReceiverParameter
val ext = e.function.extensionReceiverParameter
val parameters =
if (ext != null) {
listOf(ext) + e.function.codeQlValueParameters
listOf(ext) + e.function.valueParameters
} else {
e.function.codeQlValueParameters
e.function.valueParameters
}
var types = parameters.map { it.type }
@@ -6670,7 +6670,7 @@ open class KotlinFileExtractor(
is IrFunction -> {
if (
ownerParent.dispatchReceiverParameter == owner &&
ownerParent.codeQlExtensionReceiverParameter != null
ownerParent.extensionReceiverParameter != null
) {
val ownerParent2 = ownerParent.parent
@@ -7089,7 +7089,7 @@ open class KotlinFileExtractor(
makeReceiverInfo(callableReferenceExpr.dispatchReceiver, 0)
private val extensionReceiverInfo =
makeReceiverInfo(
callableReferenceExpr.codeQlExtensionReceiver,
callableReferenceExpr.extensionReceiver,
if (dispatchReceiverInfo == null) 0 else 1
)
@@ -7627,8 +7627,8 @@ open class KotlinFileExtractor(
}
val expressionTypeArguments =
(0 until propertyReferenceExpr.codeQlTypeArgumentsCount).mapNotNull {
propertyReferenceExpr.codeQlGetTypeArgument(it)
(0 until propertyReferenceExpr.typeArgumentsCount).mapNotNull {
propertyReferenceExpr.getTypeArgument(it)
}
val idPropertyRef = tw.getFreshIdLabel<DbPropertyref>()
@@ -7829,7 +7829,7 @@ open class KotlinFileExtractor(
if (
functionReferenceExpr.dispatchReceiver != null &&
functionReferenceExpr.codeQlExtensionReceiver != null
functionReferenceExpr.extensionReceiver != 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.codeQlExtensionReceiverParameter != null
target.owner.extensionReceiverParameter != null
) {
logger.errorElement(
"Unexpected: dispatch and extension parameters are both non-null",
@@ -7899,8 +7899,8 @@ open class KotlinFileExtractor(
null
}
expressionTypeArguments =
(0 until functionReferenceExpr.codeQlTypeArgumentsCount).mapNotNull {
functionReferenceExpr.codeQlGetTypeArgument(it)
(0 until functionReferenceExpr.typeArgumentsCount).mapNotNull {
functionReferenceExpr.getTypeArgument(it)
}
dispatchReceiverIdx = -1
}
@@ -7965,7 +7965,7 @@ open class KotlinFileExtractor(
functionReferenceExpr,
declarationParent,
null,
{ it.codeQlValueParameters.size == 1 }
{ it.valueParameters.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.codeQlTypeArgumentsCount).map { c.codeQlGetTypeArgument(it) }.requireNoNullsOrNull()
(0 until c.typeArgumentsCount).map { c.getTypeArgument(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<IrValueParameter>()
val extParam = samMember.codeQlExtensionReceiverParameter
val extParam = samMember.extensionReceiverParameter
if (extParam != null) {
parameters.add(extParam)
}
parameters.addAll(samMember.codeQlValueParameters)
parameters.addAll(samMember.valueParameters)
fun extractArgument(
p: IrValueParameter,
@@ -9032,7 +9032,7 @@ open class KotlinFileExtractor(
elementToReportOn: IrElement,
declarationParent: IrDeclarationParent,
compilerGeneratedKindOverride: CompilerGeneratedKinds? = null,
superConstructorSelector: (IrFunction) -> Boolean = { it.codeQlValueParameters.isEmpty() },
superConstructorSelector: (IrFunction) -> Boolean = { it.valueParameters.isEmpty() },
extractSuperconstructorArgs: (Label<DbSuperconstructorinvocationstmt>) -> Unit = {},
): Label<out DbClassorinterface> {
// Write class

View File

@@ -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 com.github.codeql.utils.versions.codeQlAddAnnotations
import org.jetbrains.kotlin.ir.types.addAnnotations
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)?.codeQlExtensionReceiverParameter?.let {
((p.getter ?: p.setter)?.extensionReceiverParameter?.let {
useType(erase(it.type)).javaResult.signature
} ?: "")
@@ -368,7 +368,7 @@ open class KotlinUsesExtractor(
// useDeclarationParent -> useFunction
// -> extractFunctionLaterIfExternalFileMember, which would result for `fun <T> f(t:
// T) { ... }` for example.
(listOfNotNull(d.codeQlExtensionReceiverParameter) + d.codeQlValueParameters)
(listOfNotNull(d.extensionReceiverParameter) + d.valueParameters)
.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<IrSimpleFunction> { replacementDecl ->
replacementDecl.name == f.name &&
replacementDecl.codeQlValueParameters.size == f.codeQlValueParameters.size &&
replacementDecl.codeQlValueParameters.zip(f.codeQlValueParameters).all {
replacementDecl.valueParameters.size == f.valueParameters.size &&
replacementDecl.valueParameters.zip(f.valueParameters).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.codeQlGetValueArgument(0) as? CodeQLIrConst<Boolean>)?.value as? Boolean ?: true
(it.getValueArgument(0) as? CodeQLIrConst<Boolean>)?.value as? Boolean ?: true
}
private fun addJavaLoweringArgumentWildcards(
@@ -1376,9 +1376,9 @@ open class KotlinUsesExtractor(
f.parent,
parentId,
getFunctionShortName(f).nameInDB,
(maybeParameterList ?: f.codeQlValueParameters).map { it.type },
(maybeParameterList ?: f.valueParameters).map { it.type },
getAdjustedReturnType(f),
f.codeQlExtensionReceiverParameter?.type,
f.extensionReceiverParameter?.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.codeQlValueParameters.map { it.type }.
// f.valueParameters.map { it.type }.
parameterTypes: List<IrType>,
// The return type of the function; normally f.returnType.
returnType: IrType,
// The extension receiver of the function, if any; normally
// f.codeQlExtensionReceiverParameter?.type.
// f.extensionReceiverParameter?.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.codeQlValueParameters.isNotEmpty() ||
f.valueParameters.isNotEmpty() ||
f.returnType.classFqName?.asString() != "kotlin.collections.MutableSet"
) {
return f.returnType
@@ -1587,7 +1587,7 @@ open class KotlinUsesExtractor(
val otherKeySet =
parentClass.declarations.findSubType<IrFunction> {
it.name.asString() == "keySet" && it.codeQlValueParameters.size == 1
it.name.asString() == "keySet" && it.valueParameters.size == 1
} ?: return f.returnType
return otherKeySet.returnType.codeQlWithHasQuestionMark(false)
@@ -1695,8 +1695,8 @@ open class KotlinUsesExtractor(
javaClass.declarations.findSubType<IrFunction> { decl ->
!decl.isFakeOverride &&
decl.name.asString() == jvmName &&
decl.codeQlValueParameters.size == f.codeQlValueParameters.size &&
decl.codeQlValueParameters.zip(f.codeQlValueParameters).all { p ->
decl.valueParameters.size == f.valueParameters.size &&
decl.valueParameters.zip(f.valueParameters).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.codeQlAddAnnotations(listOf(RawTypeAnnotation.annotationConstructor))
t.addAnnotations(listOf(RawTypeAnnotation.annotationConstructor))
else t
}
}
@@ -2153,7 +2153,7 @@ open class KotlinUsesExtractor(
val idxOffset =
if (
declarationParent is IrFunction &&
declarationParent.codeQlExtensionReceiverParameter != null
declarationParent.extensionReceiverParameter != 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)?.codeQlExtensionReceiverParameter?.type
(it.getter ?: it.setter)?.extensionReceiverParameter?.type
}
fun getFieldLabel(f: IrField): String {
@@ -2222,14 +2222,14 @@ open class KotlinUsesExtractor(
val setter = p.setter
val func = getter ?: setter
val ext = func?.codeQlExtensionReceiverParameter
val ext = func?.extensionReceiverParameter
return if (ext == null) {
"@\"property;{$parentId};${p.name.asString()}\""
} else {
val returnType =
getter?.returnType
?: setter?.codeQlValueParameters?.singleOrNull()?.type
?: setter?.valueParameters?.singleOrNull()?.type
?: pluginContext.irBuiltIns.unitType
val typeParams = getFunctionTypeParameters(func)

View File

@@ -1,10 +1,5 @@
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
@@ -100,7 +95,7 @@ class MetaAnnotationSupport(
JvmAnnotationNames.REPEATABLE_ANNOTATION
}
return if (jvmRepeatable != null) {
((jvmRepeatable.codeQlGetValueArgument(0) as? IrClassReference)?.symbol as? IrClassSymbol)
((jvmRepeatable.getValueArgument(0) as? IrClassReference)?.symbol as? IrClassSymbol)
?.owner
} else {
getOrCreateSyntheticRepeatableAnnotationContainer(annotationClass)
@@ -122,12 +117,12 @@ class MetaAnnotationSupport(
)
return null
} else {
return codeQlAnnotationFromSymbolOwner(
return IrConstructorCallImpl.fromSymbolOwner(
containerClass.defaultType,
containerConstructor.symbol
)
.apply {
codeQlPutValueArgument(
putValueArgument(
0,
IrVarargImpl(
UNDEFINED_OFFSET,
@@ -149,7 +144,7 @@ class MetaAnnotationSupport(
// Taken from AdditionalClassAnnotationLowering.kt
private fun loadAnnotationTargets(targetEntry: IrConstructorCall): Set<KotlinTarget>? {
val valueArgument = targetEntry.codeQlGetValueArgument(0) as? IrVararg ?: return null
val valueArgument = targetEntry.getValueArgument(0) as? IrVararg ?: return null
return valueArgument.elements
.filterIsInstance<IrGetEnumValue>()
.mapNotNull { KotlinTarget.valueOrNull(it.symbol.owner.name.asString()) }
@@ -235,14 +230,14 @@ class MetaAnnotationSupport(
)
}
return codeQlAnnotationFromSymbolOwner(
return IrConstructorCallImpl.fromSymbolOwner(
UNDEFINED_OFFSET,
UNDEFINED_OFFSET,
targetConstructor.returnType,
targetConstructor.symbol,
0
)
.apply { codeQlPutValueArgument(0, vararg) }
.apply { putValueArgument(0, vararg) }
}
private val javaAnnotationRetention by lazy {
@@ -268,7 +263,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)?.codeQlGetValueArgument(0) as? IrGetEnumValue
getAnnotation(StandardNames.FqNames.retention)?.getValueArgument(0) as? IrGetEnumValue
?: return null
val retentionArgumentValue = retentionArgument.symbol.owner
return KotlinRetention.valueOf(retentionArgumentValue.name.asString())
@@ -288,7 +283,7 @@ class MetaAnnotationSupport(
val targetConstructor =
retentionType.declarations.firstIsInstanceOrNull<IrConstructor>() ?: return null
return codeQlAnnotationFromSymbolOwner(
return IrConstructorCallImpl.fromSymbolOwner(
UNDEFINED_OFFSET,
UNDEFINED_OFFSET,
targetConstructor.returnType,
@@ -296,7 +291,7 @@ class MetaAnnotationSupport(
0
)
.apply {
codeQlPutValueArgument(
putValueArgument(
0,
IrGetEnumValueImpl(
UNDEFINED_OFFSET,
@@ -338,7 +333,7 @@ class MetaAnnotationSupport(
return
}
val newParam = thisReceiever.copyTo(this)
codeQlSetDispatchReceiverParameter(newParam)
dispatchReceiverParameter = newParam
body =
factory
.createBlockBody(UNDEFINED_OFFSET, UNDEFINED_OFFSET)
@@ -411,7 +406,7 @@ class MetaAnnotationSupport(
val repeatableContainerAnnotation =
kotlinAnnotationRepeatableContainer?.constructors?.single()
codeQlSetAnnotations(containerClass,
containerClass.annotations =
annotationClass.annotations
.filter {
it.isAnnotationWithEqualFqName(StandardNames.FqNames.retention) ||
@@ -420,7 +415,7 @@ class MetaAnnotationSupport(
.map { it.deepCopyWithSymbols(containerClass) } +
listOfNotNull(
repeatableContainerAnnotation?.let {
codeQlAnnotationFromSymbolOwner(
IrConstructorCallImpl.fromSymbolOwner(
UNDEFINED_OFFSET,
UNDEFINED_OFFSET,
it.returnType,
@@ -429,7 +424,6 @@ class MetaAnnotationSupport(
)
}
)
)
containerClass
}
@@ -468,14 +462,14 @@ class MetaAnnotationSupport(
containerClass.symbol,
containerClass.defaultType
)
return codeQlAnnotationFromSymbolOwner(
return IrConstructorCallImpl.fromSymbolOwner(
UNDEFINED_OFFSET,
UNDEFINED_OFFSET,
repeatableConstructor.returnType,
repeatableConstructor.symbol,
0
)
.apply { codeQlPutValueArgument(0, containerReference) }
.apply { putValueArgument(0, containerReference) }
}
private val javaAnnotationDocumented by lazy {
@@ -494,7 +488,7 @@ class MetaAnnotationSupport(
javaAnnotationDocumented?.declarations?.firstIsInstanceOrNull<IrConstructor>()
?: return null
return codeQlAnnotationFromSymbolOwner(
return IrConstructorCallImpl.fromSymbolOwner(
UNDEFINED_OFFSET,
UNDEFINED_OFFSET,
documentedConstructor.returnType,

View File

@@ -1,7 +1,6 @@
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
@@ -332,7 +331,7 @@ open class FileTrapWriter(
is IrCall -> {
// Calls have incorrect startOffset, so we adjust them:
val dr = e.dispatchReceiver?.let { getStartOffset(it) }
val er = e.codeQlExtensionReceiver?.let { getStartOffset(it) }
val er = e.extensionReceiver?.let { getStartOffset(it) }
offsetMinOf(e.startOffset, dr, er)
}
else -> e.startOffset

View File

@@ -2,7 +2,6 @@ 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.*
@@ -12,7 +11,7 @@ import org.jetbrains.kotlin.ir.util.parentClassOrNull
private fun IrValueParameter.isExtensionReceiver(): Boolean {
val parentFun = parent as? IrFunction ?: return false
return parentFun.codeQlExtensionReceiverParameter == this
return parentFun.extensionReceiverParameter == this
}
open class CommentExtractor(

View File

@@ -1,8 +1,6 @@
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
@@ -78,9 +76,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.codeQlValueArgumentsCount == 1) {
if (t is IrSimpleType && a.valueArgumentsCount == 1) {
val owner = t.classifier.owner
val v = a.codeQlGetValueArgument(0)
val v = a.getValueArgument(0)
if (owner is IrClass) {
val aPkg = owner.packageFqName?.asString()
val name = owner.name.asString()

View File

@@ -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 com.github.codeql.utils.versions.codeQlAddAnnotations
import org.jetbrains.kotlin.ir.types.addAnnotations
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()
codeQlAnnotationFromSymbolOwner(constructor.constructedClassType, constructor.symbol)
IrConstructorCallImpl.fromSymbolOwner(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.codeQlAddAnnotations(listOf(RawTypeAnnotation.annotationConstructor))
this.addAnnotations(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.codeQlAddAnnotations(listOf(RawTypeAnnotation.annotationConstructor))
result.addAnnotations(listOf(RawTypeAnnotation.annotationConstructor))
else result
}

View File

@@ -1,70 +0,0 @@
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<IrValueParameter>
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
val IrMemberAccessExpression<*>.codeQlExtensionReceiver: IrExpression?
get() = extensionReceiver
// 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<IrConstructorCall>
fun IrType.codeQlAddAnnotations(annotations: List<IrConstructorCall>): IrType =
addAnnotations(annotations)
// IrMutableAnnotationContainer.annotations setter: in pre-2.4.0, annotations is var with List<IrConstructorCall>
fun codeQlSetAnnotations(container: org.jetbrains.kotlin.ir.declarations.IrMutableAnnotationContainer, annotations: List<IrConstructorCall>) {
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<IrConstructorCall> 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)

View File

@@ -3,32 +3,10 @@
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)
}
}

View File

@@ -3,35 +3,11 @@
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)
}
}

View File

@@ -1,121 +0,0 @@
@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<IrValueParameter>
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.
val IrMemberAccessExpression<*>.codeQlExtensionReceiver: IrExpression?
get() {
val erp = extensionReceiverParameterIndex() ?: return null
return arguments[erp]
}
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<IrAnnotation>
// IrConstructorCall implements IrAnnotation in 2.4.0, so filterIsInstance is identity
fun IrType.codeQlAddAnnotations(annotations: List<IrConstructorCall>): IrType =
addAnnotations(annotations.filterIsInstance<IrAnnotation>())
// IrMutableAnnotationContainer.annotations setter: in 2.4.0, expects List<IrAnnotation>
fun codeQlSetAnnotations(container: org.jetbrains.kotlin.ir.declarations.IrMutableAnnotationContainer, annotations: List<IrConstructorCall>) {
container.annotations = annotations.filterIsInstance<IrAnnotation>()
}
// 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)

View File

@@ -1,45 +0,0 @@
@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() = "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
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)
}
}
}

View File

@@ -1,13 +0,0 @@
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
}

View File

@@ -1 +0,0 @@
com.github.codeql.KotlinExtractorComponentRegistrar

View File

@@ -1,5 +1,8 @@
# when updating this list, `bazel mod tidy` should be run from `codeql` to update `MODULE.bazel`
VERSIONS = [
"1.8.0",
"1.9.0-Beta",
"1.9.20-Beta",
"2.0.0-RC1",
"2.0.20-Beta2",
"2.1.0-Beta1",
@@ -8,7 +11,6 @@ VERSIONS = [
"2.2.20-Beta2",
"2.3.0",
"2.3.20",
"2.4.0",
]
def _version_to_tuple(v):

View File

@@ -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.10.",
"markdownMessage": "The Kotlin version installed (`999.999.999`) is too recent for this version of CodeQL. Install a version lower than 2.3.30.",
"severity": "error",
"source": {
"extractorName": "java",

View File

@@ -1,3 +0,0 @@
[VALUE_NOT_IN_TYPE] predicate callableBinding(@caller callerid, @callable callee): Value 44417 of field callee is not in type @callable. Appears in tuple (-16777158,44417)
Relevant element: callee=44417
Full ID for 44417: @"callable;(0).f((55))(55)". The ID may expand to @"callable;{@"class;Test"}.f({@"type;int"}){@"type;int"}"

View File

@@ -2,9 +2,9 @@ exprs
| Test.java:5:19:5:25 | Integer | Integer |
| Test.java:5:38:5:44 | Integer | Integer |
| Test.java:5:58:5:58 | p | Integer |
| user.kt:2:3:2:16 | x | int |
| user.kt:2:7:2:7 | x | int |
| user.kt:2:11:2:11 | t | Test |
| user.kt:2:11:2:16 | <Call to unknown method> | int |
| user.kt:2:11:2:16 | f(...) | Integer |
| user.kt:2:13:2:16 | <implicit not null> | int |
| user.kt:2:13:2:16 | int | int |
| user.kt:2:15:2:15 | 5 | int |

View File

@@ -6,6 +6,6 @@ def test(codeql, java_full):
codeql.database.create(
command=[
f"javac {java_srcs} -d build",
"kotlinc -language-version 2.0 user.kt -cp build",
"kotlinc -language-version 1.9 user.kt -cp build",
]
)

View File

@@ -1,2 +1,2 @@
| user.kt:3:14:3:22 | getF(...) | file:///!unknown-binary-location/lib/TestKt.class:0:0:0:0 | getF |
| user.kt:3:26:3:28 | getF(...) | file:///!unknown-binary-location/lib/TestKt.class:0:0:0:0 | getF |
| user.kt:3:14:3:22 | getF(...) | lib/lib/TestKt.class:0:0:0:0 | getF |
| user.kt:3:26:3:28 | getF(...) | lib/lib/TestKt.class:0:0:0:0 | getF |

View File

@@ -2,5 +2,5 @@ import commands
def test(codeql, java_full):
commands.run("kotlinc -language-version 2.0 test.kt -d lib")
codeql.database.create(command="kotlinc -language-version 2.0 user.kt -cp lib")
commands.run("kotlinc -language-version 1.9 test.kt -d lib")
codeql.database.create(command="kotlinc -language-version 1.9 user.kt -cp lib")

View File

@@ -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: true | 1 |
| Uses Kotlin 2: false | 1 |

View File

@@ -1,2 +1,2 @@
def test(codeql, java_full):
codeql.database.create(command="kotlinc -J-Xmx2G -language-version 2.0 SomeClass.kt")
codeql.database.create(command="kotlinc -J-Xmx2G -language-version 1.9 SomeClass.kt")

View File

@@ -1,2 +1,3 @@
| AKt.class:0:0:0:0 | AKt | true |
| B.kt:0:0:0:0 | BKt | true |
| C.kt:1:1:3:1 | C | false |

View File

@@ -2,5 +2,5 @@ import commands
def test(codeql, java_full):
commands.run("kotlinc -language-version 2.0 A.kt")
codeql.database.create(command="kotlinc -cp . -language-version 2.0 B.kt C.kt")
commands.run("kotlinc -language-version 1.9 A.kt")
codeql.database.create(command="kotlinc -cp . -language-version 1.9 B.kt C.kt")

View File

@@ -0,0 +1,4 @@
| equals | Test |
| hashCode | Test |
| toString | Test |
| toString | java.lang.CharSequence |

View File

@@ -3,4 +3,4 @@ import commands
def test(codeql, java_full):
commands.run(["javac", "Test.java", "-d", "bin"])
codeql.database.create(command="kotlinc -language-version 2.0 user.kt -cp bin")
codeql.database.create(command="kotlinc -language-version 1.9 user.kt -cp bin")

View File

@@ -1,9 +0,0 @@
[VALUE_NOT_IN_TYPE] predicate callableBinding(@caller callerid, @callable callee): Value 43828 of field callee is not in type @callable. Appears in tuple (-16776213,43828)
Relevant element: callee=43828
Full ID for 43828: @"callable;(0).takesComparable((35),(35))(36)". The ID may expand to @"callable;{@"class;JavaDefns"}.takesComparable({@"class;java.lang.Comparable;{@"wildcard;super{@"class;java.lang.CharSequence"}"}"},{@"class;java.lang.Comparable;{@"wildcard;super{@"class;java.lang.CharSequence"}"}"}){@"type;void"}"
[VALUE_NOT_IN_TYPE] predicate callableBinding(@caller callerid, @callable callee): Value 43832 of field callee is not in type @callable. Appears in tuple (-16776208,43832)
Relevant element: callee=43832
Full ID for 43832: @"callable;(0).takesArrayOfComparable((54),(54))(36)". The ID may expand to @"callable;{@"class;JavaDefns"}.takesArrayOfComparable({@"array;1;{@"class;java.lang.Comparable;{@"wildcard;super(19)"}"}"},{@"array;1;{@"class;java.lang.Comparable;{@"wildcard;super(19)"}"}"}){@"type;void"}"
[VALUE_NOT_IN_TYPE] predicate callableBinding(@caller callerid, @callable callee): Value 43837 of field callee is not in type @callable. Appears in tuple (-16776201,43837)
Relevant element: callee=43837
Full ID for 43837: @"callable;(0).<init>((35),(35))(36)". The ID may expand to @"callable;{@"class;JavaDefns"}.<init>({@"class;java.lang.Comparable;{@"wildcard;super{@"class;java.lang.CharSequence"}"}"},{@"class;java.lang.Comparable;{@"wildcard;super{@"class;java.lang.CharSequence"}"}"}){@"type;void"}"

View File

@@ -8,16 +8,16 @@
| JavaDefns | takesComparable | invar | Comparable<CharSequence> |
| JavaDefns | takesNestedComparable | innerContravar | Comparable<Comparable<? super CharSequence>> |
| JavaDefns | takesNestedComparable | outerContravar | Comparable<? super Comparable<CharSequence>> |
| JavaDefns2 | JavaDefns2 | p0 | Comparable<? super CharSequence> |
| JavaDefns2 | JavaDefns2 | p0 | Comparable<CharSequence> |
| JavaDefns2 | JavaDefns2 | p1 | Comparable<? super CharSequence> |
| JavaDefns2 | returnsInvariant | return | Comparable<CharSequence> |
| JavaDefns2 | returnsWildcard | return | Comparable<? super CharSequence> |
| JavaDefns2 | takesArrayOfComparable | p0 | Comparable<? super CharSequence>[] |
| JavaDefns2 | takesArrayOfComparable | p0 | Comparable<CharSequence>[] |
| JavaDefns2 | takesArrayOfComparable | p1 | Comparable<? super CharSequence>[] |
| JavaDefns2 | takesComparable | p0 | Comparable<? super CharSequence> |
| JavaDefns2 | takesComparable | p0 | Comparable<CharSequence> |
| JavaDefns2 | takesComparable | p1 | Comparable<? super CharSequence> |
| JavaDefns2 | takesNestedComparable | p0 | Comparable<? super Comparable<? super CharSequence>> |
| JavaDefns2 | takesNestedComparable | p1 | Comparable<? super Comparable<? super CharSequence>> |
| JavaDefns2 | takesNestedComparable | p0 | Comparable<Comparable<? super CharSequence>> |
| JavaDefns2 | takesNestedComparable | p1 | Comparable<? super Comparable<CharSequence>> |
| KotlinDefns | returnsContravar | return | Comparable<CharSequence> |
| KotlinDefns | returnsContravarForced | return | Comparable<? super CharSequence> |
| KotlinDefns | returnsCovar | return | List<CharSequence> |

View File

@@ -8,6 +8,6 @@ def test(codeql, java_full):
command=[
"kotlinc kotlindefns.kt",
"javac JavaUser.java JavaDefns.java -cp .",
"kotlinc -language-version 2.0 -cp . kotlinuser.kt",
"kotlinc -language-version 1.9 -cp . kotlinuser.kt",
]
)

View File

@@ -1,4 +0,0 @@
---
category: feature
---
* Kotlin 2.4.0 can now be analysed.

View File

@@ -1,4 +0,0 @@
---
category: deprecated
---
* Kotlin versions below 2.0.0 are no longer supported for analysis. The minimum supported version is now Kotlin 2.0.0.

View File

@@ -4,67 +4,33 @@
overlay[local?]
module;
private import internal.rangeanalysis.BoundSpecific
private import java as J
private import semmle.code.java.dataflow.SSA
private import semmle.code.java.dataflow.RangeUtils as RU
private import codeql.rangeanalysis.Bound as SharedBound
private newtype TBound =
TBoundZero() or
TBoundSsa(SsaVariable v) { v.getSourceVariable().getType() instanceof IntegralType } or
TBoundExpr(Expr e) {
interestingExprBound(e) and
not exists(SsaVariable v | e = v.getAUse())
private module BoundDefs implements SharedBound::BoundDefinitions<J::Location> {
class SsaVariable extends Ssa::SsaDefinition {
/** Gets a use of this variable. */
Expr getAUse() { result = super.getARead() }
}
/**
* A bound that may be inferred for an expression plus/minus an integer delta.
*/
abstract class Bound extends TBound {
/** Gets a textual representation of this bound. */
abstract string toString();
class SsaSourceVariable = Ssa::SourceVariable;
/** Gets an expression that equals this bound plus `delta`. */
abstract Expr getExpr(int delta);
class Type = J::Type;
/** Gets an expression that equals this bound. */
Expr getExpr() { result = this.getExpr(0) }
class Expr = J::Expr;
/** Gets the location of this bound. */
abstract Location getLocation();
class IntegralType = J::IntegralType;
class ConstantIntegerExpr = RU::ConstantIntegerExpr;
/** Holds if `e` is a bound expression and it is not an SSA variable read. */
predicate interestingExprBound(Expr e) {
e.(J::FieldRead).getField() instanceof J::ArrayLengthField
}
}
/**
* The bound that corresponds to the integer 0. This is used to represent all
* integer bounds as bounds are always accompanied by an added integer delta.
*/
class ZeroBound extends Bound, TBoundZero {
override string toString() { result = "0" }
module BoundImpl = SharedBound::Bound<J::Location, BoundDefs>;
override Expr getExpr(int delta) { result.(ConstantIntegerExpr).getIntValue() = delta }
override Location getLocation() { result.hasLocationInfo("", 0, 0, 0, 0) }
}
/**
* A bound corresponding to the value of an SSA variable.
*/
class SsaBound extends Bound, TBoundSsa {
/** Gets the SSA variable that equals this bound. */
SsaVariable getSsa() { this = TBoundSsa(result) }
override string toString() { result = this.getSsa().toString() }
override Expr getExpr(int delta) { result = this.getSsa().getAUse() and delta = 0 }
override Location getLocation() { result = this.getSsa().getLocation() }
}
/**
* A bound that corresponds to the value of a specific expression that might be
* interesting, but isn't otherwise represented by the value of an SSA variable.
*/
class ExprBound extends Bound, TBoundExpr {
override string toString() { result = this.getExpr().toString() }
override Expr getExpr(int delta) { this = TBoundExpr(result) and delta = 0 }
override Location getLocation() { result = this.getExpr().getLocation() }
}
import BoundImpl

View File

@@ -1,27 +0,0 @@
/**
* Provides Java-specific definitions for bounds.
*/
overlay[local?]
module;
private import java as J
private import semmle.code.java.dataflow.SSA as Ssa
private import semmle.code.java.dataflow.RangeUtils as RU
class SsaVariable extends Ssa::SsaDefinition {
/** Gets a use of this variable. */
Expr getAUse() { result = super.getARead() }
}
class Expr = J::Expr;
class Location = J::Location;
class IntegralType = J::IntegralType;
class ConstantIntegerExpr = RU::ConstantIntegerExpr;
/** Holds if `e` is a bound expression and it is not an SSA variable read. */
predicate interestingExprBound(Expr e) {
e.(J::FieldRead).getField() instanceof J::ArrayLengthField
}

View File

@@ -1,16 +0,0 @@
[VALUE_NOT_IN_TYPE] predicate callableBinding(@caller callerid, @callable callee): Value 79083 of field callee is not in type @callable. Appears in tuple (-16776495,79083)
Relevant element: callee=79083
Full ID for 79083: @"callable;(21913).toString()(64)". The ID may expand to @"callable;{@"class;java.nio.file.Path"}.toString(){@"class;java.lang.String"}"
[VALUE_NOT_IN_TYPE] predicate callableBinding(@caller callerid, @callable callee): Value 79083 of field callee is not in type @callable. Appears in tuple (-16776429,79083)
Relevant element: callee=79083
Full ID for 79083: @"callable;(21913).toString()(64)". The ID may expand to @"callable;{@"class;java.nio.file.Path"}.toString(){@"class;java.lang.String"}"
[VALUE_NOT_IN_TYPE] predicate callableBinding(@caller callerid, @callable callee): Value 79083 of field callee is not in type @callable. Appears in tuple (-16776357,79083)
Relevant element: callee=79083
Full ID for 79083: @"callable;(21913).toString()(64)". The ID may expand to @"callable;{@"class;java.nio.file.Path"}.toString(){@"class;java.lang.String"}"
[VALUE_NOT_IN_TYPE] predicate callableBinding(@caller callerid, @callable callee): Value 79083 of field callee is not in type @callable. Appears in tuple (-16776266,79083)
Relevant element: callee=79083
Full ID for 79083: @"callable;(21913).toString()(64)". The ID may expand to @"callable;{@"class;java.nio.file.Path"}.toString(){@"class;java.lang.String"}"
[VALUE_NOT_IN_TYPE] predicate callableBinding(@caller callerid, @callable callee): Value 79083 of field callee is not in type @callable. Appears in tuple (-16776200,79083)
Relevant element: callee=79083
Full ID for 79083: @"callable;(21913).toString()(64)". The ID may expand to @"callable;{@"class;java.nio.file.Path"}.toString(){@"class;java.lang.String"}"
[VALUE_NOT_IN_TYPE] predicate callableBinding(@caller callerid, @callable callee): More errors, not displayed. There are 16 values of field callee that are not in type @callable for a relation of size 1821

View File

@@ -1,31 +0,0 @@
| Test.java:137:22:137:27 | source | Unexpected result: hasTaintFlow |
| Test.java:141:35:141:51 | // $ hasTaintFlow | Missing result: hasTaintFlow |
| Test.java:148:22:148:27 | source | Unexpected result: hasTaintFlow |
| Test.java:152:35:152:51 | // $ hasTaintFlow | Missing result: hasTaintFlow |
| Test.java:159:22:159:27 | source | Unexpected result: hasTaintFlow |
| Test.java:163:35:163:51 | // $ hasTaintFlow | Missing result: hasTaintFlow |
| Test.java:178:35:178:51 | // $ hasTaintFlow | Missing result: hasTaintFlow |
| Test.java:181:35:181:51 | // $ hasTaintFlow | Missing result: hasTaintFlow |
| Test.java:189:35:189:51 | // $ hasTaintFlow | Missing result: hasTaintFlow |
| Test.java:192:35:192:51 | // $ hasTaintFlow | Missing result: hasTaintFlow |
| Test.java:200:35:200:51 | // $ hasTaintFlow | Missing result: hasTaintFlow |
| Test.java:203:35:203:51 | // $ hasTaintFlow | Missing result: hasTaintFlow |
| Test.java:362:22:362:27 | source | Unexpected result: hasTaintFlow |
| Test.java:366:35:366:51 | // $ hasTaintFlow | Missing result: hasTaintFlow |
| Test.java:373:22:373:27 | source | Unexpected result: hasTaintFlow |
| Test.java:377:35:377:51 | // $ hasTaintFlow | Missing result: hasTaintFlow |
| Test.java:384:22:384:27 | source | Unexpected result: hasTaintFlow |
| Test.java:388:35:388:51 | // $ hasTaintFlow | Missing result: hasTaintFlow |
| Test.java:402:22:402:27 | source | Unexpected result: hasTaintFlow |
| Test.java:406:35:406:51 | // $ hasTaintFlow | Missing result: hasTaintFlow |
| Test.java:413:22:413:27 | source | Unexpected result: hasTaintFlow |
| Test.java:417:35:417:51 | // $ hasTaintFlow | Missing result: hasTaintFlow |
| Test.java:424:22:424:27 | source | Unexpected result: hasTaintFlow |
| Test.java:428:35:428:51 | // $ hasTaintFlow | Missing result: hasTaintFlow |
| Test.java:436:22:436:27 | source | Unexpected result: hasTaintFlow |
| Test.java:440:35:440:51 | // $ hasTaintFlow | Missing result: hasTaintFlow |
| Test.java:447:22:447:27 | source | Unexpected result: hasTaintFlow |
| Test.java:451:35:451:51 | // $ hasTaintFlow | Missing result: hasTaintFlow |
| Test.java:458:22:458:27 | source | Unexpected result: hasTaintFlow |
| Test.java:462:35:462:51 | // $ hasTaintFlow | Missing result: hasTaintFlow |
| Test.java:604:31:604:47 | // $ hasTaintFlow | Missing result: hasTaintFlow |

View File

@@ -1 +0,0 @@
| CleartextStorageSharedPrefsTest.java:110:84:110:118 | // $ hasCleartextStorageSharedPrefs | Missing result: hasCleartextStorageSharedPrefs |

View File

@@ -1,6 +0,0 @@
[VALUE_NOT_IN_TYPE] predicate callableBinding(@caller callerid, @callable callee): Value 80453 of field callee is not in type @callable. Appears in tuple (-16773210,80453)
Relevant element: callee=80453
Full ID for 80453: @"callable;(846).toString()(21)". The ID may expand to @"callable;{@"class;java.lang.CharSequence"}.toString(){@"class;java.lang.String"}"
[VALUE_NOT_IN_TYPE] predicate callableBinding(@caller callerid, @callable callee): Value 80453 of field callee is not in type @callable. Appears in tuple (-16773194,80453)
Relevant element: callee=80453
Full ID for 80453: @"callable;(846).toString()(21)". The ID may expand to @"callable;{@"class;java.lang.CharSequence"}.toString(){@"class;java.lang.String"}"

View File

@@ -0,0 +1,111 @@
/**
* Provides classes for representing abstract bounds for use in, for example, range analysis.
*/
overlay[local?]
module;
private import codeql.util.Location
signature module BoundDefinitions<LocationSig Location> {
class Type;
class IntegralType extends Type;
class ConstantIntegerExpr extends Expr {
int getIntValue();
}
class SsaSourceVariable {
Type getType();
}
class SsaVariable {
SsaSourceVariable getSourceVariable();
string toString();
Location getLocation();
Expr getAUse();
}
class Expr {
string toString();
Location getLocation();
}
predicate interestingExprBound(Expr e);
}
/**
* Provides classes for representing abstract bounds for use in, for example, range analysis.
* This is a generic implementation of bounds that relies on language specific modules to provide language-specific definitions of expressions, SSA variables, etc.
*/
overlay[local?]
module Bound<LocationSig Location, BoundDefinitions<Location> Defs> {
private import Defs
private newtype TBound =
TBoundZero() or
TBoundSsa(SsaVariable v) { v.getSourceVariable().getType() instanceof IntegralType } or
TBoundExpr(Expr e) {
interestingExprBound(e) and
not exists(SsaVariable v | e = v.getAUse())
}
/**
* A bound that may be inferred for an expression plus/minus an integer delta.
*/
abstract class Bound extends TBound {
/** Gets a textual representation of this bound. */
abstract string toString();
/** Gets an expression that equals this bound plus `delta`. */
abstract Expr getExpr(int delta);
/** Gets an expression that equals this bound. */
Expr getExpr() { result = this.getExpr(0) }
/** Gets the location of this bound. */
abstract Location getLocation();
}
/**
* The bound that corresponds to the integer 0. This is used to represent all
* integer bounds as bounds are always accompanied by an added integer delta.
*/
class ZeroBound extends Bound, TBoundZero {
override string toString() { result = "0" }
override Expr getExpr(int delta) { result.(ConstantIntegerExpr).getIntValue() = delta }
override Location getLocation() { result.hasLocationInfo("", 0, 0, 0, 0) }
}
/**
* A bound corresponding to the value of an SSA variable.
*/
class SsaBound extends Bound, TBoundSsa {
/** Gets the SSA variable that equals this bound. */
SsaVariable getSsa() { this = TBoundSsa(result) }
override string toString() { result = this.getSsa().toString() }
override Expr getExpr(int delta) { result = this.getSsa().getAUse() and delta = 0 }
override Location getLocation() { result = this.getSsa().getLocation() }
}
/**
* A bound that corresponds to the value of a specific expression that might be
* interesting, but isn't otherwise represented by the value of an SSA variable.
*/
class ExprBound extends Bound, TBoundExpr {
override string toString() { result = this.getExpr().toString() }
override Expr getExpr(int delta) { this = TBoundExpr(result) and delta = 0 }
override Location getLocation() { result = this.getExpr().getLocation() }
}
}