Skip to content

Commit f9ba190

Browse files
authored
Merge pull request #9830 from smowton/smowton/fix/kotlin-annotation-class-accessors
Kotlin: annotation properties should be java.lang.Class not KClass
2 parents a8197b2 + 2a2b939 commit f9ba190

File tree

7 files changed

+198
-7
lines changed

7 files changed

+198
-7
lines changed

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

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -628,14 +628,16 @@ open class KotlinFileExtractor(
628628
private fun extractValueParameter(vp: IrValueParameter, parent: Label<out DbCallable>, idx: Int, typeSubstitution: TypeSubstitution?, parentSourceDeclaration: Label<out DbCallable>, classTypeArgsIncludingOuterClasses: List<IrTypeArgument>?, extractTypeAccess: Boolean, locOverride: Label<DbLocation>? = null): TypeResults {
629629
with("value parameter", vp) {
630630
val location = locOverride ?: getLocation(vp, classTypeArgsIncludingOuterClasses)
631-
val maybeErasedType = (vp.parent as? IrFunction)?.let {
631+
val maybeAlteredType = (vp.parent as? IrFunction)?.let {
632632
if (overridesCollectionsMethodWithAlteredParameterTypes(it))
633633
eraseCollectionsMethodParameterType(vp.type, it.name.asString(), idx)
634+
else if ((vp.parent as? IrConstructor)?.parentClassOrNull?.kind == ClassKind.ANNOTATION_CLASS)
635+
kClassToJavaClass(vp.type)
634636
else
635637
null
636638
} ?: vp.type
637639
val javaType = (vp.parent as? IrFunction)?.let { getJavaCallable(it)?.let { jCallable -> getJavaValueParameterType(jCallable, idx) } }
638-
val typeWithWildcards = addJavaLoweringWildcards(maybeErasedType, !hasWildcardSuppressionAnnotation(vp), javaType)
640+
val typeWithWildcards = addJavaLoweringWildcards(maybeAlteredType, !hasWildcardSuppressionAnnotation(vp), javaType)
639641
val substitutedType = typeSubstitution?.let { it(typeWithWildcards, TypeContext.OTHER, pluginContext) } ?: typeWithWildcards
640642
val id = useValueParameter(vp, parent)
641643
if (extractTypeAccess) {
@@ -734,14 +736,17 @@ open class KotlinFileExtractor(
734736
val initializer: IrExpressionBody?
735737
val lhsType: TypeResults?
736738
val vId: Label<out DbVariable>?
739+
val isAnnotationClassField: Boolean
737740
if (f is IrField) {
738741
static = f.isStatic
739742
initializer = f.initializer
740-
lhsType = useType(f.type)
743+
isAnnotationClassField = isAnnotationClassField(f)
744+
lhsType = useType(if (isAnnotationClassField) kClassToJavaClass(f.type) else f.type)
741745
vId = useField(f)
742746
} else if (f is IrEnumEntry) {
743747
static = true
744748
initializer = f.initializerExpression
749+
isAnnotationClassField = false
745750
lhsType = getEnumEntryType(f)
746751
if (lhsType == null) {
747752
return
@@ -762,7 +767,7 @@ open class KotlinFileExtractor(
762767
tw.writeStmts_exprstmt(stmtId, blockAndFunctionId.first, idx++, blockAndFunctionId.second)
763768
tw.writeHasLocation(stmtId, declLocId)
764769
val assignmentId = tw.getFreshIdLabel<DbAssignexpr>()
765-
val type = useType(expr.type)
770+
val type = useType(if (isAnnotationClassField) kClassToJavaClass(expr.type) else expr.type)
766771
tw.writeExprs_assignexpr(assignmentId, type.javaResult.id, stmtId, 0)
767772
tw.writeExprsKotlinType(assignmentId, type.kotlinResult.id)
768773
tw.writeHasLocation(assignmentId, declLocId)
@@ -936,7 +941,8 @@ open class KotlinFileExtractor(
936941
with("field", f) {
937942
DeclarationStackAdjuster(f).use {
938943
val fNameSuffix = getExtensionReceiverType(f)?.let { it.classFqName?.asString()?.replace(".", "$$") } ?: ""
939-
return extractField(useField(f), "${f.name.asString()}$fNameSuffix", f.type, parentId, tw.getLocation(f), f.visibility, f, isExternalDeclaration(f), f.isFinal)
944+
val extractType = if (isAnnotationClassField(f)) kClassToJavaClass(f.type) else f.type
945+
return extractField(useField(f), "${f.name.asString()}$fNameSuffix", extractType, parentId, tw.getLocation(f), f.visibility, f, isExternalDeclaration(f), f.isFinal)
940946
}
941947
}
942948
}
@@ -2917,14 +2923,17 @@ open class KotlinFileExtractor(
29172923
if (owner is IrValueParameter && owner.index == -1 && !owner.isExtensionReceiver()) {
29182924
extractThisAccess(e, exprParent, callable)
29192925
} else {
2920-
extractVariableAccess(useValueDeclaration(owner), e.type, tw.getLocation(e), exprParent.parent, exprParent.idx, callable, exprParent.enclosingStmt)
2926+
val isAnnotationClassParameter = ((owner as? IrValueParameter)?.parent as? IrConstructor)?.parentClassOrNull?.kind == ClassKind.ANNOTATION_CLASS
2927+
val extractType = if (isAnnotationClassParameter) kClassToJavaClass(e.type) else e.type
2928+
extractVariableAccess(useValueDeclaration(owner), extractType, tw.getLocation(e), exprParent.parent, exprParent.idx, callable, exprParent.enclosingStmt)
29212929
}
29222930
}
29232931
is IrGetField -> {
29242932
val exprParent = parent.expr(e, callable)
29252933
val owner = tryReplaceAndroidSyntheticField(e.symbol.owner)
29262934
val locId = tw.getLocation(e)
2927-
extractVariableAccess(useField(owner), e.type, locId, exprParent.parent, exprParent.idx, callable, exprParent.enclosingStmt).also { id ->
2935+
val fieldType = if (isAnnotationClassField(owner)) kClassToJavaClass(e.type) else e.type
2936+
extractVariableAccess(useField(owner), fieldType, locId, exprParent.parent, exprParent.idx, callable, exprParent.enclosingStmt).also { id ->
29282937
val receiver = e.receiver
29292938
if (receiver != null) {
29302939
extractExpressionExpr(receiver, callable, id, -1, exprParent.enclosingStmt)

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

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1103,7 +1103,56 @@ open class KotlinUsesExtractor(
11031103
return "@\"$prefix;{$parentId}.$name($paramTypeIds){$returnTypeId}${typeArgSuffix}\""
11041104
}
11051105

1106+
val javaLangClass by lazy {
1107+
val result = pluginContext.referenceClass(FqName("java.lang.Class"))?.owner
1108+
result?.let { extractExternalClassLater(it) }
1109+
result
1110+
}
1111+
1112+
fun kClassToJavaClass(t: IrType): IrType {
1113+
when(t) {
1114+
is IrSimpleType -> {
1115+
if (t.classifier == pluginContext.irBuiltIns.kClassClass) {
1116+
javaLangClass?.let { jlc ->
1117+
return jlc.symbol.typeWithArguments(t.arguments)
1118+
}
1119+
} else {
1120+
t.classOrNull?.let { tCls ->
1121+
if (t.isArray() || t.isNullableArray()) {
1122+
(t.arguments.singleOrNull() as? IrTypeProjection)?.let { elementTypeArg ->
1123+
val elementType = elementTypeArg.type
1124+
val replacedElementType = kClassToJavaClass(elementType)
1125+
if (replacedElementType !== elementType) {
1126+
val newArg = makeTypeProjection(replacedElementType, elementTypeArg.variance)
1127+
return tCls.typeWithArguments(listOf(newArg)).codeQlWithHasQuestionMark(t.isNullableArray())
1128+
}
1129+
}
1130+
}
1131+
}
1132+
}
1133+
}
1134+
}
1135+
return t
1136+
}
1137+
1138+
fun isAnnotationClassField(f: IrField) =
1139+
f.correspondingPropertySymbol?.let {
1140+
isAnnotationClassProperty(it)
1141+
} ?: false
1142+
1143+
private fun isAnnotationClassProperty(p: IrPropertySymbol) =
1144+
p.owner.parentClassOrNull?.kind == ClassKind.ANNOTATION_CLASS
1145+
11061146
fun getAdjustedReturnType(f: IrFunction) : IrType {
1147+
// Replace annotation val accessor types as needed:
1148+
(f as? IrSimpleFunction)?.correspondingPropertySymbol?.let {
1149+
if (isAnnotationClassProperty(it) && f == it.owner.getter) {
1150+
val replaced = kClassToJavaClass(f.returnType)
1151+
if (replaced != f.returnType)
1152+
return replaced
1153+
}
1154+
}
1155+
11071156
// The return type of `java.util.concurrent.ConcurrentHashMap<K,V>.keySet/0` is defined as `Set<K>` in the stubs inside the Android SDK.
11081157
// This does not match the Java SDK return type: `ConcurrentHashMap.KeySetView<K,V>`, so it's adjusted here.
11091158
// This is a deliberate change in the Android SDK: https://github.com/AndroidSDKSources/android-sdk-sources-for-api-level-31/blob/2c56b25f619575bea12f9c5520ed2259620084ac/java/util/concurrent/ConcurrentHashMap.java#L1244-L1249
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
test.kt:
2+
# 0| [CompilationUnit] test
3+
# 3| 1: [Interface] A
4+
# 3| 1: [Constructor] A
5+
#-----| 4: (Parameters)
6+
# 3| 0: [Parameter] c1
7+
# 3| 0: [TypeAccess] Class<?>
8+
# 3| 0: [WildcardTypeAccess] ? ...
9+
# 3| 1: [Parameter] c2
10+
# 3| 0: [TypeAccess] Class<? extends CharSequence>
11+
# 3| 0: [WildcardTypeAccess] ? ...
12+
# 3| 0: [TypeAccess] CharSequence
13+
# 3| 2: [Parameter] c3
14+
# 3| 0: [TypeAccess] Class<String>
15+
# 3| 0: [TypeAccess] String
16+
# 3| 3: [Parameter] c4
17+
# 3| 0: [TypeAccess] Class<?>[]
18+
# 3| 0: [TypeAccess] Class<?>
19+
# 3| 0: [WildcardTypeAccess] ? ...
20+
# 3| 5: [BlockStmt] { ... }
21+
# 3| 0: [SuperConstructorInvocationStmt] super(...)
22+
# 3| 1: [BlockStmt] { ... }
23+
# 3| 0: [ExprStmt] <Expr>;
24+
# 3| 0: [KtInitializerAssignExpr] ...=...
25+
# 3| 0: [VarAccess] c1
26+
# 3| 1: [ExprStmt] <Expr>;
27+
# 3| 0: [KtInitializerAssignExpr] ...=...
28+
# 3| 0: [VarAccess] c2
29+
# 3| 2: [ExprStmt] <Expr>;
30+
# 3| 0: [KtInitializerAssignExpr] ...=...
31+
# 3| 0: [VarAccess] c3
32+
# 3| 3: [ExprStmt] <Expr>;
33+
# 3| 0: [KtInitializerAssignExpr] ...=...
34+
# 3| 0: [VarAccess] c4
35+
# 3| 2: [FieldDeclaration] Class<?> c1;
36+
# 3| -1: [TypeAccess] Class<?>
37+
# 3| 0: [WildcardTypeAccess] ? ...
38+
# 3| 0: [VarAccess] c1
39+
# 3| 3: [Method] c1
40+
# 3| 3: [TypeAccess] Class<?>
41+
# 3| 0: [WildcardTypeAccess] ? ...
42+
# 3| 5: [BlockStmt] { ... }
43+
# 3| 0: [ReturnStmt] return ...
44+
# 3| 0: [VarAccess] this.c1
45+
# 3| -1: [ThisAccess] this
46+
# 3| 4: [FieldDeclaration] Class<? extends CharSequence> c2;
47+
# 3| -1: [TypeAccess] Class<? extends CharSequence>
48+
# 3| 0: [WildcardTypeAccess] ? ...
49+
# 3| 0: [TypeAccess] CharSequence
50+
# 3| 0: [VarAccess] c2
51+
# 3| 5: [Method] c2
52+
# 3| 3: [TypeAccess] Class<? extends CharSequence>
53+
# 3| 0: [WildcardTypeAccess] ? ...
54+
# 3| 0: [TypeAccess] CharSequence
55+
# 3| 5: [BlockStmt] { ... }
56+
# 3| 0: [ReturnStmt] return ...
57+
# 3| 0: [VarAccess] this.c2
58+
# 3| -1: [ThisAccess] this
59+
# 3| 6: [FieldDeclaration] Class<String> c3;
60+
# 3| -1: [TypeAccess] Class<String>
61+
# 3| 0: [TypeAccess] String
62+
# 3| 0: [VarAccess] c3
63+
# 3| 7: [Method] c3
64+
# 3| 3: [TypeAccess] Class<String>
65+
# 3| 0: [TypeAccess] String
66+
# 3| 5: [BlockStmt] { ... }
67+
# 3| 0: [ReturnStmt] return ...
68+
# 3| 0: [VarAccess] this.c3
69+
# 3| -1: [ThisAccess] this
70+
# 3| 8: [FieldDeclaration] Class<?>[] c4;
71+
# 3| -1: [TypeAccess] Class<?>[]
72+
# 3| 0: [TypeAccess] Class<?>
73+
# 3| 0: [WildcardTypeAccess] ? ...
74+
# 3| 0: [VarAccess] c4
75+
# 3| 9: [Method] c4
76+
# 3| 3: [TypeAccess] Class<?>[]
77+
# 3| 0: [TypeAccess] Class<?>
78+
# 3| 0: [WildcardTypeAccess] ? ...
79+
# 3| 5: [BlockStmt] { ... }
80+
# 3| 0: [ReturnStmt] return ...
81+
# 3| 0: [VarAccess] this.c4
82+
# 3| -1: [ThisAccess] this
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
semmle/code/java/PrintAst.ql
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
classExprs
2+
| test.kt:3:20:3:36 | ...=... | Class<?> |
3+
| test.kt:3:20:3:36 | Class<?> | Class<?> |
4+
| test.kt:3:20:3:36 | Class<?> | Class<?> |
5+
| test.kt:3:20:3:36 | Class<?> | Class<?> |
6+
| test.kt:3:20:3:36 | c1 | Class<?> |
7+
| test.kt:3:20:3:36 | c1 | Class<?> |
8+
| test.kt:3:20:3:36 | this.c1 | Class<?> |
9+
| test.kt:3:39:3:70 | ...=... | Class<? extends CharSequence> |
10+
| test.kt:3:39:3:70 | Class<? extends CharSequence> | Class<? extends CharSequence> |
11+
| test.kt:3:39:3:70 | Class<? extends CharSequence> | Class<? extends CharSequence> |
12+
| test.kt:3:39:3:70 | Class<? extends CharSequence> | Class<? extends CharSequence> |
13+
| test.kt:3:39:3:70 | c2 | Class<? extends CharSequence> |
14+
| test.kt:3:39:3:70 | c2 | Class<? extends CharSequence> |
15+
| test.kt:3:39:3:70 | this.c2 | Class<? extends CharSequence> |
16+
| test.kt:3:73:3:94 | ...=... | Class<String> |
17+
| test.kt:3:73:3:94 | Class<String> | Class<String> |
18+
| test.kt:3:73:3:94 | Class<String> | Class<String> |
19+
| test.kt:3:73:3:94 | Class<String> | Class<String> |
20+
| test.kt:3:73:3:94 | c3 | Class<String> |
21+
| test.kt:3:73:3:94 | c3 | Class<String> |
22+
| test.kt:3:73:3:94 | this.c3 | Class<String> |
23+
| test.kt:3:97:3:120 | ...=... | Class<?>[] |
24+
| test.kt:3:97:3:120 | Class<?> | Class<?> |
25+
| test.kt:3:97:3:120 | Class<?> | Class<?> |
26+
| test.kt:3:97:3:120 | Class<?> | Class<?> |
27+
| test.kt:3:97:3:120 | Class<?>[] | Class<?>[] |
28+
| test.kt:3:97:3:120 | Class<?>[] | Class<?>[] |
29+
| test.kt:3:97:3:120 | Class<?>[] | Class<?>[] |
30+
| test.kt:3:97:3:120 | c4 | Class<?>[] |
31+
| test.kt:3:97:3:120 | c4 | Class<?>[] |
32+
| test.kt:3:97:3:120 | this.c4 | Class<?>[] |
33+
#select
34+
| test.kt:3:20:3:36 | c1 | Class<?> |
35+
| test.kt:3:39:3:70 | c2 | Class<? extends CharSequence> |
36+
| test.kt:3:73:3:94 | c3 | Class<String> |
37+
| test.kt:3:97:3:120 | c4 | Class<?>[] |
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import kotlin.reflect.KClass
2+
3+
annotation class A(val c1: KClass<*>, val c2: KClass<out CharSequence>, val c3: KClass<String>, val c4: Array<KClass<*>>) { }
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import java
2+
3+
query predicate classExprs(Expr e, string tstr) {
4+
tstr = e.getType().toString() and
5+
tstr.matches("%Class%")
6+
}
7+
8+
from Method m
9+
where m.getDeclaringType().fromSource()
10+
select m, m.getReturnType().toString()

0 commit comments

Comments
 (0)