mirror of
https://github.com/github/codeql.git
synced 2025-12-17 01:03:14 +01:00
Merge pull request #10822 from smowton/smowton/feature/kotlin-collection-literals
Koltin: support collection literals
This commit is contained in:
@@ -1753,7 +1753,7 @@ open class KotlinFileExtractor(
|
||||
private fun extractsDefaultsCall(
|
||||
syntacticCallTarget: IrFunction,
|
||||
locId: Label<DbLocation>,
|
||||
callsite: IrCall,
|
||||
resultType: IrType,
|
||||
enclosingCallable: Label<out DbCallable>,
|
||||
callsiteParent: Label<out DbExprparent>,
|
||||
childIdx: Int,
|
||||
@@ -1768,7 +1768,7 @@ open class KotlinFileExtractor(
|
||||
useFunction<DbCallable>(callTarget)
|
||||
}
|
||||
val defaultMethodLabel = getDefaultsMethodLabel(callTarget)
|
||||
val id = extractMethodAccessWithoutArgs(callsite.type, locId, enclosingCallable, callsiteParent, childIdx, enclosingStmt, defaultMethodLabel)
|
||||
val id = extractMethodAccessWithoutArgs(resultType, locId, enclosingCallable, callsiteParent, childIdx, enclosingStmt, defaultMethodLabel)
|
||||
|
||||
if (callTarget.isLocalFunction()) {
|
||||
extractTypeAccess(getLocallyVisibleFunctionLabels(callTarget).type, locId, id, -1, enclosingCallable, enclosingStmt)
|
||||
@@ -1871,7 +1871,8 @@ open class KotlinFileExtractor(
|
||||
|
||||
fun extractRawMethodAccess(
|
||||
syntacticCallTarget: IrFunction,
|
||||
callsite: IrCall,
|
||||
locElement: IrElement,
|
||||
resultType: IrType,
|
||||
enclosingCallable: Label<out DbCallable>,
|
||||
callsiteParent: Label<out DbExprparent>,
|
||||
childIdx: Int,
|
||||
@@ -1883,13 +1884,13 @@ open class KotlinFileExtractor(
|
||||
extractClassTypeArguments: Boolean = false,
|
||||
superQualifierSymbol: IrClassSymbol? = null) {
|
||||
|
||||
val locId = tw.getLocation(callsite)
|
||||
val locId = tw.getLocation(locElement)
|
||||
|
||||
if (valueArguments.any { it == null }) {
|
||||
extractsDefaultsCall(
|
||||
syntacticCallTarget,
|
||||
locId,
|
||||
callsite,
|
||||
resultType,
|
||||
enclosingCallable,
|
||||
callsiteParent,
|
||||
childIdx,
|
||||
@@ -1902,7 +1903,7 @@ open class KotlinFileExtractor(
|
||||
extractRawMethodAccess(
|
||||
syntacticCallTarget,
|
||||
locId,
|
||||
callsite.type,
|
||||
resultType,
|
||||
enclosingCallable,
|
||||
callsiteParent,
|
||||
childIdx,
|
||||
@@ -2233,7 +2234,7 @@ open class KotlinFileExtractor(
|
||||
return
|
||||
}
|
||||
|
||||
extractRawMethodAccess(syntacticCallTarget, c, callable, parent, idx, enclosingStmt, (0 until c.valueArgumentsCount).map { c.getValueArgument(it) }, c.dispatchReceiver, c.extensionReceiver, typeArgs, extractClassTypeArguments, c.superQualifierSymbol)
|
||||
extractRawMethodAccess(syntacticCallTarget, c, c.type, callable, parent, idx, enclosingStmt, (0 until c.valueArgumentsCount).map { c.getValueArgument(it) }, c.dispatchReceiver, c.extensionReceiver, typeArgs, extractClassTypeArguments, c.superQualifierSymbol)
|
||||
}
|
||||
|
||||
fun extractSpecialEnumFunction(fnName: String){
|
||||
@@ -2337,7 +2338,7 @@ open class KotlinFileExtractor(
|
||||
}
|
||||
isFunction(target, "kotlin", "String", "plus", true) -> {
|
||||
findJdkIntrinsicOrWarn("stringPlus", c)?.let { stringPlusFn ->
|
||||
extractRawMethodAccess(stringPlusFn, c, callable, parent, idx, enclosingStmt, listOf(c.extensionReceiver, c.getValueArgument(0)), null, null)
|
||||
extractRawMethodAccess(stringPlusFn, c, c.type, callable, parent, idx, enclosingStmt, listOf(c.extensionReceiver, c.getValueArgument(0)), null, null)
|
||||
}
|
||||
}
|
||||
isNumericFunction(target, listOf("plus", "minus", "times", "div", "rem", "and", "or", "xor", "shl", "shr", "ushr")) -> {
|
||||
@@ -2582,7 +2583,7 @@ open class KotlinFileExtractor(
|
||||
}
|
||||
isFunction(target, "kotlin", "Any", "toString", true) -> {
|
||||
stringValueOfObjectMethod?.let {
|
||||
extractRawMethodAccess(it, c, callable, parent, idx, enclosingStmt, listOf(c.extensionReceiver), null, null)
|
||||
extractRawMethodAccess(it, c, c.type, callable, parent, idx, enclosingStmt, listOf(c.extensionReceiver), null, null)
|
||||
}
|
||||
}
|
||||
isBuiltinCallKotlin(c, "enumValues") -> {
|
||||
@@ -2632,6 +2633,24 @@ open class KotlinFileExtractor(
|
||||
|| isBuiltinCallKotlin(c, "byteArrayOf")
|
||||
|| isBuiltinCallKotlin(c, "booleanArrayOf") -> {
|
||||
|
||||
|
||||
val isPrimitiveArrayCreation = !isBuiltinCallKotlin(c, "arrayOf")
|
||||
val elementType = if (isPrimitiveArrayCreation) {
|
||||
c.type.getArrayElementType(pluginContext.irBuiltIns)
|
||||
} else {
|
||||
// TODO: is there any reason not to always use getArrayElementType?
|
||||
if (c.typeArgumentsCount == 1) {
|
||||
c.getTypeArgument(0).also {
|
||||
if (it == null) {
|
||||
logger.errorElement("Type argument missing in an arrayOf call", c)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
logger.errorElement("Expected to find one type argument in arrayOf call", c)
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
val arg = if (c.valueArgumentsCount == 1) c.getValueArgument(0) else {
|
||||
logger.errorElement("Expected to find only one (vararg) argument in ${c.symbol.owner.name.asString()} call", c)
|
||||
null
|
||||
@@ -2642,59 +2661,7 @@ open class KotlinFileExtractor(
|
||||
}
|
||||
}
|
||||
|
||||
// If this is [someType]ArrayOf(*x), x, otherwise null
|
||||
val clonedArray = arg?.let {
|
||||
if (arg.elements.size == 1) {
|
||||
val onlyElement = arg.elements[0]
|
||||
if (onlyElement is IrSpreadElement)
|
||||
onlyElement.expression
|
||||
else null
|
||||
} else null
|
||||
}
|
||||
|
||||
if (clonedArray != null) {
|
||||
// This is an array clone: extract is as a call to java.lang.Object.clone
|
||||
objectCloneMethod?.let {
|
||||
extractRawMethodAccess(it, c, callable, parent, idx, enclosingStmt, listOf(), clonedArray, null)
|
||||
}
|
||||
} else {
|
||||
// This is array creation: extract it as a call to new ArrayType[] { ... }
|
||||
val id = tw.getFreshIdLabel<DbArraycreationexpr>()
|
||||
val type = useType(c.type)
|
||||
tw.writeExprs_arraycreationexpr(id, type.javaResult.id, parent, idx)
|
||||
tw.writeExprsKotlinType(id, type.kotlinResult.id)
|
||||
val locId = tw.getLocation(c)
|
||||
tw.writeHasLocation(id, locId)
|
||||
tw.writeCallableEnclosingExpr(id, callable)
|
||||
|
||||
if (isBuiltinCallKotlin(c, "arrayOf")) {
|
||||
if (c.typeArgumentsCount == 1) {
|
||||
val typeArgument = c.getTypeArgument(0)
|
||||
if (typeArgument == null) {
|
||||
logger.errorElement("Type argument missing in an arrayOf call", c)
|
||||
} else {
|
||||
extractTypeAccessRecursive(typeArgument, locId, id, -1, callable, enclosingStmt, TypeContext.GENERIC_ARGUMENT)
|
||||
}
|
||||
} else {
|
||||
logger.errorElement("Expected to find one type argument in arrayOf call", c )
|
||||
}
|
||||
} else {
|
||||
val elementType = c.type.getArrayElementType(pluginContext.irBuiltIns)
|
||||
extractTypeAccessRecursive(elementType, locId, id, -1, callable, enclosingStmt)
|
||||
}
|
||||
|
||||
arg?.let {
|
||||
val initId = tw.getFreshIdLabel<DbArrayinit>()
|
||||
tw.writeExprs_arrayinit(initId, type.javaResult.id, id, -2)
|
||||
tw.writeExprsKotlinType(initId, type.kotlinResult.id)
|
||||
tw.writeHasLocation(initId, locId)
|
||||
tw.writeCallableEnclosingExpr(initId, callable)
|
||||
tw.writeStatementEnclosingExpr(initId, enclosingStmt)
|
||||
it.elements.forEachIndexed { i, arg -> extractVarargElement(arg, callable, initId, i, enclosingStmt) }
|
||||
|
||||
extractConstantInteger(it.elements.size, locId, id, 0, callable, enclosingStmt)
|
||||
}
|
||||
}
|
||||
extractArrayCreation(arg, c.type, elementType, isPrimitiveArrayCreation, c, parent, idx, callable, enclosingStmt)
|
||||
}
|
||||
isBuiltinCall(c, "<get-java>", "kotlin.jvm") -> {
|
||||
// Special case for KClass<*>.java, which is used in the Parcelize plugin. In normal cases, this is already rewritten to the property referenced below:
|
||||
@@ -2714,7 +2681,7 @@ open class KotlinFileExtractor(
|
||||
val argType = (ext.type as? IrSimpleType)?.arguments?.firstOrNull()?.typeOrNull
|
||||
val typeArguments = if (argType == null) listOf() else listOf(argType)
|
||||
|
||||
extractRawMethodAccess(getter, c, callable, parent, idx, enclosingStmt, listOf(), null, ext, typeArguments)
|
||||
extractRawMethodAccess(getter, c, c.type, callable, parent, idx, enclosingStmt, listOf(), null, ext, typeArguments)
|
||||
}
|
||||
}
|
||||
isFunction(target, "kotlin", "(some array type)", { isArrayType(it) }, "iterator") -> {
|
||||
@@ -2745,7 +2712,7 @@ open class KotlinFileExtractor(
|
||||
else -> pluginContext.irBuiltIns.anyNType
|
||||
}
|
||||
}
|
||||
extractRawMethodAccess(iteratorFn, c, callable, parent, idx, enclosingStmt, listOf(c.dispatchReceiver), null, null, typeArgs)
|
||||
extractRawMethodAccess(iteratorFn, c, c.type, callable, parent, idx, enclosingStmt, listOf(c.dispatchReceiver), null, null, typeArgs)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2834,6 +2801,7 @@ open class KotlinFileExtractor(
|
||||
extractRawMethodAccess(
|
||||
realCallee,
|
||||
c,
|
||||
c.type,
|
||||
callable,
|
||||
parent,
|
||||
idx,
|
||||
@@ -2861,6 +2829,7 @@ open class KotlinFileExtractor(
|
||||
extractRawMethodAccess(
|
||||
realCallee,
|
||||
c,
|
||||
c.type,
|
||||
callable,
|
||||
parent,
|
||||
idx,
|
||||
@@ -2878,6 +2847,51 @@ open class KotlinFileExtractor(
|
||||
}
|
||||
}
|
||||
|
||||
private fun extractArrayCreation(elementList: IrVararg?, resultType: IrType, elementType: IrType?, allowPrimitiveElementType: Boolean, locElement: IrElement, parent: Label<out DbExprparent>, idx: Int, enclosingCallable: Label<out DbCallable>, enclosingStmt: Label<out DbStmt>) {
|
||||
// If this is [someType]ArrayOf(*x), x, otherwise null
|
||||
val clonedArray = elementList?.let {
|
||||
if (it.elements.size == 1) {
|
||||
val onlyElement = it.elements[0]
|
||||
if (onlyElement is IrSpreadElement)
|
||||
onlyElement.expression
|
||||
else null
|
||||
} else null
|
||||
}
|
||||
|
||||
if (clonedArray != null) {
|
||||
// This is an array clone: extract is as a call to java.lang.Object.clone
|
||||
objectCloneMethod?.let {
|
||||
extractRawMethodAccess(it, locElement, resultType, enclosingCallable, parent, idx, enclosingStmt, listOf(), clonedArray, null)
|
||||
}
|
||||
} else {
|
||||
// This is array creation: extract it as a call to new ArrayType[] { ... }
|
||||
val id = tw.getFreshIdLabel<DbArraycreationexpr>()
|
||||
val type = useType(resultType)
|
||||
tw.writeExprs_arraycreationexpr(id, type.javaResult.id, parent, idx)
|
||||
tw.writeExprsKotlinType(id, type.kotlinResult.id)
|
||||
val locId = tw.getLocation(locElement)
|
||||
tw.writeHasLocation(id, locId)
|
||||
tw.writeCallableEnclosingExpr(id, enclosingCallable)
|
||||
|
||||
if (elementType != null) {
|
||||
val typeContext = if (allowPrimitiveElementType) TypeContext.OTHER else TypeContext.GENERIC_ARGUMENT
|
||||
extractTypeAccessRecursive(elementType, locId, id, -1, enclosingCallable, enclosingStmt, typeContext)
|
||||
}
|
||||
|
||||
if (elementList != null) {
|
||||
val initId = tw.getFreshIdLabel<DbArrayinit>()
|
||||
tw.writeExprs_arrayinit(initId, type.javaResult.id, id, -2)
|
||||
tw.writeExprsKotlinType(initId, type.kotlinResult.id)
|
||||
tw.writeHasLocation(initId, locId)
|
||||
tw.writeCallableEnclosingExpr(initId, enclosingCallable)
|
||||
tw.writeStatementEnclosingExpr(initId, enclosingStmt)
|
||||
elementList.elements.forEachIndexed { i, arg -> extractVarargElement(arg, enclosingCallable, initId, i, enclosingStmt) }
|
||||
|
||||
extractConstantInteger(elementList.elements.size, locId, id, 0, enclosingCallable, enclosingStmt)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun extractNewExpr(
|
||||
methodId: Label<out DbConstructor>,
|
||||
constructedType: TypeResults,
|
||||
@@ -3661,14 +3675,12 @@ open class KotlinFileExtractor(
|
||||
extractTypeOperatorCall(e, callable, exprParent.parent, exprParent.idx, exprParent.enclosingStmt)
|
||||
}
|
||||
is IrVararg -> {
|
||||
var spread = e.elements.getOrNull(0) as? IrSpreadElement
|
||||
if (spread == null || e.elements.size != 1) {
|
||||
logger.errorElement("Unexpected IrVararg", e)
|
||||
return
|
||||
}
|
||||
// There are lowered IR cases when the vararg expression is not within a call, such as
|
||||
// val temp0 = [*expr]
|
||||
extractExpression(spread.expression, callable, parent)
|
||||
// val temp0 = [*expr].
|
||||
// This AST element can also occur as a collection literal in an annotation class, such as
|
||||
// annotation class Ann(val strings: Array<String> = [])
|
||||
val exprParent = parent.expr(e, callable)
|
||||
extractArrayCreation(e, e.type, e.varargElementType, true, e, exprParent.parent, exprParent.idx, callable, exprParent.enclosingStmt)
|
||||
}
|
||||
is IrGetObjectValue -> {
|
||||
// For `object MyObject { ... }`, the .class has an
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
test.kt:
|
||||
# 0| [CompilationUnit] test
|
||||
# 1| 1: [Interface] Ann
|
||||
# 1| 1: [Constructor] Ann
|
||||
#-----| 4: (Parameters)
|
||||
# 1| 0: [Parameter] arr1
|
||||
# 1| 0: [TypeAccess] String[]
|
||||
# 1| 0: [TypeAccess] String
|
||||
# 1| 1: [Parameter] arr2
|
||||
# 1| 0: [TypeAccess] int[]
|
||||
# 1| 5: [BlockStmt] { ... }
|
||||
# 1| 0: [SuperConstructorInvocationStmt] super(...)
|
||||
# 1| 1: [BlockStmt] { ... }
|
||||
# 1| 0: [ExprStmt] <Expr>;
|
||||
# 1| 0: [KtInitializerAssignExpr] ...=...
|
||||
# 1| 0: [VarAccess] arr1
|
||||
# 1| 1: [ExprStmt] <Expr>;
|
||||
# 1| 0: [KtInitializerAssignExpr] ...=...
|
||||
# 1| 0: [VarAccess] arr2
|
||||
# 1| 2: [Constructor] Ann
|
||||
#-----| 4: (Parameters)
|
||||
# 1| 0: [Parameter] p0
|
||||
# 1| 0: [TypeAccess] String[]
|
||||
# 1| 1: [Parameter] p1
|
||||
# 1| 0: [TypeAccess] int[]
|
||||
# 1| 2: [Parameter] p2
|
||||
# 1| 0: [TypeAccess] int
|
||||
# 1| 3: [Parameter] p3
|
||||
# 1| 0: [TypeAccess] DefaultConstructorMarker
|
||||
# 1| 5: [BlockStmt] { ... }
|
||||
# 1| 0: [IfStmt] if (...)
|
||||
# 1| 0: [EQExpr] ... == ...
|
||||
# 1| 0: [AndBitwiseExpr] ... & ...
|
||||
# 1| 0: [IntegerLiteral] 1
|
||||
# 1| 1: [VarAccess] p2
|
||||
# 1| 1: [IntegerLiteral] 0
|
||||
# 1| 1: [ExprStmt] <Expr>;
|
||||
# 1| 0: [AssignExpr] ...=...
|
||||
# 1| 0: [VarAccess] p0
|
||||
# 0| 1: [ArrayCreationExpr] new String[]
|
||||
# 0| -2: [ArrayInit] {...}
|
||||
# 0| 0: [StringLiteral] hello
|
||||
# 0| 1: [StringLiteral] world
|
||||
# 0| -1: [TypeAccess] String
|
||||
# 0| 0: [IntegerLiteral] 2
|
||||
# 1| 1: [IfStmt] if (...)
|
||||
# 1| 0: [EQExpr] ... == ...
|
||||
# 1| 0: [AndBitwiseExpr] ... & ...
|
||||
# 1| 0: [IntegerLiteral] 2
|
||||
# 1| 1: [VarAccess] p2
|
||||
# 1| 1: [IntegerLiteral] 0
|
||||
# 1| 1: [ExprStmt] <Expr>;
|
||||
# 1| 0: [AssignExpr] ...=...
|
||||
# 1| 0: [VarAccess] p1
|
||||
# 0| 1: [ArrayCreationExpr] new int[]
|
||||
# 0| -2: [ArrayInit] {...}
|
||||
# 0| 0: [IntegerLiteral] 1
|
||||
# 0| 1: [IntegerLiteral] 2
|
||||
# 0| 2: [IntegerLiteral] 3
|
||||
# 0| -1: [TypeAccess] int
|
||||
# 0| 0: [IntegerLiteral] 3
|
||||
# 1| 2: [ThisConstructorInvocationStmt] this(...)
|
||||
# 1| 0: [VarAccess] p0
|
||||
# 1| 1: [VarAccess] p1
|
||||
# 1| 3: [FieldDeclaration] String[] arr1;
|
||||
# 1| -1: [TypeAccess] String[]
|
||||
# 1| 0: [TypeAccess] String
|
||||
# 1| 0: [VarAccess] arr1
|
||||
# 1| 4: [Method] arr1
|
||||
# 1| 3: [TypeAccess] String[]
|
||||
# 1| 0: [TypeAccess] String
|
||||
# 1| 5: [BlockStmt] { ... }
|
||||
# 1| 0: [ReturnStmt] return ...
|
||||
# 1| 0: [VarAccess] this.arr1
|
||||
# 1| -1: [ThisAccess] this
|
||||
# 1| 5: [Method] arr2
|
||||
# 1| 3: [TypeAccess] int[]
|
||||
# 1| 5: [BlockStmt] { ... }
|
||||
# 1| 0: [ReturnStmt] return ...
|
||||
# 1| 0: [VarAccess] this.arr2
|
||||
# 1| -1: [ThisAccess] this
|
||||
# 1| 6: [FieldDeclaration] int[] arr2;
|
||||
# 1| -1: [TypeAccess] int[]
|
||||
# 1| 0: [VarAccess] arr2
|
||||
@@ -0,0 +1 @@
|
||||
semmle/code/java/PrintAst.ql
|
||||
@@ -0,0 +1 @@
|
||||
annotation class Ann(val arr1: Array<String> = ["hello", "world"], val arr2: IntArray = [1, 2, 3]) { }
|
||||
Reference in New Issue
Block a user