diff --git a/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt b/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt index 5e0d96c9cb0..e80ba95e1bf 100644 --- a/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt +++ b/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt @@ -6,6 +6,7 @@ import com.github.codeql.utils.versions.functionN import com.github.codeql.utils.versions.isUnderscoreParameter import com.semmle.extractor.java.OdasaOutput import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext +import org.jetbrains.kotlin.backend.common.ir.allOverridden import org.jetbrains.kotlin.backend.common.lower.parents import org.jetbrains.kotlin.backend.common.pop import org.jetbrains.kotlin.builtins.functions.BuiltInFunctionArity @@ -1855,16 +1856,27 @@ open class KotlinFileExtractor( // Ensure the real target gets extracted, as we might not every directly touch it thanks to this call being redirected to a $default method. useFunction(callTarget) } - val defaultMethodLabel = getDefaultsMethodLabel(callTarget) - val id = extractMethodAccessWithoutArgs(resultType, locId, enclosingCallable, callsiteParent, childIdx, enclosingStmt, defaultMethodLabel) - if (callTarget.isLocalFunction()) { - extractTypeAccess(getLocallyVisibleFunctionLabels(callTarget).type, locId, id, -1, enclosingCallable, enclosingStmt) - } else { - extractStaticTypeAccessQualifierUnchecked(callTarget.parent, id, locId, enclosingCallable, enclosingStmt) + // Default parameter values are inherited by overrides; in this case the call should dispatch against the $default method belonging to the class + // that specified the default values, which will in turn dynamically dispatch back to the relevant override. + val overriddenCallTarget = (callTarget as? IrSimpleFunction)?.allOverridden(true)?.firstOrNull { + it.overriddenSymbols.isEmpty() && it.valueParameters.any { p -> p.defaultValue != null } + } ?: callTarget + if (isExternalDeclaration(overriddenCallTarget)) { + // Likewise, ensure the overridden target gets extracted. + useFunction(overriddenCallTarget) } - extractDefaultsCallArguments(id, callTarget, enclosingCallable, enclosingStmt, valueArguments, dispatchReceiver, extensionReceiver) + val defaultMethodLabel = getDefaultsMethodLabel(overriddenCallTarget) + val id = extractMethodAccessWithoutArgs(resultType, locId, enclosingCallable, callsiteParent, childIdx, enclosingStmt, defaultMethodLabel) + + if (overriddenCallTarget.isLocalFunction()) { + extractTypeAccess(getLocallyVisibleFunctionLabels(overriddenCallTarget).type, locId, id, -1, enclosingCallable, enclosingStmt) + } else { + extractStaticTypeAccessQualifierUnchecked(overriddenCallTarget.parent, id, locId, enclosingCallable, enclosingStmt) + } + + extractDefaultsCallArguments(id, overriddenCallTarget, enclosingCallable, enclosingStmt, valueArguments, dispatchReceiver, extensionReceiver) } private fun extractDefaultsCallArguments( diff --git a/java/ql/test/kotlin/library-tests/inherited-default-value/test.expected b/java/ql/test/kotlin/library-tests/inherited-default-value/test.expected new file mode 100644 index 00000000000..88b180f7c32 --- /dev/null +++ b/java/ql/test/kotlin/library-tests/inherited-default-value/test.expected @@ -0,0 +1,2 @@ +| test.kt:3:8:3:28 | f(...) | test.kt:3:8:3:28 | f | test.kt:1:1:5:1 | A | +| test.kt:11:21:11:23 | f$default(...) | test.kt:3:8:3:28 | f$default | test.kt:1:1:5:1 | A | diff --git a/java/ql/test/kotlin/library-tests/inherited-default-value/test.kt b/java/ql/test/kotlin/library-tests/inherited-default-value/test.kt new file mode 100644 index 00000000000..b8b1e0e0788 --- /dev/null +++ b/java/ql/test/kotlin/library-tests/inherited-default-value/test.kt @@ -0,0 +1,13 @@ +open class A { + + open fun f(x: Int = 0) = x + +} + +class B : A() { + + override fun f(x: Int) = x + 1 + + fun user() = this.f() + +} diff --git a/java/ql/test/kotlin/library-tests/inherited-default-value/test.ql b/java/ql/test/kotlin/library-tests/inherited-default-value/test.ql new file mode 100644 index 00000000000..64bf5dd515f --- /dev/null +++ b/java/ql/test/kotlin/library-tests/inherited-default-value/test.ql @@ -0,0 +1,4 @@ +import java + +from MethodAccess ma +select ma, ma.getCallee(), ma.getCallee().getDeclaringType()