mirror of
https://github.com/github/codeql.git
synced 2026-06-29 16:47:09 +02:00
Kotlin extractor: scope Object-method redeclaration recovery
Why this is needed: - library-tests/java-kotlin-collection-type-generic-methods/test.ql regressed with extra equals(Object) rows on generic collection/map/list declaration variants. - At the same time, java-interface-redeclares-tostring must still recover Object-method redeclarations for Java binary interfaces under K2. What changed: - In K2 ASM probing, treat classes with kotlin.Metadata as non-Java binaries for javaBinaryDeclaresMethod, so Java-redeclaration recovery does not fire on Kotlin binary classes. - Keep equals(Object) K2 Any/Any? compatibility handling, but constrain the workaround to non-generic parent classes and skip it when a concrete sibling declaration already exists. - Preserve the existing toString/hashCode redeclaration recovery path for affected Java binaries. Effect: - Removes the spurious equals(Object) rows in java-kotlin-collection-type-generic-methods while retaining expected Object-method extraction in java-interface-redeclares-tostring. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -180,11 +180,20 @@ open class KotlinFileExtractor(
|
||||
return try {
|
||||
val bytes = virtualFile.contentsToByteArray()
|
||||
var found = false
|
||||
var hasKotlinMetadata = false
|
||||
val reader = org.jetbrains.org.objectweb.asm.ClassReader(bytes)
|
||||
reader.accept(
|
||||
object : org.jetbrains.org.objectweb.asm.ClassVisitor(
|
||||
org.jetbrains.org.objectweb.asm.Opcodes.ASM9
|
||||
) {
|
||||
override fun visitAnnotation(
|
||||
descriptor: String,
|
||||
visible: Boolean
|
||||
): org.jetbrains.org.objectweb.asm.AnnotationVisitor? {
|
||||
if (descriptor == "Lkotlin/Metadata;") hasKotlinMetadata = true
|
||||
return null
|
||||
}
|
||||
|
||||
override fun visitMethod(
|
||||
access: Int,
|
||||
methodName: String,
|
||||
@@ -200,7 +209,7 @@ open class KotlinFileExtractor(
|
||||
org.jetbrains.org.objectweb.asm.ClassReader.SKIP_DEBUG or
|
||||
org.jetbrains.org.objectweb.asm.ClassReader.SKIP_FRAMES
|
||||
)
|
||||
found
|
||||
if (hasKotlinMetadata) false else found
|
||||
} catch (e: Exception) {
|
||||
logger.warn("Failed to check binary class methods for ${c.fqNameWhenAvailable}: $e")
|
||||
null
|
||||
@@ -212,9 +221,29 @@ open class KotlinFileExtractor(
|
||||
private fun isJavaBinaryDeclaration(f: IrFunction) =
|
||||
f.parentClassOrNull?.let { javaBinaryDeclaresMethod(it, f.name.asString()) } ?: false
|
||||
|
||||
private fun hasConcreteSiblingObjectMethod(f: IrFunction): Boolean {
|
||||
val parentClass = f.parentClassOrNull ?: return false
|
||||
return parentClass.declarations
|
||||
.asSequence()
|
||||
.filterIsInstance<IrFunction>()
|
||||
.filter { sibling ->
|
||||
sibling !== f &&
|
||||
sibling.name == f.name &&
|
||||
sibling.codeQlValueParameters.size == f.codeQlValueParameters.size
|
||||
}
|
||||
.any { sibling ->
|
||||
val hasInvisibleFakeVisibility =
|
||||
sibling.visibility.let {
|
||||
it is DelegatedDescriptorVisibility && it.delegate == Visibilities.InvisibleFake
|
||||
}
|
||||
!sibling.isFakeOverride && !hasInvisibleFakeVisibility
|
||||
}
|
||||
}
|
||||
|
||||
private fun isJavaBinaryObjectMethodRedeclaration(d: IrDeclaration) =
|
||||
when (d) {
|
||||
is IrFunction ->
|
||||
d.parentClassOrNull?.typeParameters?.isEmpty() == true &&
|
||||
when (d.name.asString()) {
|
||||
"toString" -> d.codeQlValueParameters.isEmpty()
|
||||
"hashCode" -> d.codeQlValueParameters.isEmpty()
|
||||
@@ -226,7 +255,9 @@ open class KotlinFileExtractor(
|
||||
?.type
|
||||
?.let { it.isNullableAny() || it.isAny() } ?: false
|
||||
else -> false
|
||||
} && isJavaBinaryDeclaration(d)
|
||||
} &&
|
||||
!hasConcreteSiblingObjectMethod(d) &&
|
||||
isJavaBinaryDeclaration(d)
|
||||
else -> false
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user