Skip to content

Commit d700fdd

Browse files
committed
Kotlin: handle default parameter values inherited from an overridden function
1 parent 37869e8 commit d700fdd

File tree

4 files changed

+36
-5
lines changed

4 files changed

+36
-5
lines changed

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

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import com.github.codeql.utils.versions.functionN
66
import com.github.codeql.utils.versions.isUnderscoreParameter
77
import com.semmle.extractor.java.OdasaOutput
88
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
9+
import org.jetbrains.kotlin.backend.common.ir.allOverridden
910
import org.jetbrains.kotlin.backend.common.lower.parents
1011
import org.jetbrains.kotlin.backend.common.pop
1112
import org.jetbrains.kotlin.builtins.functions.BuiltInFunctionArity
@@ -1855,16 +1856,27 @@ open class KotlinFileExtractor(
18551856
// Ensure the real target gets extracted, as we might not every directly touch it thanks to this call being redirected to a $default method.
18561857
useFunction<DbCallable>(callTarget)
18571858
}
1858-
val defaultMethodLabel = getDefaultsMethodLabel(callTarget)
1859+
1860+
// Default parameter values are inherited by overrides; in this case the call should dispatch against the $default method belonging to the class
1861+
// that specified the default values, which will in turn dynamically dispatch back to the relevant override.
1862+
val overriddenCallTarget = (callTarget as? IrSimpleFunction)?.allOverridden(true)?.firstOrNull {
1863+
it.overriddenSymbols.isEmpty() && it.valueParameters.any { p -> p.defaultValue != null }
1864+
} ?: callTarget
1865+
if (isExternalDeclaration(overriddenCallTarget)) {
1866+
// Likewise, ensure the overridden target gets extracted.
1867+
useFunction<DbCallable>(overriddenCallTarget)
1868+
}
1869+
1870+
val defaultMethodLabel = getDefaultsMethodLabel(overriddenCallTarget)
18591871
val id = extractMethodAccessWithoutArgs(resultType, locId, enclosingCallable, callsiteParent, childIdx, enclosingStmt, defaultMethodLabel)
18601872

1861-
if (callTarget.isLocalFunction()) {
1862-
extractTypeAccess(getLocallyVisibleFunctionLabels(callTarget).type, locId, id, -1, enclosingCallable, enclosingStmt)
1873+
if (overriddenCallTarget.isLocalFunction()) {
1874+
extractTypeAccess(getLocallyVisibleFunctionLabels(overriddenCallTarget).type, locId, id, -1, enclosingCallable, enclosingStmt)
18631875
} else {
1864-
extractStaticTypeAccessQualifierUnchecked(callTarget.parent, id, locId, enclosingCallable, enclosingStmt)
1876+
extractStaticTypeAccessQualifierUnchecked(overriddenCallTarget.parent, id, locId, enclosingCallable, enclosingStmt)
18651877
}
18661878

1867-
extractDefaultsCallArguments(id, callTarget, enclosingCallable, enclosingStmt, valueArguments, dispatchReceiver, extensionReceiver)
1879+
extractDefaultsCallArguments(id, overriddenCallTarget, enclosingCallable, enclosingStmt, valueArguments, dispatchReceiver, extensionReceiver)
18681880
}
18691881

18701882
private fun extractDefaultsCallArguments(
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
| test.kt:3:8:3:28 | f(...) | test.kt:3:8:3:28 | f | test.kt:1:1:5:1 | A |
2+
| test.kt:11:21:11:23 | f$default(...) | test.kt:3:8:3:28 | f$default | test.kt:1:1:5:1 | A |
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
open class A {
2+
3+
open fun f(x: Int = 0) = x
4+
5+
}
6+
7+
class B : A() {
8+
9+
override fun f(x: Int) = x + 1
10+
11+
fun user() = this.f()
12+
13+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import java
2+
3+
from MethodAccess ma
4+
select ma, ma.getCallee(), ma.getCallee().getDeclaringType()

0 commit comments

Comments
 (0)