diff --git a/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt b/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt index 0b975d9b829..4ff255a0fd1 100644 --- a/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt +++ b/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt @@ -4391,6 +4391,55 @@ open class KotlinFileExtractor( tw.writeExprsKotlinType(id, type.kotlinResult.id) unaryopDisp(id) } + // Fold unaryMinus applied to a numeric constant into a single negative literal. + // In K2 mode, `-123L` is emitted as IrCall(unaryMinus, IrConst(123L)) rather than + // IrConst(-123L) as in K1. Folding restores K1 behaviour and makes negative + // numeric literals directly queryable in both modes. + isNumericFunction(target, "unaryMinus") && c.dispatchReceiver is CodeQLIrConst<*> -> { + val receiver = c.dispatchReceiver as CodeQLIrConst<*> + val v = receiver.value + val type = useType(c.type) + // In K2 the IrCall's startOffset equals the receiver's startOffset, so the + // minus sign (one character before the literal) must be recovered from the source. + val locId = + if (receiver.startOffset > 0) + tw.getLocation(receiver.startOffset - 1, c.endOffset) + else + tw.getLocation(c) + when (v) { + is Int -> + extractConstantInteger(-v, locId, parent, idx, callable, enclosingStmt) + is Short -> + extractConstantInteger(-v.toInt(), locId, parent, idx, callable, enclosingStmt) + is Byte -> + extractConstantInteger(-v.toInt(), locId, parent, idx, callable, enclosingStmt) + is Long -> + exprIdOrFresh(null).also { id -> + tw.writeExprs_longliteral(id, type.javaResult.id, parent, idx) + tw.writeExprsKotlinType(id, type.kotlinResult.id) + extractExprContext(id, locId, callable, enclosingStmt) + tw.writeNamestrings((-v).toString(), (-v).toString(), id) + } + is Float -> + exprIdOrFresh(null).also { id -> + tw.writeExprs_floatingpointliteral(id, type.javaResult.id, parent, idx) + tw.writeExprsKotlinType(id, type.kotlinResult.id) + extractExprContext(id, locId, callable, enclosingStmt) + tw.writeNamestrings((-v).toString(), (-v).toString(), id) + } + is Double -> + exprIdOrFresh(null).also { id -> + tw.writeExprs_doubleliteral(id, type.javaResult.id, parent, idx) + tw.writeExprsKotlinType(id, type.kotlinResult.id) + extractExprContext(id, locId, callable, enclosingStmt) + tw.writeNamestrings((-v).toString(), (-v).toString(), id) + } + else -> + logger.errorElement( + "Unexpected constant type for unaryMinus folding: ${v?.javaClass}", c + ) + } + } isNumericFunction(target, "inv", "unaryMinus", "unaryPlus") -> { val type = useType(c.type) val id: Label = diff --git a/java/ql/test-kotlin2/library-tests/literals/literals.expected b/java/ql/test-kotlin2/library-tests/literals/literals.expected index bb6f6fd4739..3cea3c83ac5 100644 --- a/java/ql/test-kotlin2/library-tests/literals/literals.expected +++ b/java/ql/test-kotlin2/library-tests/literals/literals.expected @@ -11,16 +11,16 @@ | literals.kt:13:30:13:33 | -123 | LongLiteral | | literals.kt:14:30:14:31 | 0 | LongLiteral | | literals.kt:15:30:15:33 | 123 | LongLiteral | -| literals.kt:16:31:16:34 | 123 | LongLiteral | +| literals.kt:16:30:16:34 | -123 | LongLiteral | | literals.kt:17:30:17:33 | 15 | LongLiteral | | literals.kt:18:30:18:39 | 11 | LongLiteral | | literals.kt:19:30:19:38 | 1234567 | LongLiteral | | literals.kt:20:32:20:35 | 0.0 | FloatLiteral | | literals.kt:21:32:21:37 | 123.4 | FloatLiteral | -| literals.kt:22:33:22:38 | 123.4 | FloatLiteral | +| literals.kt:22:32:22:38 | -123.4 | FloatLiteral | | literals.kt:23:34:23:36 | 0.0 | DoubleLiteral | | literals.kt:24:34:24:38 | 123.4 | DoubleLiteral | -| literals.kt:25:35:25:39 | 123.4 | DoubleLiteral | +| literals.kt:25:34:25:39 | -123.4 | DoubleLiteral | | literals.kt:26:30:26:32 | c | CharacterLiteral | | literals.kt:27:30:27:33 | \n | CharacterLiteral | | literals.kt:28:34:28:35 | "" | StringLiteral |