mirror of
https://github.com/github/codeql.git
synced 2025-12-21 03:06:31 +01:00
Kotlin: Handle file classes better
This commit is contained in:
@@ -34,7 +34,7 @@ open class KotlinFileExtractor(
|
||||
genericSpecialisationsExtracted: MutableSet<String>
|
||||
): KotlinUsesExtractor(logger, tw, dependencyCollector, externalClassExtractor, primitiveTypeMapping, pluginContext, genericSpecialisationsExtracted) {
|
||||
|
||||
fun extractDeclaration(declaration: IrDeclaration, parentId: Label<out DbReftype>) {
|
||||
fun extractDeclaration(declaration: IrDeclaration) {
|
||||
when (declaration) {
|
||||
is IrClass -> {
|
||||
if (isExternalDeclaration(declaration)) {
|
||||
@@ -43,14 +43,30 @@ open class KotlinFileExtractor(
|
||||
extractClassSource(declaration)
|
||||
}
|
||||
}
|
||||
is IrFunction -> extractFunctionIfReal(declaration, parentId)
|
||||
is IrFunction -> {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val parentId = useDeclarationParent(declaration.parent, false) as Label<DbReftype>
|
||||
extractFunctionIfReal(declaration, parentId)
|
||||
}
|
||||
is IrAnonymousInitializer -> {
|
||||
// Leaving this intentionally empty. init blocks are extracted during class extraction.
|
||||
}
|
||||
is IrProperty -> extractProperty(declaration, parentId)
|
||||
is IrEnumEntry -> extractEnumEntry(declaration, parentId)
|
||||
is IrField -> extractField(declaration, parentId)
|
||||
is IrTypeAlias -> extractTypeAlias(declaration) // TODO: Pass in and use parentId
|
||||
is IrProperty -> {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val parentId = useDeclarationParent(declaration.parent, false) as Label<DbReftype>
|
||||
extractProperty(declaration, parentId)
|
||||
}
|
||||
is IrEnumEntry -> {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val parentId = useDeclarationParent(declaration.parent, false) as Label<DbReftype>
|
||||
extractEnumEntry(declaration, parentId)
|
||||
}
|
||||
is IrField -> {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val parentId = useDeclarationParent(declaration.parent, false) as Label<DbReftype>
|
||||
extractField(declaration, parentId)
|
||||
}
|
||||
is IrTypeAlias -> extractTypeAlias(declaration)
|
||||
else -> logger.warnElement(Severity.ErrorSevere, "Unrecognised IrDeclaration: " + declaration.javaClass, declaration)
|
||||
}
|
||||
}
|
||||
@@ -251,7 +267,7 @@ open class KotlinFileExtractor(
|
||||
extractEnclosingClass(c, id, locId, listOf())
|
||||
|
||||
c.typeParameters.map { extractTypeParameter(it) }
|
||||
c.declarations.map { extractDeclaration(it, id) }
|
||||
c.declarations.map { extractDeclaration(it) }
|
||||
extractObjectInitializerFunction(c, id)
|
||||
if(c.isNonCompanionObject) {
|
||||
// For `object MyObject { ... }`, the .class has an
|
||||
@@ -443,9 +459,7 @@ open class KotlinFileExtractor(
|
||||
if (f.isLocalFunction())
|
||||
getLocallyVisibleFunctionLabels(f).function
|
||||
else
|
||||
// TODO: figure out whether to standardise on naming top-level functions for the file-class
|
||||
// or (as temporarily done here) for their containing package.
|
||||
useFunction<DbCallable>(f, if (f.parent is IrFile) useDeclarationParent(f.parent) else parentId)
|
||||
useFunction<DbCallable>(f, parentId)
|
||||
|
||||
val extReceiver = f.extensionReceiverParameter
|
||||
val idxOffset = if (extReceiver != null) 1 else 0
|
||||
@@ -2499,7 +2513,8 @@ open class KotlinFileExtractor(
|
||||
|
||||
if (parent is IrFile) {
|
||||
if (this is KotlinSourceFileExtractor && this.file == parent) {
|
||||
tw.writeEnclInReftype(id, this.fileClass)
|
||||
val fileId = extractFileClass(parent)
|
||||
tw.writeEnclInReftype(id, fileId)
|
||||
} else {
|
||||
logger.warn(Severity.ErrorSevere, "Unexpected file parent found")
|
||||
}
|
||||
|
||||
@@ -22,54 +22,13 @@ class KotlinSourceFileExtractor(
|
||||
) :
|
||||
KotlinFileExtractor(logger, tw, null, externalClassExtractor, primitiveTypeMapping, pluginContext, genericSpecialisationsExtracted) {
|
||||
|
||||
val fileClass by lazy {
|
||||
extractFileClass(file)
|
||||
}
|
||||
|
||||
fun extractFileContents(id: Label<DbFile>) {
|
||||
val locId = tw.getWholeFileLocation()
|
||||
val pkg = file.fqName.asString()
|
||||
val pkgId = extractPackage(pkg)
|
||||
tw.writeHasLocation(id, locId)
|
||||
tw.writeCupackage(id, pkgId)
|
||||
// TODO: Use of fileClass looks like it will defeat laziness since 3502e5c5720e981c913bdafdccf7f5e9237be070
|
||||
// TODO: Fix, then reenable consistency-queries/file_classes.ql
|
||||
file.declarations.map { extractDeclaration(it, fileClass) }
|
||||
file.declarations.map { extractDeclaration(it) }
|
||||
CommentExtractor(this).extract()
|
||||
}
|
||||
|
||||
@OptIn(kotlin.ExperimentalStdlibApi::class) // Annotation required by kotlin versions < 1.5
|
||||
fun extractFileClass(f: IrFile): Label<out DbClass> {
|
||||
val fileName = f.fileEntry.name
|
||||
val pkg = f.fqName.asString()
|
||||
val defaultName = fileName.replaceFirst(Regex(""".*[/\\]"""), "").replaceFirst(Regex("""\.kt$"""), "").replaceFirstChar({ it.uppercase() }) + "Kt"
|
||||
var jvmName = defaultName
|
||||
for(a: IrConstructorCall in f.annotations) {
|
||||
val t = a.type
|
||||
if(t is IrSimpleType && a.valueArgumentsCount == 1) {
|
||||
val owner = t.classifier.owner
|
||||
val v = a.getValueArgument(0)
|
||||
if(owner is IrClass) {
|
||||
val aPkg = owner.packageFqName?.asString()
|
||||
val name = owner.name.asString()
|
||||
if(aPkg == "kotlin.jvm" && name == "JvmName" && v is IrConst<*>) {
|
||||
val value = v.value
|
||||
if(value is String) {
|
||||
jvmName = value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
val qualClassName = if (pkg.isEmpty()) jvmName else "$pkg.$jvmName"
|
||||
val label = "@\"class;$qualClassName\""
|
||||
val id: Label<DbClass> = tw.getLabelFor(label)
|
||||
val locId = tw.getWholeFileLocation()
|
||||
val pkgId = extractPackage(pkg)
|
||||
tw.writeClasses(id, jvmName, pkgId, id)
|
||||
tw.writeFile_class(id)
|
||||
tw.writeHasLocation(id, locId)
|
||||
return id
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@ import org.jetbrains.kotlin.backend.jvm.codegen.isRawType
|
||||
import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap
|
||||
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.expressions.IrConst
|
||||
import org.jetbrains.kotlin.ir.expressions.IrConstructorCall
|
||||
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
@@ -39,6 +41,42 @@ open class KotlinUsesExtractor(
|
||||
return id
|
||||
}
|
||||
|
||||
|
||||
@OptIn(kotlin.ExperimentalStdlibApi::class) // Annotation required by kotlin versions < 1.5
|
||||
fun extractFileClass(f: IrFile): Label<out DbClass> {
|
||||
val fileName = f.fileEntry.name
|
||||
val pkg = f.fqName.asString()
|
||||
val defaultName = fileName.replaceFirst(Regex(""".*[/\\]"""), "").replaceFirst(Regex("""\.kt$"""), "").replaceFirstChar({ it.uppercase() }) + "Kt"
|
||||
var jvmName = defaultName
|
||||
for(a: IrConstructorCall in f.annotations) {
|
||||
val t = a.type
|
||||
if(t is IrSimpleType && a.valueArgumentsCount == 1) {
|
||||
val owner = t.classifier.owner
|
||||
val v = a.getValueArgument(0)
|
||||
if(owner is IrClass) {
|
||||
val aPkg = owner.packageFqName?.asString()
|
||||
val name = owner.name.asString()
|
||||
if(aPkg == "kotlin.jvm" && name == "JvmName" && v is IrConst<*>) {
|
||||
val value = v.value
|
||||
if(value is String) {
|
||||
jvmName = value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
val qualClassName = if (pkg.isEmpty()) jvmName else "$pkg.$jvmName"
|
||||
val label = "@\"class;$qualClassName\""
|
||||
val id: Label<DbClass> = tw.getLabelFor(label)
|
||||
val fileId = tw.mkFileId(f.path, false)
|
||||
val locId = tw.getWholeFileLocation(fileId)
|
||||
val pkgId = extractPackage(pkg)
|
||||
tw.writeClasses(id, jvmName, pkgId, id)
|
||||
tw.writeFile_class(id)
|
||||
tw.writeHasLocation(id, locId)
|
||||
return id
|
||||
}
|
||||
|
||||
data class UseClassInstanceResult(val typeResult: TypeResult<DbClassorinterface>, val javaClass: IrClass)
|
||||
/**
|
||||
* A triple of a type's database label, its signature for use in callable signatures, and its short name for use
|
||||
@@ -481,9 +519,24 @@ class X {
|
||||
}
|
||||
}
|
||||
|
||||
fun useDeclarationParent(dp: IrDeclarationParent, classTypeArguments: List<IrTypeArgument>? = null, inReceiverContext: Boolean = false): Label<out DbElement> =
|
||||
fun useDeclarationParent(
|
||||
// The declaration parent according to Kotlin
|
||||
dp: IrDeclarationParent,
|
||||
// Whether the type of entity whose parent this is can be a
|
||||
// top-level entity in the JVM's eyes. If so, then its parent may
|
||||
// be a file; otherwise, if dp is a file foo.kt, then the parent
|
||||
// is really the JVM class FooKt.
|
||||
canBeTopLevel: Boolean,
|
||||
classTypeArguments: List<IrTypeArgument>? = null,
|
||||
inReceiverContext: Boolean = false):
|
||||
Label<out DbElement> =
|
||||
when(dp) {
|
||||
is IrFile -> usePackage(dp.fqName.asString())
|
||||
is IrFile ->
|
||||
if(canBeTopLevel) {
|
||||
usePackage(dp.fqName.asString())
|
||||
} else {
|
||||
extractFileClass(dp)
|
||||
}
|
||||
is IrClass -> if (classTypeArguments != null && !dp.isAnonymousObject) useClassInstance(dp, classTypeArguments, inReceiverContext).typeResult.id else useClassSource(dp)
|
||||
is IrFunction -> useFunction(dp)
|
||||
else -> {
|
||||
@@ -525,7 +578,7 @@ class X {
|
||||
extensionReceiverParameter: IrValueParameter?,
|
||||
classTypeArguments: List<IrTypeArgument>? = null
|
||||
): String {
|
||||
val parentId = useDeclarationParent(parent, classTypeArguments, true)
|
||||
val parentId = useDeclarationParent(parent, false, classTypeArguments, true)
|
||||
return getFunctionLabel(parentId, name, parameters, returnType, extensionReceiverParameter)
|
||||
}
|
||||
|
||||
@@ -726,7 +779,7 @@ class X {
|
||||
}
|
||||
|
||||
fun getTypeParameterLabel(param: IrTypeParameter): String {
|
||||
val parentLabel = useDeclarationParent(param.parent)
|
||||
val parentLabel = useDeclarationParent(param.parent, false)
|
||||
return "@\"typevar;{$parentLabel};${param.name}\""
|
||||
}
|
||||
|
||||
@@ -844,7 +897,7 @@ class X {
|
||||
* `parent` is null.
|
||||
*/
|
||||
fun getValueParameterLabel(vp: IrValueParameter, parent: Label<out DbCallable>?): String {
|
||||
val parentId = parent ?: useDeclarationParent(vp.parent)
|
||||
val parentId = parent ?: useDeclarationParent(vp.parent, false)
|
||||
val idx = vp.index
|
||||
if (idx < 0) {
|
||||
// We're not extracting this and this@TYPE parameters of functions:
|
||||
@@ -858,7 +911,7 @@ class X {
|
||||
tw.getLabelFor(getValueParameterLabel(vp, parent))
|
||||
|
||||
fun getFieldLabel(f: IrField): String {
|
||||
val parentId = useDeclarationParent(f.parent)
|
||||
val parentId = useDeclarationParent(f.parent, false)
|
||||
return "@\"field;{$parentId};${f.name.asString()}\""
|
||||
}
|
||||
|
||||
@@ -866,7 +919,7 @@ class X {
|
||||
tw.getLabelFor(getFieldLabel(f))
|
||||
|
||||
fun getPropertyLabel(p: IrProperty) =
|
||||
getPropertyLabel(p, useDeclarationParent(p.parent))
|
||||
getPropertyLabel(p, useDeclarationParent(p.parent, false))
|
||||
|
||||
fun getPropertyLabel(p: IrProperty, parentId: Label<out DbElement>) =
|
||||
"@\"property;{$parentId};${p.name.asString()}\""
|
||||
@@ -878,7 +931,7 @@ class X {
|
||||
tw.getLabelFor(getPropertyLabel(p, parentId))
|
||||
|
||||
fun getEnumEntryLabel(ee: IrEnumEntry): String {
|
||||
val parentId = useDeclarationParent(ee.parent)
|
||||
val parentId = useDeclarationParent(ee.parent, false)
|
||||
return "@\"field;{$parentId};${ee.name.asString()}\""
|
||||
}
|
||||
|
||||
@@ -886,7 +939,7 @@ class X {
|
||||
tw.getLabelFor(getEnumEntryLabel(ee))
|
||||
|
||||
private fun getTypeAliasLabel(ta: IrTypeAlias): String {
|
||||
val parentId = useDeclarationParent(ta.parent)
|
||||
val parentId = useDeclarationParent(ta.parent, true)
|
||||
return "@\"type_alias;{$parentId};${ta.name.asString()}\""
|
||||
}
|
||||
|
||||
|
||||
@@ -130,7 +130,28 @@ open class TrapWriter (protected val lm: TrapLabelManager, private val bw: Buffe
|
||||
* location.
|
||||
*/
|
||||
val unknownLocation: Label<DbLocation> by lazy {
|
||||
getLocation(unknownFileId, 0, 0, 0, 0)
|
||||
getWholeFileLocation(unknownFileId)
|
||||
}
|
||||
|
||||
fun mkFileId(filePath: String, populateFileTables: Boolean): Label<DbFile> {
|
||||
// If a file is in a jar, then the Kotlin compiler gives
|
||||
// `<jar file>!/<path within jar>` as its path. We need to split
|
||||
// it as appropriate, to make the right file ID.
|
||||
val populateFile = PopulateFile(this)
|
||||
val splitFilePath = filePath.split("!/")
|
||||
if(splitFilePath.size == 1) {
|
||||
return populateFile.getFileLabel(File(filePath), populateFileTables)
|
||||
} else {
|
||||
return populateFile.getFileInJarLabel(File(splitFilePath.get(0)), splitFilePath.get(1), populateFileTables)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If you have an ID for a file, then this gets a label for the
|
||||
* location representing the whole of that file.
|
||||
*/
|
||||
fun getWholeFileLocation(fileId: Label<DbFile>): Label<DbLocation> {
|
||||
return getLocation(fileId, 0, 0, 0, 0)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -180,16 +201,7 @@ open class FileTrapWriter (
|
||||
val filePath: String,
|
||||
populateFileTables: Boolean
|
||||
): TrapWriter (lm, bw) {
|
||||
// If a file is in a jar, then the Kotlin compiler gives
|
||||
// `<jar file>!/<path within jar>` as its path. We need to split
|
||||
// it as appropriate, to make the right file ID.
|
||||
val populateFile = PopulateFile(this)
|
||||
val splitFilePath = filePath.split("!/")
|
||||
val fileId =
|
||||
if(splitFilePath.size == 1)
|
||||
populateFile.getFileLabel(File(filePath), populateFileTables)
|
||||
else
|
||||
populateFile.getFileInJarLabel(File(splitFilePath.get(0)), splitFilePath.get(1), populateFileTables)
|
||||
val fileId = mkFileId(filePath, populateFileTables)
|
||||
|
||||
/**
|
||||
* Gets a label for the location of `e`.
|
||||
@@ -201,7 +213,7 @@ open class FileTrapWriter (
|
||||
* Gets a label for the location representing the whole of this file.
|
||||
*/
|
||||
fun getWholeFileLocation(): Label<DbLocation> {
|
||||
return getLocation(fileId, 0, 0, 0, 0)
|
||||
return getWholeFileLocation(fileId)
|
||||
}
|
||||
/**
|
||||
* Gets a label for the location corresponding to `startOffset` and
|
||||
|
||||
Reference in New Issue
Block a user