dependency-analysis-gradle-plugin
dependency-analysis-gradle-plugin copied to clipboard
Annotations that reference multiple classes are not correctly exposed as part of the public ABI
Plugin version 1.12.1-SNAPSHOT
Gradle version 7.4.2
Describe the bug If an annotation references multiple types the plugin does not consider them as part of the abi.
For example given the following annotations:
@Target(ElementType.TYPE)
public @interface MyJavaSingleAnnotation {
Class<?> clazz();
}
@Target(ElementType.TYPE)
public @interface MyJavaMultipleAnnotation {
Class<?>[] clazz() default {};
}
class InnerClassJ {}
@MyJavaSingleAnnotation(clazz = InnerClassJ.class)
public class MyCoolJavaSingleClassInA {}
@MyJavaMultipleAnnotation(clazz = {
InnerClassJ.class, MyCoolJavaSingleClassInA.class
})
public class MyCoolJavaClassInA {}
And kotlin code
@Target(AnnotationTarget.CLASS)
annotation class MyKotlinAnnotationSingle(
val clazz: KClass<*>
)
@Target(AnnotationTarget.CLASS)
annotation class MyKotlinAnnotationMultiple(
val clazz: Array<KClass<*>>,
)
class InnerClass {}
@MyKotlinAnnotationSingle(InnerClass::class)
class MyCoolSingleClassInA {}
@MyKotlinAnnotationMultiple([InnerClass::class, MyCoolClassInA::class])
class MyCoolClassInA {}
The public-abi file generated looks as follows:
cat module-a/build/reports/dependency-analysis/main/abi-dump.txt
@Lkotlin/Metadata;
public final class InnerClass {
public fun <init> ()V
}
@Lcom/test/MyKotlinAnnotationMultiple;
@Lkotlin/Metadata;
public final class MyCoolClassInA {
public fun <init> ()V
}
@Lcom/test/MyKotlinAnnotationSingle;
@LInnerClass;
@Lkotlin/Metadata;
public final class MyCoolSingleClassInA {
public fun <init> ()V
}
@Lcom/test/MyJavaMultipleAnnotation;
public class com/test/MyCoolJavaClassInA {
public fun <init> ()V
}
@Lcom/test/MyJavaSingleAnnotation;
@Lcom/test/InnerClassJ;
public class com/test/MyCoolJavaSingleClassInA {
public fun <init> ()V
}
Notice that the multiple annotations (in both java and kotlin) do not correctly reference the two classes that are used within them while the single one does.
To Reproduce Steps to reproduce the behavior: I've attached a sample project that reproduces the issue.
Expected behavior We would expect the following abi file to be outputted. Notice that the Multiple* annotations now correctly reference all the classes that are utilized.
@Lkotlin/Metadata;
public final class InnerClass {
public fun <init> ()V
}
@Lcom/test/MyKotlinAnnotationMultiple;
@LInnerClass;
@LMyCoolClassInA;
@Lkotlin/Metadata;
public final class MyCoolClassInA {
public fun <init> ()V
}
@Lcom/test/MyKotlinAnnotationSingle;
@LInnerClass;
@Lkotlin/Metadata;
public final class MyCoolSingleClassInA {
public fun <init> ()V
}
@Lcom/test/MyJavaMultipleAnnotation;
@Lcom/test/InnerClassJ;
@Lcom/test/MyCoolJavaSingleClassInA;
public class com/test/MyCoolJavaClassInA {
public fun <init> ()V
}
@Lcom/test/MyJavaSingleAnnotation;
@Lcom/test/InnerClassJ;
public class com/test/MyCoolJavaSingleClassInA {
public fun <init> ()V
}
Additional context It seems like the root cause of the bug is
anno.values.orEmpty().filterIsInstance<Type>().forEach { type ->
types.add(type.descriptor)
}
annotation values can also be of type ArrayList<*> which might then contain references to the types. PR incoming.
Thanks for the issue. The public ABI dump is maintained as a best effort; it's not actually used for anything within the plugin. It's basically a human-readable form of the ABI. So, just to clarify, are you only talking about that txt file, or does the plugin also produce incorrect advice?
I believe the plugin also produces incorrect advice. If from Module B you reference a class in Module A in a annotation that contains an array of references then it is not correctly identified as needing to be part of Module B's API.
For:
//@MyJavaMultipleAnnotation(clazz = {
// ClassInBModule.class, JavaClassInAModule.class
//})
@MyJavaSingleAnnotation(clazz = JavaClassInAModule.class)
public class MyCoolJavaClassInB {
}
with the following gradle setup:
dependencies {
implementation(project(":module-a"))
}
Running on the first annotation will result in the following advice:
Advice for :module-b
Existing dependencies which should be modified to be as indicated:
api(project(":module-a")) (was implementation)
But running on the first annotation produces no advice. Local testing of #764 does result in advice being produced for the multiple annotation.