Kotlin: fix method types when an inherited method implements a collection type

In this circumstance the compiler seems to generate a specialised version of the implementing function with its argument type replaced by the interface-implementing child class' type parameter. However it stores a back-pointer to the real declared function, which we should use as the call target.
This commit is contained in:
Chris Smowton
2022-10-29 11:26:59 +01:00
parent 85790fcade
commit 8266a22332
5 changed files with 81 additions and 1 deletions

View File

@@ -17,6 +17,7 @@ import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI
import org.jetbrains.kotlin.ir.backend.js.utils.realOverrideTarget
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.lazy.IrLazyFunction
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
import org.jetbrains.kotlin.ir.symbols.*
@@ -2011,6 +2012,18 @@ open class KotlinFileExtractor(
}
}
private fun getCalleeRealOverrideTarget(f: IrFunction): IrFunction {
val target = f.target.realOverrideTarget
return if (overridesCollectionsMethodWithAlteredParameterTypes(f))
// Cope with the case where an inherited callee can be rewritten with substituted parameter types
// if the child class uses it to implement a collections interface
// (for example, `class A { boolean contains(Object o) { ... } }; class B<T> extends A implements Set<T> { ... }`
// leads to generating a function `A.contains(B::T)`, with `initialSignatureFunction` pointing to `A.contains(Object)`.
(target as? IrLazyFunction)?.initialSignatureFunction ?: target
else
target
}
fun extractRawMethodAccess(
syntacticCallTarget: IrFunction,
locId: Label<DbLocation>,
@@ -2028,7 +2041,7 @@ open class KotlinFileExtractor(
extractClassTypeArguments: Boolean = false,
superQualifierSymbol: IrClassSymbol? = null) {
val callTarget = syntacticCallTarget.target.realOverrideTarget
val callTarget = getCalleeRealOverrideTarget(syntacticCallTarget)
val methodId = getCalleeMethodId(callTarget, drType, extractClassTypeArguments)
if (methodId == null) {
logger.warn("No method to bind call to for raw method access")

View File

@@ -0,0 +1,59 @@
import java.util.*;
public class Test {
public boolean contains(Object o) { return false; }
}
class SetImpl<T> extends Test implements Set<T> {
public int size() {
return 0;
}
public boolean isEmpty() {
return false;
}
public Iterator<T> iterator() {
return null;
}
public Object[] toArray() {
return new Object[0];
}
public <T1> T1[] toArray(T1[] a) {
return null;
}
public boolean add(T t) {
return false;
}
public boolean remove(Object o) {
return false;
}
public boolean containsAll(Collection<?> c) {
return false;
}
public boolean addAll(Collection<? extends T> c) {
return false;
}
public boolean retainAll(Collection<?> c) {
return false;
}
public boolean removeAll(Collection<?> c) {
return false;
}
public void clear() {
}
}

View File

@@ -0,0 +1,2 @@
| user.kt:1:42:1:58 | contains(...) |
| user.kt:1:63:1:74 | contains(...) |

View File

@@ -0,0 +1,5 @@
import java
from MethodAccess ma
where ma.getEnclosingCallable().fromSource()
select ma

View File

@@ -0,0 +1 @@
private fun user(s: SetImpl<String>) = s.contains("Hello") && "world" in s