Skip to content

Commit 1fb4e13

Browse files
authored
Merge pull request #13960 from igfoo/igfoo/parent
Kotlin: Handle Kotlin 2 parents better
2 parents e56cd0b + 3b9bd16 commit 1fb4e13

File tree

8 files changed

+221
-44
lines changed

8 files changed

+221
-44
lines changed

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

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -88,12 +88,10 @@ class ExternalDeclExtractor(val logger: FileLogger, val invocationTrapFile: Stri
8888
nextBatch.forEach { workPair ->
8989
val (irDecl, possiblyLongSignature) = workPair
9090
extractElement(irDecl, possiblyLongSignature, false) { trapFileBW, signature, manager ->
91-
val containingClass = getContainingClassOrSelf(irDecl)
92-
if (containingClass == null) {
93-
logger.errorElement("Unable to get containing class", irDecl)
91+
val binaryPath = getIrDeclarationBinaryPath(irDecl)
92+
if (binaryPath == null) {
93+
logger.errorElement("Unable to get binary path", irDecl)
9494
} else {
95-
val binaryPath = getIrClassBinaryPath(containingClass)
96-
9795
// We want our comments to be the first thing in the file,
9896
// so start off with a PlainTrapWriter
9997
val tw = PlainTrapWriter(logger.loggerBase, TrapLabelManager(), trapFileBW, diagnosticTrapWriter)

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

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ open class KotlinFileExtractor(
191191
}
192192
}
193193
is IrFunction -> {
194-
val parentId = useDeclarationParent(declaration.parent, false)?.cast<DbReftype>()
194+
val parentId = useDeclarationParentOf(declaration, false)?.cast<DbReftype>()
195195
if (parentId != null) {
196196
extractFunction(declaration, parentId, extractBody = extractFunctionBodies, extractMethodAndParameterTypeAccesses = extractFunctionBodies, extractAnnotations = extractAnnotations, null, listOf())
197197
}
@@ -201,21 +201,21 @@ open class KotlinFileExtractor(
201201
// Leaving this intentionally empty. init blocks are extracted during class extraction.
202202
}
203203
is IrProperty -> {
204-
val parentId = useDeclarationParent(declaration.parent, false)?.cast<DbReftype>()
204+
val parentId = useDeclarationParentOf(declaration, false)?.cast<DbReftype>()
205205
if (parentId != null) {
206206
extractProperty(declaration, parentId, extractBackingField = true, extractFunctionBodies = extractFunctionBodies, extractPrivateMembers = extractPrivateMembers, extractAnnotations = extractAnnotations, null, listOf())
207207
}
208208
Unit
209209
}
210210
is IrEnumEntry -> {
211-
val parentId = useDeclarationParent(declaration.parent, false)?.cast<DbReftype>()
211+
val parentId = useDeclarationParentOf(declaration, false)?.cast<DbReftype>()
212212
if (parentId != null) {
213213
extractEnumEntry(declaration, parentId, extractPrivateMembers, extractFunctionBodies)
214214
}
215215
Unit
216216
}
217217
is IrField -> {
218-
val parentId = useDeclarationParent(getFieldParent(declaration), false)?.cast<DbReftype>()
218+
val parentId = useDeclarationParentOf(declaration, false)?.cast<DbReftype>()
219219
if (parentId != null) {
220220
// For consistency with the Java extractor, enum entries get type accesses only if we're extracting from .kt source (i.e., when `extractFunctionBodies` is set)
221221
extractField(declaration, parentId, extractAnnotationEnumTypeAccesses = extractFunctionBodies)
@@ -408,11 +408,10 @@ open class KotlinFileExtractor(
408408

409409
private fun getLocation(decl: IrDeclaration, typeArgs: List<IrTypeArgument>?): Label<DbLocation> {
410410
return if (typeArgs != null && typeArgs.isNotEmpty()) {
411-
val c = getContainingClassOrSelf(decl)
412-
if (c == null) {
411+
val binaryPath = getIrDeclarationBinaryPath(decl)
412+
if (binaryPath == null) {
413413
tw.getLocation(decl)
414414
} else {
415-
val binaryPath = getIrClassBinaryPath(c)
416415
val newTrapWriter = tw.makeFileTrapWriter(binaryPath, true)
417416
newTrapWriter.getWholeFileLocation()
418417
}
@@ -1285,7 +1284,7 @@ open class KotlinFileExtractor(
12851284
val sourceParentId =
12861285
maybeSourceParentId ?:
12871286
if (typeSubstitution != null)
1288-
useDeclarationParent(f.parent, false)
1287+
useDeclarationParentOf(f, false)
12891288
else
12901289
parentId
12911290
if (sourceParentId == null) {
@@ -1617,7 +1616,7 @@ open class KotlinFileExtractor(
16171616
}
16181617

16191618
if (bf != null && extractBackingField) {
1620-
val fieldParentId = useDeclarationParent(getFieldParent(bf), false)
1619+
val fieldParentId = useDeclarationParentOf(bf, false)
16211620
if (fieldParentId != null) {
16221621
val fieldId = extractField(bf, fieldParentId.cast(), extractFunctionBodies)
16231622
tw.writeKtPropertyBackingFields(id, fieldId)
@@ -2091,7 +2090,7 @@ open class KotlinFileExtractor(
20912090

20922091
private fun getDefaultsMethodLabel(f: IrFunction): Label<out DbCallable>? {
20932092
val classTypeArgsIncludingOuterClasses = null
2094-
val parentId = useDeclarationParent(f.parent, false, classTypeArgsIncludingOuterClasses, true)
2093+
val parentId = useDeclarationParentOf(f, false, classTypeArgsIncludingOuterClasses, true)
20952094
if (parentId == null) {
20962095
logger.errorElement("Couldn't get parent ID for defaults method", f)
20972096
return null
@@ -2154,7 +2153,7 @@ open class KotlinFileExtractor(
21542153
if (overriddenCallTarget.isLocalFunction()) {
21552154
extractTypeAccess(getLocallyVisibleFunctionLabels(overriddenCallTarget).type, locId, id, -1, enclosingCallable, enclosingStmt)
21562155
} else {
2157-
extractStaticTypeAccessQualifierUnchecked(overriddenCallTarget.parent, id, locId, enclosingCallable, enclosingStmt)
2156+
extractStaticTypeAccessQualifierUnchecked(overriddenCallTarget, id, locId, enclosingCallable, enclosingStmt)
21582157
}
21592158

21602159
extractDefaultsCallArguments(id, overriddenCallTarget, enclosingCallable, enclosingStmt, valueArguments, dispatchReceiver, extensionReceiver)
@@ -2380,8 +2379,17 @@ open class KotlinFileExtractor(
23802379
extractValueArguments(argParent, idxOffset)
23812380
}
23822381

2383-
private fun extractStaticTypeAccessQualifierUnchecked(parent: IrDeclarationParent, parentExpr: Label<out DbExprparent>, locId: Label<DbLocation>, enclosingCallable: Label<out DbCallable>?, enclosingStmt: Label<out DbStmt>?) {
2384-
if (parent is IrClass) {
2382+
private fun extractStaticTypeAccessQualifierUnchecked(target: IrDeclaration, parentExpr: Label<out DbExprparent>, locId: Label<DbLocation>, enclosingCallable: Label<out DbCallable>?, enclosingStmt: Label<out DbStmt>?) {
2383+
val parent = target.parent
2384+
if (parent is IrExternalPackageFragment) {
2385+
// This is in a file class.
2386+
val fqName = getFileClassFqName(target)
2387+
if (fqName == null) {
2388+
logger.error("Can't get FqName for element in external package fragment ${target.javaClass}")
2389+
} else {
2390+
extractTypeAccess(useFileClassType(fqName), locId, parentExpr, -1, enclosingCallable, enclosingStmt)
2391+
}
2392+
} else if (parent is IrClass) {
23852393
extractTypeAccessRecursive(parent.toRawType(), locId, parentExpr, -1, enclosingCallable, enclosingStmt)
23862394
} else if (parent is IrFile) {
23872395
extractTypeAccess(useFileClassType(parent), locId, parentExpr, -1, enclosingCallable, enclosingStmt)
@@ -2392,7 +2400,7 @@ open class KotlinFileExtractor(
23922400

23932401
private fun extractStaticTypeAccessQualifier(target: IrDeclaration, parentExpr: Label<out DbExprparent>, locId: Label<DbLocation>, enclosingCallable: Label<out DbCallable>?, enclosingStmt: Label<out DbStmt>?) {
23942402
if (target.shouldExtractAsStatic) {
2395-
extractStaticTypeAccessQualifierUnchecked(target.parent, parentExpr, locId, enclosingCallable, enclosingStmt)
2403+
extractStaticTypeAccessQualifierUnchecked(target, parentExpr, locId, enclosingCallable, enclosingStmt)
23962404
}
23972405
}
23982406

@@ -3841,7 +3849,7 @@ open class KotlinFileExtractor(
38413849
val type = useType(pluginContext.irBuiltIns.unitType)
38423850
val locId = tw.getLocation(e)
38433851
val parentClass = irConstructor.parentAsClass
3844-
val parentId = useDeclarationParent(parentClass, false, null, true)
3852+
val parentId = useDeclarationParentOf(irConstructor, false, null, true)
38453853
if (parentId == null) {
38463854
logger.errorElement("Cannot get parent ID for obinit", e)
38473855
return

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

Lines changed: 87 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package com.github.codeql
22

33
import com.github.codeql.utils.*
44
import com.github.codeql.utils.versions.codeQlWithHasQuestionMark
5+
import com.github.codeql.utils.versions.getFileClassFqName
56
import com.github.codeql.utils.versions.getKotlinType
67
import com.github.codeql.utils.versions.isRawType
78
import com.semmle.extractor.java.OdasaOutput
@@ -71,18 +72,41 @@ open class KotlinUsesExtractor(
7172
TypeResult(fakeKotlinType(), "", "")
7273
)
7374

75+
fun useFileClassType(fqName: FqName) = TypeResults(
76+
TypeResult(extractFileClass(fqName), "", ""),
77+
TypeResult(fakeKotlinType(), "", "")
78+
)
79+
80+
private fun useFileClassType(pkg: String, jvmName: String) = TypeResults(
81+
TypeResult(extractFileClass(pkg, jvmName), "", ""),
82+
TypeResult(fakeKotlinType(), "", "")
83+
)
84+
7485
fun extractFileClass(f: IrFile): Label<out DbClassorinterface> {
7586
val pkg = f.fqName.asString()
7687
val jvmName = getFileClassName(f)
88+
val id = extractFileClass(pkg, jvmName)
89+
if (tw.lm.fileClassLocationsExtracted.add(f)) {
90+
val fileId = tw.mkFileId(f.path, false)
91+
val locId = tw.getWholeFileLocation(fileId)
92+
tw.writeHasLocation(id, locId)
93+
}
94+
return id
95+
}
96+
97+
private fun extractFileClass(fqName: FqName): Label<out DbClassorinterface> {
98+
val pkg = if (fqName.isRoot()) "" else fqName.parent().asString()
99+
val jvmName = fqName.shortName().asString()
100+
return extractFileClass(pkg, jvmName)
101+
}
102+
103+
private fun extractFileClass(pkg: String, jvmName: String): Label<out DbClassorinterface> {
77104
val qualClassName = if (pkg.isEmpty()) jvmName else "$pkg.$jvmName"
78105
val label = "@\"class;$qualClassName\""
79106
val id: Label<DbClassorinterface> = tw.getLabelFor(label) {
80-
val fileId = tw.mkFileId(f.path, false)
81-
val locId = tw.getWholeFileLocation(fileId)
82107
val pkgId = extractPackage(pkg)
83108
tw.writeClasses_or_interfaces(it, jvmName, pkgId, it)
84109
tw.writeFile_class(it)
85-
tw.writeHasLocation(it, locId)
86110

87111
addModifiers(it, "public", "final")
88112
}
@@ -250,7 +274,11 @@ open class KotlinUsesExtractor(
250274
is IrClass -> extractExternalClassLater(parent)
251275
is IrFunction -> extractExternalEnclosingClassLater(parent)
252276
is IrFile -> logger.error("extractExternalEnclosingClassLater but no enclosing class.")
253-
else -> logger.error("Unrecognised extractExternalEnclosingClassLater: " + d.javaClass)
277+
is IrExternalPackageFragment -> {
278+
// The parent is a (multi)file class. We don't need
279+
// extract it separately.
280+
}
281+
else -> logger.error("Unrecognised extractExternalEnclosingClassLater ${parent.javaClass} for ${d.javaClass}")
254282
}
255283
}
256284

@@ -293,7 +321,17 @@ open class KotlinUsesExtractor(
293321

294322
private fun extractFunctionLaterIfExternalFileMember(f: IrFunction) {
295323
if (isExternalFileClassMember(f)) {
296-
extractExternalClassLater(f.parentAsClass)
324+
val p = f.parent
325+
when (p) {
326+
is IrClass -> extractExternalClassLater(p)
327+
is IrExternalPackageFragment -> {
328+
// The parent is a (multi)file class. We don't need
329+
// extract it separately.
330+
}
331+
else -> {
332+
logger.warn("Unexpected parent type ${p.javaClass} for external file class member")
333+
}
334+
}
297335
(f as? IrSimpleFunction)?.correspondingPropertySymbol?.let {
298336
extractPropertyLaterIfExternalFileMember(it.owner)
299337
// No need to extract the function specifically, as the property's
@@ -761,6 +799,41 @@ open class KotlinUsesExtractor(
761799
}
762800
}
763801

802+
private fun parentOf(d: IrDeclaration): IrDeclarationParent {
803+
if (d is IrField) {
804+
return getFieldParent(d)
805+
}
806+
return d.parent
807+
}
808+
809+
fun useDeclarationParentOf(
810+
// The declaration
811+
d: IrDeclaration,
812+
// Whether the type of entity whose parent this is can be a
813+
// top-level entity in the JVM's eyes. If so, then its parent may
814+
// be a file; otherwise, if dp is a file foo.kt, then the parent
815+
// is really the JVM class FooKt.
816+
canBeTopLevel: Boolean,
817+
classTypeArguments: List<IrTypeArgument>? = null,
818+
inReceiverContext: Boolean = false):
819+
Label<out DbElement>? {
820+
821+
val parent = parentOf(d)
822+
if (parent is IrExternalPackageFragment) {
823+
// This is in a file class.
824+
val fqName = getFileClassFqName(d)
825+
if (fqName == null) {
826+
logger.error("Can't get FqName for element in external package fragment ${d.javaClass}")
827+
return null
828+
}
829+
return extractFileClass(fqName)
830+
}
831+
return useDeclarationParent(parent, canBeTopLevel, classTypeArguments, inReceiverContext)
832+
}
833+
834+
// Generally, useDeclarationParentOf should be used instead of
835+
// calling this directly, as this cannot handle
836+
// IrExternalPackageFragment
764837
fun useDeclarationParent(
765838
// The declaration parent according to Kotlin
766839
dp: IrDeclarationParent,
@@ -792,8 +865,7 @@ open class KotlinUsesExtractor(
792865
}
793866
is IrFunction -> useFunction(dp)
794867
is IrExternalPackageFragment -> {
795-
// TODO
796-
logger.error("Unhandled IrExternalPackageFragment")
868+
logger.error("Unable to handle IrExternalPackageFragment as an IrDeclarationParent")
797869
null
798870
}
799871
else -> {
@@ -1035,7 +1107,7 @@ open class KotlinUsesExtractor(
10351107
* in.
10361108
*/
10371109
fun getFunctionLabel(f: IrFunction, classTypeArgsIncludingOuterClasses: List<IrTypeArgument>?): String? {
1038-
val parentId = useDeclarationParent(f.parent, false, classTypeArgsIncludingOuterClasses, true)
1110+
val parentId = useDeclarationParentOf(f, false, classTypeArgsIncludingOuterClasses, true)
10391111
if (parentId == null) {
10401112
logger.error("Couldn't get parent ID for function label")
10411113
return null
@@ -1332,7 +1404,7 @@ open class KotlinUsesExtractor(
13321404
return ids.function.cast<T>()
13331405
}
13341406
val javaFun = kotlinFunctionToJavaEquivalent(f, noReplace)
1335-
val parentId = useDeclarationParent(javaFun.parent, false, classTypeArgsIncludingOuterClasses, true)
1407+
val parentId = useDeclarationParentOf(javaFun, false, classTypeArgsIncludingOuterClasses, true)
13361408
if (parentId == null) {
13371409
logger.error("Couldn't find parent ID for function ${f.name.asString()}")
13381410
return null
@@ -1639,7 +1711,7 @@ open class KotlinUsesExtractor(
16391711
val overriddenParentAttributes = (declarationParent as? IrFunction)?.let {
16401712
(this as? KotlinFileExtractor)?.declarationStack?.findOverriddenAttributes(it)
16411713
}
1642-
val parentId = parent ?: overriddenParentAttributes?.id ?: useDeclarationParent(declarationParent, false)
1714+
val parentId = parent ?: overriddenParentAttributes?.id ?: useDeclarationParentOf(vp, false)
16431715

16441716
val idxBase = overriddenParentAttributes?.valueParameters?.indexOf(vp) ?: vp.index
16451717
val idxOffset = if (declarationParent is IrFunction && declarationParent.extensionReceiverParameter != null)
@@ -1667,7 +1739,7 @@ open class KotlinUsesExtractor(
16671739
it.isConst || it.isLateinit
16681740
} ?: false
16691741

1670-
fun getFieldParent(f: IrField) =
1742+
private fun getFieldParent(f: IrField) =
16711743
f.parentClassOrNull?.let {
16721744
if (it.isCompanion && isDirectlyExposableCompanionObjectField(f))
16731745
it.parent
@@ -1684,7 +1756,7 @@ open class KotlinUsesExtractor(
16841756
}
16851757

16861758
fun getFieldLabel(f: IrField): String {
1687-
val parentId = useDeclarationParent(getFieldParent(f), false)
1759+
val parentId = useDeclarationParentOf(f, false)
16881760
// Distinguish backing fields of properties based on their extension receiver type;
16891761
// otherwise two extension properties declared in the same enclosing context will get
16901762
// clashing trap labels. These are always private, so we can just make up a label without
@@ -1697,7 +1769,7 @@ open class KotlinUsesExtractor(
16971769
tw.getLabelFor<DbField>(getFieldLabel(f)).also { extractFieldLaterIfExternalFileMember(f) }
16981770

16991771
fun getPropertyLabel(p: IrProperty): String? {
1700-
val parentId = useDeclarationParent(p.parent, false)
1772+
val parentId = useDeclarationParentOf(p, false)
17011773
if (parentId == null) {
17021774
return null
17031775
} else {
@@ -1729,15 +1801,15 @@ open class KotlinUsesExtractor(
17291801
}
17301802

17311803
fun getEnumEntryLabel(ee: IrEnumEntry): String {
1732-
val parentId = useDeclarationParent(ee.parent, false)
1804+
val parentId = useDeclarationParentOf(ee, false)
17331805
return "@\"field;{$parentId};${ee.name.asString()}\""
17341806
}
17351807

17361808
fun useEnumEntry(ee: IrEnumEntry): Label<out DbField> =
17371809
tw.getLabelFor(getEnumEntryLabel(ee))
17381810

17391811
fun getTypeAliasLabel(ta: IrTypeAlias): String {
1740-
val parentId = useDeclarationParent(ta.parent, true)
1812+
val parentId = useDeclarationParentOf(ta, true)
17411813
return "@\"type_alias;{$parentId};${ta.name.asString()}\""
17421814
}
17431815

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,15 @@ class TrapLabelManager {
4848
* duplication.
4949
*/
5050
val genericSpecialisationsExtracted = HashSet<String>()
51+
52+
/**
53+
* Sometimes, when we extract a file class we don't have the IrFile
54+
* for it, so we are not able to give it a location. This means that
55+
* the location is written outside of the label creation.
56+
* This allows us to keep track of whether we've written the location
57+
* already in this TRAP file, to avoid duplication.
58+
*/
59+
val fileClassLocationsExtracted = HashSet<IrFile>()
5160
}
5261

5362
/**

0 commit comments

Comments
 (0)