From d4efdfcbfaf64c0caa0bf684155f7b1c985a97c8 Mon Sep 17 00:00:00 2001 From: Anders Fugmann Date: Tue, 7 Jul 2026 17:54:14 +0200 Subject: [PATCH] kotlin-extractor: fix NotNullExpr start offset to span from operand In K1 mode, the IrCall node for the !! operator stores startOffset at the '!' character rather than at the start of the operand. This means that x!! was previously reported as spanning from '!' to the end of the expression, omitting the operand from the location. Fix this by computing the NotNullExpr location from the value argument's startOffset (the operand) to c.endOffset. This matches the K2 behaviour where the IrCall.startOffset already points at the operand. The fix guards against invalid (< 0) offsets on either side and falls back to the default IrCall location in those cases. Updated expected files in test-kotlin1: - exprs/unaryOp.expected: !! locations now start at operand column - exprs/exprs.expected: same (NotNullExpr entries corrected) - exprs/binop.expected: !! child locations now correct (parent binop location for s!!.plus(5) still differs due to a separate K1 IR limitation where the enclosing call inherits the wrong receiver offset) - controlflow/basic/bbStmts.expected: CFG references to !! now use the improved location - controlflow/basic/getASuccessor.expected: same Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../src/main/kotlin/KotlinFileExtractor.kt | 12 +++++++++--- .../controlflow/basic/bbStmts.expected | 18 +++++++++--------- .../controlflow/basic/getASuccessor.expected | 6 +++--- .../library-tests/exprs/binop.expected | 4 ++-- .../library-tests/exprs/exprs.expected | 6 +++--- .../library-tests/exprs/unaryOp.expected | 6 +++--- 6 files changed, 29 insertions(+), 23 deletions(-) diff --git a/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt b/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt index a318528010f..49705f57d2e 100644 --- a/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt +++ b/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt @@ -3084,9 +3084,10 @@ open class KotlinFileExtractor( id: Label, c: IrCall, callable: Label, - enclosingStmt: Label + enclosingStmt: Label, + customLocId: Label? = null ) { - val locId = tw.getLocation(c) + val locId = customLocId ?: tw.getLocation(c) extractExprContext(id, locId, callable, enclosingStmt) val dr = c.dispatchReceiver @@ -4584,7 +4585,12 @@ open class KotlinFileExtractor( val type = useType(c.type) tw.writeExprs_notnullexpr(id, type.javaResult.id, parent, idx) tw.writeExprsKotlinType(id, type.kotlinResult.id) - unaryOp(id, c, callable, enclosingStmt) + // In K1 mode the IrCall.startOffset for !! points to the '!' character rather + // than the start of the operand. Use the operand's startOffset instead so that + // the NotNullExpr spans from the operand to the end of '!!'. + val operandStart = c.codeQlGetValueArgument(0)?.startOffset?.takeIf { it >= 0 } + val notNullLocId = if (operandStart != null && c.endOffset >= 0) tw.getLocation(operandStart, c.endOffset) else null + unaryOp(id, c, callable, enclosingStmt, notNullLocId) } isBuiltinCallInternal(c, "THROW_CCE") -> { // TODO diff --git a/java/ql/test-kotlin1/library-tests/controlflow/basic/bbStmts.expected b/java/ql/test-kotlin1/library-tests/controlflow/basic/bbStmts.expected index 61d80537663..783ea4712fe 100644 --- a/java/ql/test-kotlin1/library-tests/controlflow/basic/bbStmts.expected +++ b/java/ql/test-kotlin1/library-tests/controlflow/basic/bbStmts.expected @@ -251,18 +251,18 @@ | Test.kt:91:1:98:1 | Entry | 3 | Test.kt:92:6:95:2 | { ... } | | Test.kt:91:1:98:1 | Entry | 4 | Test.kt:93:7:93:7 | var ...; | | Test.kt:91:1:98:1 | Entry | 5 | Test.kt:93:7:93:7 | Before x | -| Test.kt:91:1:98:1 | Entry | 6 | Test.kt:93:12:93:13 | Before ...!! | +| Test.kt:91:1:98:1 | Entry | 6 | Test.kt:93:11:93:13 | Before ...!! | | Test.kt:91:1:98:1 | Entry | 7 | Test.kt:93:11:93:11 | o | -| Test.kt:91:1:98:1 | Entry | 8 | Test.kt:93:12:93:13 | ...!! | +| Test.kt:91:1:98:1 | Entry | 8 | Test.kt:93:11:93:13 | ...!! | | Test.kt:91:1:98:1 | Exit | 0 | Test.kt:91:1:98:1 | Exit | | Test.kt:91:1:98:1 | Normal Exit | 0 | Test.kt:91:1:98:1 | Normal Exit | -| Test.kt:93:12:93:13 | After ...!! | 0 | Test.kt:93:12:93:13 | After ...!! | -| Test.kt:93:12:93:13 | After ...!! | 1 | Test.kt:93:7:93:7 | x | -| Test.kt:93:12:93:13 | After ...!! | 2 | Test.kt:93:7:93:7 | After x | -| Test.kt:93:12:93:13 | After ...!! | 3 | Test.kt:93:7:93:7 | After var ...; | -| Test.kt:93:12:93:13 | After ...!! | 4 | Test.kt:94:3:94:10 | Before return ... | -| Test.kt:93:12:93:13 | After ...!! | 5 | Test.kt:94:10:94:10 | 1 | -| Test.kt:93:12:93:13 | After ...!! | 6 | Test.kt:94:3:94:10 | return ... | +| Test.kt:93:11:93:13 | After ...!! | 0 | Test.kt:93:11:93:13 | After ...!! | +| Test.kt:93:11:93:13 | After ...!! | 1 | Test.kt:93:7:93:7 | x | +| Test.kt:93:11:93:13 | After ...!! | 2 | Test.kt:93:7:93:7 | After x | +| Test.kt:93:11:93:13 | After ...!! | 3 | Test.kt:93:7:93:7 | After var ...; | +| Test.kt:93:11:93:13 | After ...!! | 4 | Test.kt:94:3:94:10 | Before return ... | +| Test.kt:93:11:93:13 | After ...!! | 5 | Test.kt:94:10:94:10 | 1 | +| Test.kt:93:11:93:13 | After ...!! | 6 | Test.kt:94:3:94:10 | return ... | | Test.kt:95:4:97:2 | catch (...) | 0 | Test.kt:95:4:97:2 | catch (...) | | Test.kt:95:4:97:2 | catch (...) | 1 | Test.kt:95:11:95:33 | e | | Test.kt:95:11:95:33 | After e [match] | 0 | Test.kt:95:11:95:33 | After e [match] | diff --git a/java/ql/test-kotlin1/library-tests/controlflow/basic/getASuccessor.expected b/java/ql/test-kotlin1/library-tests/controlflow/basic/getASuccessor.expected index 5f4663d14b5..67f03324b42 100644 --- a/java/ql/test-kotlin1/library-tests/controlflow/basic/getASuccessor.expected +++ b/java/ql/test-kotlin1/library-tests/controlflow/basic/getASuccessor.expected @@ -150,9 +150,9 @@ | Test.kt:92:6:95:2 | { ... } | BlockStmt | Test.kt:93:7:93:7 | var ...; | LocalVariableDeclStmt | | Test.kt:93:7:93:7 | var ...; | LocalVariableDeclStmt | Test.kt:93:11:93:11 | o | VarAccess | | Test.kt:93:7:93:7 | x | LocalVariableDeclExpr | Test.kt:94:10:94:10 | 1 | IntegerLiteral | -| Test.kt:93:11:93:11 | o | VarAccess | Test.kt:93:12:93:13 | ...!! | NotNullExpr | -| Test.kt:93:12:93:13 | ...!! | NotNullExpr | Test.kt:93:7:93:7 | x | LocalVariableDeclExpr | -| Test.kt:93:12:93:13 | ...!! | NotNullExpr | Test.kt:95:4:97:2 | catch (...) | CatchClause | +| Test.kt:93:11:93:11 | o | VarAccess | Test.kt:93:11:93:13 | ...!! | NotNullExpr | +| Test.kt:93:11:93:13 | ...!! | NotNullExpr | Test.kt:93:7:93:7 | x | LocalVariableDeclExpr | +| Test.kt:93:11:93:13 | ...!! | NotNullExpr | Test.kt:95:4:97:2 | catch (...) | CatchClause | | Test.kt:94:3:94:10 | return ... | ReturnStmt | Test.kt:91:1:98:1 | Normal Exit | Method | | Test.kt:94:10:94:10 | 1 | IntegerLiteral | Test.kt:94:3:94:10 | return ... | ReturnStmt | | Test.kt:95:4:97:2 | catch (...) | CatchClause | Test.kt:95:11:95:33 | e | LocalVariableDeclExpr | diff --git a/java/ql/test-kotlin1/library-tests/exprs/binop.expected b/java/ql/test-kotlin1/library-tests/exprs/binop.expected index f69701028d5..c0d798a2679 100644 --- a/java/ql/test-kotlin1/library-tests/exprs/binop.expected +++ b/java/ql/test-kotlin1/library-tests/exprs/binop.expected @@ -96,8 +96,8 @@ | exprs.kt:141:12:141:20 | ... + ... | exprs.kt:141:12:141:14 | 123 | exprs.kt:141:18:141:20 | 456 | | exprs.kt:167:8:167:16 | ... (value not-equals) ... | exprs.kt:167:8:167:8 | r | exprs.kt:167:13:167:16 | null | | exprs.kt:196:31:196:37 | ... + ... | exprs.kt:196:31:196:32 | getA1(...) | exprs.kt:196:36:196:37 | a2 | -| exprs.kt:211:20:211:29 | ... + ... | exprs.kt:211:20:211:21 | ...!! | exprs.kt:211:28:211:28 | 5 | -| exprs.kt:212:19:212:25 | ... + ... | exprs.kt:212:20:212:21 | ...!! | exprs.kt:212:25:212:25 | 5 | +| exprs.kt:211:20:211:29 | ... + ... | exprs.kt:211:19:211:21 | ...!! | exprs.kt:211:28:211:28 | 5 | +| exprs.kt:212:19:212:25 | ... + ... | exprs.kt:212:19:212:21 | ...!! | exprs.kt:212:25:212:25 | 5 | | exprs.kt:230:12:230:47 | ... (value equals) ... | exprs.kt:230:12:230:27 | notNullPrimitive | exprs.kt:230:32:230:47 | notNullPrimitive | | exprs.kt:231:12:231:48 | ... (value equals) ... | exprs.kt:231:12:231:27 | notNullPrimitive | exprs.kt:231:32:231:48 | nullablePrimitive | | exprs.kt:232:12:232:49 | ... (value equals) ... | exprs.kt:232:12:232:28 | nullablePrimitive | exprs.kt:232:33:232:49 | nullablePrimitive | diff --git a/java/ql/test-kotlin1/library-tests/exprs/exprs.expected b/java/ql/test-kotlin1/library-tests/exprs/exprs.expected index 83fbd974893..a0ab1de18b9 100644 --- a/java/ql/test-kotlin1/library-tests/exprs/exprs.expected +++ b/java/ql/test-kotlin1/library-tests/exprs/exprs.expected @@ -1572,7 +1572,7 @@ | exprs.kt:201:22:201:28 | Object | file://:0:0:0:0 | | TypeAccess | | exprs.kt:202:9:202:9 | y | exprs.kt:201:1:203:1 | notNullAssertion | LocalVariableDeclExpr | | exprs.kt:202:18:202:18 | x | exprs.kt:201:1:203:1 | notNullAssertion | VarAccess | -| exprs.kt:202:19:202:20 | ...!! | exprs.kt:201:1:203:1 | notNullAssertion | NotNullExpr | +| exprs.kt:202:18:202:20 | ...!! | exprs.kt:201:1:203:1 | notNullAssertion | NotNullExpr | | exprs.kt:206:5:217:5 | Unit | file://:0:0:0:0 | | TypeAccess | | exprs.kt:206:11:206:18 | Object | file://:0:0:0:0 | | TypeAccess | | exprs.kt:206:21:206:30 | String | file://:0:0:0:0 | | TypeAccess | @@ -1592,13 +1592,13 @@ | exprs.kt:210:23:210:23 | 5 | exprs.kt:206:5:217:5 | x | IntegerLiteral | | exprs.kt:211:13:211:14 | b2 | exprs.kt:206:5:217:5 | x | LocalVariableDeclExpr | | exprs.kt:211:19:211:19 | s | exprs.kt:206:5:217:5 | x | VarAccess | -| exprs.kt:211:20:211:21 | ...!! | exprs.kt:206:5:217:5 | x | NotNullExpr | +| exprs.kt:211:19:211:21 | ...!! | exprs.kt:206:5:217:5 | x | NotNullExpr | | exprs.kt:211:20:211:29 | ... + ... | exprs.kt:206:5:217:5 | x | AddExpr | | exprs.kt:211:28:211:28 | 5 | exprs.kt:206:5:217:5 | x | IntegerLiteral | | exprs.kt:212:13:212:14 | b3 | exprs.kt:206:5:217:5 | x | LocalVariableDeclExpr | | exprs.kt:212:19:212:19 | s | exprs.kt:206:5:217:5 | x | VarAccess | +| exprs.kt:212:19:212:21 | ...!! | exprs.kt:206:5:217:5 | x | NotNullExpr | | exprs.kt:212:19:212:25 | ... + ... | exprs.kt:206:5:217:5 | x | AddExpr | -| exprs.kt:212:20:212:21 | ...!! | exprs.kt:206:5:217:5 | x | NotNullExpr | | exprs.kt:212:25:212:25 | 5 | exprs.kt:206:5:217:5 | x | IntegerLiteral | | exprs.kt:213:13:213:14 | c0 | exprs.kt:206:5:217:5 | x | LocalVariableDeclExpr | | exprs.kt:213:18:213:36 | Color | exprs.kt:206:5:217:5 | x | TypeAccess | diff --git a/java/ql/test-kotlin1/library-tests/exprs/unaryOp.expected b/java/ql/test-kotlin1/library-tests/exprs/unaryOp.expected index 03b5d64a7f4..487226320cc 100644 --- a/java/ql/test-kotlin1/library-tests/exprs/unaryOp.expected +++ b/java/ql/test-kotlin1/library-tests/exprs/unaryOp.expected @@ -2,9 +2,9 @@ | exprs.kt:32:15:32:26 | !... | exprs.kt:32:15:32:26 | contains(...) | | exprs.kt:79:15:79:22 | ~... | exprs.kt:79:15:79:16 | lx | | exprs.kt:121:14:121:16 | !... | exprs.kt:121:15:121:16 | b1 | -| exprs.kt:202:19:202:20 | ...!! | exprs.kt:202:18:202:18 | x | -| exprs.kt:211:20:211:21 | ...!! | exprs.kt:211:19:211:19 | s | -| exprs.kt:212:20:212:21 | ...!! | exprs.kt:212:19:212:19 | s | +| exprs.kt:202:18:202:20 | ...!! | exprs.kt:202:18:202:18 | x | +| exprs.kt:211:19:211:21 | ...!! | exprs.kt:211:19:211:19 | s | +| exprs.kt:212:19:212:21 | ...!! | exprs.kt:212:19:212:19 | s | | exprs.kt:286:5:286:6 | -... | exprs.kt:286:6:286:6 | i | | exprs.kt:287:5:287:6 | +... | exprs.kt:287:6:287:6 | i | | exprs.kt:288:5:288:6 | -... | exprs.kt:288:6:288:6 | d |