Skip to content

Commit 6f3ce86

Browse files
committed
feat: add meta-annotation for multi-level annotation propagation
Allow annotations to be inherited at multiple levels of the type hierarchy, enabling both broad and specific configuration of mutators. Use case: Configure mutators that share common types. For example, annotate a fuzz test method to apply default settings to all String mutators, while still allowing individual String parameters to override those settings with different values. Without this feature, an annotation could only appear once in the inheritance chain, preventing this layered configuration approach.
1 parent 90e92bc commit 6f3ce86

File tree

3 files changed

+45
-1
lines changed

3 files changed

+45
-1
lines changed

src/main/java/com/code_intelligence/jazzer/mutation/support/PropertyConstraintSupport.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import static com.code_intelligence.jazzer.mutation.support.TypeSupport.withExtraAnnotations;
2020
import static java.util.Arrays.stream;
2121

22+
import com.code_intelligence.jazzer.mutation.utils.IgnoreRecursiveConflicts;
2223
import com.code_intelligence.jazzer.mutation.utils.PropertyConstraint;
2324
import java.lang.annotation.Annotation;
2425
import java.lang.reflect.AnnotatedType;
@@ -58,7 +59,8 @@ private static String constraintFrom(Annotation constraint) {
5859
}
5960

6061
private static boolean hasConstraint(AnnotatedType target, Annotation constraint) {
61-
return target.getAnnotation(constraint.annotationType()) != null;
62+
return constraint.annotationType().getAnnotation(IgnoreRecursiveConflicts.class) == null
63+
&& target.getAnnotation(constraint.annotationType()) != null;
6264
}
6365

6466
private PropertyConstraintSupport() {}

src/main/java/com/code_intelligence/jazzer/mutation/support/TypeSupport.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727

2828
import com.code_intelligence.jazzer.mutation.annotation.NotNull;
2929
import com.code_intelligence.jazzer.mutation.annotation.WithLength;
30+
import com.code_intelligence.jazzer.mutation.utils.IgnoreRecursiveConflicts;
3031
import com.code_intelligence.jazzer.mutation.utils.PropertyConstraint;
3132
import java.lang.annotation.Annotation;
3233
import java.lang.annotation.Inherited;
@@ -578,6 +579,9 @@ private static Annotation[] checkExtraAnnotations(
578579
.collect(Collectors.toCollection(HashSet::new));
579580
for (Annotation annotation : extraAnnotations) {
580581
boolean added = existingAnnotationTypes.add(annotation.annotationType());
582+
if (annotation.annotationType().isAnnotationPresent(IgnoreRecursiveConflicts.class)) {
583+
continue;
584+
}
581585
require(added, annotation + " already directly present on " + base);
582586
}
583587
return extraAnnotations;
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*
2+
* Copyright 2025 Code Intelligence GmbH
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.code_intelligence.jazzer.mutation.utils;
18+
19+
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
20+
import static java.lang.annotation.RetentionPolicy.RUNTIME;
21+
22+
import java.lang.annotation.Documented;
23+
import java.lang.annotation.Retention;
24+
import java.lang.annotation.Target;
25+
26+
/**
27+
* A meta-annotation to turn off the check in {@code checkExtraAnnotations} that throws if some
28+
* annotation is present multiple times on a type. This allows annotations to be propagated down the
29+
* type hierarchy and accumulated along the way.
30+
*
31+
* <p>E.g. {@code @A("data1") List<@A("data2") String> arg} - the String mutator can see of
32+
* {@code @A("data1")} and {@code @A("data2")}, but the List mutator can only see
33+
* {@code @A("data1")}.
34+
*/
35+
@Target(ANNOTATION_TYPE)
36+
@Retention(RUNTIME)
37+
@Documented
38+
public @interface IgnoreRecursiveConflicts {}

0 commit comments

Comments
 (0)