mirror of
https://github.com/github/codeql.git
synced 2026-04-30 03:05:15 +02:00
WIP: type arg extraction
This commit is contained in:
@@ -9,6 +9,7 @@ import org.jetbrains.kotlin.ir.IrStatement
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin.*
|
||||
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.util.packageFqName
|
||||
@@ -217,7 +218,7 @@ class KotlinFileExtractor(val logger: FileLogger, val tw: FileTrapWriter, val fi
|
||||
|
||||
fun extractDeclaration(declaration: IrDeclaration, optParentid: Optional<Label<out DbReftype>>) {
|
||||
when (declaration) {
|
||||
is IrClass -> extractClass(declaration)
|
||||
is IrClass -> useClass(declaration, listOf())
|
||||
is IrFunction -> extractFunction(declaration, if (optParentid.isPresent()) optParentid.get() else fileClass)
|
||||
is IrAnonymousInitializer -> {
|
||||
// Leaving this intentionally empty. init blocks are extracted during class extraction.
|
||||
@@ -258,7 +259,8 @@ class KotlinFileExtractor(val logger: FileLogger, val tw: FileTrapWriter, val fi
|
||||
s.classifier.owner is IrClass -> {
|
||||
val classifier: IrClassifierSymbol = s.classifier
|
||||
val cls: IrClass = classifier.owner as IrClass
|
||||
return useClass(cls)
|
||||
|
||||
return useClass(cls, s.arguments)
|
||||
}
|
||||
s.classifier.owner is IrTypeParameter -> {
|
||||
return useTypeParameter(s.classifier.owner as IrTypeParameter)
|
||||
@@ -275,7 +277,7 @@ class KotlinFileExtractor(val logger: FileLogger, val tw: FileTrapWriter, val fi
|
||||
fun getLabel(element: IrElement) : String? {
|
||||
when (element) {
|
||||
is IrFile -> return "@\"${element.path};sourcefile\"" // todo: remove copy-pasted code
|
||||
is IrClass -> return getClassLabel(element)
|
||||
is IrClass -> return getClassLabel(element, listOf())
|
||||
is IrTypeParameter -> return getTypeParameterLabel(element)
|
||||
is IrFunction -> return getFunctionLabel(element)
|
||||
is IrValueParameter -> return getValueParameterLabel(element)
|
||||
@@ -317,33 +319,78 @@ class KotlinFileExtractor(val logger: FileLogger, val tw: FileTrapWriter, val fi
|
||||
// todo: add type bounds
|
||||
}
|
||||
|
||||
private fun getClassLabel(c: IrClass): String {
|
||||
private fun getClassLabel(c: IrClass, typeArgs: List<IrTypeArgument>): String {
|
||||
val pkg = c.packageFqName?.asString() ?: ""
|
||||
val cls = c.name.asString()
|
||||
val qualClassName = if (pkg.isEmpty()) cls else "$pkg.$cls"
|
||||
val label = "@\"class;$qualClassName\""
|
||||
var label = "@\"class;$qualClassName"
|
||||
|
||||
if (typeArgs.isNotEmpty()) {
|
||||
for (arg in typeArgs) {
|
||||
val argId = getTypeArgumentLabel(arg, c)
|
||||
label += ";{$argId}"
|
||||
}
|
||||
}
|
||||
|
||||
label += "\""
|
||||
return label
|
||||
}
|
||||
|
||||
fun addClassLabel(c: IrClass): Label<out DbClassorinterface> {
|
||||
val label = getClassLabel(c)
|
||||
private fun getTypeArgumentLabel(
|
||||
arg: IrTypeArgument,
|
||||
reportOn: IrElement
|
||||
): Label<out DbReftype> {
|
||||
when (arg) {
|
||||
is IrStarProjection -> {
|
||||
// todo handle this
|
||||
logger.warnElement(Severity.ErrorSevere, "Star is not yet handled.", reportOn)
|
||||
return fakeLabel()
|
||||
}
|
||||
is IrTypeProjection -> {
|
||||
return useType(arg.type) as Label<out DbReftype>
|
||||
}
|
||||
else -> {
|
||||
logger.warnElement(Severity.ErrorSevere, "Unexpected type argument.", reportOn)
|
||||
return fakeLabel()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun addClassLabel(c: IrClass, typeArgs: List<IrTypeArgument>): Label<out DbClassorinterface> {
|
||||
var label = getClassLabel(c, typeArgs)
|
||||
val id: Label<DbClassorinterface> = tw.getLabelFor(label)
|
||||
return id
|
||||
}
|
||||
|
||||
fun useClass(c: IrClass): Label<out DbClassorinterface> {
|
||||
// todo: fix this
|
||||
if (c.origin == IrDeclarationOrigin.IR_EXTERNAL_DECLARATION_STUB ||
|
||||
c.origin == IrDeclarationOrigin.IR_EXTERNAL_JAVA_DECLARATION_STUB) {
|
||||
if(tw.getExistingLabelFor<DbClass>(getClassLabel(c)) == null) {
|
||||
return extractClass(c)
|
||||
}
|
||||
fun useClass(c: IrClass, typeArgs: List<IrTypeArgument>): Label<out DbClassorinterface> {
|
||||
// todo: this feels a bit arbitrary:
|
||||
// It is introduced because the return type of a constructor is the type with its
|
||||
// type parameters passed as type arguments.
|
||||
// todo: investigate if this can only happen with constructor-like calls? If so, we could handle these there.
|
||||
// todo: what happens with nested generics?
|
||||
val args = if (typeArgsMatchTypeParameters(typeArgs, c.typeParameters)) {
|
||||
listOf()
|
||||
} else {
|
||||
typeArgs
|
||||
}
|
||||
return addClassLabel(c)
|
||||
|
||||
val classId = getClassLabel(c, args)
|
||||
return tw.getExistingLabelFor<DbClass>(classId) ?: extractClass(c, args)
|
||||
}
|
||||
|
||||
fun extractClass(c: IrClass): Label<out DbClassorinterface> {
|
||||
val id = addClassLabel(c)
|
||||
private fun typeArgsMatchTypeParameters(typeArgs: List<IrTypeArgument>, typeParameters: List<IrTypeParameter>): Boolean {
|
||||
val args = typeArgs.map { if (it !is IrTypeProjection) null else it.type }
|
||||
for ((idx, ta) in args.withIndex()){
|
||||
val tp = typeParameters.elementAtOrNull(idx)
|
||||
if (tp?.symbol?.typeWith() != ta) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
fun extractClass(c: IrClass, typeArgs: List<IrTypeArgument>): Label<out DbClassorinterface> {
|
||||
val id = addClassLabel(c, typeArgs)
|
||||
val pkg = c.packageFqName?.asString() ?: ""
|
||||
val cls = c.name.asString()
|
||||
val pkgId = extractPackage(pkg)
|
||||
@@ -369,7 +416,7 @@ class KotlinFileExtractor(val logger: FileLogger, val tw: FileTrapWriter, val fi
|
||||
t.classifier.owner is IrClass -> {
|
||||
val classifier: IrClassifierSymbol = t.classifier
|
||||
val tcls: IrClass = classifier.owner as IrClass
|
||||
val l = useClass(tcls)
|
||||
val l = useClass(tcls, t.arguments)
|
||||
tw.writeExtendsReftype(id, l)
|
||||
} else -> {
|
||||
logger.warn(Severity.ErrorSevere, "Unexpected simple type supertype: " + t.javaClass + ": " + t.render())
|
||||
@@ -381,11 +428,21 @@ class KotlinFileExtractor(val logger: FileLogger, val tw: FileTrapWriter, val fi
|
||||
}
|
||||
}
|
||||
|
||||
c.typeParameters.map { extractTypeParameter(it, Optional.of(id)) }
|
||||
if (typeArgs.isNotEmpty()) {
|
||||
for ((idx, arg) in typeArgs.withIndex()) {
|
||||
val argId = getTypeArgumentLabel(arg, c)
|
||||
tw.writeTypeArgs(argId, idx, id)
|
||||
}
|
||||
tw.writeIsParameterized(id)
|
||||
val unbound = useClass(c, listOf())
|
||||
tw.writeErasure(id, unbound)
|
||||
} else {
|
||||
c.typeParameters.map { extractTypeParameter(it, Optional.of(id)) }
|
||||
|
||||
c.declarations.map { extractDeclaration(it, Optional.of(id)) }
|
||||
c.declarations.map { extractDeclaration(it, Optional.of(id)) }
|
||||
|
||||
extractObjectInitializerFunction(c, id)
|
||||
extractObjectInitializerFunction(c, id)
|
||||
}
|
||||
|
||||
return id
|
||||
}
|
||||
@@ -393,7 +450,7 @@ class KotlinFileExtractor(val logger: FileLogger, val tw: FileTrapWriter, val fi
|
||||
fun useType(t: IrType): Label<out DbType> {
|
||||
when(t) {
|
||||
is IrSimpleType -> return useSimpleType(t)
|
||||
is IrClass -> return useClass(t)
|
||||
is IrClass -> return useClass(t, listOf())
|
||||
else -> {
|
||||
logger.warn(Severity.ErrorSevere, "Unrecognised IrType: " + t.javaClass)
|
||||
return fakeLabel()
|
||||
@@ -404,7 +461,7 @@ class KotlinFileExtractor(val logger: FileLogger, val tw: FileTrapWriter, val fi
|
||||
fun useDeclarationParent(dp: IrDeclarationParent): Label<out DbElement> {
|
||||
when(dp) {
|
||||
is IrFile -> return usePackage(dp.fqName.asString())
|
||||
is IrClass -> return useClass(dp)
|
||||
is IrClass -> return useClass(dp, listOf())
|
||||
is IrFunction -> return useFunction(dp)
|
||||
else -> {
|
||||
logger.warnElement(Severity.ErrorSevere, "Unrecognised IrDeclarationParent: " + dp.javaClass, dp)
|
||||
@@ -414,9 +471,15 @@ class KotlinFileExtractor(val logger: FileLogger, val tw: FileTrapWriter, val fi
|
||||
}
|
||||
|
||||
fun erase (t: IrType): IrType {
|
||||
if(t is IrSimpleType) {
|
||||
if(t.classifier.owner is IrTypeParameter) {
|
||||
return erase((t.classifier.owner as IrTypeParameter).superTypes.get(0))
|
||||
if (t is IrSimpleType) {
|
||||
val classifier = t.classifier
|
||||
val owner = classifier.owner
|
||||
if(owner is IrTypeParameter) {
|
||||
return erase(owner.superTypes.get(0))
|
||||
}
|
||||
|
||||
if (owner is IrClass) {
|
||||
return (classifier as IrClassSymbol).typeWith()
|
||||
}
|
||||
}
|
||||
return t
|
||||
@@ -746,22 +809,27 @@ class KotlinFileExtractor(val logger: FileLogger, val tw: FileTrapWriter, val fi
|
||||
tw.writeExprs_methodaccess(id, typeId, parent, idx)
|
||||
tw.writeHasLocation(id, locId)
|
||||
tw.writeCallableBinding(id, methodId)
|
||||
|
||||
// type arguments at index -2, -3, ...
|
||||
for (argIdx in 0 until c.typeArgumentsCount) {
|
||||
val arg = c.getTypeArgument(argIdx)!!
|
||||
val argTypeId = useType(arg)
|
||||
val argId = tw.getFreshIdLabel<DbUnannotatedtypeaccess>()
|
||||
tw.writeExprs_unannotatedtypeaccess(argId, argTypeId, id,argIdx * -1 - 2)
|
||||
}
|
||||
id
|
||||
}
|
||||
}
|
||||
val dr = c.dispatchReceiver
|
||||
val offset = if(dr == null) 0 else 1
|
||||
if(dr != null) {
|
||||
extractExpression(dr, callable, exprId, 0) // todo: should this be at index -1 instead?
|
||||
extractExpression(dr, callable, exprId, -1)
|
||||
}
|
||||
for(i in 0 until c.valueArgumentsCount) {
|
||||
val arg = c.getValueArgument(i)
|
||||
if(arg != null) {
|
||||
extractExpression(arg, callable, exprId, i + offset)
|
||||
extractExpression(arg, callable, exprId, i)
|
||||
}
|
||||
}
|
||||
|
||||
// todo: type arguments at index -2, -3, ...
|
||||
}
|
||||
|
||||
private fun extractConstructorCall(
|
||||
|
||||
@@ -475,7 +475,7 @@ class GenericCall extends Call {
|
||||
result.(Wildcard).getUpperBound().getType() = v.getUpperBoundType()
|
||||
}
|
||||
|
||||
private RefType getAnExplicitTypeArgument(TypeVariable v) {
|
||||
private Type getAnExplicitTypeArgument(TypeVariable v) {
|
||||
exists(GenericCallable gen, MethodAccess call, int i |
|
||||
this = call and
|
||||
gen = call.getCallee() and
|
||||
@@ -485,8 +485,8 @@ class GenericCall extends Call {
|
||||
}
|
||||
|
||||
/** Gets a type argument of the call for the given `TypeVariable`. */
|
||||
RefType getATypeArgument(TypeVariable v) {
|
||||
result = this.getAnExplicitTypeArgument(v)
|
||||
Type getATypeArgument(TypeVariable v) {
|
||||
result = getAnExplicitTypeArgument(v)
|
||||
or
|
||||
not exists(this.getAnExplicitTypeArgument(v)) and
|
||||
result = this.getAnInferredTypeArgument(v)
|
||||
|
||||
30
java/ql/test/kotlin/library-tests/generics/DB-CHECK.expected
Normal file
30
java/ql/test/kotlin/library-tests/generics/DB-CHECK.expected
Normal file
@@ -0,0 +1,30 @@
|
||||
[VALUE_NOT_IN_TYPE] predicate typeArgs(@reftype argumentid, int pos, @typeorcallable parentid): Value 11 of field argumentid is not in type @reftype. The value is however in the following types: @primitive. Appears in tuple (11,0,132)
|
||||
Relevant element: argumentid=11
|
||||
Full ID for 11: @"type;int"
|
||||
Relevant element: parentid=132
|
||||
Full ID for 132: @"class;foo.bar.C1;(11);(11)". The ID may expand to @"class;foo.bar.C1;{@"type;int"};{@"type;int"}"
|
||||
[VALUE_NOT_IN_TYPE] predicate typeArgs(@reftype argumentid, int pos, @typeorcallable parentid): Value 11 of field argumentid is not in type @reftype. The value is however in the following types: @primitive. Appears in tuple (11,1,132)
|
||||
Relevant element: argumentid=11
|
||||
Full ID for 11: @"type;int"
|
||||
Relevant element: parentid=132
|
||||
Full ID for 132: @"class;foo.bar.C1;(11);(11)". The ID may expand to @"class;foo.bar.C1;{@"type;int"};{@"type;int"}"
|
||||
[VALUE_NOT_IN_TYPE] predicate typeArgs(@reftype argumentid, int pos, @typeorcallable parentid): Value 11 of field argumentid is not in type @reftype. The value is however in the following types: @primitive. Appears in tuple (11,1,157)
|
||||
Relevant element: argumentid=11
|
||||
Full ID for 11: @"type;int"
|
||||
Relevant element: parentid=157
|
||||
Full ID for 157: @"class;foo.bar.C1;(13);(11)". The ID may expand to @"class;foo.bar.C1;{@"type;string"};{@"type;int"}"
|
||||
[VALUE_NOT_IN_TYPE] predicate typeArgs(@reftype argumentid, int pos, @typeorcallable parentid): Value 13 of field argumentid is not in type @reftype. The value is however in the following types: @primitive. Appears in tuple (13,0,146)
|
||||
Relevant element: argumentid=13
|
||||
Full ID for 13: @"type;string"
|
||||
Relevant element: parentid=146
|
||||
Full ID for 146: @"class;foo.bar.C1;(13);(13)". The ID may expand to @"class;foo.bar.C1;{@"type;string"};{@"type;string"}"
|
||||
[VALUE_NOT_IN_TYPE] predicate typeArgs(@reftype argumentid, int pos, @typeorcallable parentid): Value 13 of field argumentid is not in type @reftype. The value is however in the following types: @primitive. Appears in tuple (13,0,157)
|
||||
Relevant element: argumentid=13
|
||||
Full ID for 13: @"type;string"
|
||||
Relevant element: parentid=157
|
||||
Full ID for 157: @"class;foo.bar.C1;(13);(11)". The ID may expand to @"class;foo.bar.C1;{@"type;string"};{@"type;int"}"
|
||||
[VALUE_NOT_IN_TYPE] predicate typeArgs(@reftype argumentid, int pos, @typeorcallable parentid): Value 13 of field argumentid is not in type @reftype. The value is however in the following types: @primitive. Appears in tuple (13,1,146)
|
||||
Relevant element: argumentid=13
|
||||
Full ID for 13: @"type;string"
|
||||
Relevant element: parentid=146
|
||||
Full ID for 146: @"class;foo.bar.C1;(13);(13)". The ID may expand to @"class;foo.bar.C1;{@"type;string"};{@"type;string"}"
|
||||
27
java/ql/test/kotlin/library-tests/generics/generics.expected
Normal file
27
java/ql/test/kotlin/library-tests/generics/generics.expected
Normal file
@@ -0,0 +1,27 @@
|
||||
genericType
|
||||
| generics.kt:11:1:11:19 | C0 | generics.kt:11:15:11:15 | V | 0 |
|
||||
| generics.kt:13:1:18:1 | C1 | generics.kt:13:10:13:10 | T | 0 |
|
||||
| generics.kt:13:1:18:1 | C1 | generics.kt:13:13:13:13 | W | 1 |
|
||||
parameterizedType
|
||||
| generics.kt:11:1:11:19 | C0 | generics.kt:11:1:11:19 | C0 | 0 | generics.kt:7:6:7:6 | S |
|
||||
| generics.kt:11:1:11:19 | C0 | generics.kt:11:1:11:19 | C0 | 0 | generics.kt:11:15:11:15 | V |
|
||||
| generics.kt:11:1:11:19 | C0 | generics.kt:11:1:11:19 | C0 | 0 | generics.kt:13:13:13:13 | W |
|
||||
| generics.kt:13:1:18:1 | C1 | generics.kt:13:1:18:1 | C1 | 0 | file://:0:0:0:0 | int |
|
||||
| generics.kt:13:1:18:1 | C1 | generics.kt:13:1:18:1 | C1 | 0 | file://:0:0:0:0 | string |
|
||||
| generics.kt:13:1:18:1 | C1 | generics.kt:13:1:18:1 | C1 | 0 | file://:0:0:0:0 | string |
|
||||
| generics.kt:13:1:18:1 | C1 | generics.kt:13:1:18:1 | C1 | 0 | generics.kt:13:10:13:10 | T |
|
||||
| generics.kt:13:1:18:1 | C1 | generics.kt:13:1:18:1 | C1 | 0 | generics.kt:15:10:15:10 | U |
|
||||
| generics.kt:13:1:18:1 | C1 | generics.kt:13:1:18:1 | C1 | 1 | file://:0:0:0:0 | int |
|
||||
| generics.kt:13:1:18:1 | C1 | generics.kt:13:1:18:1 | C1 | 1 | file://:0:0:0:0 | int |
|
||||
| generics.kt:13:1:18:1 | C1 | generics.kt:13:1:18:1 | C1 | 1 | file://:0:0:0:0 | string |
|
||||
| generics.kt:13:1:18:1 | C1 | generics.kt:13:1:18:1 | C1 | 1 | generics.kt:13:13:13:13 | W |
|
||||
| generics.kt:13:1:18:1 | C1 | generics.kt:13:1:18:1 | C1 | 1 | generics.kt:15:10:15:10 | U |
|
||||
genericFunction
|
||||
| generics.kt:3:1:5:1 | f0 | generics.kt:3:6:3:6 | S | 0 |
|
||||
| generics.kt:7:1:9:1 | f1 | generics.kt:7:6:7:6 | S | 0 |
|
||||
| generics.kt:15:5:17:5 | f2 | generics.kt:15:10:15:10 | U | 0 |
|
||||
| generics.kt:21:5:21:23 | f4 | generics.kt:21:10:21:10 | P | 0 |
|
||||
genericCall
|
||||
| generics.kt:27:17:27:22 | f2(...) | generics.kt:15:10:15:10 | U | file://:0:0:0:0 | string |
|
||||
| generics.kt:30:17:30:21 | f2(...) | generics.kt:15:10:15:10 | U | file://:0:0:0:0 | int |
|
||||
| generics.kt:32:8:32:12 | f4(...) | generics.kt:21:10:21:10 | P | file://:0:0:0:0 | int |
|
||||
@@ -1,10 +1,33 @@
|
||||
package foo.bar
|
||||
|
||||
fun <S> Int.f(s: S): S {
|
||||
fun <S> Int.f0(s: S): S {
|
||||
return s
|
||||
}
|
||||
|
||||
class C1<T>(val t: T) {
|
||||
fun f1(t: T) {}
|
||||
fun <U> f2(u: U) {}
|
||||
fun <S> Int.f1(s: S): C0<S>? {
|
||||
return null
|
||||
}
|
||||
|
||||
open class C0<V> {}
|
||||
|
||||
class C1<T, W>(val t: T) : C0<W>() {
|
||||
fun f1(t: T) {}
|
||||
fun <U> f2(u: U): C1<U, U> {
|
||||
return C1<U, U>(u)
|
||||
}
|
||||
}
|
||||
|
||||
class C2() {
|
||||
fun <P> f4(p: P) {}
|
||||
}
|
||||
|
||||
fun m() {
|
||||
val c1 = C1<Int, Int>(1)
|
||||
c1.f1(2)
|
||||
val x1 = c1.f2("")
|
||||
val c2 = C1<String, Int>("")
|
||||
c2.f1("a")
|
||||
val x2 = c2.f2(3)
|
||||
val c3 = C2()
|
||||
c3.f4(5)
|
||||
}
|
||||
13
java/ql/test/kotlin/library-tests/generics/generics.ql
Normal file
13
java/ql/test/kotlin/library-tests/generics/generics.ql
Normal file
@@ -0,0 +1,13 @@
|
||||
import java
|
||||
|
||||
query predicate genericType(GenericType t, TypeVariable tv, int i) { t.getTypeParameter(i) = tv }
|
||||
|
||||
query predicate parameterizedType(ParameterizedType t, GenericType gt, int i, RefType ta) {
|
||||
t.getGenericType() = gt and t.getTypeArgument(i) = ta
|
||||
}
|
||||
|
||||
query predicate genericFunction(GenericCallable c, TypeVariable tv, int i) {
|
||||
c.getTypeParameter(i) = tv
|
||||
}
|
||||
|
||||
query predicate genericCall(GenericCall c, TypeVariable tv, Type t) { c.getATypeArgument(tv) = t }
|
||||
@@ -1,5 +0,0 @@
|
||||
genericType
|
||||
| generics.kt:7:10:7:10 | T | generics.kt:7:1:10:1 | C1 |
|
||||
genericFunction
|
||||
| generics.kt:3:6:3:6 | S | generics.kt:3:1:5:1 | f |
|
||||
| generics.kt:9:10:9:10 | U | generics.kt:9:5:9:23 | f2 |
|
||||
@@ -1,5 +0,0 @@
|
||||
import java
|
||||
|
||||
query predicate genericType(TypeVariable tv, RefType rt) { tv.getGenericType() = rt }
|
||||
|
||||
query predicate genericFunction(TypeVariable tv, GenericCallable c) { tv.getGenericCallable() = c }
|
||||
Reference in New Issue
Block a user