Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 8accd35

Browse files
smowtonigfoo
authored andcommitted
Substitute in generic type arguments when extracting the supertypes of an instantiated type
1 parent 2dcd49c commit 8accd35

7 files changed

Lines changed: 184 additions & 4 deletions

File tree

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ open class KotlinFileExtractor(
116116
val unbound = useClassSource(c)
117117
tw.writeErasure(id, unbound)
118118
extractClassModifiers(c, id)
119-
extractClassSupertypes(c, id)
119+
extractClassSupertypes(c, id, typeArgs)
120120

121121
val locId = tw.getLocation(c)
122122
tw.writeHasLocation(id, locId)

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

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.github.codeql
22

3+
import com.github.codeql.utils.substituteTypeArguments
34
import com.semmle.extractor.java.OdasaOutput
45
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
56
import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap
@@ -235,7 +236,7 @@ open class KotlinUsesExtractor(
235236
dimensions,
236237
componentTypeResults.javaResult.id)
237238

238-
extractClassSupertypes(arrayType.classifier.owner as IrClass, it)
239+
extractClassSupertypes(arrayType.classifier.owner as IrClass, it, arrayType.arguments)
239240

240241
// array.length
241242
val length = tw.getLabelFor<DbField>("@\"field;{$it};length\"")
@@ -577,8 +578,15 @@ class X {
577578
}
578579
}
579580

