Skip to content

Commit 197be69

Browse files
authored
Merge pull request github#11069 from smowton/smowton/fix/kotlin-vararg-constructor-defaults
Kotlin vararg constructors: don't call a default-parameter constructor just because there are no varargs
2 parents b59cb77 + 39520e5 commit 197be69

File tree

3 files changed

+56
-8
lines changed

3 files changed

+56
-8
lines changed

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

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1809,7 +1809,7 @@ open class KotlinFileExtractor(
18091809
}
18101810

18111811
private fun getDefaultsMethodLabel(f: IrFunction): Label<out DbCallable> {
1812-
val defaultsMethodName = getDefaultsMethodName(f)
1812+
val defaultsMethodName = if (f is IrConstructor) "<init>" else getDefaultsMethodName(f)
18131813
val normalArgTypes = getDefaultsMethodArgTypes(f)
18141814
val extensionParamType = f.extensionReceiverParameter?.let { erase(it.type) }
18151815

@@ -1961,6 +1961,15 @@ open class KotlinFileExtractor(
19611961
target
19621962
}
19631963

1964+
private fun callUsesDefaultArguments(callTarget: IrFunction, valueArguments: List<IrExpression?>): Boolean {
1965+
val varargParam = callTarget.valueParameters.withIndex().find { it.value.isVararg }
1966+
// If the vararg param is the only one not specified, and it has no default value, then we don't need to call a $default method,
1967+
// as omitting it already implies passing an empty vararg array.
1968+
val nullAllowedIdx = if (varargParam != null && varargParam.value.defaultValue == null) varargParam.index else -1
1969+
return valueArguments.withIndex().any { (index, it) -> it == null && index != nullAllowedIdx }
1970+
}
1971+
1972+
19641973
fun extractRawMethodAccess(
19651974
syntacticCallTarget: IrFunction,
19661975
locElement: IrElement,
@@ -1977,12 +1986,8 @@ open class KotlinFileExtractor(
19771986
superQualifierSymbol: IrClassSymbol? = null) {
19781987

19791988
val locId = tw.getLocation(locElement)
1980-
val varargParam = syntacticCallTarget.valueParameters.withIndex().find { it.value.isVararg }
1981-
// If the vararg param is the only one not specified, and it has no default value, then we don't need to call a $default method,
1982-
// as omitting it already implies passing an empty vararg array.
1983-
val nullAllowedIdx = if (varargParam != null && varargParam.value.defaultValue == null) varargParam.index else -1
19841989

1985-
if (valueArguments.withIndex().any { (index, it) -> it == null && index != nullAllowedIdx }) {
1990+
if (callUsesDefaultArguments(syntacticCallTarget, valueArguments)) {
19861991
extractsDefaultsCall(
19871992
syntacticCallTarget,
19881993
locId,
@@ -3034,8 +3039,7 @@ open class KotlinFileExtractor(
30343039
val valueArgs = (0 until e.valueArgumentsCount).map { e.getValueArgument(it) }
30353040
// For now, don't try to use default methods for enum constructor calls,
30363041
// which have null arguments even though the parameters don't give default values.
3037-
val anyDefaultArgs = e !is IrEnumConstructorCall && valueArgs.any { it == null }
3038-
val id = if (anyDefaultArgs) {
3042+
val id = if (e !is IrEnumConstructorCall && callUsesDefaultArguments(e.symbol.owner, valueArgs)) {
30393043
extractNewExpr(getDefaultsMethodLabel(e.symbol.owner).cast(), type, locId, parent, idx, callable, enclosingStmt).also {
30403044
extractDefaultsCallArguments(it, e.symbol.owner, callable, enclosingStmt, valueArgs, null, null)
30413045
}

java/ql/test/kotlin/library-tests/parameter-defaults/PrintAst.expected

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,23 @@ test.kt:
395395
# 1| 2: [NullLiteral] null
396396
# 1| 3: [IntegerLiteral] 3
397397
# 1| 4: [NullLiteral] null
398+
# 232| 11: [Method] varargsConstructorUser
399+
# 232| 3: [TypeAccess] Unit
400+
# 232| 5: [BlockStmt] { ... }
401+
# 233| 0: [ExprStmt] <Expr>;
402+
# 233| 0: [ImplicitCoercionToUnitExpr] <implicit coercion to unit>
403+
# 233| 0: [TypeAccess] Unit
404+
# 233| 1: [ClassInstanceExpr] new VarargsConstructorTest(...)
405+
# 233| -3: [TypeAccess] VarargsConstructorTest
406+
# 233| 0: [StringLiteral] varargs constructor test sunk
407+
# 234| 1: [ExprStmt] <Expr>;
408+
# 234| 0: [ImplicitCoercionToUnitExpr] <implicit coercion to unit>
409+
# 234| 0: [TypeAccess] Unit
410+
# 234| 1: [ClassInstanceExpr] new VarargsConstructorTest(...)
411+
# 234| -3: [TypeAccess] VarargsConstructorTest
412+
# 234| 0: [StringLiteral] varargs constructor test sunk 2
413+
# 234| 1: [StringLiteral] varargs constructor test not sunk 1
414+
# 234| 2: [StringLiteral] varargs constructor test not sunk 2
398415
# 3| 2: [Class] TestMember
399416
# 3| 1: [Constructor] TestMember
400417
# 3| 5: [BlockStmt] { ... }
@@ -1688,3 +1705,19 @@ test.kt:
16881705
# 180| -1: [VarAccess] p0
16891706
# 180| 0: [VarAccess] p1
16901707
# 180| 1: [VarAccess] p2
1708+
# 226| 18: [Class] VarargsConstructorTest
1709+
# 226| 1: [Constructor] VarargsConstructorTest
1710+
#-----| 4: (Parameters)
1711+
# 226| 0: [Parameter] x
1712+
# 226| 0: [TypeAccess] String
1713+
# 226| 1: [Parameter] y
1714+
# 226| 0: [TypeAccess] String[]
1715+
# 226| 0: [WildcardTypeAccess] ? ...
1716+
# 226| 0: [TypeAccess] String
1717+
# 226| 5: [BlockStmt] { ... }
1718+
# 226| 0: [SuperConstructorInvocationStmt] super(...)
1719+
# 226| 1: [BlockStmt] { ... }
1720+
# 228| 0: [ExprStmt] <Expr>;
1721+
# 228| 0: [MethodAccess] sink(...)
1722+
# 228| -1: [TypeAccess] TestKt
1723+
# 228| 0: [VarAccess] x

java/ql/test/kotlin/library-tests/parameter-defaults/test.kt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,3 +222,14 @@ fun varargsUserOnlySinkRegularArgs() {
222222
varargsTestOnlySinkRegularArgs(x = "two-varargs-before sunk 3", "two-vararg-first not sunk 3", "two-vararg-second not sunk 3", z = "two-varargs-after sunk 3")
223223
varargsTestOnlySinkRegularArgs("no-z-parmeter sunk 3", "no-z-parameter first vararg not sunk 3", "no-z-parameter second vararg not sunk 3")
224224
}
225+
226+
class VarargsConstructorTest(x: String, vararg y: String) {
227+
init {
228+
sink(x)
229+
}
230+
}
231+
232+
fun varargsConstructorUser() {
233+
VarargsConstructorTest("varargs constructor test sunk")
234+
VarargsConstructorTest("varargs constructor test sunk 2", "varargs constructor test not sunk 1", "varargs constructor test not sunk 2")
235+
}

0 commit comments

Comments
 (0)