Skip to content

Commit f52b1e4

Browse files
authored
Merge pull request github#24 from github/kotlin-expressions-1
Extract break/continue/throw
2 parents 4865847 + cca6975 commit f52b1e4

File tree

8 files changed

+135
-4
lines changed

8 files changed

+135
-4
lines changed

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

Lines changed: 53 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -634,6 +634,8 @@ class KotlinFileExtractor(val logger: FileLogger, val tw: FileTrapWriter, val fi
634634
}
635635
}
636636

637+
private val loopIdMap: MutableMap<IrLoop, Label<out DbKtloopstmt>> = mutableMapOf()
638+
637639
fun extractExpression(e: IrExpression, callable: Label<out DbCallable>, parent: Label<out DbExprparent>, idx: Int) {
638640
when(e) {
639641
is IrCall -> extractCall(e, callable, parent, idx)
@@ -716,22 +718,42 @@ class KotlinFileExtractor(val logger: FileLogger, val tw: FileTrapWriter, val fi
716718

717719
extractExpression(e.value, callable, id, 1)
718720
}
721+
is IrThrow -> {
722+
val id = tw.getFreshIdLabel<DbThrowstmt>()
723+
val locId = tw.getLocation(e)
724+
tw.writeStmts_throwstmt(id, parent, idx, callable)
725+
tw.writeHasLocation(id, locId)
726+
extractExpression(e.value, callable, id, 0)
727+
}
728+
is IrBreak -> {
729+
val id = tw.getFreshIdLabel<DbBreakstmt>()
730+
tw.writeStmts_breakstmt(id, parent, idx, callable)
731+
extractBreakContinue(e, id)
732+
}
733+
is IrContinue -> {
734+
val id = tw.getFreshIdLabel<DbContinuestmt>()
735+
tw.writeStmts_continuestmt(id, parent, idx, callable)
736+
extractBreakContinue(e, id)
737+
}
719738
is IrReturn -> {
720739
val id = tw.getFreshIdLabel<DbReturnstmt>()
721740
val locId = tw.getLocation(e)
722741
tw.writeStmts_returnstmt(id, parent, idx, callable)
723742
tw.writeHasLocation(id, locId)
724743
extractExpression(e.value, callable, id, 0)
725-
} is IrContainerExpression -> {
744+
}
745+
is IrContainerExpression -> {
726746
val id = tw.getFreshIdLabel<DbBlock>()
727747
val locId = tw.getLocation(e)
728748
tw.writeStmts_block(id, parent, idx, callable)
729749
tw.writeHasLocation(id, locId)
730750
e.statements.forEachIndexed { i, s ->
731751
extractStatement(s, callable, id, i)
732752
}
733-
} is IrWhileLoop -> {
753+
}
754+
is IrWhileLoop -> {
734755
val id = tw.getFreshIdLabel<DbWhilestmt>()
756+
loopIdMap[e] = id
735757
val locId = tw.getLocation(e)
736758
tw.writeStmts_whilestmt(id, parent, idx, callable)
737759
tw.writeHasLocation(id, locId)
@@ -740,8 +762,11 @@ class KotlinFileExtractor(val logger: FileLogger, val tw: FileTrapWriter, val fi
740762
if(body != null) {
741763
extractExpression(body, callable, id, 1)
742764
}
743-
} is IrDoWhileLoop -> {
765+
loopIdMap.remove(e)
766+
}
767+
is IrDoWhileLoop -> {
744768
val id = tw.getFreshIdLabel<DbDostmt>()
769+
loopIdMap[e] = id
745770
val locId = tw.getLocation(e)
746771
tw.writeStmts_dostmt(id, parent, idx, callable)
747772
tw.writeHasLocation(id, locId)
@@ -750,7 +775,9 @@ class KotlinFileExtractor(val logger: FileLogger, val tw: FileTrapWriter, val fi
750775
if(body != null) {
751776
extractExpression(body, callable, id, 1)
752777
}
753-
} is IrWhen -> {
778+
loopIdMap.remove(e)
779+
}
780+
is IrWhen -> {
754781
val id = tw.getFreshIdLabel<DbWhenexpr>()
755782
val typeId = useType(e.type)
756783
val locId = tw.getLocation(e)
@@ -784,5 +811,27 @@ class KotlinFileExtractor(val logger: FileLogger, val tw: FileTrapWriter, val fi
784811
}
785812
}
786813
}
814+
815+
private fun extractBreakContinue(
816+
e: IrBreakContinue,
817+
id: Label<out DbBreakcontinuestmt>
818+
) {
819+
val locId = tw.getLocation(e)
820+
@Suppress("UNCHECKED_CAST")
821+
tw.writeHasLocation(id as Label<out DbLocatable>, locId)
822+
val label = e.label
823+
if (label != null) {
824+
@Suppress("UNCHECKED_CAST")
825+
tw.writeNamestrings(label, "", id as Label<out DbNamedexprorstmt>)
826+
}
827+
828+
val loopId = loopIdMap[e.loop]
829+
if (loopId == null) {
830+
logger.warnElement(Severity.ErrorSevere, "Missing break/continue target", e)
831+
return
832+
}
833+
834+
tw.writeKtBreakContinueTarget(id, loopId)
835+
}
787836
}
788837

java/ql/lib/config/semmlecode.dbscheme

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1046,4 +1046,15 @@ ktCommentSectionSubjectNames(
10461046
ktCommentOwner(
10471047
int id: @ktcomment ref,
10481048
int owner: @top ref
1049+
)
1050+
1051+
@breakcontinuestmt = @breakstmt
1052+
| @continuestmt;
1053+
1054+
@ktloopstmt = @whilestmt
1055+
| @dostmt;
1056+
1057+
ktBreakContinueTarget(
1058+
unique int id: @breakcontinuestmt ref,
1059+
int target: @ktloopstmt ref
10491060
)

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

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -898,3 +898,27 @@ class SuperConstructorInvocationStmt extends Stmt, ConstructorCall, @superconstr
898898

899899
override string getAPrimaryQlClass() { result = "SuperConstructorInvocationStmt" }
900900
}
901+
902+
/** A Kotlin loop statement. */
903+
class KtLoopStmt extends Stmt, @ktloopstmt {
904+
KtLoopStmt() {
905+
this instanceof WhileStmt or
906+
this instanceof DoStmt
907+
}
908+
}
909+
910+
/** A Kotlin `break` or `continue` statement. */
911+
abstract class KtBreakContinueStmt extends Stmt, @breakcontinuestmt {
912+
KtLoopStmt loop;
913+
914+
KtBreakContinueStmt() { ktBreakContinueTarget(this, loop) }
915+
916+
/** Gets the target loop statement of this `break`. */
917+
KtLoopStmt getLoopStmt() { result = loop }
918+
}
919+
920+
/** A Kotlin `break` statement. */
921+
class KtBreakStmt extends BreakStmt, KtBreakContinueStmt { }
922+
923+
/** A Kotlin `continue` statement. */
924+
class KtContinueStmt extends ContinueStmt, KtBreakContinueStmt { }

java/ql/test/kotlin/library-tests/stmts/exprs.expected

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,16 @@
4343
| stmts.kt:19:12:19:12 | x | VarAccess |
4444
| stmts.kt:19:12:19:16 | ... + ... | AddExpr |
4545
| stmts.kt:19:16:19:16 | y | VarAccess |
46+
| stmts.kt:23:18:23:18 | x | VarAccess |
47+
| stmts.kt:23:18:23:24 | ... < ... | LTExpr |
48+
| stmts.kt:23:22:23:24 | 100 | IntegerLiteral |
49+
| stmts.kt:25:13:25:33 | when ... | WhenExpr |
50+
| stmts.kt:25:17:25:17 | x | VarAccess |
51+
| stmts.kt:25:17:25:21 | ... > ... | GTExpr |
52+
| stmts.kt:25:21:25:21 | y | VarAccess |
53+
| stmts.kt:26:18:26:18 | y | VarAccess |
54+
| stmts.kt:26:18:26:24 | ... > ... | GTExpr |
55+
| stmts.kt:26:22:26:24 | 100 | IntegerLiteral |
56+
| stmts.kt:28:11:28:11 | x | VarAccess |
57+
| stmts.kt:28:11:28:15 | ... > ... | GTExpr |
58+
| stmts.kt:28:15:28:15 | y | VarAccess |
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
breakLabel
2+
| stmts.kt:25:24:25:33 | break | loop |
3+
continueLabel
4+
breakTarget
5+
| stmts.kt:25:24:25:33 | break | stmts.kt:23:11:27:5 | while (...) |
6+
continueTarget
7+
| stmts.kt:29:9:29:16 | continue | stmts.kt:28:5:29:16 | while (...) |
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import java
2+
3+
query predicate breakLabel(BreakStmt s, string label) { s.getLabel() = label }
4+
5+
query predicate continueLabel(ContinueStmt s, string label) { s.getLabel() = label }
6+
7+
query predicate breakTarget(KtBreakStmt s, KtLoopStmt l) { s.getLoopStmt() = l }
8+
9+
query predicate continueTarget(KtContinueStmt s, KtLoopStmt l) { s.getLoopStmt() = l }

java/ql/test/kotlin/library-tests/stmts/stmts.expected

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,12 @@
1414
| stmts.kt:17:35:17:43 | { ... } | BlockStmt |
1515
| stmts.kt:17:50:17:58 | { ... } | BlockStmt |
1616
| stmts.kt:19:5:19:16 | return ... | ReturnStmt |
17+
| stmts.kt:22:27:30:1 | { ... } | BlockStmt |
18+
| stmts.kt:23:11:27:5 | while (...) | WhileStmt |
19+
| stmts.kt:23:27:27:5 | { ... } | BlockStmt |
20+
| stmts.kt:24:9:26:25 | do ... while (...) | DoStmt |
21+
| stmts.kt:24:9:26:25 | { ... } | BlockStmt |
22+
| stmts.kt:24:13:26:9 | { ... } | BlockStmt |
23+
| stmts.kt:25:24:25:33 | break | BreakStmt |
24+
| stmts.kt:28:5:29:16 | while (...) | WhileStmt |
25+
| stmts.kt:29:9:29:16 | continue | ContinueStmt |

java/ql/test/kotlin/library-tests/stmts/stmts.kt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,12 @@ fun topLevelMethod(x: Int, y: Int): Int {
1919
return x + y
2020
}
2121

22+
fun loops(x: Int, y: Int) {
23+
loop@ while (x < 100) {
24+
do {
25+
if (x > y) break@loop
26+
} while (y > 100)
27+
}
28+
while(x > y)
29+
continue
30+
}

0 commit comments

Comments
 (0)