From 8bbb34a498f7c93c7397cf58933f6815738ae35e Mon Sep 17 00:00:00 2001 From: Chris Smowton Date: Tue, 22 Nov 2022 16:19:41 +0000 Subject: [PATCH] Convert kotlin.Deprecated back into a no-arg java.lang.Deprecated if applicable This at least maintains consistency with the Java extractor, although we lose its arguments if any were supplied Java-side. --- .../src/main/kotlin/KotlinFileExtractor.kt | 19 ++++++- .../PrintAst.expected | 50 +++++++++++++++++++ .../annotation-id-consistency/User.java | 2 +- .../deprecatedAnnotationTypes.expected | 5 ++ .../deprecatedAnnotationTypes.ql | 11 ++++ .../annotation-id-consistency/ktUser.kt | 4 ++ .../kotlin/annotation-id-consistency/test.kt | 11 ++++ 7 files changed, 99 insertions(+), 3 deletions(-) create mode 100644 java/ql/integration-tests/all-platforms/kotlin/annotation-id-consistency/deprecatedAnnotationTypes.expected create mode 100644 java/ql/integration-tests/all-platforms/kotlin/annotation-id-consistency/deprecatedAnnotationTypes.ql diff --git a/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt b/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt index bd58a446f9d..452aa5b0c50 100644 --- a/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt +++ b/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt @@ -498,8 +498,19 @@ open class KotlinFileExtractor( extractEnumTypeAccesses: Boolean, contextLabel: String? = null ): Label { + val isConvertedJavaDeprecatedAnnotation = (constructorCall.type as? IrSimpleType)?.classFqName?.asString() == "kotlin.Deprecated" && + constructorCall.source is JavaSourceElement + + val extractType = + ( + if (isConvertedJavaDeprecatedAnnotation) + pluginContext.referenceClass(FqName("java.lang.Deprecated"))?.defaultType + else + null + ) ?: erase(constructorCall.type) + // Erase the type here because the JVM lowering erases the annotation type, and so the Java extractor will see it in erased form. - val t = useType(erase(constructorCall.type)) + val t = useType(extractType) val annotationContextLabel = contextLabel ?: "{${t.javaResult.id}}" val id = tw.getLabelFor("@\"annotation;{$parent};$annotationContextLabel\"") tw.writeExprs_declannotation(id, t.javaResult.id, parent, idx) @@ -508,7 +519,11 @@ open class KotlinFileExtractor( val locId = tw.getLocation(constructorCall) tw.writeHasLocation(id, locId) - for (i in 0 until constructorCall.valueArgumentsCount) { + // If this is `java.lang.Deprecated`, extract an annotation without parameters -- whatever the original source + // may have supplied has been lost. + val paramCount = if (isConvertedJavaDeprecatedAnnotation) 0 else constructorCall.valueArgumentsCount + + for (i in 0 until paramCount) { val param = constructorCall.symbol.owner.valueParameters[i] val prop = constructorCall.symbol.owner.parentAsClass.declarations .filterIsInstance() diff --git a/java/ql/integration-tests/all-platforms/kotlin/annotation-id-consistency/PrintAst.expected b/java/ql/integration-tests/all-platforms/kotlin/annotation-id-consistency/PrintAst.expected index 646291dc565..89fde9ad673 100644 --- a/java/ql/integration-tests/all-platforms/kotlin/annotation-id-consistency/PrintAst.expected +++ b/java/ql/integration-tests/all-platforms/kotlin/annotation-id-consistency/PrintAst.expected @@ -18,6 +18,12 @@ User.java: # 4| 2: [ExprStmt] ; # 4| 0: [ClassInstanceExpr] new Annotated(...) # 4| -3: [TypeAccess] Annotated +# 4| 3: [ExprStmt] ; +# 4| 0: [ClassInstanceExpr] new HasJavaDeprecatedAnnotationUsedByJava(...) +# 4| -3: [TypeAccess] HasJavaDeprecatedAnnotationUsedByJava +# 4| 4: [ExprStmt] ; +# 4| 0: [ClassInstanceExpr] new HasKotlinDeprecatedAnnotationUsedByJava(...) +# 4| -3: [TypeAccess] HasKotlinDeprecatedAnnotationUsedByJava ktUser.kt: # 0| [CompilationUnit] ktUser # 1| 1: [Class] KtUser @@ -32,6 +38,20 @@ ktUser.kt: # 4| 1: [LocalVariableDeclExpr] a # 4| 0: [ClassInstanceExpr] new AnnotatedUsedByKotlin(...) # 4| -3: [TypeAccess] AnnotatedUsedByKotlin +# 5| 1: [LocalVariableDeclStmt] var ...; +# 5| 1: [LocalVariableDeclExpr] b +# 5| 0: [ClassInstanceExpr] new HasJavaDeprecatedAnnotationUsedByKotlin(...) +# 5| -3: [TypeAccess] HasJavaDeprecatedAnnotationUsedByKotlin +# 6| 2: [LocalVariableDeclStmt] var ...; +# 6| 1: [LocalVariableDeclExpr] c +# 6| 0: [ClassInstanceExpr] new HasKotlinDeprecatedAnnotationUsedByKotlin(...) +# 6| -3: [TypeAccess] HasKotlinDeprecatedAnnotationUsedByKotlin +# 8| 3: [ExprStmt] ; +# 8| 0: [ImplicitCoercionToUnitExpr] +# 8| 0: [TypeAccess] Unit +# 8| 1: [MethodAccess] isJavaLetter(...) +# 8| -1: [TypeAccess] Character +# 8| 0: [CharacterLiteral] a test.kt: # 0| [CompilationUnit] test # 4| 1: [Interface] Ann1 @@ -235,6 +255,36 @@ test.kt: # 27| 5: [BlockStmt] { ... } # 27| 0: [SuperConstructorInvocationStmt] super(...) # 35| 1: [BlockStmt] { ... } +# 37| 10: [Class] HasJavaDeprecatedAnnotationUsedByJava +#-----| -3: (Annotations) +# 37| 1: [Annotation] Deprecated +# 38| 1: [Constructor] HasJavaDeprecatedAnnotationUsedByJava +# 37| 5: [BlockStmt] { ... } +# 37| 0: [SuperConstructorInvocationStmt] super(...) +# 38| 1: [BlockStmt] { ... } +# 40| 11: [Class] HasKotlinDeprecatedAnnotationUsedByJava +#-----| -3: (Annotations) +# 40| 1: [Annotation] Deprecated +# 0| 1: [StringLiteral] "Kotlin deprecation message 1" +# 41| 1: [Constructor] HasKotlinDeprecatedAnnotationUsedByJava +# 40| 5: [BlockStmt] { ... } +# 40| 0: [SuperConstructorInvocationStmt] super(...) +# 41| 1: [BlockStmt] { ... } +# 43| 12: [Class] HasJavaDeprecatedAnnotationUsedByKotlin +#-----| -3: (Annotations) +# 43| 1: [Annotation] Deprecated +# 44| 1: [Constructor] HasJavaDeprecatedAnnotationUsedByKotlin +# 43| 5: [BlockStmt] { ... } +# 43| 0: [SuperConstructorInvocationStmt] super(...) +# 44| 1: [BlockStmt] { ... } +# 46| 13: [Class] HasKotlinDeprecatedAnnotationUsedByKotlin +#-----| -3: (Annotations) +# 46| 1: [Annotation] Deprecated +# 0| 1: [StringLiteral] "Kotlin deprecation message 2" +# 47| 1: [Constructor] HasKotlinDeprecatedAnnotationUsedByKotlin +# 46| 5: [BlockStmt] { ... } +# 46| 0: [SuperConstructorInvocationStmt] super(...) +# 47| 1: [BlockStmt] { ... } # 0| [TypeLiteral] String.class # 0| 0: [TypeAccess] String # 0| [ArrayInit] {...} diff --git a/java/ql/integration-tests/all-platforms/kotlin/annotation-id-consistency/User.java b/java/ql/integration-tests/all-platforms/kotlin/annotation-id-consistency/User.java index 7061beedb93..004e770ba96 100644 --- a/java/ql/integration-tests/all-platforms/kotlin/annotation-id-consistency/User.java +++ b/java/ql/integration-tests/all-platforms/kotlin/annotation-id-consistency/User.java @@ -1,7 +1,7 @@ public class User { public static void user(Ann1 a1, Ann2 a2) { - a1.x(); a2.z(); new Annotated(); + a1.x(); a2.z(); new Annotated(); new HasJavaDeprecatedAnnotationUsedByJava(); new HasKotlinDeprecatedAnnotationUsedByJava(); } } diff --git a/java/ql/integration-tests/all-platforms/kotlin/annotation-id-consistency/deprecatedAnnotationTypes.expected b/java/ql/integration-tests/all-platforms/kotlin/annotation-id-consistency/deprecatedAnnotationTypes.expected new file mode 100644 index 00000000000..76685549259 --- /dev/null +++ b/java/ql/integration-tests/all-platforms/kotlin/annotation-id-consistency/deprecatedAnnotationTypes.expected @@ -0,0 +1,5 @@ +| HasJavaDeprecatedAnnotationUsedByJava | java.lang.Deprecated | +| HasJavaDeprecatedAnnotationUsedByKotlin | java.lang.Deprecated | +| HasKotlinDeprecatedAnnotationUsedByJava | kotlin.Deprecated | +| HasKotlinDeprecatedAnnotationUsedByKotlin | kotlin.Deprecated | +| isJavaLetter | java.lang.Deprecated | diff --git a/java/ql/integration-tests/all-platforms/kotlin/annotation-id-consistency/deprecatedAnnotationTypes.ql b/java/ql/integration-tests/all-platforms/kotlin/annotation-id-consistency/deprecatedAnnotationTypes.ql new file mode 100644 index 00000000000..d8face48b56 --- /dev/null +++ b/java/ql/integration-tests/all-platforms/kotlin/annotation-id-consistency/deprecatedAnnotationTypes.ql @@ -0,0 +1,11 @@ +import java + +from Annotatable a, Annotation ann +where + ( + a.(Method).hasQualifiedName("java.lang", "Character", "isJavaLetter") or + a.(ClassOrInterface).fromSource() + ) and + ann = a.getAnAnnotation() and + ann.getType().getName() = "Deprecated" +select a.toString(), a.getAnAnnotation().getType().getQualifiedName() diff --git a/java/ql/integration-tests/all-platforms/kotlin/annotation-id-consistency/ktUser.kt b/java/ql/integration-tests/all-platforms/kotlin/annotation-id-consistency/ktUser.kt index c7b2ce4fb56..cb593ce11f1 100644 --- a/java/ql/integration-tests/all-platforms/kotlin/annotation-id-consistency/ktUser.kt +++ b/java/ql/integration-tests/all-platforms/kotlin/annotation-id-consistency/ktUser.kt @@ -2,6 +2,10 @@ public class KtUser { fun user() { val a = AnnotatedUsedByKotlin() + val b = HasJavaDeprecatedAnnotationUsedByKotlin() + val c = HasKotlinDeprecatedAnnotationUsedByKotlin() + // Use a Java-defined function carrying a java.lang.Deprecated annotation: + java.lang.Character.isJavaLetter('a') } } diff --git a/java/ql/integration-tests/all-platforms/kotlin/annotation-id-consistency/test.kt b/java/ql/integration-tests/all-platforms/kotlin/annotation-id-consistency/test.kt index de53d808df6..44746342ae6 100644 --- a/java/ql/integration-tests/all-platforms/kotlin/annotation-id-consistency/test.kt +++ b/java/ql/integration-tests/all-platforms/kotlin/annotation-id-consistency/test.kt @@ -34,3 +34,14 @@ class Annotated { } @AnnWithDefaults class AnnotatedUsedByKotlin { } +@java.lang.Deprecated +class HasJavaDeprecatedAnnotationUsedByJava + +@kotlin.Deprecated("Kotlin deprecation message 1") +class HasKotlinDeprecatedAnnotationUsedByJava + +@java.lang.Deprecated +class HasJavaDeprecatedAnnotationUsedByKotlin + +@kotlin.Deprecated("Kotlin deprecation message 2") +class HasKotlinDeprecatedAnnotationUsedByKotlin