Skip to content

Commit 67aa6c7

Browse files
authored
Merge pull request github#10822 from smowton/smowton/feature/kotlin-collection-literals
Koltin: support collection literals
2 parents 19bcd28 + 595a66a commit 67aa6c7

File tree

4 files changed

+169
-71
lines changed

4 files changed

+169
-71
lines changed

java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt

Lines changed: 83 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1753,7 +1753,7 @@ open class KotlinFileExtractor(
17531753
private fun extractsDefaultsCall(
17541754
syntacticCallTarget: IrFunction,
17551755
locId: Label<DbLocation>,
1756-
callsite: IrCall,
1756+
resultType: IrType,
17571757
enclosingCallable: Label<out DbCallable>,
17581758
callsiteParent: Label<out DbExprparent>,
17591759
childIdx: Int,
@@ -1768,7 +1768,7 @@ open class KotlinFileExtractor(
17681768
useFunction<DbCallable>(callTarget)
17691769
}
17701770
val defaultMethodLabel = getDefaultsMethodLabel(callTarget)
1771-
val id = extractMethodAccessWithoutArgs(callsite.type, locId, enclosingCallable, callsiteParent, childIdx, enclosingStmt, defaultMethodLabel)
1771+
val id = extractMethodAccessWithoutArgs(resultType, locId, enclosingCallable, callsiteParent, childIdx, enclosingStmt, defaultMethodLabel)
17721772

17731773
if (callTarget.isLocalFunction()) {
17741774
extractTypeAccess(getLocallyVisibleFunctionLabels(callTarget).type, locId, id, -1, enclosingCallable, enclosingStmt)
@@ -1871,7 +1871,8 @@ open class KotlinFileExtractor(
18711871

18721872
fun extractRawMethodAccess(
18731873
syntacticCallTarget: IrFunction,
1874-
callsite: IrCall,
1874+
locElement: IrElement,
1875+
resultType: IrType,
18751876
enclosingCallable: Label<out DbCallable>,
18761877
callsiteParent: Label<out DbExprparent>,
18771878
childIdx: Int,
@@ -1883,13 +1884,13 @@ open class KotlinFileExtractor(
18831884
extractClassTypeArguments: Boolean = false,
18841885
superQualifierSymbol: IrClassSymbol? = null) {
18851886

1886-
val locId = tw.getLocation(callsite)
1887+
val locId = tw.getLocation(locElement)
18871888

18881889
if (valueArguments.any { it == null }) {
18891890
extractsDefaultsCall(
18901891
syntacticCallTarget,
18911892
locId,
1892-
callsite,
1893+
resultType,
18931894
enclosingCallable,
18941895
callsiteParent,
18951896
childIdx,
@@ -1902,7 +1903,7 @@ open class KotlinFileExtractor(
19021903
extractRawMethodAccess(
19031904
syntacticCallTarget,
19041905
locId,
1905-
callsite.type,
1906+
resultType,
19061907
enclosingCallable,
19071908
callsiteParent,
19081909
childIdx,
@@ -2233,7 +2234,7 @@ open class KotlinFileExtractor(
22332234
return
22342235
}
22352236

2236-
extractRawMethodAccess(syntacticCallTarget, c, callable, parent, idx, enclosingStmt, (0 until c.valueArgumentsCount).map { c.getValueArgument(it) }, c.dispatchReceiver, c.extensionReceiver, typeArgs, extractClassTypeArguments, c.superQualifierSymbol)
2237+
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)
22372238
}
22382239

22392240
fun extractSpecialEnumFunction(fnName: String){
@@ -2337,7 +2338,7 @@ open class KotlinFileExtractor(
23372338
}
23382339
isFunction(target, "kotlin", "String", "plus", true) -> {
23392340
findJdkIntrinsicOrWarn("stringPlus", c)?.let { stringPlusFn ->
2340-
extractRawMethodAccess(stringPlusFn, c, callable, parent, idx, enclosingStmt, listOf(c.extensionReceiver, c.getValueArgument(0)), null, null)
2341+
extractRawMethodAccess(stringPlusFn, c, c.type, callable, parent, idx, enclosingStmt, listOf(c.extensionReceiver, c.getValueArgument(0)), null, null)
23412342
}
23422343
}
23432344
isNumericFunction(target, listOf("plus", "minus", "times", "div", "rem", "and", "or", "xor", "shl", "shr", "ushr")) -> {
@@ -2582,7 +2583,7 @@ open class KotlinFileExtractor(
25822583
}
25832584
isFunction(target, "kotlin", "Any", "toString", true) -> {
25842585
stringValueOfObjectMethod?.let {
2585-
extractRawMethodAccess(it, c, callable, parent, idx, enclosingStmt, listOf(c.extensionReceiver), null, null)
2586+
extractRawMethodAccess(it, c, c.type, callable, parent, idx, enclosingStmt, listOf(c.extensionReceiver), null, null)
25862587
}
25872588
}
25882589
isBuiltinCallKotlin(c, "enumValues") -> {
@@ -2632,69 +2633,35 @@ open class KotlinFileExtractor(
26322633
|| isBuiltinCallKotlin(c, "byteArrayOf")
26332634
|| isBuiltinCallKotlin(c, "booleanArrayOf") -> {
26342635

2635-
val arg = if (c.valueArgumentsCount == 1) c.getValueArgument(0) else {
2636-
logger.errorElement("Expected to find only one (vararg) argument in ${c.symbol.owner.name.asString()} call", c)
2637-
null
2638-
}?.let {
2639-
if (it is IrVararg) it else {
2640-
logger.errorElement("Expected to find vararg argument in ${c.symbol.owner.name.asString()} call", c)
2641-
null
2642-
}
2643-
}
26442636

2645-
// If this is [someType]ArrayOf(*x), x, otherwise null
2646-
val clonedArray = arg?.let {
2647-
if (arg.elements.size == 1) {
2648-
val onlyElement = arg.elements[0]
2649-
if (onlyElement is IrSpreadElement)
2650-
onlyElement.expression
2651-
else null
2652-
} else null
2653-
}
2654-
2655-
if (clonedArray != null) {
2656-
// This is an array clone: extract is as a call to java.lang.Object.clone
2657-
objectCloneMethod?.let {
2658-
extractRawMethodAccess(it, c, callable, parent, idx, enclosingStmt, listOf(), clonedArray, null)
2659-
}
2637+
val isPrimitiveArrayCreation = !isBuiltinCallKotlin(c, "arrayOf")
2638+
val elementType = if (isPrimitiveArrayCreation) {
2639+
c.type.getArrayElementType(pluginContext.irBuiltIns)
26602640
} else {
2661-
// This is array creation: extract it as a call to new ArrayType[] { ... }
2662-
val id = tw.getFreshIdLabel<DbArraycreationexpr>()
2663-
val type = useType(c.type)
2664-
tw.writeExprs_arraycreationexpr(id, type.javaResult.id, parent, idx)
2665-
tw.writeExprsKotlinType(id, type.kotlinResult.id)
2666-
val locId = tw.getLocation(c)
2667-
tw.writeHasLocation(id, locId)
2668-
tw.writeCallableEnclosingExpr(id, callable)
2669-
2670-
if (isBuiltinCallKotlin(c, "arrayOf")) {
2671-
if (c.typeArgumentsCount == 1) {
2672-
val typeArgument = c.getTypeArgument(0)
2673-
if (typeArgument == null) {
2641+
// TODO: is there any reason not to always use getArrayElementType?
2642+
if (c.typeArgumentsCount == 1) {
2643+
c.getTypeArgument(0).also {
2644+
if (it == null) {
26742645
logger.errorElement("Type argument missing in an arrayOf call", c)
2675-
} else {
2676-
extractTypeAccessRecursive(typeArgument, locId, id, -1, callable, enclosingStmt, TypeContext.GENERIC_ARGUMENT)
26772646
}
2678-
} else {
2679-
logger.errorElement("Expected to find one type argument in arrayOf call", c )
26802647
}
26812648
} else {
2682-
val elementType = c.type.getArrayElementType(pluginContext.irBuiltIns)
2683-
extractTypeAccessRecursive(elementType, locId, id, -1, callable, enclosingStmt)
2649+
logger.errorElement("Expected to find one type argument in arrayOf call", c)
2650+
null
26842651
}
2652+
}
26852653

2686-
arg?.let {
2687-
val initId = tw.getFreshIdLabel<DbArrayinit>()
2688-
tw.writeExprs_arrayinit(initId, type.javaResult.id, id, -2)
2689-
tw.writeExprsKotlinType(initId, type.kotlinResult.id)
2690-
tw.writeHasLocation(initId, locId)
2691-
tw.writeCallableEnclosingExpr(initId, callable)
2692-
tw.writeStatementEnclosingExpr(initId, enclosingStmt)
2693-
it.elements.forEachIndexed { i, arg -> extractVarargElement(arg, callable, initId, i, enclosingStmt) }
2694-
2695-
extractConstantInteger(it.elements.size, locId, id, 0, callable, enclosingStmt)
2654+
val arg = if (c.valueArgumentsCount == 1) c.getValueArgument(0) else {
2655+
logger.errorElement("Expected to find only one (vararg) argument in ${c.symbol.owner.name.asString()} call", c)
2656+
null
2657+
}?.let {
2658+
if (it is IrVararg) it else {
2659+
logger.errorElement("Expected to find vararg argument in ${c.symbol.owner.name.asString()} call", c)
2660+
null
26962661
}
26972662
}
2663+
2664+
extractArrayCreation(arg, c.type, elementType, isPrimitiveArrayCreation, c, parent, idx, callable, enclosingStmt)
26982665
}
26992666
isBuiltinCall(c, "<get-java>", "kotlin.jvm") -> {
27002667
// 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(
27142681
val argType = (ext.type as? IrSimpleType)?.arguments?.firstOrNull()?.typeOrNull
27152682
val typeArguments = if (argType == null) listOf() else listOf(argType)
27162683

2717-
extractRawMethodAccess(getter, c, callable, parent, idx, enclosingStmt, listOf(), null, ext, typeArguments)
2684+
extractRawMethodAccess(getter, c, c.type, callable, parent, idx, enclosingStmt, listOf(), null, ext, typeArguments)
27182685
}
27192686
}
27202687
isFunction(target, "kotlin", "(some array type)", { isArrayType(it) }, "iterator") -> {
@@ -2745,7 +2712,7 @@ open class KotlinFileExtractor(
27452712
else -> pluginContext.irBuiltIns.anyNType
27462713
}
27472714
}
2748-
extractRawMethodAccess(iteratorFn, c, callable, parent, idx, enclosingStmt, listOf(c.dispatchReceiver), null, null, typeArgs)
2715+
extractRawMethodAccess(iteratorFn, c, c.type, callable, parent, idx, enclosingStmt, listOf(c.dispatchReceiver), null, null, typeArgs)
27492716
}
27502717
}
27512718
}
@@ -2834,6 +2801,7 @@ open class KotlinFileExtractor(
28342801
extractRawMethodAccess(
28352802
realCallee,
28362803
c,
2804+
c.type,
28372805
callable,
28382806
parent,
28392807
idx,
@@ -2861,6 +2829,7 @@ open class KotlinFileExtractor(
28612829
extractRawMethodAccess(
28622830
realCallee,
28632831
c,
2832+
c.type,
28642833
callable,
28652834
parent,
28662835
idx,
@@ -2878,6 +2847,51 @@ open class KotlinFileExtractor(
28782847
}
28792848
}
28802849

2850+
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>) {
2851+
// If this is [someType]ArrayOf(*x), x, otherwise null
2852+
val clonedArray = elementList?.let {
2853+
if (it.elements.size == 1) {
2854+
val onlyElement = it.elements[0]
2855+
if (onlyElement is IrSpreadElement)
2856+
onlyElement.expression
2857+
else null
2858+
} else null
2859+
}
2860+
2861+
if (clonedArray != null) {
2862+
// This is an array clone: extract is as a call to java.lang.Object.clone
2863+
objectCloneMethod?.let {
2864+
extractRawMethodAccess(it, locElement, resultType, enclosingCallable, parent, idx, enclosingStmt, listOf(), clonedArray, null)
2865+
}
2866+
} else {
2867+
// This is array creation: extract it as a call to new ArrayType[] { ... }
2868+
val id = tw.getFreshIdLabel<DbArraycreationexpr>()
2869+
val type = useType(resultType)
2870+
tw.writeExprs_arraycreationexpr(id, type.javaResult.id, parent, idx)
2871+
tw.writeExprsKotlinType(id, type.kotlinResult.id)
2872+
val locId = tw.getLocation(locElement)
2873+
tw.writeHasLocation(id, locId)
2874+
tw.writeCallableEnclosingExpr(id, enclosingCallable)
2875+
2876+
if (elementType != null) {
2877+
val typeContext = if (allowPrimitiveElementType) TypeContext.OTHER else TypeContext.GENERIC_ARGUMENT
2878+
extractTypeAccessRecursive(elementType, locId, id, -1, enclosingCallable, enclosingStmt, typeContext)
2879+
}
2880+
2881+
if (elementList != null) {
2882+
val initId = tw.getFreshIdLabel<DbArrayinit>()
2883+
tw.writeExprs_arrayinit(initId, type.javaResult.id, id, -2)
2884+
tw.writeExprsKotlinType(initId, type.kotlinResult.id)
2885+
tw.writeHasLocation(initId, locId)
2886+
tw.writeCallableEnclosingExpr(initId, enclosingCallable)
2887+
tw.writeStatementEnclosingExpr(initId, enclosingStmt)
2888+
elementList.elements.forEachIndexed { i, arg -> extractVarargElement(arg, enclosingCallable, initId, i, enclosingStmt) }
2889+
2890+
extractConstantInteger(elementList.elements.size, locId, id, 0, enclosingCallable, enclosingStmt)
2891+
}
2892+
}
2893+
}
2894+
28812895
private fun extractNewExpr(
28822896
methodId: Label<out DbConstructor>,
28832897
constructedType: TypeResults,
@@ -3661,14 +3675,12 @@ open class KotlinFileExtractor(
36613675
extractTypeOperatorCall(e, callable, exprParent.parent, exprParent.idx, exprParent.enclosingStmt)
36623676
}
36633677
is IrVararg -> {
3664-
var spread = e.elements.getOrNull(0) as? IrSpreadElement
3665-
if (spread == null || e.elements.size != 1) {
3666-
logger.errorElement("Unexpected IrVararg", e)
3667-
return
3668-
}
36693678
// There are lowered IR cases when the vararg expression is not within a call, such as
3670-
// val temp0 = [*expr]
3671-
extractExpression(spread.expression, callable, parent)
3679+
// val temp0 = [*expr].
3680+
// This AST element can also occur as a collection literal in an annotation class, such as
3681+
// annotation class Ann(val strings: Array<String> = [])
3682+
val exprParent = parent.expr(e, callable)
3683+
extractArrayCreation(e, e.type, e.varargElementType, true, e, exprParent.parent, exprParent.idx, callable, exprParent.enclosingStmt)
36723684
}
36733685
is IrGetObjectValue -> {
36743686
// For `object MyObject { ... }`, the .class has an
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
test.kt:
2+
# 0| [CompilationUnit] test
3+
# 1| 1: [Interface] Ann
4+
# 1| 1: [Constructor] Ann
5+
#-----| 4: (Parameters)
6+
# 1| 0: [Parameter] arr1
7+
# 1| 0: [TypeAccess] String[]
8+
# 1| 0: [TypeAccess] String
9+
# 1| 1: [Parameter] arr2
10+
# 1| 0: [TypeAccess] int[]
11+
# 1| 5: [BlockStmt] { ... }
12+
# 1| 0: [SuperConstructorInvocationStmt] super(...)
13+
# 1| 1: [BlockStmt] { ... }
14+
# 1| 0: [ExprStmt] <Expr>;
15+
# 1| 0: [KtInitializerAssignExpr] ...=...
16+
# 1| 0: [VarAccess] arr1
17+
# 1| 1: [ExprStmt] <Expr>;
18+
# 1| 0: [KtInitializerAssignExpr] ...=...
19+
# 1| 0: [VarAccess] arr2
20+
# 1| 2: [Constructor] Ann
21+
#-----| 4: (Parameters)
22+
# 1| 0: [Parameter] p0
23+
# 1| 0: [TypeAccess] String[]
24+
# 1| 1: [Parameter] p1
25+
# 1| 0: [TypeAccess] int[]
26+
# 1| 2: [Parameter] p2
27+
# 1| 0: [TypeAccess] int
28+
# 1| 3: [Parameter] p3
29+
# 1| 0: [TypeAccess] DefaultConstructorMarker
30+
# 1| 5: [BlockStmt] { ... }
31+
# 1| 0: [IfStmt] if (...)
32+
# 1| 0: [EQExpr] ... == ...
33+
# 1| 0: [AndBitwiseExpr] ... & ...
34+
# 1| 0: [IntegerLiteral] 1
35+
# 1| 1: [VarAccess] p2
36+
# 1| 1: [IntegerLiteral] 0
37+
# 1| 1: [ExprStmt] <Expr>;
38+
# 1| 0: [AssignExpr] ...=...
39+
# 1| 0: [VarAccess] p0
40+
# 0| 1: [ArrayCreationExpr] new String[]
41+
# 0| -2: [ArrayInit] {...}
42+
# 0| 0: [StringLiteral] hello
43+
# 0| 1: [StringLiteral] world
44+
# 0| -1: [TypeAccess] String
45+
# 0| 0: [IntegerLiteral] 2
46+
# 1| 1: [IfStmt] if (...)
47+
# 1| 0: [EQExpr] ... == ...
48+
# 1| 0: [AndBitwiseExpr] ... & ...
49+
# 1| 0: [IntegerLiteral] 2
50+
# 1| 1: [VarAccess] p2
51+
# 1| 1: [IntegerLiteral] 0
52+
# 1| 1: [ExprStmt] <Expr>;
53+
# 1| 0: [AssignExpr] ...=...
54+
# 1| 0: [VarAccess] p1
55+
# 0| 1: [ArrayCreationExpr] new int[]
56+
# 0| -2: [ArrayInit] {...}
57+
# 0| 0: [IntegerLiteral] 1
58+
# 0| 1: [IntegerLiteral] 2
59+
# 0| 2: [IntegerLiteral] 3
60+
# 0| -1: [TypeAccess] int
61+
# 0| 0: [IntegerLiteral] 3
62+
# 1| 2: [ThisConstructorInvocationStmt] this(...)
63+
# 1| 0: [VarAccess] p0
64+
# 1| 1: [VarAccess] p1
65+
# 1| 3: [FieldDeclaration] String[] arr1;
66+
# 1| -1: [TypeAccess] String[]
67+
# 1| 0: [TypeAccess] String
68+
# 1| 0: [VarAccess] arr1
69+
# 1| 4: [Method] arr1
70+
# 1| 3: [TypeAccess] String[]
71+
# 1| 0: [TypeAccess] String
72+
# 1| 5: [BlockStmt] { ... }
73+
# 1| 0: [ReturnStmt] return ...
74+
# 1| 0: [VarAccess] this.arr1
75+
# 1| -1: [ThisAccess] this
76+
# 1| 5: [Method] arr2
77+
# 1| 3: [TypeAccess] int[]
78+
# 1| 5: [BlockStmt] { ... }
79+
# 1| 0: [ReturnStmt] return ...
80+
# 1| 0: [VarAccess] this.arr2
81+
# 1| -1: [ThisAccess] this
82+
# 1| 6: [FieldDeclaration] int[] arr2;
83+
# 1| -1: [TypeAccess] int[]
84+
# 1| 0: [VarAccess] arr2
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
semmle/code/java/PrintAst.ql
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
annotation class Ann(val arr1: Array<String> = ["hello", "world"], val arr2: IntArray = [1, 2, 3]) { }

0 commit comments

Comments
 (0)