Annotations with target TYPE_USE are missing from JavaField instances
Hi,
While working on some ArchUnit checks (I find the tool really useful so far!), I found that annotations with a target of @Target(TYPE_USE) are ignored (for example with @Nullable from Jspecify).
To reproduce, I created my own annotation:
@Target({TYPE_USE})
@Retention(RUNTIME)
public @interface Foo {}
Inspecting an ArchUnit JavaField resulting from importing a class with a field annotated with this annotation, no JavaAnnotations are present.
When adding or using FIELD as target, the JavaAnnotation is present on the JavaField instance.
@Target({TYPE_USE, FIELD})
@Retention(RUNTIME)
public @interface Foo {}
It would be great if these annotations were imported!
JSpecify is getting broad use in the wild and the lack of support for checking TYPE_USE annotations from Java 8 is getting in the way of adoption.
http://types.cs.washington.edu/jsr308/specification/java-annotation-design.pdf
Is this considered for an upcoming release of ArchUnit?
Inspecting an ArchUnit JavaField resulting from importing a class with a field annotated with this annotation, no JavaAnnotations are present.
In this example, you would be annotating the field's type use, not the field itself. You cannot annotate the field itself because fields aren't declared as a valid target of the annotation. So ArchUnit's behavior is correct in this case. To see the annotation, you would have to query the field for its type, and then query the type for its annotations.
Note that type use annotations (allowed by TYPE_USE) are different from annotations present at the class definition that is used in the field's type. A type annotation would be something like List<@Foo String>. Also, since the type use is annotated, List<@Foo String> is different from List<@Bar String>, and neither class List nor class String knows of any of those annotations, since you aren't annotating the classes but the way in which their corresponding types are used. Nor is the field annotated here, because List<@Foo String> is different from @Foo List<String>.
The underlying problem, however, is that ArchUnit has JavaType which does not distinguish between type definition and type use, and seems to be unable to obtain the annotations for type uses, as @eskatos described.
As a workaround one can use the reflect() functions from ArchUnit types to fallback to regular Java reflection in order to grab these annotations.
@eskatos do you have an example perhaps? I tried the following, but it didn't yield any result:
class.reflect().getDeclaredField("myField").getType().getAnnotations()
I did not try on a field but for a method return type I used:
JavaMethod method = ... arch unit;
method.reflect().getAnnotatedReturnType().getAnnotation(Foo.class);
For fields I expect this to work:
JavaClass clazz = ... arch unit;
clazz.reflect().getDeclaredField("myField").getAnnotatedType().getAnnotation(Foo.class);
Ah, great, thanks, getAnnotatedType() does the trick! 👌 So the following is indeed a valid workaround:
clazz.reflect().getDeclaredField("myField").getAnnotatedType().getAnnotation(Nullable.class)