Skip to content

Commit c4ba644

Browse files
authored
Merge pull request github#10952 from smowton/smowton/fix/java-interface-redeclares-tostring
Kotlin: extract interface redeclarations of `Object` methods
2 parents b9f1cc5 + d171dec commit c4ba644

File tree

7 files changed

+53
-7
lines changed

7 files changed

+53
-7
lines changed

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

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import org.jetbrains.kotlin.load.java.structure.JavaClass
2727
import org.jetbrains.kotlin.load.java.structure.JavaMethod
2828
import org.jetbrains.kotlin.load.java.structure.JavaTypeParameter
2929
import org.jetbrains.kotlin.load.java.structure.JavaTypeParameterListOwner
30+
import org.jetbrains.kotlin.load.java.structure.impl.classFiles.BinaryJavaClass
3031
import org.jetbrains.kotlin.name.FqName
3132
import org.jetbrains.kotlin.types.Variance
3233
import org.jetbrains.kotlin.util.OperatorNameConventions
@@ -98,15 +99,29 @@ open class KotlinFileExtractor(
9899
}
99100
}
100101

102+
private fun javaBinaryDeclaresMethod(c: IrClass, name: String) =
103+
((c.source as? JavaSourceElement)?.javaElement as? BinaryJavaClass)?.methods?.any { it.name.asString() == name }
104+
105+
private fun isJavaBinaryDeclaration(f: IrFunction) =
106+
f.parentClassOrNull?.let { javaBinaryDeclaresMethod(it, f.name.asString()) } ?: false
107+
108+
private fun isJavaBinaryObjectMethodRedeclaration(d: IrDeclaration) =
109+
when (d) {
110+
is IrFunction ->
111+
when (d.name.asString()) {
112+
"toString" -> d.valueParameters.isEmpty()
113+
"hashCode" -> d.valueParameters.isEmpty()
114+
"equals" -> d.valueParameters.singleOrNull()?.type?.isNullableAny() ?: false
115+
else -> false
116+
} && isJavaBinaryDeclaration(d)
117+
else -> false
118+
}
119+
101120
@OptIn(ObsoleteDescriptorBasedAPI::class)
102121
private fun isFake(d: IrDeclarationWithVisibility): Boolean {
103-
val visibility = d.visibility
104-
if (visibility is DelegatedDescriptorVisibility && visibility.delegate == Visibilities.InvisibleFake) {
105-
return true
106-
}
107-
if (d.isFakeOverride) {
122+
val hasFakeVisibility = d.visibility.let { it is DelegatedDescriptorVisibility && it.delegate == Visibilities.InvisibleFake } || d.isFakeOverride
123+
if (hasFakeVisibility && !isJavaBinaryObjectMethodRedeclaration(d))
108124
return true
109-
}
110125
try {
111126
if ((d as? IrFunction)?.descriptor?.isHiddenToOvercomeSignatureClash == true) {
112127
return true
@@ -908,7 +923,9 @@ open class KotlinFileExtractor(
908923
else
909924
null
910925
} else {
911-
forceExtractFunction(f, parentId, extractBody, extractMethodAndParameterTypeAccesses, typeSubstitution, classTypeArgsIncludingOuterClasses).also {
926+
// Work around an apparent bug causing redeclarations of `fun toString(): String` specifically in interfaces loaded from Java classes show up like fake overrides.
927+
val overriddenVisibility = if (f.isFakeOverride && isJavaBinaryObjectMethodRedeclaration(f)) OverriddenFunctionAttributes(visibility = DescriptorVisibilities.PUBLIC) else null
928+
forceExtractFunction(f, parentId, extractBody, extractMethodAndParameterTypeAccesses, typeSubstitution, classTypeArgsIncludingOuterClasses, overriddenAttributes = overriddenVisibility).also {
912929
// The defaults-forwarder function is a static utility, not a member, so we only need to extract this for the unspecialised instance of this class.
913930
if (classTypeArgsIncludingOuterClasses.isNullOrEmpty())
914931
extractDefaultsFunction(f, parentId, extractBody, extractMethodAndParameterTypeAccesses)
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
public interface Test {
2+
String toString();
3+
int hashCode();
4+
boolean equals(Object other);
5+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
| equals | Test |
2+
| hashCode | Test |
3+
| toString | Test |
4+
| toString | java.lang.CharSequence |
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
from create_database_utils import *
2+
3+
runSuccessfully(["javac", "Test.java", "-d", "bin"])
4+
run_codeql_database_create(["kotlinc user.kt -cp bin"], lang="java")
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import java
2+
3+
from Method m
4+
where
5+
m.getDeclaringType().getName() = ["Test", "CharSequence"] and
6+
m.getName() = ["toString", "equals", "hashCode"]
7+
select m.getName(), m.getDeclaringType().getQualifiedName()
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
fun f(t: Test, cs: CharSequence) = t.toString() + cs.toString() + t.equals(1) + t.hashCode()

java/ql/test/kotlin/library-tests/java-kotlin-collection-type-generic-methods/test.expected

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ methodWithDuplicate
110110
| Collection<E> | addAll | Collection<? extends E> |
111111
| Collection<E> | contains | Object |
112112
| Collection<E> | containsAll | Collection<?> |
113+
| Collection<E> | equals | Object |
113114
| Collection<E> | remove | Object |
114115
| Collection<E> | removeAll | Collection<?> |
115116
| Collection<E> | removeIf | Predicate<? super E> |
@@ -120,6 +121,7 @@ methodWithDuplicate
120121
| Collection<Entry<K,V>> | addAll | Collection<? extends Entry<K,V>> |
121122
| Collection<Entry<K,V>> | contains | Object |
122123
| Collection<Entry<K,V>> | containsAll | Collection<?> |
124+
| Collection<Entry<K,V>> | equals | Object |
123125
| Collection<Entry<K,V>> | remove | Object |
124126
| Collection<Entry<K,V>> | removeAll | Collection<?> |
125127
| Collection<Entry<K,V>> | removeIf | Predicate<? super Entry<K,V>> |
@@ -130,6 +132,7 @@ methodWithDuplicate
130132
| Collection<K> | addAll | Collection<? extends K> |
131133
| Collection<K> | contains | Object |
132134
| Collection<K> | containsAll | Collection<?> |
135+
| Collection<K> | equals | Object |
133136
| Collection<K> | remove | Object |
134137
| Collection<K> | removeAll | Collection<?> |
135138
| Collection<K> | removeIf | Predicate<? super K> |
@@ -151,6 +154,7 @@ methodWithDuplicate
151154
| Collection<V> | addAll | Collection<? extends V> |
152155
| Collection<V> | contains | Object |
153156
| Collection<V> | containsAll | Collection<?> |
157+
| Collection<V> | equals | Object |
154158
| Collection<V> | remove | Object |
155159
| Collection<V> | removeAll | Collection<?> |
156160
| Collection<V> | removeIf | Predicate<? super V> |
@@ -188,6 +192,7 @@ methodWithDuplicate
188192
| List<E> | contains | Object |
189193
| List<E> | containsAll | Collection<?> |
190194
| List<E> | copyOf | Collection<? extends E> |
195+
| List<E> | equals | Object |
191196
| List<E> | get | int |
192197
| List<E> | indexOf | Object |
193198
| List<E> | lastIndexOf | Object |
@@ -270,6 +275,7 @@ methodWithDuplicate
270275
| Map<Identity,Entry<?>> | copyOf | Map<? extends K,? extends V> |
271276
| Map<Identity,Entry<?>> | entry | K |
272277
| Map<Identity,Entry<?>> | entry | V |
278+
| Map<Identity,Entry<?>> | equals | Object |
273279
| Map<Identity,Entry<?>> | forEach | BiConsumer<? super Identity,? super Entry<?>> |
274280
| Map<Identity,Entry<?>> | get | Object |
275281
| Map<Identity,Entry<?>> | getOrDefault | Entry<?> |
@@ -300,6 +306,7 @@ methodWithDuplicate
300306
| Map<K,V> | copyOf | Map<? extends K,? extends V> |
301307
| Map<K,V> | entry | K |
302308
| Map<K,V> | entry | V |
309+
| Map<K,V> | equals | Object |
303310
| Map<K,V> | forEach | BiConsumer<? super K,? super V> |
304311
| Map<K,V> | get | Object |
305312
| Map<K,V> | getOrDefault | Object |
@@ -330,6 +337,7 @@ methodWithDuplicate
330337
| Map<Object,Object> | copyOf | Map<? extends K,? extends V> |
331338
| Map<Object,Object> | entry | K |
332339
| Map<Object,Object> | entry | V |
340+
| Map<Object,Object> | equals | Object |
333341
| Map<Object,Object> | forEach | BiConsumer<? super Object,? super Object> |
334342
| Map<Object,Object> | get | Object |
335343
| Map<Object,Object> | getOrDefault | Object |

0 commit comments

Comments
 (0)