diff --git a/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt b/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt index aaa95b5f4df..08848f77b3f 100644 --- a/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt +++ b/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt @@ -1868,12 +1868,13 @@ open class KotlinFileExtractor( extractStaticTypeAccessQualifierUnchecked(overriddenCallTarget.parent, id, locId, enclosingCallable, enclosingStmt) } - extractDefaultsCallArguments(id, overriddenCallTarget, enclosingCallable, enclosingStmt, valueArguments, dispatchReceiver, extensionReceiver) + extractDefaultsCallArguments(id, overriddenCallTarget, locId, enclosingCallable, enclosingStmt, valueArguments, dispatchReceiver, extensionReceiver) } private fun extractDefaultsCallArguments( id: Label, callTarget: IrFunction, + locId: Label, enclosingCallable: Label, enclosingStmt: Label, valueArguments: List, @@ -1900,7 +1901,7 @@ open class KotlinFileExtractor( IrConstImpl.defaultValueForType(0, 0, getDefaultsMethodLastArgType(callTarget)) ) - extractCallValueArguments(id, valueArgsWithDummies + extraArgs, enclosingStmt, enclosingCallable, nextIdx, extractVarargAsArray = true) + extractCallValueArguments(id, valueArgsWithDummies + extraArgs, callTarget, locId, enclosingStmt, enclosingCallable, nextIdx, extractVarargAsArray = true) } private fun getFunctionInvokeMethod(typeArgs: List): IrFunction? { @@ -2022,7 +2023,7 @@ open class KotlinFileExtractor( childIdx, enclosingStmt, valueArguments.size, - { argParent, idxOffset -> extractCallValueArguments(argParent, valueArguments, enclosingStmt, enclosingCallable, idxOffset) }, + { argParent, idxOffset -> extractCallValueArguments(argParent, valueArguments, syntacticCallTarget, locId, enclosingStmt, enclosingCallable, idxOffset) }, dispatchReceiver?.type, dispatchReceiver?.let { { callId -> extractExpressionExpr(dispatchReceiver, enclosingCallable, callId, -1, enclosingStmt) } }, extensionReceiver?.let { { argParent -> extractExpressionExpr(extensionReceiver, enclosingCallable, argParent, 0, enclosingStmt) } }, @@ -2116,19 +2117,38 @@ open class KotlinFileExtractor( this is IrEnumEntry - private fun extractCallValueArguments(callId: Label, call: IrFunctionAccessExpression, enclosingStmt: Label, enclosingCallable: Label, idxOffset: Int) = - extractCallValueArguments(callId, (0 until call.valueArgumentsCount).map { call.getValueArgument(it) }, enclosingStmt, enclosingCallable, idxOffset) + private fun extractCallValueArguments(callId: Label, call: IrFunctionAccessExpression, callTarget: IrFunction, fallbackLocation: Label, enclosingStmt: Label, enclosingCallable: Label, idxOffset: Int) = + extractCallValueArguments(callId, (0 until call.valueArgumentsCount).map { call.getValueArgument(it) }, callTarget, fallbackLocation, enclosingStmt, enclosingCallable, idxOffset) - private fun extractCallValueArguments(callId: Label, valueArguments: List, enclosingStmt: Label, enclosingCallable: Label, idxOffset: Int, extractVarargAsArray: Boolean = false) { + private fun extractCallValueArguments(callId: Label, valueArguments: List, callTarget: IrFunction, fallbackLocation: Label, enclosingStmt: Label, enclosingCallable: Label, idxOffset: Int, extractVarargAsArray: Boolean = false) { var i = 0 valueArguments.forEach { arg -> - if(arg != null) { + if (arg != null) { if (arg is IrVararg && !extractVarargAsArray) { arg.elements.forEachIndexed { varargNo, vararg -> extractVarargElement(vararg, enclosingCallable, callId, i + idxOffset + varargNo, enclosingStmt) } i += arg.elements.size } else { extractExpressionExpr(arg, enclosingCallable, callId, (i++) + idxOffset, enclosingStmt) } + } else { + val realCallTarget = callTarget.target.realOverrideTarget + + // Generated constructor calls to kotlin.Enum have no arguments in IR, but the constructor takes two parameters. + if (realCallTarget is IrConstructor && + realCallTarget.parentClassOrNull?.symbol == pluginContext.irBuiltIns.enumClass && + realCallTarget.valueParameters.size == 2 && + i < realCallTarget.valueParameters.size) { + + if (i == 0 && realCallTarget.valueParameters[i].type == pluginContext.irBuiltIns.stringType) { + + val id = extractNull(pluginContext.irBuiltIns.stringType, fallbackLocation, callId, (i++) + idxOffset, enclosingCallable, enclosingStmt) + tw.writeCompiler_generated(id, CompilerGeneratedKinds.ENUM_CONSTRUCTOR_ARGUMENT.kind) + } else if (i == 1 && realCallTarget.valueParameters[i].type == pluginContext.irBuiltIns.intType) { + + val id = extractConstantInteger(0, fallbackLocation, callId, (i++) + idxOffset, enclosingCallable, enclosingStmt) + tw.writeCompiler_generated(id, CompilerGeneratedKinds.ENUM_CONSTRUCTOR_ARGUMENT.kind) + } + } } } } @@ -3067,11 +3087,11 @@ open class KotlinFileExtractor( // which have null arguments even though the parameters don't give default values. val id = if (e !is IrEnumConstructorCall && callUsesDefaultArguments(e.symbol.owner, valueArgs)) { extractNewExpr(getDefaultsMethodLabel(e.symbol.owner).cast(), type, locId, parent, idx, callable, enclosingStmt).also { - extractDefaultsCallArguments(it, e.symbol.owner, callable, enclosingStmt, valueArgs, null, null) + extractDefaultsCallArguments(it, e.symbol.owner, locId, callable, enclosingStmt, valueArgs, null, null) } } else { extractNewExpr(e.symbol.owner, eType.arguments, type, locId, parent, idx, callable, enclosingStmt).also { - extractCallValueArguments(it, e, enclosingStmt, callable, 0) + extractCallValueArguments(it, e, e.symbol.owner, locId, enclosingStmt, callable, 0) } } @@ -3396,6 +3416,14 @@ open class KotlinFileExtractor( extractExprContext(it, locId, callable, enclosingStmt) } + private fun extractNull(t: IrType, locId: Label, parent: Label, idx: Int, callable: Label, enclosingStmt: Label) = + tw.getFreshIdLabel().also { + val type = useType(t) + tw.writeExprs_nullliteral(it, type.javaResult.id, parent, idx) + tw.writeExprsKotlinType(it, type.kotlinResult.id) + extractExprContext(it, locId, callable, enclosingStmt) + } + private fun extractAssignExpr(type: IrType, locId: Label, parent: Label, idx: Int, callable: Label, enclosingStmt: Label) = tw.getFreshIdLabel().also { val typeResults = useType(type) @@ -3436,7 +3464,7 @@ open class KotlinFileExtractor( tw.writeHasLocation(id, locId) tw.writeCallableBinding(id.cast(), methodId) - extractCallValueArguments(id, e, id, callable, 0) + extractCallValueArguments(id, e, e.symbol.owner, locId, id, callable, 0) val dr = e.dispatchReceiver if (dr != null) { extractExpressionExpr(dr, callable, id, -1, id) @@ -3614,12 +3642,7 @@ open class KotlinFileExtractor( tw.writeNamestrings(v.toString(), v.toString(), id) } v == null -> { - val id = tw.getFreshIdLabel() - val type = useType(e.type) // class;kotlin.Nothing - val locId = tw.getLocation(e) - tw.writeExprs_nullliteral(id, type.javaResult.id, exprParent.parent, exprParent.idx) - tw.writeExprsKotlinType(id, type.kotlinResult.id) - extractExprContext(id, locId, callable, exprParent.enclosingStmt) + extractNull(e.type, tw.getLocation(e), exprParent.parent, exprParent.idx, callable, exprParent.enclosingStmt) } else -> { logger.errorElement("Unrecognised IrConst: " + v.javaClass, e) @@ -5516,5 +5539,6 @@ open class KotlinFileExtractor( JVMOVERLOADS_METHOD(9), DEFAULT_ARGUMENTS_METHOD(10), INTERFACE_FORWARDER(11), + ENUM_CONSTRUCTOR_ARGUMENT(12), } } diff --git a/java/ql/consistency-queries/callArgs.ql b/java/ql/consistency-queries/callArgs.ql new file mode 100644 index 00000000000..d4a58765ca0 --- /dev/null +++ b/java/ql/consistency-queries/callArgs.ql @@ -0,0 +1,9 @@ +import java + +from Call call, Callable c +where + count(call.getAnArgument()) != c.getNumberOfParameters() and + call.getCallee() = c and + not exists(Parameter p | c.getAParameter() = p and p.isVarargs()) and + call.getFile().isKotlinSourceFile() +select call, c, count(call.getAnArgument()), c.getNumberOfParameters() diff --git a/java/ql/test/kotlin/library-tests/classes/PrintAst.expected b/java/ql/test/kotlin/library-tests/classes/PrintAst.expected index 5bce291c436..698a4f874c9 100644 --- a/java/ql/test/kotlin/library-tests/classes/PrintAst.expected +++ b/java/ql/test/kotlin/library-tests/classes/PrintAst.expected @@ -174,6 +174,8 @@ classes.kt: # 49| 0: [ClassInstanceExpr] new Enum(...) # 49| -3: [TypeAccess] Enum # 49| 0: [TypeAccess] Direction +# 49| 0: [NullLiteral] null +# 49| 1: [IntegerLiteral] 0 # 49| 1: [BlockStmt] { ... } # 50| 5: [FieldDeclaration] Direction NORTH; # 50| -1: [TypeAccess] Direction @@ -209,6 +211,8 @@ classes.kt: # 53| 0: [ClassInstanceExpr] new Enum(...) # 53| -3: [TypeAccess] Enum # 53| 0: [TypeAccess] Color +# 53| 0: [NullLiteral] null +# 53| 1: [IntegerLiteral] 0 # 53| 1: [BlockStmt] { ... } # 53| 0: [ExprStmt] ; # 53| 0: [KtInitializerAssignExpr] ...=... diff --git a/java/ql/test/kotlin/library-tests/exprs/PrintAst.expected b/java/ql/test/kotlin/library-tests/exprs/PrintAst.expected index 82386f83447..f821bdadd5b 100644 --- a/java/ql/test/kotlin/library-tests/exprs/PrintAst.expected +++ b/java/ql/test/kotlin/library-tests/exprs/PrintAst.expected @@ -3358,6 +3358,8 @@ exprs.kt: # 174| 0: [ClassInstanceExpr] new Enum(...) # 174| -3: [TypeAccess] Enum # 174| 0: [TypeAccess] Direction +# 174| 0: [NullLiteral] null +# 174| 1: [IntegerLiteral] 0 # 174| 1: [BlockStmt] { ... } # 175| 5: [FieldDeclaration] Direction NORTH; # 175| -1: [TypeAccess] Direction @@ -3393,6 +3395,8 @@ exprs.kt: # 178| 0: [ClassInstanceExpr] new Enum(...) # 178| -3: [TypeAccess] Enum # 178| 0: [TypeAccess] Color +# 178| 0: [NullLiteral] null +# 178| 1: [IntegerLiteral] 0 # 178| 1: [BlockStmt] { ... } # 178| 0: [ExprStmt] ; # 178| 0: [KtInitializerAssignExpr] ...=... diff --git a/java/ql/test/kotlin/library-tests/exprs/exprs.expected b/java/ql/test/kotlin/library-tests/exprs/exprs.expected index 4e1d625ee1c..08a9891f64b 100644 --- a/java/ql/test/kotlin/library-tests/exprs/exprs.expected +++ b/java/ql/test/kotlin/library-tests/exprs/exprs.expected @@ -1470,9 +1470,11 @@ | exprs.kt:170:9:170:17 | r2.height | exprs.kt:165:1:172:1 | foo | VarAccess | | exprs.kt:170:9:170:21 | ...=... | exprs.kt:165:1:172:1 | foo | AssignExpr | | exprs.kt:170:21:170:21 | 3 | exprs.kt:165:1:172:1 | foo | IntegerLiteral | +| exprs.kt:174:1:176:1 | 0 | exprs.kt:174:6:176:1 | Direction | IntegerLiteral | | exprs.kt:174:1:176:1 | Direction | exprs.kt:174:6:176:1 | Direction | TypeAccess | | exprs.kt:174:1:176:1 | Enum | exprs.kt:174:6:176:1 | Direction | TypeAccess | | exprs.kt:174:1:176:1 | new Enum(...) | exprs.kt:174:6:176:1 | Direction | ClassInstanceExpr | +| exprs.kt:174:1:176:1 | null | exprs.kt:174:6:176:1 | Direction | NullLiteral | | exprs.kt:175:5:175:10 | ...=... | exprs.kt:0:0:0:0 | | KtInitializerAssignExpr | | exprs.kt:175:5:175:10 | Direction | exprs.kt:0:0:0:0 | | TypeAccess | | exprs.kt:175:5:175:10 | Direction | exprs.kt:0:0:0:0 | | TypeAccess | @@ -1497,9 +1499,11 @@ | exprs.kt:175:25:175:28 | Direction | file://:0:0:0:0 | | TypeAccess | | exprs.kt:175:25:175:28 | Direction.EAST | exprs.kt:0:0:0:0 | | VarAccess | | exprs.kt:175:25:175:28 | new Direction(...) | exprs.kt:0:0:0:0 | | ClassInstanceExpr | +| exprs.kt:178:1:182:1 | 0 | exprs.kt:178:6:182:1 | Color | IntegerLiteral | | exprs.kt:178:1:182:1 | Color | exprs.kt:178:6:182:1 | Color | TypeAccess | | exprs.kt:178:1:182:1 | Enum | exprs.kt:178:6:182:1 | Color | TypeAccess | | exprs.kt:178:1:182:1 | new Enum(...) | exprs.kt:178:6:182:1 | Color | ClassInstanceExpr | +| exprs.kt:178:1:182:1 | null | exprs.kt:178:6:182:1 | Color | NullLiteral | | exprs.kt:178:18:178:29 | ...=... | exprs.kt:178:6:182:1 | Color | KtInitializerAssignExpr | | exprs.kt:178:18:178:29 | int | file://:0:0:0:0 | | TypeAccess | | exprs.kt:178:18:178:29 | int | file://:0:0:0:0 | | TypeAccess | diff --git a/java/ql/test/kotlin/library-tests/exprs_typeaccess/PrintAst.expected b/java/ql/test/kotlin/library-tests/exprs_typeaccess/PrintAst.expected index 424aa49f4ef..77b2fbb794e 100644 --- a/java/ql/test/kotlin/library-tests/exprs_typeaccess/PrintAst.expected +++ b/java/ql/test/kotlin/library-tests/exprs_typeaccess/PrintAst.expected @@ -88,6 +88,8 @@ A.kt: # 23| 0: [ClassInstanceExpr] new Enum(...) # 23| -3: [TypeAccess] Enum # 23| 0: [TypeAccess] Enu +# 23| 0: [NullLiteral] null +# 23| 1: [IntegerLiteral] 0 # 23| 1: [BlockStmt] { ... } # 24| 5: [FieldDeclaration] Enu A; # 24| -1: [TypeAccess] Enu diff --git a/java/ql/test/kotlin/library-tests/methods/exprs.expected b/java/ql/test/kotlin/library-tests/methods/exprs.expected index 6cac3840f74..76a48c189c4 100644 --- a/java/ql/test/kotlin/library-tests/methods/exprs.expected +++ b/java/ql/test/kotlin/library-tests/methods/exprs.expected @@ -231,9 +231,11 @@ | enumClass.kt:0:0:0:0 | EnumWithFunctions[] | TypeAccess | | enumClass.kt:0:0:0:0 | String | TypeAccess | | enumClass.kt:0:0:0:0 | String | TypeAccess | +| enumClass.kt:1:1:4:1 | 0 | IntegerLiteral | | enumClass.kt:1:1:4:1 | Enum | TypeAccess | | enumClass.kt:1:1:4:1 | EnumClass | TypeAccess | | enumClass.kt:1:1:4:1 | new Enum(...) | ClassInstanceExpr | +| enumClass.kt:1:1:4:1 | null | NullLiteral | | enumClass.kt:1:22:1:31 | ...=... | KtInitializerAssignExpr | | enumClass.kt:1:22:1:31 | int | TypeAccess | | enumClass.kt:1:22:1:31 | int | TypeAccess | @@ -256,9 +258,11 @@ | enumClass.kt:3:5:3:12 | EnumClass.enum2 | VarAccess | | enumClass.kt:3:5:3:12 | new EnumClass(...) | ClassInstanceExpr | | enumClass.kt:3:11:3:11 | 1 | IntegerLiteral | +| enumClass.kt:6:1:16:1 | 0 | IntegerLiteral | | enumClass.kt:6:1:16:1 | Enum | TypeAccess | | enumClass.kt:6:1:16:1 | EnumWithFunctions | TypeAccess | | enumClass.kt:6:1:16:1 | new Enum(...) | ClassInstanceExpr | +| enumClass.kt:6:1:16:1 | null | NullLiteral | | enumClass.kt:8:3:11:4 | ...=... | KtInitializerAssignExpr | | enumClass.kt:8:3:11:4 | | ImplicitCoercionToUnitExpr | | enumClass.kt:8:3:11:4 | EnumWithFunctions | TypeAccess |