Skip to content

Commit a3a28c7

Browse files
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>
1 parent 7e3b32c commit a3a28c7

1 file changed

Lines changed: 33 additions & 2 deletions

File tree

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

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -180,11 +180,20 @@ open class KotlinFileExtractor(
180180
return try {
181181
val bytes = virtualFile.contentsToByteArray()
182182
var found = false
183+
var hasKotlinMetadata = false
183184
val reader = org.jetbrains.org.objectweb.asm.ClassReader(bytes)
184185
reader.accept(
185186
object : org.jetbrains.org.objectweb.asm.ClassVisitor(
186187
org.jetbrains.org.objectweb.asm.Opcodes.ASM9
187188
) {
189+
override fun visitAnnotation(
190+
descriptor: String,
191+
visible: Boolean
192+
): org.jetbrains.org.objectweb.asm.AnnotationVisitor? {
193+
if (descriptor == "Lkotlin/Metadata;") hasKotlinMetadata = true
194+
return null
195+
}
196+
188197
override fun visitMethod(
189198
access: Int,
190199
methodName: String,
@@ -200,7 +209,7 @@ open class KotlinFileExtractor(
200209
org.jetbrains.org.objectweb.asm.ClassReader.SKIP_DEBUG or
201210
org.jetbrains.org.objectweb.asm.ClassReader.SKIP_FRAMES
202211
)
203-
found
212+
if (hasKotlinMetadata) false else found
204213
} catch (e: Exception) {
205214
logger.warn("Failed to check binary class methods for ${c.fqNameWhenAvailable}: $e")
206215
null
@@ -212,9 +221,29 @@ open class KotlinFileExtractor(
212221
private fun isJavaBinaryDeclaration(f: IrFunction) =
213222
f.parentClassOrNull?.let { javaBinaryDeclaresMethod(it, f.name.asString()) } ?: false
214223

224+
private fun hasConcreteSiblingObjectMethod(f: IrFunction): Boolean {
225+
val parentClass = f.parentClassOrNull ?: return false
226+
return parentClass.declarations
227+
.asSequence()
228+
.filterIsInstance<IrFunction>()
229+
.filter { sibling ->
230+
sibling !== f &&
231+
sibling.name == f.name &&
232+
sibling.codeQlValueParameters.size == f.codeQlValueParameters.size
233+
}
234+
.any { sibling ->
235+
val hasInvisibleFakeVisibility =
236+
sibling.visibility.let {
237+
it is DelegatedDescriptorVisibility && it.delegate == Visibilities.InvisibleFake
238+
}
239+
!sibling.isFakeOverride && !hasInvisibleFakeVisibility
240+
}
241+
}
242+
215243
private fun isJavaBinaryObjectMethodRedeclaration(d: IrDeclaration) =
216244
when (d) {
217245
is IrFunction ->
246+
d.parentClassOrNull?.typeParameters?.isEmpty() == true &&
218247
when (d.name.asString()) {
219248
"toString" -> d.codeQlValueParameters.isEmpty()
220249
"hashCode" -> d.codeQlValueParameters.isEmpty()
@@ -226,7 +255,9 @@ open class KotlinFileExtractor(
226255
?.type
227256
?.let { it.isNullableAny() || it.isAny() } ?: false
228257
else -> false
229-
} && isJavaBinaryDeclaration(d)
258+
} &&
259+
!hasConcreteSiblingObjectMethod(d) &&
260+
isJavaBinaryDeclaration(d)
230261
else -> false
231262
}
232263

0 commit comments

Comments
 (0)