Skip to content

Commit ecfbd5e

Browse files
authored
Merge pull request #10674 from tamasvajk/kotlin-implements
Kotlin: extract `implInterface`
2 parents ad83fc8 + d286136 commit ecfbd5e

File tree

5 files changed

+131
-11
lines changed

5 files changed

+131
-11
lines changed

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

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -266,10 +266,9 @@ open class KotlinFileExtractor(
266266
val pkg = c.packageFqName?.asString() ?: ""
267267
val cls = classLabelResults.shortName
268268
val pkgId = extractPackage(pkg)
269-
val kind = c.kind
270269
// TODO: There's lots of duplication between this and extractClassSource.
271270
// Can we share it?
272-
if(kind == ClassKind.INTERFACE || kind == ClassKind.ANNOTATION_CLASS) {
271+
if (c.isInterfaceLike) {
273272
val interfaceId = id.cast<DbInterface>()
274273
val sourceInterfaceId = useClassSource(c).cast<DbInterface>()
275274
tw.writeInterfaces(interfaceId, cls, pkgId, sourceInterfaceId)
@@ -278,6 +277,7 @@ open class KotlinFileExtractor(
278277
val sourceClassId = useClassSource(c).cast<DbClass>()
279278
tw.writeClasses(classId, cls, pkgId, sourceClassId)
280279

280+
val kind = c.kind
281281
if (kind == ClassKind.ENUM_CLASS) {
282282
tw.writeIsEnumType(classId)
283283
} else if (kind != ClassKind.CLASS && kind != ClassKind.OBJECT) {
@@ -405,14 +405,14 @@ open class KotlinFileExtractor(
405405
val pkg = c.packageFqName?.asString() ?: ""
406406
val cls = if (c.isAnonymousObject) "" else c.name.asString()
407407
val pkgId = extractPackage(pkg)
408-
val kind = c.kind
409-
if (kind == ClassKind.INTERFACE || kind == ClassKind.ANNOTATION_CLASS) {
408+
if (c.isInterfaceLike) {
410409
val interfaceId = id.cast<DbInterface>()
411410
tw.writeInterfaces(interfaceId, cls, pkgId, interfaceId)
412411
} else {
413412
val classId = id.cast<DbClass>()
414413
tw.writeClasses(classId, cls, pkgId, classId)
415414

415+
val kind = c.kind
416416
if (kind == ClassKind.ENUM_CLASS) {
417417
tw.writeIsEnumType(classId)
418418
} else if (kind != ClassKind.CLASS && kind != ClassKind.OBJECT) {
@@ -4955,7 +4955,7 @@ open class KotlinFileExtractor(
49554955

49564956
addModifiers(id, "final")
49574957
addVisibilityModifierToLocalOrAnonymousClass(id)
4958-
extractClassSupertypes(superTypes, listOf(), id, inReceiverContext = true)
4958+
extractClassSupertypes(superTypes, listOf(), id, isInterface = false, inReceiverContext = true)
49594959

49604960
extractEnclosingClass(declarationParent, id, null, locId, listOf())
49614961

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

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1496,10 +1496,10 @@ open class KotlinUsesExtractor(
14961496
* Argument `inReceiverContext` will be passed onto the `useClassInstance` invocation for each supertype.
14971497
*/
14981498
fun extractClassSupertypes(c: IrClass, id: Label<out DbReftype>, mode: ExtractSupertypesMode = ExtractSupertypesMode.Unbound, inReceiverContext: Boolean = false) {
1499-
extractClassSupertypes(c.superTypes, c.typeParameters, id, mode, inReceiverContext)
1499+
extractClassSupertypes(c.superTypes, c.typeParameters, id, c.isInterfaceLike, mode, inReceiverContext)
15001500
}
15011501

1502-
fun extractClassSupertypes(superTypes: List<IrType>, typeParameters: List<IrTypeParameter>, id: Label<out DbReftype>, mode: ExtractSupertypesMode = ExtractSupertypesMode.Unbound, inReceiverContext: Boolean = false) {
1502+
fun extractClassSupertypes(superTypes: List<IrType>, typeParameters: List<IrTypeParameter>, id: Label<out DbReftype>, isInterface: Boolean, mode: ExtractSupertypesMode = ExtractSupertypesMode.Unbound, inReceiverContext: Boolean = false) {
15031503
// Note we only need to substitute type args here because it is illegal to directly extend a type variable.
15041504
// (For example, we can't have `class A<E> : E`, but can have `class A<E> : Comparable<E>`)
15051505
val subbedSupertypes = when(mode) {
@@ -1514,12 +1514,15 @@ open class KotlinUsesExtractor(
15141514
for(t in subbedSupertypes) {
15151515
when(t) {
15161516
is IrSimpleType -> {
1517-
val owner = t.classifier.owner
1518-
when (owner) {
1517+
when (val owner = t.classifier.owner) {
15191518
is IrClass -> {
15201519
val typeArgs = if (t.arguments.isNotEmpty() && mode is ExtractSupertypesMode.Raw) null else t.arguments
15211520
val l = useClassInstance(owner, typeArgs, inReceiverContext).typeResult.id
1522-
tw.writeExtendsReftype(id, l)
1521+
if (isInterface || !owner.isInterfaceLike) {
1522+
tw.writeExtendsReftype(id, l)
1523+
} else {
1524+
tw.writeImplInterface(id.cast(), l.cast())
1525+
}
15231526
}
15241527
else -> {
15251528
logger.error("Unexpected simple type supertype: " + t.javaClass + ": " + t.render())
Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
package com.github.codeql.utils
22

3+
import org.jetbrains.kotlin.descriptors.ClassKind
34
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
5+
import org.jetbrains.kotlin.ir.declarations.IrClass
46
import org.jetbrains.kotlin.ir.declarations.IrFunction
57

68
fun IrFunction.isLocalFunction(): Boolean {
79
return this.visibility == DescriptorVisibilities.LOCAL
8-
}
10+
}
11+
12+
val IrClass.isInterfaceLike get() = kind == ClassKind.INTERFACE || kind == ClassKind.ANNOTATION_CLASS

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

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,107 @@
1+
extendsOrImplements
2+
| classes.kt:2:1:2:18 | ClassOne | file://<external>/Object.class:0:0:0:0 | Object | extends |
3+
| classes.kt:4:1:6:1 | ClassTwo | file://<external>/Object.class:0:0:0:0 | Object | extends |
4+
| classes.kt:8:1:10:1 | ClassThree | file://<external>/Object.class:0:0:0:0 | Object | extends |
5+
| classes.kt:12:1:15:1 | ClassFour | classes.kt:8:1:10:1 | ClassThree | extends |
6+
| classes.kt:17:1:18:1 | ClassFive | classes.kt:12:1:15:1 | ClassFour | extends |
7+
| classes.kt:20:1:22:1 | IF1 | file://<external>/Object.class:0:0:0:0 | Object | extends |
8+
| classes.kt:24:1:26:1 | IF2 | file://<external>/Object.class:0:0:0:0 | Object | extends |
9+
| classes.kt:28:1:30:1 | ClassSix | classes.kt:12:1:15:1 | ClassFour | extends |
10+
| classes.kt:28:1:30:1 | ClassSix | classes.kt:20:1:22:1 | IF1 | implements |
11+
| classes.kt:28:1:30:1 | ClassSix | classes.kt:24:1:26:1 | IF2 | implements |
12+
| classes.kt:34:1:47:1 | ClassSeven | file://<external>/Object.class:0:0:0:0 | Object | extends |
13+
| classes.kt:49:1:51:1 | Direction | file://<external>/Enum.class:0:0:0:0 | Enum<Direction> | extends |
14+
| classes.kt:53:1:57:1 | Color | file://<external>/Enum.class:0:0:0:0 | Enum<Color> | extends |
15+
| classes.kt:59:1:59:23 | Interface1 | file://<external>/Object.class:0:0:0:0 | Object | extends |
16+
| classes.kt:60:1:60:23 | Interface2 | file://<external>/Object.class:0:0:0:0 | Object | extends |
17+
| classes.kt:61:1:61:26 | Interface3 | file://<external>/Object.class:0:0:0:0 | Object | extends |
18+
| classes.kt:63:1:91:1 | Class1 | file://<external>/Object.class:0:0:0:0 | Object | extends |
19+
| classes.kt:66:20:66:54 | new Object(...) { ... } | classes.kt:59:1:59:23 | Interface1 | implements |
20+
| classes.kt:66:20:66:54 | new Object(...) { ... } | classes.kt:60:1:60:23 | Interface2 | implements |
21+
| classes.kt:68:20:68:74 | new Object(...) { ... } | classes.kt:59:1:59:23 | Interface1 | implements |
22+
| classes.kt:68:20:68:74 | new Object(...) { ... } | classes.kt:60:1:60:23 | Interface2 | implements |
23+
| classes.kt:68:20:68:74 | new Object(...) { ... } | file://<external>/Interface3.class:0:0:0:0 | Interface3<String> | implements |
24+
| classes.kt:72:16:77:10 | new Object(...) { ... } | classes.kt:59:1:59:23 | Interface1 | implements |
25+
| classes.kt:72:16:77:10 | new Object(...) { ... } | classes.kt:60:1:60:23 | Interface2 | implements |
26+
| classes.kt:75:24:75:33 | new Object(...) { ... } | file://<external>/Object.class:0:0:0:0 | Object | extends |
27+
| classes.kt:81:16:81:38 | new Interface1(...) { ... } | classes.kt:59:1:59:23 | Interface1 | implements |
28+
| classes.kt:85:16:85:25 | new Object(...) { ... } | file://<external>/Object.class:0:0:0:0 | Object | extends |
29+
| classes.kt:89:16:89:44 | new Interface3<Integer>(...) { ... } | file://<external>/Interface3.class:0:0:0:0 | Interface3<Integer> | implements |
30+
| classes.kt:93:1:93:26 | pulicClass | file://<external>/Object.class:0:0:0:0 | Object | extends |
31+
| classes.kt:94:1:94:29 | privateClass | file://<external>/Object.class:0:0:0:0 | Object | extends |
32+
| classes.kt:95:1:95:31 | internalClass | file://<external>/Object.class:0:0:0:0 | Object | extends |
33+
| classes.kt:96:1:96:34 | noExplicitVisibilityClass | file://<external>/Object.class:0:0:0:0 | Object | extends |
34+
| classes.kt:98:1:104:1 | nestedClassVisibilities | file://<external>/Object.class:0:0:0:0 | Object | extends |
35+
| classes.kt:99:5:99:36 | pulicNestedClass | file://<external>/Object.class:0:0:0:0 | Object | extends |
36+
| classes.kt:100:5:100:43 | protectedNestedClass | file://<external>/Object.class:0:0:0:0 | Object | extends |
37+
| classes.kt:101:5:101:39 | privateNestedClass | file://<external>/Object.class:0:0:0:0 | Object | extends |
38+
| classes.kt:102:5:102:41 | internalNestedClass | file://<external>/Object.class:0:0:0:0 | Object | extends |
39+
| classes.kt:103:5:103:44 | noExplicitVisibilityNestedClass | file://<external>/Object.class:0:0:0:0 | Object | extends |
40+
| classes.kt:106:1:106:27 | sealedClass | file://<external>/Object.class:0:0:0:0 | Object | extends |
41+
| classes.kt:107:1:107:23 | openClass | file://<external>/Object.class:0:0:0:0 | Object | extends |
42+
| classes.kt:109:1:136:1 | C1 | file://<external>/Object.class:0:0:0:0 | Object | extends |
43+
| classes.kt:111:9:113:9 | Local1 | file://<external>/Object.class:0:0:0:0 | Object | extends |
44+
| classes.kt:118:9:123:9 | | file://<external>/Object.class:0:0:0:0 | Object | extends |
45+
| classes.kt:119:13:121:13 | Local2 | file://<external>/Object.class:0:0:0:0 | Object | extends |
46+
| classes.kt:127:16:134:9 | new Object(...) { ... } | file://<external>/Object.class:0:0:0:0 | Object | extends |
47+
| classes.kt:129:17:131:17 | Local3 | file://<external>/Object.class:0:0:0:0 | Object | extends |
48+
| classes.kt:138:1:148:1 | Cl0 | file://<external>/Object.class:0:0:0:0 | Object | extends |
49+
| classes.kt:140:9:146:9 | | file://<external>/Object.class:0:0:0:0 | Object | extends |
50+
| classes.kt:141:13:145:13 | Cl1 | file://<external>/Object.class:0:0:0:0 | Object | extends |
51+
| classes.kt:150:1:156:1 | Cl00 | file://<external>/Object.class:0:0:0:0 | Object | extends |
52+
| classes.kt:151:5:155:5 | Cl01 | file://<external>/Object.class:0:0:0:0 | Object | extends |
53+
| classes.kt:159:5:159:14 | X | file://<external>/Object.class:0:0:0:0 | Object | extends |
54+
| classes.kt:162:13:162:22 | new Object(...) { ... } | file://<external>/Object.class:0:0:0:0 | Object | extends |
55+
| file://<external>/C1$<no name provided>$Local3.class:0:0:0:0 | Local3<Integer> | file://<external>/Object.class:0:0:0:0 | Object | extends |
56+
| file://<external>/C1$Local1.class:0:0:0:0 | Local1<Integer> | file://<external>/Object.class:0:0:0:0 | Object | extends |
57+
| file://<external>/C1$Local2.class:0:0:0:0 | Local2<Integer> | file://<external>/Object.class:0:0:0:0 | Object | extends |
58+
| file://<external>/Generic.class:0:0:0:0 | Generic<Integer> | file://<external>/Object.class:0:0:0:0 | Object | extends |
59+
| file://<external>/Generic.class:0:0:0:0 | Generic<String> | file://<external>/Object.class:0:0:0:0 | Object | extends |
60+
| file://<external>/Interface3.class:0:0:0:0 | Interface3<Integer> | file://<external>/Object.class:0:0:0:0 | Object | extends |
61+
| file://<external>/Interface3.class:0:0:0:0 | Interface3<String> | file://<external>/Object.class:0:0:0:0 | Object | extends |
62+
| file://<external>/Outer$C0.class:0:0:0:0 | C0<String> | file://<external>/Object.class:0:0:0:0 | Object | extends |
63+
| file://<external>/Outer$C0.class:0:0:0:0 | C0<U2> | file://<external>/Object.class:0:0:0:0 | Object | extends |
64+
| file://<external>/Outer$C1.class:0:0:0:0 | C1<String> | file://<external>/Object.class:0:0:0:0 | Object | extends |
65+
| file://<external>/Outer$C1.class:0:0:0:0 | C1<U2> | file://<external>/Object.class:0:0:0:0 | Object | extends |
66+
| file://<external>/Outer$C1.class:0:0:0:0 | C1<U3> | file://<external>/Object.class:0:0:0:0 | Object | extends |
67+
| file://<external>/SuperChain1.class:0:0:0:0 | SuperChain1<T3,String> | file://<external>/Object.class:0:0:0:0 | Object | extends |
68+
| file://<external>/SuperChain1.class:0:0:0:0 | SuperChain1<T5,String> | file://<external>/Object.class:0:0:0:0 | Object | extends |
69+
| file://<external>/SuperChain2.class:0:0:0:0 | SuperChain2<T5,String> | file://<external>/SuperChain1.class:0:0:0:0 | SuperChain1<T5,String> | extends |
70+
| generic_anonymous.kt:1:1:9:1 | Generic | file://<external>/Object.class:0:0:0:0 | Object | extends |
71+
| generic_anonymous.kt:3:19:5:3 | new Object(...) { ... } | file://<external>/Object.class:0:0:0:0 | Object | extends |
72+
| generic_anonymous.kt:15:1:33:1 | Outer | file://<external>/Object.class:0:0:0:0 | Object | extends |
73+
| generic_anonymous.kt:16:5:18:5 | C0 | file://<external>/Object.class:0:0:0:0 | Object | extends |
74+
| generic_anonymous.kt:20:5:22:5 | C1 | file://<external>/Object.class:0:0:0:0 | Object | extends |
75+
| generic_anonymous.kt:25:9:31:9 | | file://<external>/Object.class:0:0:0:0 | Object | extends |
76+
| generic_anonymous.kt:26:13:26:37 | new Object(...) { ... } | file://<external>/Outer$C0.class:0:0:0:0 | C0<U2> | implements |
77+
| generic_anonymous.kt:26:13:26:37 | new Object(...) { ... } | file://<external>/Outer$C1.class:0:0:0:0 | C1<U3> | implements |
78+
| generic_anonymous.kt:27:13:27:37 | new Object(...) { ... } | file://<external>/Outer$C0.class:0:0:0:0 | C0<U2> | implements |
79+
| generic_anonymous.kt:27:13:27:37 | new Object(...) { ... } | file://<external>/Outer$C1.class:0:0:0:0 | C1<U2> | implements |
80+
| generic_anonymous.kt:28:13:28:41 | new Object(...) { ... } | file://<external>/Outer$C0.class:0:0:0:0 | C0<U2> | implements |
81+
| generic_anonymous.kt:28:13:28:41 | new Object(...) { ... } | file://<external>/Outer$C1.class:0:0:0:0 | C1<String> | implements |
82+
| generic_anonymous.kt:29:13:29:29 | new C0<U2>(...) { ... } | file://<external>/Outer$C0.class:0:0:0:0 | C0<U2> | implements |
83+
| generic_anonymous.kt:30:13:30:33 | new C0<String>(...) { ... } | file://<external>/Outer$C0.class:0:0:0:0 | C0<String> | implements |
84+
| localClassField.kt:1:1:11:1 | A | file://<external>/Object.class:0:0:0:0 | Object | extends |
85+
| localClassField.kt:3:9:3:19 | L | file://<external>/Object.class:0:0:0:0 | Object | extends |
86+
| localClassField.kt:8:9:8:19 | L | file://<external>/Object.class:0:0:0:0 | Object | extends |
87+
| local_anonymous.kt:3:1:36:1 | Class1 | file://<external>/Object.class:0:0:0:0 | Object | extends |
88+
| local_anonymous.kt:5:16:7:9 | new Object(...) { ... } | file://<external>/Object.class:0:0:0:0 | Object | extends |
89+
| local_anonymous.kt:11:9:11:24 | | file://<external>/Object.class:0:0:0:0 | Object | extends |
90+
| local_anonymous.kt:16:23:16:49 | new Function2<Integer,Integer,Integer>(...) { ... } | file://<external>/Function2.class:0:0:0:0 | Function2<Integer,Integer,Integer> | implements |
91+
| local_anonymous.kt:16:23:16:49 | new Function2<Integer,Integer,Integer>(...) { ... } | file://<external>/Object.class:0:0:0:0 | Object | extends |
92+
| local_anonymous.kt:17:23:17:49 | new Function2<Integer,Integer,Integer>(...) { ... } | file://<external>/Function2.class:0:0:0:0 | Function2<Integer,Integer,Integer> | implements |
93+
| local_anonymous.kt:17:23:17:49 | new Function2<Integer,Integer,Integer>(...) { ... } | file://<external>/Object.class:0:0:0:0 | Object | extends |
94+
| local_anonymous.kt:21:21:21:31 | new Function1<Class1,Unit>(...) { ... } | file://<external>/Function1.class:0:0:0:0 | Function1<Class1,Unit> | implements |
95+
| local_anonymous.kt:21:21:21:31 | new Function1<Class1,Unit>(...) { ... } | file://<external>/FunctionReference.class:0:0:0:0 | FunctionReference | extends |
96+
| local_anonymous.kt:25:9:25:27 | LocalClass | file://<external>/Object.class:0:0:0:0 | Object | extends |
97+
| local_anonymous.kt:29:31:35:5 | new Object(...) { ... } | file://<external>/Object.class:0:0:0:0 | Object | extends |
98+
| local_anonymous.kt:38:1:38:23 | Interface2 | file://<external>/Object.class:0:0:0:0 | Object | extends |
99+
| local_anonymous.kt:39:1:45:1 | Class2 | file://<external>/Object.class:0:0:0:0 | Object | extends |
100+
| local_anonymous.kt:40:14:44:5 | new Interface2(...) { ... } | local_anonymous.kt:38:1:38:23 | Interface2 | implements |
101+
| superChain.kt:1:1:1:33 | SuperChain1 | file://<external>/Object.class:0:0:0:0 | Object | extends |
102+
| superChain.kt:2:1:2:60 | SuperChain2 | file://<external>/SuperChain1.class:0:0:0:0 | SuperChain1<T3,String> | extends |
103+
| superChain.kt:3:1:3:60 | SuperChain3 | file://<external>/SuperChain2.class:0:0:0:0 | SuperChain2<T5,String> | extends |
104+
#select
1105
| classes.kt:2:1:2:18 | ClassOne | file://<external>/Object.class:0:0:0:0 | Object |
2106
| classes.kt:4:1:6:1 | ClassTwo | file://<external>/Object.class:0:0:0:0 | Object |
3107
| classes.kt:8:1:10:1 | ClassThree | file://<external>/Object.class:0:0:0:0 | Object |

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,12 @@ where
1616
c.getSourceDeclaration().fromSource() and
1717
superType = c.getASupertype()
1818
select c, superType
19+
20+
query predicate extendsOrImplements(ClassOrInterface c, Type superType, string kind) {
21+
c.getSourceDeclaration().fromSource() and
22+
(
23+
extendsReftype(c, superType) and kind = "extends"
24+
or
25+
implInterface(c, superType) and kind = "implements"
26+
)
27+
}

0 commit comments

Comments
 (0)