diff --git a/java/kotlin-extractor/src/main/kotlin/KotlinExtractorExtension.kt b/java/kotlin-extractor/src/main/kotlin/KotlinExtractorExtension.kt index 466241f3d5e..8ccee447183 100644 --- a/java/kotlin-extractor/src/main/kotlin/KotlinExtractorExtension.kt +++ b/java/kotlin-extractor/src/main/kotlin/KotlinExtractorExtension.kt @@ -1396,32 +1396,66 @@ open class KotlinFileExtractor( } } - fun extractCall(c: IrCall, callable: Label, parent: Label, idx: Int) { - fun isBuiltin(fName: String): Boolean { - val verbose = false - fun verboseln(s: String) { if(verbose) println(s) } - verboseln("Attempting builtin match for $fName") - val target = c.symbol.owner - if (target.name.asString() != fName) { - verboseln("No match as function name is ${target.name.asString()} not $fName") - return false - } - val extensionReceiverParameter = target.extensionReceiverParameter - // TODO: Are both branches of this `if` possible?: - val targetPkg = if (extensionReceiverParameter == null) target.parent - else (extensionReceiverParameter.type as? IrSimpleType)?.classifier?.owner - if (targetPkg !is IrPackageFragment) { - verboseln("No match as didn't find target package") - return false - } - if (targetPkg.fqName.asString() != "kotlin.internal.ir") { - verboseln("No match as package name is ${targetPkg.fqName.asString()}") - return false - } - verboseln("Match") - return true + fun isBuiltinCall(c: IrCall, fName: String): Boolean { + val verbose = false + fun verboseln(s: String) { if(verbose) println(s) } + verboseln("Attempting builtin match for $fName") + val target = c.symbol.owner + if (target.name.asString() != fName) { + verboseln("No match as function name is ${target.name.asString()} not $fName") + return false } + val extensionReceiverParameter = target.extensionReceiverParameter + // TODO: Are both branches of this `if` possible?: + val targetPkg = if (extensionReceiverParameter == null) target.parent + else (extensionReceiverParameter.type as? IrSimpleType)?.classifier?.owner + if (targetPkg !is IrPackageFragment) { + verboseln("No match as didn't find target package") + return false + } + if (targetPkg.fqName.asString() != "kotlin.internal.ir") { + verboseln("No match as package name is ${targetPkg.fqName.asString()}") + return false + } + verboseln("Match") + return true + } + fun binop(id: Label, c: IrCall, callable: Label) { + val locId = tw.getLocation(c) + tw.writeHasLocation(id, locId) + tw.writeCallableEnclosingExpr(id, callable) + + val dr = c.dispatchReceiver + if(dr != null) { + logger.warnElement(Severity.ErrorSevere, "Unexpected dispatch receiver found", c) + } + if(c.valueArgumentsCount < 1) { + logger.warnElement(Severity.ErrorSevere, "No arguments found", c) + } else { + val lhs = c.getValueArgument(0) + if(lhs == null) { + logger.warnElement(Severity.ErrorSevere, "LHS null", c) + } else { + extractExpressionExpr(lhs, callable, id, 0) + } + if(c.valueArgumentsCount < 2) { + logger.warnElement(Severity.ErrorSevere, "No RHS found", c) + } else { + val rhs = c.getValueArgument(1) + if(rhs == null) { + logger.warnElement(Severity.ErrorSevere, "RHS null", c) + } else { + extractExpressionExpr(rhs, callable, id, 1) + } + } + if(c.valueArgumentsCount > 2) { + logger.warnElement(Severity.ErrorSevere, "Extra arguments found", c) + } + } + } + + fun extractCall(c: IrCall, callable: Label, parent: Label, idx: Int) { fun isFunction(pkgName: String, className: String, fName: String): Boolean { val verbose = false fun verboseln(s: String) { if(verbose) println(s) } @@ -1432,6 +1466,7 @@ open class KotlinFileExtractor( return false } val extensionReceiverParameter = target.extensionReceiverParameter + // TODO: Are both branches of this `if` possible?: val targetClass = if (extensionReceiverParameter == null) target.parent else (extensionReceiverParameter.type as? IrSimpleType)?.classifier?.owner if (targetClass !is IrClass) { @@ -1481,40 +1516,7 @@ open class KotlinFileExtractor( } } - fun binop(id: Label) { - val locId = tw.getLocation(c) - tw.writeHasLocation(id, locId) - tw.writeCallableEnclosingExpr(id, callable) - - val dr = c.dispatchReceiver - if(dr != null) { - logger.warnElement(Severity.ErrorSevere, "Unexpected dispatch receiver found", c) - } - if(c.valueArgumentsCount < 1) { - logger.warnElement(Severity.ErrorSevere, "No arguments found", c) - } else { - val lhs = c.getValueArgument(0) - if(lhs == null) { - logger.warnElement(Severity.ErrorSevere, "LHS null", c) - } else { - extractExpressionExpr(lhs, callable, id, 0) - } - if(c.valueArgumentsCount < 2) { - logger.warnElement(Severity.ErrorSevere, "No RHS found", c) - } else { - val rhs = c.getValueArgument(1) - if(rhs == null) { - logger.warnElement(Severity.ErrorSevere, "RHS null", c) - } else { - extractExpressionExpr(rhs, callable, id, 1) - } - } - if(c.valueArgumentsCount > 2) { - logger.warnElement(Severity.ErrorSevere, "Extra arguments found", c) - } - } - } - + val dr = c.dispatchReceiver when { c.origin == PLUS && (isFunction("kotlin", "Int", "plus") || isFunction("kotlin", "String", "plus")) -> { @@ -1541,62 +1543,58 @@ open class KotlinFileExtractor( tw.writeExprs_remexpr(id, type.javaResult.id, type.kotlinResult.id, parent, idx) binopDisp(id) } + // != gets desugared into not and ==. Here we resugar it. + c.origin == EXCLEQ && isFunction("kotlin", "Boolean", "not") && c.valueArgumentsCount == 0 && dr != null && dr is IrCall && isBuiltinCall(dr, "EQEQ") -> { + val id = tw.getFreshIdLabel() + val type = useType(c.type) + tw.writeExprs_neexpr(id, type.javaResult.id, type.kotlinResult.id, parent, idx) + binop(id, dr, callable) + } // compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/IrBuiltIns.kt - isBuiltin("EQEQ") -> { + isBuiltinCall(c, "EQEQ") -> { if(c.origin != EQEQ) { logger.warnElement(Severity.ErrorSevere, "Unexpected origin for EQEQ: ${c.origin}", c) } val id = tw.getFreshIdLabel() val type = useType(c.type) tw.writeExprs_eqexpr(id, type.javaResult.id, type.kotlinResult.id, parent, idx) - binop(id) + binop(id, c, callable) } -/* -TODO - c.origin == EXCLEQ -> { - val id = tw.getFreshIdLabel() - val type = useType(c.type) - val locId = tw.getLocation(c) - tw.writeExprs_neexpr(id, type.javaResult.id, type.kotlinResult.id, parent, idx) - tw.writeHasLocation(id, locId) - tw.writeCallableEnclosingExpr(id, callable) - } -*/ - isBuiltin("less") -> { + isBuiltinCall(c, "less") -> { if(c.origin != LT) { logger.warnElement(Severity.ErrorSevere, "Unexpected origin for LT: ${c.origin}", c) } val id = tw.getFreshIdLabel() val type = useType(c.type) tw.writeExprs_ltexpr(id, type.javaResult.id, type.kotlinResult.id, parent, idx) - binop(id) + binop(id, c, callable) } - isBuiltin("lessOrEqual") -> { + isBuiltinCall(c, "lessOrEqual") -> { if(c.origin != LTEQ) { logger.warnElement(Severity.ErrorSevere, "Unexpected origin for LTEQ: ${c.origin}", c) } val id = tw.getFreshIdLabel() val type = useType(c.type) tw.writeExprs_leexpr(id, type.javaResult.id, type.kotlinResult.id, parent, idx) - binop(id) + binop(id, c, callable) } - isBuiltin("greater") -> { + isBuiltinCall(c, "greater") -> { if(c.origin != GT) { logger.warnElement(Severity.ErrorSevere, "Unexpected origin for GT: ${c.origin}", c) } val id = tw.getFreshIdLabel() val type = useType(c.type) tw.writeExprs_gtexpr(id, type.javaResult.id, type.kotlinResult.id, parent, idx) - binop(id) + binop(id, c, callable) } - isBuiltin("greaterOrEqual") -> { + isBuiltinCall(c, "greaterOrEqual") -> { if(c.origin != GTEQ) { logger.warnElement(Severity.ErrorSevere, "Unexpected origin for GTEQ: ${c.origin}", c) } val id = tw.getFreshIdLabel() val type = useType(c.type) tw.writeExprs_geexpr(id, type.javaResult.id, type.kotlinResult.id, parent, idx) - binop(id) + binop(id, c, callable) } else -> { val id = tw.getFreshIdLabel() @@ -1611,7 +1609,6 @@ TODO // type arguments at index -2, -3, ... extractTypeArguments(c, id, callable, -2, true) - val dr = c.dispatchReceiver if(dr != null) { extractExpressionExpr(dr, callable, id, -1) } diff --git a/java/ql/test/kotlin/library-tests/exprs/binop.expected b/java/ql/test/kotlin/library-tests/exprs/binop.expected index cc2356bae89..526659e8bcf 100644 --- a/java/ql/test/kotlin/library-tests/exprs/binop.expected +++ b/java/ql/test/kotlin/library-tests/exprs/binop.expected @@ -3,6 +3,7 @@ | exprs.kt:8:14:8:18 | ... / ... | exprs.kt:8:14:8:14 | x | exprs.kt:8:18:8:18 | y | | exprs.kt:9:14:9:18 | ... % ... | exprs.kt:9:14:9:14 | x | exprs.kt:9:18:9:18 | y | | exprs.kt:20:15:20:20 | ... == ... | exprs.kt:20:15:20:15 | x | exprs.kt:20:20:20:20 | y | +| exprs.kt:21:15:21:20 | ... != ... | exprs.kt:21:15:21:15 | x | exprs.kt:21:20:21:20 | y | | exprs.kt:22:15:22:19 | ... < ... | exprs.kt:22:15:22:15 | x | exprs.kt:22:19:22:19 | y | | exprs.kt:23:15:23:20 | ... <= ... | exprs.kt:23:15:23:15 | x | exprs.kt:23:20:23:20 | y | | exprs.kt:24:15:24:19 | ... > ... | exprs.kt:24:15:24:15 | x | exprs.kt:24:19:24:19 | y | @@ -10,3 +11,4 @@ | exprs.kt:50:16:50:26 | ... + ... | exprs.kt:50:16:50:19 | str1 | exprs.kt:50:23:50:26 | str2 | | exprs.kt:53:12:53:23 | ... > ... | exprs.kt:53:12:53:19 | variable | exprs.kt:53:23:53:23 | 0 | | exprs.kt:57:12:57:20 | ... + ... | exprs.kt:57:12:57:14 | 123 | exprs.kt:57:18:57:20 | 456 | +| exprs.kt:83:8:83:16 | ... != ... | exprs.kt:83:8:83:8 | r | exprs.kt:83:13:83:16 | null | diff --git a/java/ql/test/kotlin/library-tests/exprs/exprs.expected b/java/ql/test/kotlin/library-tests/exprs/exprs.expected index 17ceba4cb6a..02196b93c20 100644 --- a/java/ql/test/kotlin/library-tests/exprs/exprs.expected +++ b/java/ql/test/kotlin/library-tests/exprs/exprs.expected @@ -20,6 +20,10 @@ | exprs.kt:20:15:20:15 | x | exprs.kt:4:1:58:1 | topLevelMethod | VarAccess | | exprs.kt:20:15:20:20 | ... == ... | exprs.kt:4:1:58:1 | topLevelMethod | EQExpr | | exprs.kt:20:20:20:20 | y | exprs.kt:4:1:58:1 | topLevelMethod | VarAccess | +| exprs.kt:21:5:21:20 | i14 | exprs.kt:4:1:58:1 | topLevelMethod | LocalVariableDeclExpr | +| exprs.kt:21:15:21:15 | x | exprs.kt:4:1:58:1 | topLevelMethod | VarAccess | +| exprs.kt:21:15:21:20 | ... != ... | exprs.kt:4:1:58:1 | topLevelMethod | NEExpr | +| exprs.kt:21:20:21:20 | y | exprs.kt:4:1:58:1 | topLevelMethod | VarAccess | | exprs.kt:22:5:22:19 | i15 | exprs.kt:4:1:58:1 | topLevelMethod | LocalVariableDeclExpr | | exprs.kt:22:15:22:15 | x | exprs.kt:4:1:58:1 | topLevelMethod | VarAccess | | exprs.kt:22:15:22:19 | ... < ... | exprs.kt:4:1:58:1 | topLevelMethod | LTExpr | @@ -137,6 +141,18 @@ | exprs.kt:82:5:82:25 | r | exprs.kt:81:1:88:1 | foo | LocalVariableDeclExpr | | exprs.kt:82:13:82:13 | p | exprs.kt:81:1:88:1 | foo | VarAccess | | exprs.kt:82:15:82:25 | getBounds(...) | exprs.kt:81:1:88:1 | foo | MethodAccess | +| exprs.kt:83:5:87:5 | when ... | exprs.kt:81:1:88:1 | foo | WhenExpr | +| exprs.kt:83:8:83:8 | r | exprs.kt:81:1:88:1 | foo | VarAccess | +| exprs.kt:83:8:83:16 | ... != ... | exprs.kt:81:1:88:1 | foo | NEExpr | +| exprs.kt:83:13:83:16 | null | exprs.kt:81:1:88:1 | foo | NullLiteral | +| exprs.kt:84:9:84:29 | r2 | exprs.kt:81:1:88:1 | foo | LocalVariableDeclExpr | +| exprs.kt:84:29:84:29 | (...)... | exprs.kt:81:1:88:1 | foo | CastExpr | +| exprs.kt:84:29:84:29 | Rectangle | exprs.kt:81:1:88:1 | foo | TypeAccess | +| exprs.kt:84:29:84:29 | r | exprs.kt:81:1:88:1 | foo | VarAccess | +| exprs.kt:85:9:85:30 | height | exprs.kt:81:1:88:1 | foo | LocalVariableDeclExpr | +| exprs.kt:85:25:85:30 | height | exprs.kt:81:1:88:1 | foo | VarAccess | +| exprs.kt:86:9:86:17 | ...=... | exprs.kt:81:1:88:1 | foo | AssignExpr | +| exprs.kt:86:21:86:21 | 3 | exprs.kt:81:1:88:1 | foo | IntegerLiteral | | exprs.kt:90:1:92:1 | (...) | exprs.kt:90:6:92:1 | Direction | MethodAccess | | exprs.kt:90:1:92:1 | new Enum(...) | exprs.kt:90:6:92:1 | Direction | ClassInstanceExpr | | exprs.kt:94:1:98:1 | (...) | exprs.kt:94:6:98:1 | Color | MethodAccess | @@ -151,6 +167,7 @@ | exprs.kt:102:23:102:27 | GREEN | exprs.kt:100:1:103:1 | enums | VarAccess | | file://:0:0:0:0 | Color | exprs.kt:94:6:98:1 | Color | TypeAccess | | file://:0:0:0:0 | Direction | exprs.kt:90:6:92:1 | Direction | TypeAccess | +| file://:0:0:0:0 | height | exprs.kt:81:1:88:1 | foo | VarAccess | | file://:0:0:0:0 | q | exprs.kt:72:1:79:1 | typeTests | VarAccess | | file://:0:0:0:0 | q | exprs.kt:72:1:79:1 | typeTests | VarAccess | | file://:0:0:0:0 | tmp0 | exprs.kt:4:1:58:1 | topLevelMethod | LocalVariableDeclExpr |