mirror of
https://github.com/github/codeql.git
synced 2025-12-23 04:06:37 +01:00
Merge pull request #9571 from smowton/smowton/fix/array-variance-lowering
Kotlin: Implement array type variance lowering
This commit is contained in:
@@ -637,14 +637,31 @@ open class KotlinUsesExtractor(
|
|||||||
)
|
)
|
||||||
|
|
||||||
(s.isBoxedArray && s.arguments.isNotEmpty()) || s.isPrimitiveArray() -> {
|
(s.isBoxedArray && s.arguments.isNotEmpty()) || s.isPrimitiveArray() -> {
|
||||||
var dimensions = 1
|
|
||||||
var isPrimitiveArray = s.isPrimitiveArray()
|
fun replaceComponentTypeWithAny(t: IrSimpleType, dimensions: Int): IrSimpleType =
|
||||||
val componentType = s.getArrayElementType(pluginContext.irBuiltIns)
|
if (dimensions == 0)
|
||||||
var elementType = componentType
|
pluginContext.irBuiltIns.anyType as IrSimpleType
|
||||||
|
else
|
||||||
|
t.toBuilder().also { it.arguments = (it.arguments[0] as IrTypeProjection)
|
||||||
|
.let { oldArg ->
|
||||||
|
listOf(makeTypeProjection(replaceComponentTypeWithAny(oldArg.type as IrSimpleType, dimensions - 1), oldArg.variance))
|
||||||
|
}
|
||||||
|
}.buildSimpleType()
|
||||||
|
|
||||||
|
var componentType = s.getArrayElementType(pluginContext.irBuiltIns)
|
||||||
|
var isPrimitiveArray = false
|
||||||
|
var dimensions = 0
|
||||||
|
var elementType: IrType = s
|
||||||
while (elementType.isBoxedArray || elementType.isPrimitiveArray()) {
|
while (elementType.isBoxedArray || elementType.isPrimitiveArray()) {
|
||||||
dimensions++
|
dimensions++
|
||||||
if(elementType.isPrimitiveArray())
|
if (elementType.isPrimitiveArray())
|
||||||
isPrimitiveArray = true
|
isPrimitiveArray = true
|
||||||
|
if (((elementType as IrSimpleType).arguments.singleOrNull() as? IrTypeProjection)?.variance == Variance.IN_VARIANCE) {
|
||||||
|
// Because Java's arrays are covariant, Kotlin will render Array<in X> as Object[], Array<Array<in X>> as Object[][] etc.
|
||||||
|
componentType = replaceComponentTypeWithAny(s, dimensions - 1)
|
||||||
|
elementType = pluginContext.irBuiltIns.anyType as IrSimpleType
|
||||||
|
break
|
||||||
|
}
|
||||||
elementType = elementType.getArrayElementType(pluginContext.irBuiltIns)
|
elementType = elementType.getArrayElementType(pluginContext.irBuiltIns)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -857,13 +874,33 @@ open class KotlinUsesExtractor(
|
|||||||
private val jvmWildcardAnnotation = FqName("kotlin.jvm.JvmWildcard")
|
private val jvmWildcardAnnotation = FqName("kotlin.jvm.JvmWildcard")
|
||||||
private val jvmWildcardSuppressionAnnotaton = FqName("kotlin.jvm.JvmSuppressWildcards")
|
private val jvmWildcardSuppressionAnnotaton = FqName("kotlin.jvm.JvmSuppressWildcards")
|
||||||
|
|
||||||
|
private fun arrayExtendsAdditionAllowed(t: IrSimpleType): Boolean =
|
||||||
|
// Note the array special case includes Array<*>, which does permit adding `? extends ...` (making `? extends Object[]` in that case)
|
||||||
|
// Surprisingly Array<in X> does permit this as well, though the contravariant array lowers to Object[] so this ends up `? extends Object[]` as well.
|
||||||
|
t.arguments[0].let {
|
||||||
|
when (it) {
|
||||||
|
is IrTypeProjection -> when (it.variance) {
|
||||||
|
Variance.INVARIANT -> false
|
||||||
|
Variance.IN_VARIANCE -> !(it.type.isAny() || it.type.isNullableAny())
|
||||||
|
Variance.OUT_VARIANCE -> extendsAdditionAllowed(it.type)
|
||||||
|
}
|
||||||
|
else -> true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun extendsAdditionAllowed(t: IrType) =
|
||||||
|
if (t.isBoxedArray)
|
||||||
|
arrayExtendsAdditionAllowed(t as IrSimpleType)
|
||||||
|
else
|
||||||
|
((t as? IrSimpleType)?.classOrNull?.owner?.isFinalClass) != true
|
||||||
|
|
||||||
private fun wildcardAdditionAllowed(v: Variance, t: IrType, addByDefault: Boolean) =
|
private fun wildcardAdditionAllowed(v: Variance, t: IrType, addByDefault: Boolean) =
|
||||||
when {
|
when {
|
||||||
t.hasAnnotation(jvmWildcardAnnotation) -> true
|
t.hasAnnotation(jvmWildcardAnnotation) -> true
|
||||||
!addByDefault -> false
|
!addByDefault -> false
|
||||||
t.hasAnnotation(jvmWildcardSuppressionAnnotaton) -> false
|
t.hasAnnotation(jvmWildcardSuppressionAnnotaton) -> false
|
||||||
v == Variance.IN_VARIANCE -> !(t.isNullableAny() || t.isAny())
|
v == Variance.IN_VARIANCE -> !(t.isNullableAny() || t.isAny())
|
||||||
v == Variance.OUT_VARIANCE -> ((t as? IrSimpleType)?.classOrNull?.owner?.isFinalClass) != true
|
v == Variance.OUT_VARIANCE -> extendsAdditionAllowed(t)
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
public class User {
|
||||||
|
|
||||||
|
public static void test() {
|
||||||
|
|
||||||
|
TakesArrayList tal = new TakesArrayList();
|
||||||
|
tal.invarArray(null);
|
||||||
|
// Using one method suffices to get the extractor to describe all the methods defined on takesArrayList.
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,112 @@
|
|||||||
|
public class TakesArrayList {
|
||||||
|
|
||||||
|
// There is a Java user to flag up any problems, as if the .class file differs from my
|
||||||
|
// claimed types above we'll see two overloads and two parameter types instead of one.
|
||||||
|
|
||||||
|
// Test how Array types with a variance should be lowered:
|
||||||
|
fun invarArray(a: Array<CharSequence>) { }
|
||||||
|
fun outArray(a: Array<out CharSequence>) { }
|
||||||
|
fun inArray(a: Array<in CharSequence>) { }
|
||||||
|
|
||||||
|
fun invarInvarArray(a: Array<Array<CharSequence>>) { }
|
||||||
|
fun invarOutArray(a: Array<Array<out CharSequence>>) { }
|
||||||
|
fun invarInArray(a: Array<Array<in CharSequence>>) { }
|
||||||
|
|
||||||
|
fun outInvarArray(a: Array<out Array<CharSequence>>) { }
|
||||||
|
fun outOutArray(a: Array<out Array<out CharSequence>>) { }
|
||||||
|
fun outInArray(a: Array<out Array<in CharSequence>>) { }
|
||||||
|
|
||||||
|
fun inInvarArray(a: Array<in Array<CharSequence>>) { }
|
||||||
|
fun inOutArray(a: Array<in Array<out CharSequence>>) { }
|
||||||
|
fun inInArray(a: Array<in Array<in CharSequence>>) { }
|
||||||
|
|
||||||
|
// Test how Array type arguments with a variance should acquire implicit wildcards:
|
||||||
|
fun invarArrayList(l: List<Array<CharSequence>>) { }
|
||||||
|
fun outArrayList(l: List<Array<out CharSequence>>) { }
|
||||||
|
fun inArrayList(l: List<Array<in CharSequence>>) { }
|
||||||
|
|
||||||
|
// Check the cases of nested arrays:
|
||||||
|
fun invarInvarArrayList(l: List<Array<Array<CharSequence>>>) { }
|
||||||
|
fun invarOutArrayList(l: List<Array<Array<out CharSequence>>>) { }
|
||||||
|
fun invarInArrayList(l: List<Array<Array<in CharSequence>>>) { }
|
||||||
|
fun outInvarArrayList(l: List<Array<out Array<CharSequence>>>) { }
|
||||||
|
fun outOutArrayList(l: List<Array<out Array<out CharSequence>>>) { }
|
||||||
|
fun outInArrayList(l: List<Array<out Array<in CharSequence>>>) { }
|
||||||
|
fun inInvarArrayList(l: List<Array<in Array<CharSequence>>>) { }
|
||||||
|
fun inOutArrayList(l: List<Array<in Array<out CharSequence>>>) { }
|
||||||
|
fun inInArrayList(l: List<Array<in Array<in CharSequence>>>) { }
|
||||||
|
|
||||||
|
// Now check all of that again for Comparable, whose type parameter is contravariant:
|
||||||
|
fun invarArrayComparable(c: Comparable<Array<CharSequence>>) { }
|
||||||
|
fun outArrayComparable(c: Comparable<Array<out CharSequence>>) { }
|
||||||
|
fun inArrayComparable(c: Comparable<Array<in CharSequence>>) { }
|
||||||
|
|
||||||
|
fun invarInvarArrayComparable(c: Comparable<Array<Array<CharSequence>>>) { }
|
||||||
|
fun invarOutArrayComparable(c: Comparable<Array<Array<out CharSequence>>>) { }
|
||||||
|
fun invarInArrayComparable(c: Comparable<Array<Array<in CharSequence>>>) { }
|
||||||
|
fun outInvarArrayComparable(c: Comparable<Array<out Array<CharSequence>>>) { }
|
||||||
|
fun outOutArrayComparable(c: Comparable<Array<out Array<out CharSequence>>>) { }
|
||||||
|
fun outInArrayComparable(c: Comparable<Array<out Array<in CharSequence>>>) { }
|
||||||
|
fun inInvarArrayComparable(c: Comparable<Array<in Array<CharSequence>>>) { }
|
||||||
|
fun inOutArrayComparable(c: Comparable<Array<in Array<out CharSequence>>>) { }
|
||||||
|
fun inInArrayComparable(c: Comparable<Array<in Array<in CharSequence>>>) { }
|
||||||
|
|
||||||
|
// ... duplicate all of that for a final array element (I choose String as a final type), which sometimes suppresses addition of `? extends ...`
|
||||||
|
fun invarArrayListFinal(l: List<Array<String>>) { }
|
||||||
|
fun outArrayListFinal(l: List<Array<out String>>) { }
|
||||||
|
fun inArrayListFinal(l: List<Array<in String>>) { }
|
||||||
|
|
||||||
|
fun invarInvarArrayListFinal(l: List<Array<Array<String>>>) { }
|
||||||
|
fun invarOutArrayListFinal(l: List<Array<Array<out String>>>) { }
|
||||||
|
fun invarInArrayListFinal(l: List<Array<Array<in String>>>) { }
|
||||||
|
fun outInvarArrayListFinal(l: List<Array<out Array<String>>>) { }
|
||||||
|
fun outOutArrayListFinal(l: List<Array<out Array<out String>>>) { }
|
||||||
|
fun outInArrayListFinal(l: List<Array<out Array<in String>>>) { }
|
||||||
|
fun inInvarArrayListFinal(l: List<Array<in Array<String>>>) { }
|
||||||
|
fun inOutArrayListFinal(l: List<Array<in Array<out String>>>) { }
|
||||||
|
fun inInArrayListFinal(l: List<Array<in Array<in String>>>) { }
|
||||||
|
|
||||||
|
fun invarArrayComparableFinal(c: Comparable<Array<String>>) { }
|
||||||
|
fun outArrayComparableFinal(c: Comparable<Array<out String>>) { }
|
||||||
|
fun inArrayComparableFinal(c: Comparable<Array<in String>>) { }
|
||||||
|
|
||||||
|
fun invarInvarArrayComparableFinal(c: Comparable<Array<Array<String>>>) { }
|
||||||
|
fun invarOutArrayComparableFinal(c: Comparable<Array<Array<out String>>>) { }
|
||||||
|
fun invarInArrayComparableFinal(c: Comparable<Array<Array<in String>>>) { }
|
||||||
|
fun outInvarArrayComparableFinal(c: Comparable<Array<out Array<String>>>) { }
|
||||||
|
fun outOutArrayComparableFinal(c: Comparable<Array<out Array<out String>>>) { }
|
||||||
|
fun outInArrayComparableFinal(c: Comparable<Array<out Array<in String>>>) { }
|
||||||
|
fun inInvarArrayComparableFinal(c: Comparable<Array<in Array<String>>>) { }
|
||||||
|
fun inOutArrayComparableFinal(c: Comparable<Array<in Array<out String>>>) { }
|
||||||
|
fun inInArrayComparableFinal(c: Comparable<Array<in Array<in String>>>) { }
|
||||||
|
|
||||||
|
// ... and duplicate it once more with Any as the array element, which can suppress adding `? super ...`
|
||||||
|
fun invarArrayListAny(l: List<Array<Any>>) { }
|
||||||
|
fun outArrayListAny(l: List<Array<out Any>>) { }
|
||||||
|
fun inArrayListAny(l: List<Array<in Any>>) { }
|
||||||
|
|
||||||
|
fun invarInvarArrayListAny(l: List<Array<Array<Any>>>) { }
|
||||||
|
fun invarOutArrayListAny(l: List<Array<Array<out Any>>>) { }
|
||||||
|
fun invarInArrayListAny(l: List<Array<Array<in Any>>>) { }
|
||||||
|
fun outInvarArrayListAny(l: List<Array<out Array<Any>>>) { }
|
||||||
|
fun outOutArrayListAny(l: List<Array<out Array<out Any>>>) { }
|
||||||
|
fun outInArrayListAny(l: List<Array<out Array<in Any>>>) { }
|
||||||
|
fun inInvarArrayListAny(l: List<Array<in Array<Any>>>) { }
|
||||||
|
fun inOutArrayListAny(l: List<Array<in Array<out Any>>>) { }
|
||||||
|
fun inInArrayListAny(l: List<Array<in Array<in Any>>>) { }
|
||||||
|
|
||||||
|
fun invarArrayComparableAny(c: Comparable<Array<Any>>) { }
|
||||||
|
fun outArrayComparableAny(c: Comparable<Array<out Any>>) { }
|
||||||
|
fun inArrayComparableAny(c: Comparable<Array<in Any>>) { }
|
||||||
|
|
||||||
|
fun invarInvarArrayComparableAny(c: Comparable<Array<Array<Any>>>) { }
|
||||||
|
fun invarOutArrayComparableAny(c: Comparable<Array<Array<out Any>>>) { }
|
||||||
|
fun invarInArrayComparableAny(c: Comparable<Array<Array<in Any>>>) { }
|
||||||
|
fun outInvarArrayComparableAny(c: Comparable<Array<out Array<Any>>>) { }
|
||||||
|
fun outOutArrayComparableAny(c: Comparable<Array<out Array<out Any>>>) { }
|
||||||
|
fun outInArrayComparableAny(c: Comparable<Array<out Array<in Any>>>) { }
|
||||||
|
fun inInvarArrayComparableAny(c: Comparable<Array<in Array<Any>>>) { }
|
||||||
|
fun inOutArrayComparableAny(c: Comparable<Array<in Array<out Any>>>) { }
|
||||||
|
fun inInArrayComparableAny(c: Comparable<Array<in Array<in Any>>>) { }
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
broken
|
||||||
|
#select
|
||||||
|
| inArray | Object[] |
|
||||||
|
| inArrayComparable | Comparable<? super Object[]> |
|
||||||
|
| inArrayComparableAny | Comparable<? super Object[]> |
|
||||||
|
| inArrayComparableFinal | Comparable<? super Object[]> |
|
||||||
|
| inArrayList | List<? extends Object[]> |
|
||||||
|
| inArrayListAny | List<Object[]> |
|
||||||
|
| inArrayListFinal | List<? extends Object[]> |
|
||||||
|
| inInArray | Object[] |
|
||||||
|
| inInArrayComparable | Comparable<? super Object[]> |
|
||||||
|
| inInArrayComparableAny | Comparable<? super Object[]> |
|
||||||
|
| inInArrayComparableFinal | Comparable<? super Object[]> |
|
||||||
|
| inInArrayList | List<? extends Object[]> |
|
||||||
|
| inInArrayListAny | List<? extends Object[]> |
|
||||||
|
| inInArrayListFinal | List<? extends Object[]> |
|
||||||
|
| inInvarArray | Object[] |
|
||||||
|
| inInvarArrayComparable | Comparable<? super Object[]> |
|
||||||
|
| inInvarArrayComparableAny | Comparable<? super Object[]> |
|
||||||
|
| inInvarArrayComparableFinal | Comparable<? super Object[]> |
|
||||||
|
| inInvarArrayList | List<? extends Object[]> |
|
||||||
|
| inInvarArrayListAny | List<? extends Object[]> |
|
||||||
|
| inInvarArrayListFinal | List<? extends Object[]> |
|
||||||
|
| inOutArray | Object[] |
|
||||||
|
| inOutArrayComparable | Comparable<? super Object[]> |
|
||||||
|
| inOutArrayComparableAny | Comparable<? super Object[]> |
|
||||||
|
| inOutArrayComparableFinal | Comparable<? super Object[]> |
|
||||||
|
| inOutArrayList | List<? extends Object[]> |
|
||||||
|
| inOutArrayListAny | List<? extends Object[]> |
|
||||||
|
| inOutArrayListFinal | List<? extends Object[]> |
|
||||||
|
| invarArray | CharSequence[] |
|
||||||
|
| invarArrayComparable | Comparable<? super CharSequence[]> |
|
||||||
|
| invarArrayComparableAny | Comparable<? super Object[]> |
|
||||||
|
| invarArrayComparableFinal | Comparable<? super String[]> |
|
||||||
|
| invarArrayList | List<CharSequence[]> |
|
||||||
|
| invarArrayListAny | List<Object[]> |
|
||||||
|
| invarArrayListFinal | List<String[]> |
|
||||||
|
| invarInArray | Object[][] |
|
||||||
|
| invarInArrayComparable | Comparable<? super Object[][]> |
|
||||||
|
| invarInArrayComparableAny | Comparable<? super Object[][]> |
|
||||||
|
| invarInArrayComparableFinal | Comparable<? super Object[][]> |
|
||||||
|
| invarInArrayList | List<Object[][]> |
|
||||||
|
| invarInArrayListAny | List<Object[][]> |
|
||||||
|
| invarInArrayListFinal | List<Object[][]> |
|
||||||
|
| invarInvarArray | CharSequence[][] |
|
||||||
|
| invarInvarArrayComparable | Comparable<? super CharSequence[][]> |
|
||||||
|
| invarInvarArrayComparableAny | Comparable<? super Object[][]> |
|
||||||
|
| invarInvarArrayComparableFinal | Comparable<? super String[][]> |
|
||||||
|
| invarInvarArrayList | List<CharSequence[][]> |
|
||||||
|
| invarInvarArrayListAny | List<Object[][]> |
|
||||||
|
| invarInvarArrayListFinal | List<String[][]> |
|
||||||
|
| invarOutArray | CharSequence[][] |
|
||||||
|
| invarOutArrayComparable | Comparable<? super CharSequence[][]> |
|
||||||
|
| invarOutArrayComparableAny | Comparable<? super Object[][]> |
|
||||||
|
| invarOutArrayComparableFinal | Comparable<? super String[][]> |
|
||||||
|
| invarOutArrayList | List<CharSequence[][]> |
|
||||||
|
| invarOutArrayListAny | List<Object[][]> |
|
||||||
|
| invarOutArrayListFinal | List<String[][]> |
|
||||||
|
| outArray | CharSequence[] |
|
||||||
|
| outArrayComparable | Comparable<? super CharSequence[]> |
|
||||||
|
| outArrayComparableAny | Comparable<? super Object[]> |
|
||||||
|
| outArrayComparableFinal | Comparable<? super String[]> |
|
||||||
|
| outArrayList | List<? extends CharSequence[]> |
|
||||||
|
| outArrayListAny | List<? extends Object[]> |
|
||||||
|
| outArrayListFinal | List<String[]> |
|
||||||
|
| outInArray | Object[][] |
|
||||||
|
| outInArrayComparable | Comparable<? super Object[][]> |
|
||||||
|
| outInArrayComparableAny | Comparable<? super Object[][]> |
|
||||||
|
| outInArrayComparableFinal | Comparable<? super Object[][]> |
|
||||||
|
| outInArrayList | List<? extends Object[][]> |
|
||||||
|
| outInArrayListAny | List<Object[][]> |
|
||||||
|
| outInArrayListFinal | List<? extends Object[][]> |
|
||||||
|
| outInvarArray | CharSequence[][] |
|
||||||
|
| outInvarArrayComparable | Comparable<? super CharSequence[][]> |
|
||||||
|
| outInvarArrayComparableAny | Comparable<? super Object[][]> |
|
||||||
|
| outInvarArrayComparableFinal | Comparable<? super String[][]> |
|
||||||
|
| outInvarArrayList | List<CharSequence[][]> |
|
||||||
|
| outInvarArrayListAny | List<Object[][]> |
|
||||||
|
| outInvarArrayListFinal | List<String[][]> |
|
||||||
|
| outOutArray | CharSequence[][] |
|
||||||
|
| outOutArrayComparable | Comparable<? super CharSequence[][]> |
|
||||||
|
| outOutArrayComparableAny | Comparable<? super Object[][]> |
|
||||||
|
| outOutArrayComparableFinal | Comparable<? super String[][]> |
|
||||||
|
| outOutArrayList | List<? extends CharSequence[][]> |
|
||||||
|
| outOutArrayListAny | List<? extends Object[][]> |
|
||||||
|
| outOutArrayListFinal | List<String[][]> |
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import java
|
||||||
|
|
||||||
|
class InterestingMethod extends Method {
|
||||||
|
InterestingMethod() { this.getDeclaringType().getName() = "TakesArrayList" }
|
||||||
|
}
|
||||||
|
|
||||||
|
query predicate broken(string methodName) {
|
||||||
|
methodName = any(InterestingMethod m).getName() and
|
||||||
|
count(Type t, InterestingMethod m | methodName = m.getName() and t = m.getAParamType() | t) != 1
|
||||||
|
}
|
||||||
|
|
||||||
|
from InterestingMethod m
|
||||||
|
select m.getName(), m.getAParamType().toString()
|
||||||
Reference in New Issue
Block a user