mirror of
https://github.com/github/codeql.git
synced 2026-05-01 11:45:14 +02:00
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:
@@ -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")
|
||||
|
||||
@@ -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() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
| user.kt:1:42:1:58 | contains(...) |
|
||||
| user.kt:1:63:1:74 | contains(...) |
|
||||
@@ -0,0 +1,5 @@
|
||||
import java
|
||||
|
||||
from MethodAccess ma
|
||||
where ma.getEnclosingCallable().fromSource()
|
||||
select ma
|
||||
@@ -0,0 +1 @@
|
||||
private fun user(s: SetImpl<String>) = s.contains("Hello") && "world" in s
|
||||
Reference in New Issue
Block a user