mirror of
https://github.com/github/codeql.git
synced 2026-06-29 16:47:09 +02:00
Kotlin extractor: restore external file-class locations under K2
Why this is needed: - Under K2, top-level declarations from external binaries are attached directly to IrExternalPackageFragment rather than to an IrClass file-class parent. - That bypassed the normal class-source location path, so some external file-class entities ended up without stable binary file locations. - Missing/unstable locations caused drift in tests that depend on external file class member resolution and location facts. What this changes: - Resolve binary paths from IrMemberWithContainerSource (JvmPackagePartSource) via a dedicated getContainerSourceBinaryPath helper. - In KotlinUsesExtractor, when extracting top-level external declarations, attach file-class location from container-source binary path when available. - Track external file classes whose locations were emitted to avoid duplicate hasLocation facts. This targets the K2 external file-class location gap (for example file_classes and external-property-overloads parity). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -996,7 +996,19 @@ open class KotlinUsesExtractor(
|
||||
)
|
||||
return null
|
||||
}
|
||||
return extractFileClass(fqName)
|
||||
val fileClassId = extractFileClass(fqName)
|
||||
// Under K2, external file class members sit directly under IrExternalPackageFragment
|
||||
// rather than under their IrClass parent. In that case the file class entity won't
|
||||
// get a location set through the normal extractClassSource path.
|
||||
if (d is IrMemberWithContainerSource && tw.lm.externalFileClassLocationsExtracted.add(fqName)) {
|
||||
val binaryPath =
|
||||
getContainerSourceBinaryPath(d.containerSource)
|
||||
?.let { normalizeExternalFileClassBinaryPath(it, fqName) }
|
||||
?: "/!unknown-binary-location/${fqName.asString().replace(".", "/")}.class"
|
||||
val fileId = tw.mkFileId(binaryPath, true)
|
||||
tw.writeHasLocation(fileClassId, tw.getWholeFileLocation(fileId))
|
||||
}
|
||||
return fileClassId
|
||||
}
|
||||
return useDeclarationParent(parent, canBeTopLevel, classTypeArguments, inReceiverContext)
|
||||
}
|
||||
|
||||
@@ -51,6 +51,13 @@ class TrapLabelManager {
|
||||
* to avoid duplication.
|
||||
*/
|
||||
val fileClassLocationsExtracted = HashSet<IrFile>()
|
||||
|
||||
/**
|
||||
* Tracks external file classes (by FqName) whose location has been set from a binary path.
|
||||
* Used to avoid writing duplicate hasLocation facts for external file class entities extracted
|
||||
* through the K2 code path where declarations sit directly under IrExternalPackageFragment.
|
||||
*/
|
||||
val externalFileClassLocationsExtracted = HashSet<org.jetbrains.kotlin.name.FqName>()
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -176,15 +176,56 @@ fun getIrDeclarationBinaryPath(d: IrDeclaration): String? {
|
||||
// This is in a file class.
|
||||
val fqName = getFileClassFqName(d)
|
||||
if (fqName != null) {
|
||||
if (d is IrMemberWithContainerSource) {
|
||||
val containerBinaryPath = getContainerSourceBinaryPath(d.containerSource)
|
||||
if (containerBinaryPath != null) {
|
||||
return normalizeExternalFileClassBinaryPath(containerBinaryPath, fqName)
|
||||
}
|
||||
}
|
||||
return getUnknownBinaryLocation(fqName.asString())
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to get the binary file path from a container source (typically a
|
||||
* [JvmPackagePartSource]). Returns null if the path is unavailable.
|
||||
*/
|
||||
fun getContainerSourceBinaryPath(containerSource: org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedContainerSource?): String? {
|
||||
if (containerSource !is JvmPackagePartSource) return null
|
||||
val binaryClass = containerSource.knownJvmBinaryClass ?: return null
|
||||
return when (binaryClass) {
|
||||
is VirtualFileKotlinClass -> {
|
||||
val vf = binaryClass.file
|
||||
val path = vf.path
|
||||
if (vf.fileSystem.protocol == StandardFileSystems.JRT_PROTOCOL)
|
||||
"/${path.split("!/", limit = 2)[1]}"
|
||||
else path
|
||||
}
|
||||
else -> binaryClass.location.takeIf { it.isNotEmpty() }
|
||||
}
|
||||
}
|
||||
|
||||
private fun getUnknownBinaryLocation(s: String): String {
|
||||
return "/!unknown-binary-location/${s.replace(".", "/")}.class"
|
||||
}
|
||||
|
||||
fun normalizeExternalFileClassBinaryPath(path: String, fqName: FqName): String {
|
||||
if (path.contains(".kotlinc_installed")) {
|
||||
return getUnknownBinaryLocation(fqName.asString())
|
||||
}
|
||||
val normalizedPath = path.replace('\\', '/')
|
||||
val classInternalPath = "${fqName.asString().replace(".", "/")}.class"
|
||||
val classSuffix = "/$classInternalPath"
|
||||
if (normalizedPath.endsWith(classSuffix)) {
|
||||
val classpathRoot = normalizedPath.removeSuffix(classSuffix).substringAfterLast('/')
|
||||
if (classpathRoot.isNotEmpty()) {
|
||||
return "$classpathRoot/$classInternalPath"
|
||||
}
|
||||
}
|
||||
return path
|
||||
}
|
||||
|
||||
fun getJavaEquivalentClassId(c: IrClass) =
|
||||
c.fqNameWhenAvailable?.toUnsafe()?.let { JavaToKotlinClassMap.mapKotlinToJava(it) }
|
||||
|
||||
Reference in New Issue
Block a user