580-
fun extractClassSupertypes(c: IrClass, id: Label<out DbReftype>) {
581-
for(t in c.superTypes) {
581+
fun extractClassSupertypes(c: IrClass, id: Label<out DbReftype>, typeArgsQ: List<IrTypeArgument>? = null) {
582+
// Note we only need to substitute type args here because it is illegal to directly extend a type variable.
583+
val subbedSupertypes = typeArgsQ?.let { typeArgs ->
584+
c.superTypes.map {
585+
it.substituteTypeArguments(c.typeParameters, typeArgs)
586+
}
587+
} ?: c.superTypes
588+
589+
for(t in subbedSupertypes) {
582590
when(t) {
583591
is IrSimpleType -> {
584592
when (t.classifier.owner) {
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
package com.github.codeql.utils
2+
3+
import org.jetbrains.kotlin.ir.declarations.IrTypeParameter
4+
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
5+
import org.jetbrains.kotlin.ir.types.*
6+
import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl
7+
import org.jetbrains.kotlin.ir.types.impl.IrStarProjectionImpl
8+
import org.jetbrains.kotlin.ir.types.impl.makeTypeProjection
9+
import org.jetbrains.kotlin.types.Variance
10+
11+
fun IrType.substituteTypeArguments(params: List<IrTypeParameter>, arguments: List<IrTypeArgument>) =
12+
when(this) {
13+
is IrSimpleType -> substituteTypeArguments(params.map { it.symbol }.zip(arguments).toMap())
14+
else -> this
15+
}
16+
17+
/**
18+
* Returns true if substituting `innerVariance T` into the context `outerVariance []` discards all knowledge about
19+
* what T could be.
20+
*
21+
* Note this throws away slightly more information than it could: for example, the projection "in (out List)" can refer to
22+
* any superclass of anything that implements List, which specifically excludes e.g. String, but can't be represented as
23+
* a type projection. The projection "out (in List)" on the other hand really is equivalent to "out Any?", which is to
24+
* say no bound at all.
25+
*/
26+
private fun conflictingVariance(outerVariance: Variance, innerVariance: Variance) =
27+
(outerVariance == Variance.IN_VARIANCE && innerVariance == Variance.OUT_VARIANCE) ||
28+
(outerVariance == Variance.OUT_VARIANCE && innerVariance == Variance.IN_VARIANCE)
29+
30+
/**
31+
* When substituting `innerVariance T` into the context `outerVariance []`, returns the variance part of the result
32+
* `resultVariance T`. We already know they don't conflict.
33+
*/
34+
private fun combineVariance(outerVariance: Variance, innerVariance: Variance) =
35+
when {
36+
outerVariance != Variance.INVARIANT -> outerVariance
37+
innerVariance != Variance.INVARIANT -> innerVariance
38+
else -> Variance.INVARIANT
39+
}
40+
41+
private fun subProjectedType(substitutionMap: Map<IrTypeParameterSymbol, IrTypeArgument>, t: IrSimpleType, outerVariance: Variance): IrTypeArgument =
42+
substitutionMap[t.classifier]?.let { substitutedTypeArg ->
43+
if (substitutedTypeArg is IrTypeProjection) {
44+
if (conflictingVariance(outerVariance, substitutedTypeArg.variance))
45+
IrStarProjectionImpl
46+
else {
47+
val newProjectedType = substitutedTypeArg.type.let { if (t.hasQuestionMark) it.withHasQuestionMark(true) else it }
48+
val newVariance = combineVariance(outerVariance, substitutedTypeArg.variance)
49+
makeTypeProjection(newProjectedType, newVariance)
50+
}
51+
} else {
52+
substitutedTypeArg
53+
}
54+
} ?: makeTypeProjection(t.substituteTypeArguments(substitutionMap), outerVariance)
55+
56+
fun IrSimpleType.substituteTypeArguments(substitutionMap: Map<IrTypeParameterSymbol, IrTypeArgument>): IrSimpleType {
57+
if (substitutionMap.isEmpty()) return this
58+
59+
val newArguments = arguments.map {
60+
if (it is IrTypeProjection) {
61+
val itType = it.type
62+
if (itType is IrSimpleType) {
63+
subProjectedType(substitutionMap, itType, it.variance)
64+
} else {
65+
it
66+
}
67+
} else {
68+
it
69+
}
70+
}
71+
72+
return IrSimpleTypeImpl(
73+
classifier,
74+
hasQuestionMark,
75+
newArguments,
76+
annotations
77+
)
78+
}

java/ql/test/kotlin/library-tests/generics/generics.expected

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ parameterizedType
99
| generics.kt:11:1:11:19 | C0<?> | generics.kt:11:1:11:19 | C0 | 0 | ? |
1010
| generics.kt:11:1:11:19 | C0<Integer> | generics.kt:11:1:11:19 | C0 | 0 | Integer |
1111
| generics.kt:11:1:11:19 | C0<S> | generics.kt:11:1:11:19 | C0 | 0 | S |
12+
| generics.kt:11:1:11:19 | C0<String> | generics.kt:11:1:11:19 | C0 | 0 | String |
13+
| generics.kt:11:1:11:19 | C0<U> | generics.kt:11:1:11:19 | C0 | 0 | U |
1214
| generics.kt:11:1:11:19 | C0<W> | generics.kt:11:1:11:19 | C0 | 0 | W |
1315
| generics.kt:13:1:18:1 | C1 | generics.kt:13:1:18:1 | C1 | 0 | T |
1416
| generics.kt:13:1:18:1 | C1 | generics.kt:13:1:18:1 | C1 | 1 | W |
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
| test.kt:14:3:14:29 | p1 | test.kt:3:1:3:34 | SimpleInvariant<String> | file://:0:0:0:0 | Kotlin not-null SimpleInvariant<String> | Object |
2+
| test.kt:15:3:15:33 | p2 | test.kt:3:1:3:34 | SimpleInvariant<? extends String> | file://:0:0:0:0 | Kotlin not-null SimpleInvariant<? extends String> | Object |
3+
| test.kt:16:3:16:32 | p3 | test.kt:3:1:3:34 | SimpleInvariant<? super String> | file://:0:0:0:0 | Kotlin not-null SimpleInvariant<? super String> | Object |
4+
| test.kt:17:3:17:24 | p4 | test.kt:3:1:3:34 | SimpleInvariant<?> | file://:0:0:0:0 | Kotlin not-null SimpleInvariant<?> | Object |
5+
| test.kt:18:3:18:29 | p5 | test.kt:5:1:5:75 | NestedInvariant<String> | file://:0:0:0:0 | Kotlin not-null NestedInvariant<String> | Object |
6+
| test.kt:18:3:18:29 | p5 | test.kt:5:1:5:75 | NestedInvariant<String> | file://:0:0:0:0 | Kotlin not-null NestedInvariant<String> | SimpleInvariant<SimpleInvariant<String>> |
7+
| test.kt:19:3:19:33 | p6 | test.kt:5:1:5:75 | NestedInvariant<? extends String> | file://:0:0:0:0 | Kotlin not-null NestedInvariant<? extends String> | Object |
8+
| test.kt:19:3:19:33 | p6 | test.kt:5:1:5:75 | NestedInvariant<? extends String> | file://:0:0:0:0 | Kotlin not-null NestedInvariant<? extends String> | SimpleInvariant<SimpleInvariant<? extends String>> |
9+
| test.kt:20:3:20:32 | p7 | test.kt:5:1:5:75 | NestedInvariant<? super String> | file://:0:0:0:0 | Kotlin not-null NestedInvariant<? super String> | Object |
10+
| test.kt:20:3:20:32 | p7 | test.kt:5:1:5:75 | NestedInvariant<? super String> | file://:0:0:0:0 | Kotlin not-null NestedInvariant<? super String> | SimpleInvariant<SimpleInvariant<? super String>> |
11+
| test.kt:21:3:21:24 | p8 | test.kt:5:1:5:75 | NestedInvariant<?> | file://:0:0:0:0 | Kotlin not-null NestedInvariant<?> | Object |
12+
| test.kt:21:3:21:24 | p8 | test.kt:5:1:5:75 | NestedInvariant<?> | file://:0:0:0:0 | Kotlin not-null NestedInvariant<?> | SimpleInvariant<SimpleInvariant<?>> |
13+
| test.kt:22:3:22:29 | p9 | test.kt:7:1:7:74 | NestedCovariant<String> | file://:0:0:0:0 | Kotlin not-null NestedCovariant<String> | Object |
14+
| test.kt:22:3:22:29 | p9 | test.kt:7:1:7:74 | NestedCovariant<String> | file://:0:0:0:0 | Kotlin not-null NestedCovariant<String> | SimpleInvariant<SimpleInvariant<? extends String>> |
15+
| test.kt:23:3:23:34 | p10 | test.kt:7:1:7:74 | NestedCovariant<? extends String> | file://:0:0:0:0 | Kotlin not-null NestedCovariant<? extends String> | Object |
16+
| test.kt:23:3:23:34 | p10 | test.kt:7:1:7:74 | NestedCovariant<? extends String> | file://:0:0:0:0 | Kotlin not-null NestedCovariant<? extends String> | SimpleInvariant<SimpleInvariant<? extends String>> |
17+
| test.kt:24:3:24:33 | p11 | test.kt:7:1:7:74 | NestedCovariant<? super String> | file://:0:0:0:0 | Kotlin not-null NestedCovariant<? super String> | Object |
18+
| test.kt:24:3:24:33 | p11 | test.kt:7:1:7:74 | NestedCovariant<? super String> | file://:0:0:0:0 | Kotlin not-null NestedCovariant<? super String> | SimpleInvariant<SimpleInvariant<?>> |
19+
| test.kt:25:3:25:25 | p12 | test.kt:7:1:7:74 | NestedCovariant<?> | file://:0:0:0:0 | Kotlin not-null NestedCovariant<?> | Object |
20+
| test.kt:25:3:25:25 | p12 | test.kt:7:1:7:74 | NestedCovariant<?> | file://:0:0:0:0 | Kotlin not-null NestedCovariant<?> | SimpleInvariant<SimpleInvariant<?>> |
21+
| test.kt:26:3:26:34 | p13 | test.kt:9:1:9:77 | NestedContravariant<String> | file://:0:0:0:0 | Kotlin not-null NestedContravariant<String> | Object |
22+
| test.kt:26:3:26:34 | p13 | test.kt:9:1:9:77 | NestedContravariant<String> | file://:0:0:0:0 | Kotlin not-null NestedContravariant<String> | SimpleInvariant<SimpleInvariant<? super String>> |
23+
| test.kt:27:3:27:38 | p14 | test.kt:9:1:9:77 | NestedContravariant<? extends String> | file://:0:0:0:0 | Kotlin not-null NestedContravariant<? extends String> | Object |
24+
| test.kt:27:3:27:38 | p14 | test.kt:9:1:9:77 | NestedContravariant<? extends String> | file://:0:0:0:0 | Kotlin not-null NestedContravariant<? extends String> | SimpleInvariant<SimpleInvariant<?>> |
25+
| test.kt:28:3:28:37 | p15 | test.kt:9:1:9:77 | NestedContravariant<? super String> | file://:0:0:0:0 | Kotlin not-null NestedContravariant<? super String> | Object |
26+
| test.kt:28:3:28:37 | p15 | test.kt:9:1:9:77 | NestedContravariant<? super String> | file://:0:0:0:0 | Kotlin not-null NestedContravariant<? super String> | SimpleInvariant<SimpleInvariant<? super String>> |
27+
| test.kt:29:3:29:29 | p16 | test.kt:9:1:9:77 | NestedContravariant<?> | file://:0:0:0:0 | Kotlin not-null NestedContravariant<?> | Object |
28+
| test.kt:29:3:29:29 | p16 | test.kt:9:1:9:77 | NestedContravariant<?> | file://:0:0:0:0 | Kotlin not-null NestedContravariant<?> | SimpleInvariant<SimpleInvariant<?>> |
29+
| test.kt:30:3:30:28 | p17 | test.kt:11:1:11:51 | DoubleInherit<String> | file://:0:0:0:0 | Kotlin not-null DoubleInherit<String> | NestedInvariant<String> |
30+
| test.kt:30:3:30:28 | p17 | test.kt:11:1:11:51 | DoubleInherit<String> | file://:0:0:0:0 | Kotlin not-null DoubleInherit<String> | Object |
31+
| test.kt:30:3:30:28 | p17 | test.kt:11:1:11:51 | DoubleInherit<String> | file://:0:0:0:0 | Kotlin not-null DoubleInherit<String> | SimpleInvariant<SimpleInvariant<String>> |
32+
| test.kt:31:3:31:31 | p18 | test.kt:11:1:11:51 | DoubleInherit<? super String> | file://:0:0:0:0 | Kotlin not-null DoubleInherit<? super String> | NestedInvariant<? super String> |
33+
| test.kt:31:3:31:31 | p18 | test.kt:11:1:11:51 | DoubleInherit<? super String> | file://:0:0:0:0 | Kotlin not-null DoubleInherit<? super String> | Object |
34+
| test.kt:31:3:31:31 | p18 | test.kt:11:1:11:51 | DoubleInherit<? super String> | file://:0:0:0:0 | Kotlin not-null DoubleInherit<? super String> | SimpleInvariant<SimpleInvariant<? super String>> |
35+
| test.kt:32:3:32:32 | p19 | test.kt:11:1:11:51 | DoubleInherit<? extends String> | file://:0:0:0:0 | Kotlin not-null DoubleInherit<? extends String> | NestedInvariant<? extends String> |
36+
| test.kt:32:3:32:32 | p19 | test.kt:11:1:11:51 | DoubleInherit<? extends String> | file://:0:0:0:0 | Kotlin not-null DoubleInherit<? extends String> | Object |
37+
| test.kt:32:3:32:32 | p19 | test.kt:11:1:11:51 | DoubleInherit<? extends String> | file://:0:0:0:0 | Kotlin not-null DoubleInherit<? extends String> | SimpleInvariant<SimpleInvariant<? extends String>> |
38+
| test.kt:33:3:33:23 | p20 | test.kt:11:1:11:51 | DoubleInherit<?> | file://:0:0:0:0 | Kotlin not-null DoubleInherit<?> | NestedInvariant<?> |
39+
| test.kt:33:3:33:23 | p20 | test.kt:11:1:11:51 | DoubleInherit<?> | file://:0:0:0:0 | Kotlin not-null DoubleInherit<?> | Object |
40+
| test.kt:33:3:33:23 | p20 | test.kt:11:1:11:51 | DoubleInherit<?> | file://:0:0:0:0 | Kotlin not-null DoubleInherit<?> | SimpleInvariant<SimpleInvariant<?>> |
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package main
2+
3+
open class SimpleInvariant<E1> { }
4+
5+
open class NestedInvariant<E2> : SimpleInvariant<SimpleInvariant<E2>>() { }
6+
7+
class NestedCovariant<E3> : SimpleInvariant<SimpleInvariant<out E3>>() { }
8+
9+
class NestedContravariant<E4> : SimpleInvariant<SimpleInvariant<in E4>>() { }
10+
11+
class DoubleInherit<E5> : NestedInvariant<E5>() { }
12+
13+
fun user(
14+
p1: SimpleInvariant<String>,
15+
p2: SimpleInvariant<out String>,
16+
p3: SimpleInvariant<in String>,
17+
p4: SimpleInvariant<*>,
18+
p5: NestedInvariant<String>,
19+
p6: NestedInvariant<out String>,
20+
p7: NestedInvariant<in String>,
21+
p8: NestedInvariant<*>,
22+
p9: NestedCovariant<String>,
23+
p10: NestedCovariant<out String>,
24+
p11: NestedCovariant<in String>,
25+
p12: NestedCovariant<*>,
26+
p13: NestedContravariant<String>,
27+
p14: NestedContravariant<out String>,
28+
p15: NestedContravariant<in String>,
29+
p16: NestedContravariant<*>,
30+
p17: DoubleInherit<String>,
31+
p18: DoubleInherit<in String>,
32+
p19: DoubleInherit<out String>,
33+
p20: DoubleInherit<*>
34+
) {
35+
36+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import java
2+
3+
RefType getADatabaseSubtype(RefType rt) {
4+
extendsReftype(rt, result)
5+
or
6+
implInterface(rt, result)
7+
}
8+
9+
from Parameter p, RefType paramType, KotlinType paramKtType, string paramAncestorType
10+
where p.fromSource()
11+
and p.getCallable().getName() = "user"
12+
and p.getType() = paramType
13+
and p.getKotlinType() = paramKtType
14+
// Stringified to avoid printing the source-location (i.e. stdlib path) of `Any?`
15+
and getADatabaseSubtype+(paramType).toString() = paramAncestorType
16+
select p, paramType, paramKtType, paramAncestorType

0 commit comments

Comments
 (0)