Skip to content

Commit 82c3e53

Browse files
authored
Merge pull request #10473 from tamasvajk/kotlin-suspend
Kotlin: Extract `suspend` functions
2 parents 25d1098 + 9a6b17d commit 82c3e53

26 files changed

+1010
-80
lines changed

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

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -917,6 +917,9 @@ open class KotlinFileExtractor(
917917
if (f is IrSimpleFunction && f.overriddenSymbols.isNotEmpty()) {
918918
addModifiers(id, "override")
919919
}
920+
if (f.isSuspend) {
921+
addModifiers(id, "suspend")
922+
}
920923

921924
return id
922925
}
@@ -1468,7 +1471,7 @@ open class KotlinFileExtractor(
14681471

14691472
val (isFunctionInvoke, isBigArityFunctionInvoke) =
14701473
if (drType is IrSimpleType &&
1471-
drType.isFunctionOrKFunction() &&
1474+
(drType.isFunctionOrKFunction() || drType.isSuspendFunctionOrKFunction()) &&
14721475
callTarget.name.asString() == OperatorNameConventions.INVOKE.asString()) {
14731476
Pair(true, drType.arguments.size > BuiltInFunctionArity.BIG_ARITY)
14741477
} else {
@@ -4523,17 +4526,17 @@ open class KotlinFileExtractor(
45234526
```
45244527
*/
45254528

4526-
if (!e.argument.type.isFunctionOrKFunction()) {
4527-
logger.errorElement("Expected to find expression with function type in SAM conversion.", e)
4528-
return
4529-
}
4530-
45314529
val st = e.argument.type as? IrSimpleType
45324530
if (st == null) {
45334531
logger.errorElement("Expected to find a simple type in SAM conversion.", e)
45344532
return
45354533
}
45364534

4535+
if (!st.isFunctionOrKFunction() && !st.isSuspendFunctionOrKFunction()) {
4536+
logger.errorElement("Expected to find expression with function type in SAM conversion.", e)
4537+
return
4538+
}
4539+
45374540
// Either Function1, ... Function22 or FunctionN type, but not Function23 or above.
45384541
val functionType = getFunctionalInterfaceTypeWithTypeArgs(st.arguments)
45394542
if (functionType == null) {
@@ -4596,6 +4599,10 @@ open class KotlinFileExtractor(
45964599
// the real underlying R Function<T, R>.apply(T t).
45974600
forceExtractFunction(samMember, classId, extractBody = false, extractMethodAndParameterTypeAccesses = true, typeSub, classTypeArgs, ids.function, tw.getLocation(e))
45984601

4602+
if (st.isSuspendFunctionOrKFunction()) {
4603+
addModifiers(ids.function, "suspend")
4604+
}
4605+
45994606
//body
46004607
val blockId = tw.getFreshIdLabel<DbBlock>()
46014608
tw.writeStmts_block(blockId, ids.function, 0, ids.function)

java/ql/lib/semmle/code/java/Modifier.qll

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,9 @@ abstract class Modifiable extends Element {
6767
/** Holds if this element has an `inline` modifier. */
6868
predicate isInline() { this.hasModifier("inline") }
6969

70+
/** Holds if this element has a `suspend` modifier. */
71+
predicate isSuspend() { this.hasModifier("suspend") }
72+
7073
/** Holds if this element has a `volatile` modifier. */
7174
predicate isVolatile() { this.hasModifier("volatile") }
7275

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import kotlinx.coroutines.*
2+
3+
suspend fun fn() {
4+
GlobalScope.launch {
5+
val x: Deferred<String> = async { Helper.taint() }
6+
Helper.sink(x.await()) // TODO: not found
7+
}
8+
}

java/ql/test/kotlin/library-tests/dataflow/func/functionReference.kt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@ class FunctionReference {
22

33
fun fn1(s: String) = s
44

5-
fun test() {
5+
suspend fun test() {
66
fun fn2(s: String) = s
77

88
Helper.sink(Processor().process(this::fn1, Helper.taint()))
9+
Helper.sink(Processor().processSusp(this::fn1Susp, Helper.taint()))
910
Helper.sink(Processor().process(FunctionReference::fn1, this, Helper.taint()))
1011
Helper.sink(Processor().process(this::fn1, Helper.notaint()))
1112
Helper.sink(Processor().process(::fn2, Helper.taint()))
@@ -16,4 +17,6 @@ class FunctionReference {
1617

1718
val prop: String
1819
get() = Helper.taint()
20+
21+
suspend fun fn1Susp(s: String) = s
1922
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/**
2+
* Stubs for `kotlinx.coroutines`
3+
*/
4+
5+
@file:JvmName("BuildersKt") // Required for `async`
6+
7+
package kotlinx.coroutines
8+
9+
public interface CoroutineScope
10+
public interface CoroutineContext
11+
public enum class CoroutineStart { DEFAULT }
12+
public interface Job
13+
public interface Deferred<out T> : Job {
14+
public suspend fun await(): T
15+
}
16+
17+
public object GlobalScope : CoroutineScope
18+
19+
public fun CoroutineScope.launch(
20+
context: CoroutineContext = null!!,
21+
start: CoroutineStart = CoroutineStart.DEFAULT,
22+
block: suspend CoroutineScope.() -> Unit
23+
): Job {
24+
return null!!
25+
}
26+
27+
public fun <T> CoroutineScope.async(
28+
context: CoroutineContext = null!!,
29+
start: CoroutineStart = CoroutineStart.DEFAULT,
30+
block: suspend CoroutineScope.() -> T
31+
): Deferred<T> {
32+
return null!!
33+
}

java/ql/test/kotlin/library-tests/dataflow/func/lambda.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
class Lambda {
2-
fun test() {
2+
suspend fun test() {
33
Helper.sink(Processor().process({ it: String -> Helper.notaint() }, ""))
44
Helper.sink(Processor().process({ it: String -> Helper.taint() }, ""))
5+
Helper.sink(Processor().processSusp({ it: String -> Helper.taint() }, ""))
56
Helper.sink(Processor().process({ i -> i }, Helper.taint()))
67
Helper.sink(Processor().process(fun (i: String) = i, Helper.taint()))
78

@@ -29,4 +30,4 @@ class ManualBigLambda {
2930
Helper.sink(invoke(arrayOf(Helper.taint(), Helper.notaint())))
3031
Helper.sink(invoke(arrayOf(Helper.notaint(), Helper.taint()))) // False positive
3132
}
32-
}
33+
}
Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,15 @@
11
class LocalFunction {
2-
fun test() {
2+
suspend fun test() {
33
fun fn1() = Helper.taint()
44
fun fn2(s: String) = s
55

66
Helper.sink(fn1())
77
Helper.sink(fn2(Helper.taint()))
8+
9+
suspend fun fn3() = Helper.taint()
10+
suspend fun fn4(s: String) = s
11+
12+
Helper.sink(fn3())
13+
Helper.sink(fn4(Helper.taint()))
814
}
915
}

java/ql/test/kotlin/library-tests/dataflow/func/samConversion.kt

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,22 @@ fun interface Predicate {
22
fun go(s: String): String
33
}
44

5+
fun interface PredicateSusp {
6+
suspend fun go(s: String): String
7+
}
8+
59
class SamConversion {
6-
fun test() {
10+
suspend fun test() {
711
val p1 = Predicate { Helper.taint() }
812
val p2 = Predicate { it -> it }
913

1014
Helper.sink(p1.go(""))
11-
Helper.sink(p1.go(Helper.taint()))
15+
Helper.sink(p2.go(Helper.taint()))
16+
17+
val p3 = PredicateSusp { Helper.taint() }
18+
val p4 = PredicateSusp { it -> it }
19+
20+
Helper.sink(p3.go(""))
21+
Helper.sink(p4.go(Helper.taint()))
1222
}
1323
}
Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,22 @@
11
| functionReference.kt:8:59:8:65 | taint(...) | functionReference.kt:8:33:8:66 | process(...) |
2-
| functionReference.kt:9:78:9:84 | taint(...) | functionReference.kt:9:33:9:85 | process(...) |
3-
| functionReference.kt:11:55:11:61 | taint(...) | functionReference.kt:11:33:11:62 | process(...) |
4-
| functionReference.kt:18:24:18:30 | taint(...) | functionReference.kt:14:33:14:51 | process(...) |
2+
| functionReference.kt:10:78:10:84 | taint(...) | functionReference.kt:10:33:10:85 | process(...) |
3+
| functionReference.kt:12:55:12:61 | taint(...) | functionReference.kt:12:33:12:62 | process(...) |
4+
| functionReference.kt:19:24:19:30 | taint(...) | functionReference.kt:15:33:15:51 | process(...) |
55
| lambda.kt:4:64:4:70 | taint(...) | lambda.kt:4:33:4:77 | process(...) |
6-
| lambda.kt:5:60:5:66 | taint(...) | lambda.kt:5:33:5:67 | process(...) |
7-
| lambda.kt:6:69:6:75 | taint(...) | lambda.kt:6:33:6:76 | process(...) |
8-
| lambda.kt:9:81:9:87 | taint(...) | lambda.kt:9:33:9:88 | processExt(...) |
9-
| lambda.kt:10:66:10:72 | taint(...) | lambda.kt:10:33:10:91 | processExt(...) |
10-
| lambda.kt:13:145:13:151 | taint(...) | lambda.kt:13:33:13:170 | process(...) |
11-
| lambda.kt:14:163:14:169 | taint(...) | lambda.kt:14:33:14:170 | process(...) |
12-
| lambda.kt:27:35:27:41 | taint(...) | lambda.kt:27:21:27:60 | invoke(...) |
13-
| lambda.kt:29:43:29:49 | taint(...) | lambda.kt:29:21:29:69 | invoke(...) |
14-
| lambda.kt:30:61:30:67 | taint(...) | lambda.kt:30:21:30:69 | invoke(...) |
6+
| lambda.kt:6:60:6:66 | taint(...) | lambda.kt:6:33:6:67 | process(...) |
7+
| lambda.kt:7:69:7:75 | taint(...) | lambda.kt:7:33:7:76 | process(...) |
8+
| lambda.kt:10:81:10:87 | taint(...) | lambda.kt:10:33:10:88 | processExt(...) |
9+
| lambda.kt:11:66:11:72 | taint(...) | lambda.kt:11:33:11:91 | processExt(...) |
10+
| lambda.kt:14:145:14:151 | taint(...) | lambda.kt:14:33:14:170 | process(...) |
11+
| lambda.kt:15:163:15:169 | taint(...) | lambda.kt:15:33:15:170 | process(...) |
12+
| lambda.kt:28:35:28:41 | taint(...) | lambda.kt:28:21:28:60 | invoke(...) |
13+
| lambda.kt:30:43:30:49 | taint(...) | lambda.kt:30:21:30:69 | invoke(...) |
14+
| lambda.kt:31:61:31:67 | taint(...) | lambda.kt:31:21:31:69 | invoke(...) |
1515
| localFunction.kt:3:28:3:34 | taint(...) | localFunction.kt:6:21:6:25 | fn1(...) |
1616
| localFunction.kt:7:32:7:38 | taint(...) | localFunction.kt:7:21:7:39 | fn2(...) |
17-
| samConversion.kt:7:37:7:43 | taint(...) | samConversion.kt:10:24:10:29 | go(...) |
18-
| samConversion.kt:7:37:7:43 | taint(...) | samConversion.kt:11:24:11:41 | go(...) |
17+
| localFunction.kt:9:36:9:42 | taint(...) | localFunction.kt:12:21:12:25 | fn3(...) |
18+
| localFunction.kt:13:32:13:38 | taint(...) | localFunction.kt:13:21:13:39 | fn4(...) |
19+
| samConversion.kt:11:37:11:43 | taint(...) | samConversion.kt:14:24:14:29 | go(...) |
20+
| samConversion.kt:15:34:15:40 | taint(...) | samConversion.kt:15:24:15:41 | go(...) |
21+
| samConversion.kt:17:41:17:47 | taint(...) | samConversion.kt:20:24:20:29 | go(...) |
22+
| samConversion.kt:21:34:21:40 | taint(...) | samConversion.kt:21:24:21:41 | go(...) |

java/ql/test/kotlin/library-tests/dataflow/func/util.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ class Processor {
77
return f(arg)
88
}
99

10+
suspend fun <T, R2> processSusp(f: suspend (T) -> R2, arg: T) : R2 {
11+
return f(arg)
12+
}
13+
1014
fun <T0, T1, R3> process(f: (T0, T1) -> R3, arg0: T0, arg1: T1) : R3 {
1115
return f(arg0, arg1)
1216
}

0 commit comments

Comments
 (0)