Skip to content

Commit b8b0fd8

Browse files
committed
Kotlin: Fix isUnspecialised to handle generic classes inside generic methods
1 parent 3267d7c commit b8b0fd8

File tree

8 files changed

+21
-22
lines changed

8 files changed

+21
-22
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1450,7 +1450,7 @@ open class KotlinFileExtractor(
14501450
extractNewExprForLocalFunction(ids, id, locId, enclosingCallable, enclosingStmt)
14511451
} else {
14521452
val methodId =
1453-
if (extractClassTypeArguments && drType is IrSimpleType && !isUnspecialised(drType)) {
1453+
if (extractClassTypeArguments && drType is IrSimpleType && !isUnspecialised(drType, logger)) {
14541454

14551455
val extractionMethod = if (isFunctionInvoke) {
14561456
// For `kotlin.FunctionX` and `kotlin.reflect.KFunctionX` interfaces, we're making sure that we

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ open class KotlinUsesExtractor(
220220
val extractedTypeArgs = when {
221221
extractClass.symbol.isKFunction() && typeArgs != null && typeArgs.isNotEmpty() -> listOf(typeArgs.last())
222222
extractClass.fqNameWhenAvailable == FqName("kotlin.jvm.functions.FunctionN") && typeArgs != null && typeArgs.isNotEmpty() -> listOf(typeArgs.last())
223-
typeArgs != null && isUnspecialised(c, typeArgs) -> listOf()
223+
typeArgs != null && isUnspecialised(c, typeArgs, logger) -> listOf()
224224
else -> typeArgs
225225
}
226226

@@ -479,7 +479,7 @@ open class KotlinUsesExtractor(
479479
fun useSimpleTypeClass(c: IrClass, args: List<IrTypeArgument>?, hasQuestionMark: Boolean): TypeResults {
480480
if (c.isAnonymousObject) {
481481
args?.let {
482-
if (it.isNotEmpty() && !isUnspecialised(c, it)) {
482+
if (it.isNotEmpty() && !isUnspecialised(c, it, logger)) {
483483
logger.error("Unexpected specialised instance of generic anonymous class")
484484
}
485485
}

java/kotlin-extractor/src/main/kotlin/utils/TypeSubstitution.kt

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

33
import com.github.codeql.KotlinUsesExtractor
4+
import com.github.codeql.Logger
45
import com.github.codeql.getJavaEquivalentClassId
56
import com.github.codeql.utils.versions.codeQlWithHasQuestionMark
67
import com.github.codeql.utils.versions.createImplicitParameterDeclarationWithWrappedDescriptor
@@ -25,6 +26,7 @@ import org.jetbrains.kotlin.ir.types.impl.makeTypeProjection
2526
import org.jetbrains.kotlin.ir.util.classId
2627
import org.jetbrains.kotlin.ir.util.constructedClassType
2728
import org.jetbrains.kotlin.ir.util.constructors
29+
import org.jetbrains.kotlin.ir.util.kotlinFqName
2830
import org.jetbrains.kotlin.name.FqName
2931
import org.jetbrains.kotlin.name.Name
3032
import org.jetbrains.kotlin.types.Variance
@@ -215,27 +217,34 @@ private fun matchingTypeParameters(l: IrTypeParameter?, r: IrTypeParameter): Boo
215217
}
216218

217219
// Returns true if type is C<T1, T2, ...> where C is declared `class C<T1, T2, ...> { ... }`
218-
fun isUnspecialised(paramsContainer: IrTypeParametersContainer, args: List<IrTypeArgument>): Boolean {
220+
fun isUnspecialised(paramsContainer: IrTypeParametersContainer, args: List<IrTypeArgument>, logger: Logger): Boolean {
221+
return isUnspecialised(paramsContainer, args, logger, paramsContainer)
222+
}
223+
224+
private fun isUnspecialised(paramsContainer: IrTypeParametersContainer, args: List<IrTypeArgument>, logger: Logger, origParamsContainer: IrTypeParametersContainer): Boolean {
219225
val unspecialisedHere = paramsContainer.typeParameters.zip(args).all { paramAndArg ->
220226
(paramAndArg.second as? IrTypeProjection)?.let {
221227
// Type arg refers to the class' own type parameter?
222228
it.variance == Variance.INVARIANT &&
223-
matchingTypeParameters(it.type.classifierOrNull?.owner as? IrTypeParameter, paramAndArg.first)
229+
matchingTypeParameters(it.type.classifierOrNull?.owner as? IrTypeParameter, paramAndArg.first)
224230
} ?: false
225231
}
226232
val remainingArgs = args.drop(paramsContainer.typeParameters.size)
227233

228-
val parentClass = paramsContainer.parents.firstIsInstanceOrNull<IrClass>()
234+
val parentTypeContainer = paramsContainer.parents.firstIsInstanceOrNull<IrTypeParametersContainer>()
229235

230236
val parentUnspecialised = when {
231237
remainingArgs.isEmpty() -> true
232-
parentClass == null -> false
233-
else -> isUnspecialised(parentClass, remainingArgs)
238+
parentTypeContainer == null -> {
239+
logger.error("Found more type arguments than parameters: ${origParamsContainer.kotlinFqName.asString()}")
240+
false
241+
}
242+
else -> isUnspecialised(parentTypeContainer, remainingArgs, logger, origParamsContainer)
234243
}
235244
return unspecialisedHere && parentUnspecialised
236245
}
237246

238247
// Returns true if type is C<T1, T2, ...> where C is declared `class C<T1, T2, ...> { ... }`
239-
fun isUnspecialised(type: IrSimpleType) = (type.classifier.owner as? IrClass)?.let {
240-
isUnspecialised(it, type.arguments)
248+
fun isUnspecialised(type: IrSimpleType, logger: Logger) = (type.classifier.owner as? IrClass)?.let {
249+
isUnspecialised(it, type.arguments, logger)
241250
} ?: false

java/ql/test/kotlin/library-tests/classes/CONSISTENCY/typeParametersInScope.expected

Lines changed: 0 additions & 1 deletion
This file was deleted.

java/ql/test/kotlin/library-tests/classes/PrintAst.expected

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -533,8 +533,8 @@ classes.kt:
533533
# 142| 5: [BlockStmt] { ... }
534534
# 143| 0: [LocalVariableDeclStmt] var ...;
535535
# 143| 1: [LocalVariableDeclExpr] x
536-
# 143| 0: [ClassInstanceExpr] new Cl1<U3>(...)
537-
# 143| -3: [TypeAccess] Cl1<U3>
536+
# 143| 0: [ClassInstanceExpr] new Cl1(...)
537+
# 143| -3: [TypeAccess] Cl1
538538
# 143| 0: [TypeAccess] U3
539539
# 150| 26: [Class,GenericType,ParameterizedType] Cl00
540540
#-----| -2: (Generic Parameters)
Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +0,0 @@
1-
| file://:0:0:0:0 | Unexpected specialised instance of generic anonymous class | CodeQL Kotlin extractor | 5 | | Unexpected specialised instance of generic anonymous class | 2022-09-09 11:08:33 Unexpected specialised instance of generic anonymous class ...while extracting a expression (<no name>) at generic_anonymous.kt:26:13:26:37\n ...while extracting a type operator call (<no name>) at generic_anonymous.kt:26:13:26:37\n ...while extracting a expression (<no name>) at generic_anonymous.kt:26:13:26:37\n ...while extracting a statement (<no name>) at generic_anonymous.kt:26:13:26:37\n ...while extracting a block body (<no name>) at generic_anonymous.kt:25:26:31:9\n ...while extracting a body (<no name>) at generic_anonymous.kt:25:26:31:9\n ...while extracting a function (func2) at generic_anonymous.kt:25:9:31:9\n ...while extracting a generated class (func2) at generic_anonymous.kt:25:9:31:9\n ...while extracting a statement (func2) at generic_anonymous.kt:25:9:31:9\n ...while extracting a block body (<no name>) at generic_anonymous.kt:24:22:32:5\n ...while extracting a body (<no name>) at generic_anonymous.kt:24:22:32:5\n ...while extracting a function (func1) at generic_anonymous.kt:24:5:32:5\n ...while extracting a declaration (func1) at generic_anonymous.kt:24:5:32:5\n ...while extracting a class source (Outer) at generic_anonymous.kt:15:1:33:1\n ...while extracting a declaration (Outer) at generic_anonymous.kt:15:1:33:1\n ...while extracting a file (generic_anonymous.kt) at generic_anonymous.kt:1:1:34:0\n |
2-
| file://:0:0:0:0 | Unexpected specialised instance of generic anonymous class | CodeQL Kotlin extractor | 5 | | Unexpected specialised instance of generic anonymous class | 2022-09-09 11:08:33 Unexpected specialised instance of generic anonymous class ...while extracting a expression (<no name>) at generic_anonymous.kt:27:13:27:37\n ...while extracting a type operator call (<no name>) at generic_anonymous.kt:27:13:27:37\n ...while extracting a expression (<no name>) at generic_anonymous.kt:27:13:27:37\n ...while extracting a statement (<no name>) at generic_anonymous.kt:27:13:27:37\n ...while extracting a block body (<no name>) at generic_anonymous.kt:25:26:31:9\n ...while extracting a body (<no name>) at generic_anonymous.kt:25:26:31:9\n ...while extracting a function (func2) at generic_anonymous.kt:25:9:31:9\n ...while extracting a generated class (func2) at generic_anonymous.kt:25:9:31:9\n ...while extracting a statement (func2) at generic_anonymous.kt:25:9:31:9\n ...while extracting a block body (<no name>) at generic_anonymous.kt:24:22:32:5\n ...while extracting a body (<no name>) at generic_anonymous.kt:24:22:32:5\n ...while extracting a function (func1) at generic_anonymous.kt:24:5:32:5\n ...while extracting a declaration (func1) at generic_anonymous.kt:24:5:32:5\n ...while extracting a class source (Outer) at generic_anonymous.kt:15:1:33:1\n ...while extracting a declaration (Outer) at generic_anonymous.kt:15:1:33:1\n ...while extracting a file (generic_anonymous.kt) at generic_anonymous.kt:1:1:34:0\n |
3-
| file://:0:0:0:0 | Unexpected specialised instance of generic anonymous class | CodeQL Kotlin extractor | 5 | | Unexpected specialised instance of generic anonymous class | 2022-09-09 11:08:33 Unexpected specialised instance of generic anonymous class ...while extracting a expression (<no name>) at generic_anonymous.kt:28:13:28:41\n ...while extracting a type operator call (<no name>) at generic_anonymous.kt:28:13:28:41\n ...while extracting a expression (<no name>) at generic_anonymous.kt:28:13:28:41\n ...while extracting a statement (<no name>) at generic_anonymous.kt:28:13:28:41\n ...while extracting a block body (<no name>) at generic_anonymous.kt:25:26:31:9\n ...while extracting a body (<no name>) at generic_anonymous.kt:25:26:31:9\n ...while extracting a function (func2) at generic_anonymous.kt:25:9:31:9\n ...while extracting a generated class (func2) at generic_anonymous.kt:25:9:31:9\n ...while extracting a statement (func2) at generic_anonymous.kt:25:9:31:9\n ...while extracting a block body (<no name>) at generic_anonymous.kt:24:22:32:5\n ...while extracting a body (<no name>) at generic_anonymous.kt:24:22:32:5\n ...while extracting a function (func1) at generic_anonymous.kt:24:5:32:5\n ...while extracting a declaration (func1) at generic_anonymous.kt:24:5:32:5\n ...while extracting a class source (Outer) at generic_anonymous.kt:15:1:33:1\n ...while extracting a declaration (Outer) at generic_anonymous.kt:15:1:33:1\n ...while extracting a file (generic_anonymous.kt) at generic_anonymous.kt:1:1:34:0\n |
4-
| file://:0:0:0:0 | Unexpected specialised instance of generic anonymous class | CodeQL Kotlin extractor | 5 | | Unexpected specialised instance of generic anonymous class | 2022-09-09 11:08:33 Unexpected specialised instance of generic anonymous class ...while extracting a expression (<no name>) at generic_anonymous.kt:29:13:29:29\n ...while extracting a type operator call (<no name>) at generic_anonymous.kt:29:13:29:29\n ...while extracting a expression (<no name>) at generic_anonymous.kt:29:13:29:29\n ...while extracting a statement (<no name>) at generic_anonymous.kt:29:13:29:29\n ...while extracting a block body (<no name>) at generic_anonymous.kt:25:26:31:9\n ...while extracting a body (<no name>) at generic_anonymous.kt:25:26:31:9\n ...while extracting a function (func2) at generic_anonymous.kt:25:9:31:9\n ...while extracting a generated class (func2) at generic_anonymous.kt:25:9:31:9\n ...while extracting a statement (func2) at generic_anonymous.kt:25:9:31:9\n ...while extracting a block body (<no name>) at generic_anonymous.kt:24:22:32:5\n ...while extracting a body (<no name>) at generic_anonymous.kt:24:22:32:5\n ...while extracting a function (func1) at generic_anonymous.kt:24:5:32:5\n ...while extracting a declaration (func1) at generic_anonymous.kt:24:5:32:5\n ...while extracting a class source (Outer) at generic_anonymous.kt:15:1:33:1\n ...while extracting a declaration (Outer) at generic_anonymous.kt:15:1:33:1\n ...while extracting a file (generic_anonymous.kt) at generic_anonymous.kt:1:1:34:0\n |
5-
| file://:0:0:0:0 | Unexpected specialised instance of generic anonymous class | CodeQL Kotlin extractor | 5 | | Unexpected specialised instance of generic anonymous class | 2022-09-09 11:08:33 Unexpected specialised instance of generic anonymous class ...while extracting a expression (<no name>) at generic_anonymous.kt:30:13:30:33\n ...while extracting a type operator call (<no name>) at generic_anonymous.kt:30:13:30:33\n ...while extracting a expression (<no name>) at generic_anonymous.kt:30:13:30:33\n ...while extracting a statement (<no name>) at generic_anonymous.kt:30:13:30:33\n ...while extracting a block body (<no name>) at generic_anonymous.kt:25:26:31:9\n ...while extracting a body (<no name>) at generic_anonymous.kt:25:26:31:9\n ...while extracting a function (func2) at generic_anonymous.kt:25:9:31:9\n ...while extracting a generated class (func2) at generic_anonymous.kt:25:9:31:9\n ...while extracting a statement (func2) at generic_anonymous.kt:25:9:31:9\n ...while extracting a block body (<no name>) at generic_anonymous.kt:24:22:32:5\n ...while extracting a body (<no name>) at generic_anonymous.kt:24:22:32:5\n ...while extracting a function (func1) at generic_anonymous.kt:24:5:32:5\n ...while extracting a declaration (func1) at generic_anonymous.kt:24:5:32:5\n ...while extracting a class source (Outer) at generic_anonymous.kt:15:1:33:1\n ...while extracting a declaration (Outer) at generic_anonymous.kt:15:1:33:1\n ...while extracting a file (generic_anonymous.kt) at generic_anonymous.kt:1:1:34:0\n |

java/ql/test/kotlin/library-tests/classes/paramTypes.expected

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@
88
| file://<external>/C1$<no name provided>$Local3.class:0:0:0:0 | Local3<Integer> | 0 | file://<external>/Integer.class:0:0:0:0 | Integer |
99
| file://<external>/C1$Local1.class:0:0:0:0 | Local1<Integer> | 0 | file://<external>/Integer.class:0:0:0:0 | Integer |
1010
| file://<external>/C1$Local2.class:0:0:0:0 | Local2<Integer> | 0 | file://<external>/Integer.class:0:0:0:0 | Integer |
11-
| file://<external>/Cl0$Cl1.class:0:0:0:0 | Cl1<U3> | 0 | classes.kt:141:23:141:24 | U3 |
12-
| file://<external>/Cl0.class:0:0:0:0 | Cl0<U0> | 0 | classes.kt:140:14:140:15 | U2 |
1311
| file://<external>/Generic.class:0:0:0:0 | Generic<Integer> | 0 | file://<external>/Integer.class:0:0:0:0 | Integer |
1412
| file://<external>/Generic.class:0:0:0:0 | Generic<String> | 0 | file://<external>/String.class:0:0:0:0 | String |
1513
| file://<external>/SuperChain1.class:0:0:0:0 | SuperChain1<T3,String> | 0 | superChain.kt:2:24:2:25 | T3 |

java/ql/test/kotlin/library-tests/classes/superTypes.expected

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,6 @@
4747
| file://<external>/C1$<no name provided>$Local3.class:0:0:0:0 | Local3<Integer> | file://<external>/Object.class:0:0:0:0 | Object |
4848
| file://<external>/C1$Local1.class:0:0:0:0 | Local1<Integer> | file://<external>/Object.class:0:0:0:0 | Object |
4949
| file://<external>/C1$Local2.class:0:0:0:0 | Local2<Integer> | file://<external>/Object.class:0:0:0:0 | Object |
50-
| file://<external>/Cl0$Cl1.class:0:0:0:0 | Cl1<U3> | file://<external>/Object.class:0:0:0:0 | Object |
51-
| file://<external>/Cl0.class:0:0:0:0 | Cl0<U0> | file://<external>/Object.class:0:0:0:0 | Object |
5250
| file://<external>/Generic.class:0:0:0:0 | Generic<Integer> | file://<external>/Object.class:0:0:0:0 | Object |
5351
| file://<external>/Generic.class:0:0:0:0 | Generic<String> | file://<external>/Object.class:0:0:0:0 | Object |
5452
| file://<external>/SuperChain1.class:0:0:0:0 | SuperChain1<T3,String> | file://<external>/Object.class:0:0:0:0 | Object |

0 commit comments

Comments
 (0)