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

Skip to content

Commit 8dff527

Browse files
tamasvajkigfoo
authored andcommitted
WIP: type arg extraction
1 parent 0c6e209 commit 8dff527

8 files changed

Lines changed: 197 additions & 46 deletions

File tree

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

Lines changed: 98 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import org.jetbrains.kotlin.ir.IrStatement
99
import org.jetbrains.kotlin.ir.declarations.*
1010
import org.jetbrains.kotlin.ir.expressions.*
1111
import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin.*
12+
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
1213
import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol
1314
import org.jetbrains.kotlin.ir.types.*
1415
import org.jetbrains.kotlin.ir.util.packageFqName
@@ -217,7 +218,7 @@ class KotlinFileExtractor(val logger: FileLogger, val tw: FileTrapWriter, val fi
217218

218219
fun extractDeclaration(declaration: IrDeclaration, optParentid: Optional<Label<out DbReftype>>) {
219220
when (declaration) {
220-
is IrClass -> extractClass(declaration)
221+
is IrClass -> useClass(declaration, listOf())
221222
is IrFunction -> extractFunction(declaration, if (optParentid.isPresent()) optParentid.get() else fileClass)
222223
is IrAnonymousInitializer -> {
223224
// 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
258259
s.classifier.owner is IrClass -> {
259260
val classifier: IrClassifierSymbol = s.classifier
260261
val cls: IrClass = classifier.owner as IrClass
261-
return useClass(cls)
262+
263+
return useClass(cls, s.arguments)
262264
}
263265
s.classifier.owner is IrTypeParameter -> {
264266
return useTypeParameter(s.classifier.owner as IrTypeParameter)
@@ -275,7 +277,7 @@ class KotlinFileExtractor(val logger: FileLogger, val tw: FileTrapWriter, val fi
275277
fun getLabel(element: IrElement) : String? {
276278
when (element) {
277279
is IrFile -> return "@\"${element.path};sourcefile\"" // todo: remove copy-pasted code
278-
is IrClass -> return getClassLabel(element)
280+
is IrClass -> return getClassLabel(element, listOf())
279281
is IrTypeParameter -> return getTypeParameterLabel(element)
280282
is IrFunction -> return getFunctionLabel(element)
281283
is IrValueParameter -> return getValueParameterLabel(element)
@@ -317,33 +319,78 @@ class KotlinFileExtractor(val logger: FileLogger, val tw: FileTrapWriter, val fi
317319
// todo: add type bounds
318320
}
319321

320-
private fun getClassLabel(c: IrClass): String {
322+
private fun getClassLabel(c: IrClass, typeArgs: List<IrTypeArgument>): String {
321323
val pkg = c.packageFqName?.asString() ?: ""
322324
val cls = c.name.asString()
323325
val qualClassName = if (pkg.isEmpty()) cls else "$pkg.$cls"
324-
val label = "@\"class;$qualClassName\""
326+
var label = "@\"class;$qualClassName"
327+
328+
if (typeArgs.isNotEmpty()) {
329+
for (arg in typeArgs) {
330+
val argId = getTypeArgumentLabel(arg, c)
331+
label += ";{$argId}"
332+
}
333+
}
334+
335+
label += "\""
325336
return label
326337
}
327338

328-
fun addClassLabel(c: IrClass): Label<out DbClassorinterface> {
329-
val label = getClassLabel(c)
339+
private fun getTypeArgumentLabel(
340+
arg: IrTypeArgument,
341+
reportOn: IrElement
342+
): Label<out DbReftype> {
343+
when (arg) {
344+
is IrStarProjection -> {
345+
// todo handle this
346+
logger.warnElement(Severity.ErrorSevere, "Star is not yet handled.", reportOn)
347+
return fakeLabel()
348+
}
349+
is IrTypeProjection -> {
350+
return useType(arg.type) as Label<out DbReftype>
351+
}
352+
else -> {
353+
logger.warnElement(Severity.ErrorSevere, "Unexpected type argument.", reportOn)
354+
return fakeLabel()
355+
}
356+
}
357+
}
358+
359+
fun addClassLabel(c: IrClass, typeArgs: List<IrTypeArgument>): Label<out DbClassorinterface> {
360+
var label = getClassLabel(c, typeArgs)
330361
val id: Label<DbClassorinterface> = tw.getLabelFor(label)
331362
return id
332363
}
333364

334-
fun useClass(c: IrClass): Label<out DbClassorinterface> {
335-
// todo: fix this
336-
if (c.origin == IrDeclarationOrigin.IR_EXTERNAL_DECLARATION_STUB ||
337-
c.origin == IrDeclarationOrigin.IR_EXTERNAL_JAVA_DECLARATION_STUB) {
338-
if(tw.getExistingLabelFor<DbClass>(getClassLabel(c)) == null) {
339-
return extractClass(c)
365+
fun useClass(c: IrClass, typeArgs: List<IrTypeArgument>): Label<out DbClassorinterface> {
366+
// todo: this feels a bit arbitrary:
367+
// It is introduced because the return type of a constructor is the type with its
368+
// type parameters passed as type arguments.
369+
// todo: investigate if this can only happen with constructor-like calls? If so, we could handle these there.
370+
// todo: what happens with nested generics?
371+
val args = if (typeArgsMatchTypeParameters(typeArgs, c.typeParameters)) {
372+
listOf()
373+
} else {
374+
typeArgs
375+
}
376+
377+
val classId = getClassLabel(c, args)
378+
return tw.getExistingLabelFor<DbClass>(classId) ?: extractClass(c, args)
379+
}
380+
381+
private fun typeArgsMatchTypeParameters(typeArgs: List<IrTypeArgument>, typeParameters: List<IrTypeParameter>): Boolean {
382+
val args = typeArgs.map { if (it !is IrTypeProjection) null else it.type }
383+
for ((idx, ta) in args.withIndex()){
384+
val tp = typeParameters.elementAtOrNull(idx)
385+
if (tp?.symbol?.typeWith() != ta) {
386+
return false
340387
}
341388
}
342-
return addClassLabel(c)
389+
return true
343390
}
344391

345-
fun extractClass(c: IrClass): Label<out DbClassorinterface> {
346-
val id = addClassLabel(c)
392+
fun extractClass(c: IrClass, typeArgs: List<IrTypeArgument>): Label<out DbClassorinterface> {
393+
val id = addClassLabel(c, typeArgs)
347394
val pkg = c.packageFqName?.asString() ?: ""
348395
val cls = c.name.asString()
349396
val pkgId = extractPackage(pkg)
@@ -369,7 +416,7 @@ class KotlinFileExtractor(val logger: FileLogger, val tw: FileTrapWriter, val fi
369416
t.classifier.owner is IrClass -> {
370417
val classifier: IrClassifierSymbol = t.classifier
371418
val tcls: IrClass = classifier.owner as IrClass
372-
val l = useClass(tcls)
419+
val l = useClass(tcls, t.arguments)
373420
tw.writeExtendsReftype(id, l)
374421
} else -> {
375422
logger.warn(Severity.ErrorSevere, "Unexpected simple type supertype: " + t.javaClass + ": " + t.render())
@@ -381,19 +428,29 @@ class KotlinFileExtractor(val logger: FileLogger, val tw: FileTrapWriter, val fi
381428
}
382429
}
383430

384-
c.typeParameters.map { extractTypeParameter(it, Optional.of(id)) }
431+
if (typeArgs.isNotEmpty()) {
432+
for ((idx, arg) in typeArgs.withIndex()) {
433+
val argId = getTypeArgumentLabel(arg, c)
434+
tw.writeTypeArgs(argId, idx, id)
435+
}
436+
tw.writeIsParameterized(id)
437+
val unbound = useClass(c, listOf())
438+
tw.writeErasure(id, unbound)
439+
} else {
440+
c.typeParameters.map { extractTypeParameter(it, Optional.of(id)) }
385441

386-
c.declarations.map { extractDeclaration(it, Optional.of(id)) }
442+
c.declarations.map { extractDeclaration(it, Optional.of(id)) }
387443

388-
extractObjectInitializerFunction(c, id)
444+
extractObjectInitializerFunction(c, id)
445+
}
389446

390447
return id
391448
}
392449

393450
fun useType(t: IrType): Label<out DbType> {
394451
when(t) {
395452
is IrSimpleType -> return useSimpleType(t)
396-
is IrClass -> return useClass(t)
453+
is IrClass -> return useClass(t, listOf())
397454
else -> {
398455
logger.warn(Severity.ErrorSevere, "Unrecognised IrType: " + t.javaClass)
399456
return fakeLabel()
@@ -404,7 +461,7 @@ class KotlinFileExtractor(val logger: FileLogger, val tw: FileTrapWriter, val fi
404461
fun useDeclarationParent(dp: IrDeclarationParent): Label<out DbElement> {
405462
when(dp) {
406463
is IrFile -> return usePackage(dp.fqName.asString())
407-
is IrClass -> return useClass(dp)
464+
is IrClass -> return useClass(dp, listOf())
408465
is IrFunction -> return useFunction(dp)
409466
else -> {
410467
logger.warnElement(Severity.ErrorSevere, "Unrecognised IrDeclarationParent: " + dp.javaClass, dp)
@@ -414,9 +471,15 @@ class KotlinFileExtractor(val logger: FileLogger, val tw: FileTrapWriter, val fi
414471
}
415472

416473
fun erase (t: IrType): IrType {
417-
if(t is IrSimpleType) {
418-
if(t.classifier.owner is IrTypeParameter) {
419-
return erase((t.classifier.owner as IrTypeParameter).superTypes.get(0))
474+
if (t is IrSimpleType) {
475+
val classifier = t.classifier
476+
val owner = classifier.owner
477+
if(owner is IrTypeParameter) {
478+
return erase(owner.superTypes.get(0))
479+
}
480+
481+
if (owner is IrClass) {
482+
return (classifier as IrClassSymbol).typeWith()
420483
}
421484
}
422485
return t
@@ -746,22 +809,27 @@ class KotlinFileExtractor(val logger: FileLogger, val tw: FileTrapWriter, val fi
746809
tw.writeExprs_methodaccess(id, typeId, parent, idx)
747810
tw.writeHasLocation(id, locId)
748811
tw.writeCallableBinding(id, methodId)
812+
813+
// type arguments at index -2, -3, ...
814+
for (argIdx in 0 until c.typeArgumentsCount) {
815+
val arg = c.getTypeArgument(argIdx)!!
816+
val argTypeId = useType(arg)
817+
val argId = tw.getFreshIdLabel<DbUnannotatedtypeaccess>()
818+
tw.writeExprs_unannotatedtypeaccess(argId, argTypeId, id,argIdx * -1 - 2)
819+
}
749820
id
750821
}
751822
}
752823
val dr = c.dispatchReceiver
753-
val offset = if(dr == null) 0 else 1
754824
if(dr != null) {
755-
extractExpression(dr, callable, exprId, 0) // todo: should this be at index -1 instead?
825+
extractExpression(dr, callable, exprId, -1)
756826
}
757827
for(i in 0 until c.valueArgumentsCount) {
758828
val arg = c.getValueArgument(i)
759829
if(arg != null) {
760-
extractExpression(arg, callable, exprId, i + offset)
830+
extractExpression(arg, callable, exprId, i)
761831
}
762832
}
763-
764-
// todo: type arguments at index -2, -3, ...
765833
}
766834

767835
private fun extractConstructorCall(

java/ql/lib/semmle/code/java/Generics.qll

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -475,7 +475,7 @@ class GenericCall extends Call {
475475
result.(Wildcard).getUpperBound().getType() = v.getUpperBoundType()
476476
}
477477

478-
private RefType getAnExplicitTypeArgument(TypeVariable v) {
478+
private Type getAnExplicitTypeArgument(TypeVariable v) {
479479
exists(GenericCallable gen, MethodAccess call, int i |
480480
this = call and
481481
gen = call.getCallee() and
@@ -485,8 +485,8 @@ class GenericCall extends Call {
485485
}
486486

487487
/** Gets a type argument of the call for the given `TypeVariable`. */
488-
RefType getATypeArgument(TypeVariable v) {
489-
result = this.getAnExplicitTypeArgument(v)
488+
Type getATypeArgument(TypeVariable v) {
489+
result = getAnExplicitTypeArgument(v)
490490
or
491491
not exists(this.getAnExplicitTypeArgument(v)) and
492492
result = this.getAnInferredTypeArgument(v)
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
[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)
2+
Relevant element: argumentid=11
3+
Full ID for 11: @"type;int"
4+
Relevant element: parentid=132
5+
Full ID for 132: @"class;foo.bar.C1;(11);(11)". The ID may expand to @"class;foo.bar.C1;{@"type;int"};{@"type;int"}"
6+
[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)
7+
Relevant element: argumentid=11
8+
Full ID for 11: @"type;int"
9+
Relevant element: parentid=132
10+
Full ID for 132: @"class;foo.bar.C1;(11);(11)". The ID may expand to @"class;foo.bar.C1;{@"type;int"};{@"type;int"}"
11+
[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)
12+
Relevant element: argumentid=11
13+
Full ID for 11: @"type;int"
14+
Relevant element: parentid=157
15+
Full ID for 157: @"class;foo.bar.C1;(13);(11)". The ID may expand to @"class;foo.bar.C1;{@"type;string"};{@"type;int"}"
16+
[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)
17+
Relevant element: argumentid=13
18+
Full ID for 13: @"type;string"
19+
Relevant element: parentid=146
20+
Full ID for 146: @"class;foo.bar.C1;(13);(13)". The ID may expand to @"class;foo.bar.C1;{@"type;string"};{@"type;string"}"
21+
[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)
22+
Relevant element: argumentid=13
23+
Full ID for 13: @"type;string"
24+
Relevant element: parentid=157
25+
Full ID for 157: @"class;foo.bar.C1;(13);(11)". The ID may expand to @"class;foo.bar.C1;{@"type;string"};{@"type;int"}"
26+
[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)
27+
Relevant element: argumentid=13
28+
Full ID for 13: @"type;string"
29+
Relevant element: parentid=146
30+
Full ID for 146: @"class;foo.bar.C1;(13);(13)". The ID may expand to @"class;foo.bar.C1;{@"type;string"};{@"type;string"}"
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
genericType
2+
| generics.kt:11:1:11:19 | C0 | generics.kt:11:15:11:15 | V | 0 |
3+
| generics.kt:13:1:18:1 | C1 | generics.kt:13:10:13:10 | T | 0 |
4+
| generics.kt:13:1:18:1 | C1 | generics.kt:13:13:13:13 | W | 1 |
5+
parameterizedType
6+
| generics.kt:11:1:11:19 | C0 | generics.kt:11:1:11:19 | C0 | 0 | generics.kt:7:6:7:6 | S |
7+
| generics.kt:11:1:11:19 | C0 | generics.kt:11:1:11:19 | C0 | 0 | generics.kt:11:15:11:15 | V |
8+
| generics.kt:11:1:11:19 | C0 | generics.kt:11:1:11:19 | C0 | 0 | generics.kt:13:13:13:13 | W |
9+
| generics.kt:13:1:18:1 | C1 | generics.kt:13:1:18:1 | C1 | 0 | file://:0:0:0:0 | int |
10+
| generics.kt:13:1:18:1 | C1 | generics.kt:13:1:18:1 | C1 | 0 | file://:0:0:0:0 | string |
11+
| generics.kt:13:1:18:1 | C1 | generics.kt:13:1:18:1 | C1 | 0 | file://:0:0:0:0 | string |
12+
| generics.kt:13:1:18:1 | C1 | generics.kt:13:1:18:1 | C1 | 0 | generics.kt:13:10:13:10 | T |
13+
| generics.kt:13:1:18:1 | C1 | generics.kt:13:1:18:1 | C1 | 0 | generics.kt:15:10:15:10 | U |
14+
| generics.kt:13:1:18:1 | C1 | generics.kt:13:1:18:1 | C1 | 1 | file://:0:0:0:0 | int |
15+
| generics.kt:13:1:18:1 | C1 | generics.kt:13:1:18:1 | C1 | 1 | file://:0:0:0:0 | int |
16+
| generics.kt:13:1:18:1 | C1 | generics.kt:13:1:18:1 | C1 | 1 | file://:0:0:0:0 | string |
17+
| generics.kt:13:1:18:1 | C1 | generics.kt:13:1:18:1 | C1 | 1 | generics.kt:13:13:13:13 | W |
18+
| generics.kt:13:1:18:1 | C1 | generics.kt:13:1:18:1 | C1 | 1 | generics.kt:15:10:15:10 | U |
19+
genericFunction
20+
| generics.kt:3:1:5:1 | f0 | generics.kt:3:6:3:6 | S | 0 |
21+
| generics.kt:7:1:9:1 | f1 | generics.kt:7:6:7:6 | S | 0 |
22+
| generics.kt:15:5:17:5 | f2 | generics.kt:15:10:15:10 | U | 0 |
23+
| generics.kt:21:5:21:23 | f4 | generics.kt:21:10:21:10 | P | 0 |
24+
genericCall
25+
| generics.kt:27:17:27:22 | f2(...) | generics.kt:15:10:15:10 | U | file://:0:0:0:0 | string |
26+
| generics.kt:30:17:30:21 | f2(...) | generics.kt:15:10:15:10 | U | file://:0:0:0:0 | int |
27+
| generics.kt:32:8:32:12 | f4(...) | generics.kt:21:10:21:10 | P | file://:0:0:0:0 | int |
Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,33 @@
11
package foo.bar
22

3-
fun <S> Int.f(s: S): S {
3+
fun <S> Int.f0(s: S): S {
44
return s
55
}
66

7-
class C1<T>(val t: T) {
7+
fun <S> Int.f1(s: S): C0<S>? {
8+
return null
9+
}
10+
11+
open class C0<V> {}
12+
13+
class C1<T, W>(val t: T) : C0<W>() {
814
fun f1(t: T) {}
9-
fun <U> f2(u: U) {}
15+
fun <U> f2(u: U): C1<U, U> {
16+
return C1<U, U>(u)
17+
}
1018
}
19+
20+
class C2() {
21+
fun <P> f4(p: P) {}
22+
}
23+
24+
fun m() {
25+
val c1 = C1<Int, Int>(1)
26+
c1.f1(2)
27+
val x1 = c1.f2("")
28+
val c2 = C1<String, Int>("")
29+
c2.f1("a")
30+
val x2 = c2.f2(3)
31+
val c3 = C2()
32+
c3.f4(5)
33+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import java
2+
3+
query predicate genericType(GenericType t, TypeVariable tv, int i) { t.getTypeParameter(i) = tv }
4+
5+
query predicate parameterizedType(ParameterizedType t, GenericType gt, int i, RefType ta) {
6+
t.getGenericType() = gt and t.getTypeArgument(i) = ta
7+
}
8+
9+
query predicate genericFunction(GenericCallable c, TypeVariable tv, int i) {
10+
c.getTypeParameter(i) = tv
11+
}
12+
13+
query predicate genericCall(GenericCall c, TypeVariable tv, Type t) { c.getATypeArgument(tv) = t }

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

Lines changed: 0 additions & 5 deletions
This file was deleted.

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

Lines changed: 0 additions & 5 deletions
This file was deleted.

0 commit comments

Comments
 (0)