Overview
While revisiting the synthesis logic in TypeMappedAnnotation, I discovered that meta-annotations that declare at least one attribute are still unnecessarily synthesized when synthesizing a MergedAnnotation retrieved via the MergedAnnotations API -- even though #28704 was supposed to have fixed this.
This is a side effect of the fix for #28716 (commit 622fc3e), in which I added the following check to TypeMappedAnnotation#isSynthesizable(Annotation).
if (getDistance() > 0 && this.resolvedMirrors.length > 0) {
return true;
}
The intent was to force synthesis when an attribute's value needs to be resolved from a different level of the annotation hierarchy (the motivating case being a multi-level hierarchy whose ultimate root annotation declares none of its own attributes). However, resolvedMirrors is populated by AnnotationTypeMapping.MirrorSets#resolve(), which always returns an array sized according to the number of attributes declared by the mapped annotation type -- regardless of whether any of those attributes actually participate in mirroring or an @AliasFor override. As a result, resolvedMirrors.length > 0 is true for virtually any meta-annotation that declares at least one attribute, which reintroduces the unnecessary-synthesis behavior that #28704 fixed, just narrowed to meta-annotations that happen to declare attributes.
The following reproduces the regression.
@Retention(RetentionPolicy.RUNTIME)
@interface Meta {
String value() default "meta";
}
@Meta
@Retention(RetentionPolicy.RUNTIME)
@interface Composed {
}
@Composed
class Example {
}
@Test
void test() {
Meta meta = MergedAnnotations.from(Example.class).get(Meta.class).synthesize();
// Meta declares no @AliasFor, and Composed does not override any of its
// attributes, so AnnotationTypeMapping#isSynthesizable() correctly returns
// false for this mapping -- yet the annotation is synthesized anyway.
assertThat(AnnotationUtils.isSynthesizedAnnotation(meta)).isFalse(); // fails: is true
}
Although this is technically a regression, I'll only apply a fix to main (7.1) since the regression has existed for over 4 years without causing major issues for anyone.
Related Issues
Overview
While revisiting the synthesis logic in
TypeMappedAnnotation, I discovered that meta-annotations that declare at least one attribute are still unnecessarily synthesized when synthesizing aMergedAnnotationretrieved via theMergedAnnotationsAPI -- even though #28704 was supposed to have fixed this.This is a side effect of the fix for #28716 (commit 622fc3e), in which I added the following check to
TypeMappedAnnotation#isSynthesizable(Annotation).The intent was to force synthesis when an attribute's value needs to be resolved from a different level of the annotation hierarchy (the motivating case being a multi-level hierarchy whose ultimate root annotation declares none of its own attributes). However,
resolvedMirrorsis populated byAnnotationTypeMapping.MirrorSets#resolve(), which always returns an array sized according to the number of attributes declared by the mapped annotation type -- regardless of whether any of those attributes actually participate in mirroring or an@AliasForoverride. As a result,resolvedMirrors.length > 0is true for virtually any meta-annotation that declares at least one attribute, which reintroduces the unnecessary-synthesis behavior that #28704 fixed, just narrowed to meta-annotations that happen to declare attributes.The following reproduces the regression.
Although this is technically a regression, I'll only apply a fix to
main(7.1) since the regression has existed for over 4 years without causing major issues for anyone.Related Issues