Reapply "[KeepAnno] Test that all generated files are up-to-date"

This reverts commit 88f8f008f0a2b56e709134435f1293cc0bdcfa96.

Change-Id: I197907c7c635ecb5841e79e39d5cf3a0590cef85
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepForApi.java b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepForApi.java
index 91934e0..aac3246 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepForApi.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepForApi.java
@@ -44,12 +44,14 @@
   /**
    * Specify the kind of this item pattern.
    *
-   * <p>Default kind is CLASS_AND_MEMBERS , meaning the annotated class and/or member is to be kept.
-   * When annotating a class this can be set to ONLY_CLASS to avoid patterns on any members. That
-   * can be useful when the API members are themselves explicitly annotated.
+   * <p>Default kind is {@link KeepItemKind#CLASS_AND_MEMBERS}, meaning the annotated class and/or
+   * member is to be kept. When annotating a class this can be set to {@link
+   * KeepItemKind#ONLY_CLASS} to avoid patterns on any members. That can be useful when the API
+   * members are themselves explicitly annotated.
    *
-   * <p>It is not possible to use ONLY_CLASS if annotating a member. Also, it is never valid to use
-   * kind ONLY_MEMBERS as the API surface must keep the class if any member is to be accessible.
+   * <p>It is not possible to use {@link KeepItemKind#ONLY_CLASS} if annotating a member. Also, it
+   * is never valid to use kind {@link KeepItemKind#ONLY_MEMBERS} as the API surface must keep the
+   * class if any member is to be accessible.
    *
    * @return The kind for this pattern.
    */
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/AnnotationConstants.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/AnnotationConstants.java
index 239dd0e..18b2784 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/ast/AnnotationConstants.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/AnnotationConstants.java
@@ -172,16 +172,6 @@
     public static final String CLASS_OPEN_HIERARCHY = "CLASS_OPEN_HIERARCHY";
   }
 
-  public static final class Option {
-    public static final String DESCRIPTOR =
-        "Lcom/android/tools/r8/keepanno/annotations/KeepOption;";
-    public static final String SHRINKING = "SHRINKING";
-    public static final String OPTIMIZATION = "OPTIMIZATION";
-    public static final String OBFUSCATION = "OBFUSCATION";
-    public static final String ACCESS_MODIFICATION = "ACCESS_MODIFICATION";
-    public static final String ANNOTATION_REMOVAL = "ANNOTATION_REMOVAL";
-  }
-
   public static final class MemberAccess {
     public static final String DESCRIPTOR =
         "Lcom/android/tools/r8/keepanno/annotations/MemberAccessFlags;";
diff --git a/src/test/java/com/android/tools/r8/keepanno/utils/KeepAnnoMarkdownGenerator.java b/src/test/java/com/android/tools/r8/keepanno/utils/KeepAnnoMarkdownGenerator.java
index c2dec45..26bea42 100644
--- a/src/test/java/com/android/tools/r8/keepanno/utils/KeepAnnoMarkdownGenerator.java
+++ b/src/test/java/com/android/tools/r8/keepanno/utils/KeepAnnoMarkdownGenerator.java
@@ -49,9 +49,9 @@
 
 public class KeepAnnoMarkdownGenerator {
 
-  public static void generateMarkdownDoc(Generator generator) {
+  public static void generateMarkdownDoc(Generator generator, Path projectRoot) {
     try {
-      new KeepAnnoMarkdownGenerator(generator).internalGenerateMarkdownDoc();
+      new KeepAnnoMarkdownGenerator(generator).internalGenerateMarkdownDoc(projectRoot);
     } catch (IOException e) {
       throw new RuntimeException(e);
     }
@@ -202,10 +202,11 @@
     generator.println(line);
   }
 
-  private void internalGenerateMarkdownDoc() throws IOException {
-    Path template = Paths.get("doc/keepanno-guide.template.md");
+  private void internalGenerateMarkdownDoc(Path projectRoot) throws IOException {
+    Path relativePath = Paths.get("doc", "keepanno-guide.template.md");
+    Path template = projectRoot.resolve(relativePath);
     println("[comment]: <> (DO NOT EDIT - GENERATED FILE)");
-    println("[comment]: <> (Changes should be made in " + template + ")");
+    println("[comment]: <> (Changes should be made in " + relativePath + ")");
     println();
     List<String> readAllLines = FileUtils.readAllLines(template);
     TableEntry root = new TableEntry(0, "root", "root", null);
diff --git a/src/test/java/com/android/tools/r8/keepanno/utils/KeepItemAnnotationGenerator.java b/src/test/java/com/android/tools/r8/keepanno/utils/KeepItemAnnotationGenerator.java
index 475813b..f1d26bb 100644
--- a/src/test/java/com/android/tools/r8/keepanno/utils/KeepItemAnnotationGenerator.java
+++ b/src/test/java/com/android/tools/r8/keepanno/utils/KeepItemAnnotationGenerator.java
@@ -4,30 +4,13 @@
 
 package com.android.tools.r8.keepanno.utils;
 
-import com.android.tools.r8.TestBase;
+import static com.android.tools.r8.references.Reference.classFromClass;
+import static com.android.tools.r8.references.Reference.classFromTypeName;
+
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.cfmethodgeneration.CodeGenerationBase;
-import com.android.tools.r8.keepanno.annotations.AnnotationPattern;
-import com.android.tools.r8.keepanno.annotations.CheckOptimizedOut;
-import com.android.tools.r8.keepanno.annotations.CheckRemoved;
-import com.android.tools.r8.keepanno.annotations.ClassNamePattern;
-import com.android.tools.r8.keepanno.annotations.FieldAccessFlags;
-import com.android.tools.r8.keepanno.annotations.KeepBinding;
-import com.android.tools.r8.keepanno.annotations.KeepCondition;
-import com.android.tools.r8.keepanno.annotations.KeepConstraint;
-import com.android.tools.r8.keepanno.annotations.KeepEdge;
-import com.android.tools.r8.keepanno.annotations.KeepForApi;
 import com.android.tools.r8.keepanno.annotations.KeepItemKind;
-import com.android.tools.r8.keepanno.annotations.KeepOption;
-import com.android.tools.r8.keepanno.annotations.KeepTarget;
-import com.android.tools.r8.keepanno.annotations.MemberAccessFlags;
-import com.android.tools.r8.keepanno.annotations.MethodAccessFlags;
-import com.android.tools.r8.keepanno.annotations.StringPattern;
-import com.android.tools.r8.keepanno.annotations.TypePattern;
-import com.android.tools.r8.keepanno.annotations.UsedByNative;
-import com.android.tools.r8.keepanno.annotations.UsedByReflection;
-import com.android.tools.r8.keepanno.annotations.UsesReflection;
-import com.android.tools.r8.keepanno.ast.AnnotationConstants;
+import com.android.tools.r8.references.ClassReference;
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.StringUtils.BraceType;
 import com.google.common.base.Strings;
@@ -39,6 +22,7 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
+import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
@@ -46,28 +30,194 @@
 import java.util.Arrays;
 import java.util.LinkedHashMap;
 import java.util.List;
+import java.util.function.BiConsumer;
 import java.util.function.Consumer;
 
 public class KeepItemAnnotationGenerator {
 
   public static void main(String[] args) throws IOException {
     Generator.class.getClassLoader().setDefaultAssertionStatus(true);
-    Generator.run();
+    Generator.run(
+        (file, content) -> {
+          try {
+            Files.write(file, content.getBytes(StandardCharsets.UTF_8));
+          } catch (IOException e) {
+            throw new RuntimeException(e);
+          }
+        });
   }
 
+  private static class EnumReference {
+    public final ClassReference enumClass;
+    public final String enumValue;
+
+    public EnumReference(ClassReference enumClass, String enumValue) {
+      this.enumClass = enumClass;
+      this.enumValue = enumValue;
+    }
+
+    public String name() {
+      return enumValue;
+    }
+  }
+
+  private static final ClassReference JAVA_STRING = classFromClass(String.class);
+  private static final ClassReference JAVA_RETENTION_POLICY = classFromClass(RetentionPolicy.class);
+
+  private static final String PKG = "com.android.tools.r8.keepanno.";
+  private static final String AST_PKG = PKG + "ast.";
+  private static final String ANNO_PKG = PKG + "annotations.";
+
+  private static final ClassReference ANNOTATION_CONSTANTS = astClass("AnnotationConstants");
+
+  private static final ClassReference STRING_PATTERN = annoClass("StringPattern");
+  private static final ClassReference TYPE_PATTERN = annoClass("TypePattern");
+  private static final ClassReference CLASS_NAME_PATTERN = annoClass("ClassNamePattern");
+  private static final ClassReference ANNOTATION_PATTERN = annoClass("AnnotationPattern");
+  private static final ClassReference USES_REFLECTION = annoClass("UsesReflection");
+  private static final ClassReference USED_BY_REFLECTION = annoClass("UsedByReflection");
+  private static final ClassReference USED_BY_NATIVE = annoClass("UsedByNative");
+  private static final ClassReference CHECK_REMOVED = annoClass("CheckRemoved");
+  private static final ClassReference CHECK_OPTIMIZED_OUT = annoClass("CheckOptimizedOut");
+  private static final ClassReference KEEP_EDGE = annoClass("KeepEdge");
+  private static final ClassReference KEEP_BINDING = annoClass("KeepBinding");
+  private static final ClassReference KEEP_TARGET = annoClass("KeepTarget");
+  private static final ClassReference KEEP_CONDITION = annoClass("KeepCondition");
+  private static final ClassReference KEEP_FOR_API = annoClass("KeepForApi");
+
+  private static final ClassReference KEEP_ITEM_KIND = annoClass("KeepItemKind");
+  private static final EnumReference KIND_ONLY_CLASS = enumRef(KEEP_ITEM_KIND, "ONLY_CLASS");
+  private static final EnumReference KIND_ONLY_MEMBERS = enumRef(KEEP_ITEM_KIND, "ONLY_MEMBERS");
+  private static final EnumReference KIND_ONLY_METHODS = enumRef(KEEP_ITEM_KIND, "ONLY_METHODS");
+  private static final EnumReference KIND_ONLY_FIELDS = enumRef(KEEP_ITEM_KIND, "ONLY_FIELDS");
+  private static final EnumReference KIND_CLASS_AND_MEMBERS =
+      enumRef(KEEP_ITEM_KIND, "CLASS_AND_MEMBERS");
+  private static final EnumReference KIND_CLASS_AND_METHODS =
+      enumRef(KEEP_ITEM_KIND, "CLASS_AND_METHODS");
+  private static final EnumReference KIND_CLASS_AND_FIELDS =
+      enumRef(KEEP_ITEM_KIND, "CLASS_AND_FIELDS");
+  private static final List<EnumReference> KEEP_ITEM_KIND_VALUES =
+      ImmutableList.of(
+          KIND_ONLY_CLASS,
+          KIND_ONLY_MEMBERS,
+          KIND_ONLY_METHODS,
+          KIND_ONLY_FIELDS,
+          KIND_CLASS_AND_MEMBERS,
+          KIND_CLASS_AND_METHODS,
+          KIND_CLASS_AND_FIELDS);
+
+  private static final ClassReference KEEP_CONSTRAINT = annoClass("KeepConstraint");
+  private static final EnumReference CONSTRAINT_LOOKUP = enumRef(KEEP_CONSTRAINT, "LOOKUP");
+  private static final EnumReference CONSTRAINT_NAME = enumRef(KEEP_CONSTRAINT, "NAME");
+  private static final EnumReference CONSTRAINT_VISIBILITY_RELAX =
+      enumRef(KEEP_CONSTRAINT, "VISIBILITY_RELAX");
+  private static final EnumReference CONSTRAINT_VISIBILITY_RESTRICT =
+      enumRef(KEEP_CONSTRAINT, "VISIBILITY_RESTRICT");
+  private static final EnumReference CONSTRAINT_VISIBILITY_INVARIANT =
+      enumRef(KEEP_CONSTRAINT, "VISIBILITY_INVARIANT");
+  private static final EnumReference CONSTRAINT_CLASS_INSTANTIATE =
+      enumRef(KEEP_CONSTRAINT, "CLASS_INSTANTIATE");
+  private static final EnumReference CONSTRAINT_METHOD_INVOKE =
+      enumRef(KEEP_CONSTRAINT, "METHOD_INVOKE");
+  private static final EnumReference CONSTRAINT_FIELD_GET = enumRef(KEEP_CONSTRAINT, "FIELD_GET");
+  private static final EnumReference CONSTRAINT_FIELD_SET = enumRef(KEEP_CONSTRAINT, "FIELD_SET");
+  private static final EnumReference CONSTRAINT_METHOD_REPLACE =
+      enumRef(KEEP_CONSTRAINT, "METHOD_REPLACE");
+  private static final EnumReference CONSTRAINT_FIELD_REPLACE =
+      enumRef(KEEP_CONSTRAINT, "FIELD_REPLACE");
+  private static final EnumReference CONSTRAINT_NEVER_INLINE =
+      enumRef(KEEP_CONSTRAINT, "NEVER_INLINE");
+  private static final EnumReference CONSTRAINT_CLASS_OPEN_HIERARCHY =
+      enumRef(KEEP_CONSTRAINT, "CLASS_OPEN_HIERARCHY");
+  private static final List<EnumReference> KEEP_CONSTRAINT_VALUES =
+      ImmutableList.of(
+          CONSTRAINT_LOOKUP,
+          CONSTRAINT_NAME,
+          CONSTRAINT_VISIBILITY_RELAX,
+          CONSTRAINT_VISIBILITY_RESTRICT,
+          CONSTRAINT_VISIBILITY_INVARIANT,
+          CONSTRAINT_CLASS_INSTANTIATE,
+          CONSTRAINT_METHOD_INVOKE,
+          CONSTRAINT_FIELD_GET,
+          CONSTRAINT_FIELD_SET,
+          CONSTRAINT_METHOD_REPLACE,
+          CONSTRAINT_FIELD_REPLACE,
+          CONSTRAINT_NEVER_INLINE,
+          CONSTRAINT_CLASS_OPEN_HIERARCHY);
+
+  private static final ClassReference MEMBER_ACCESS_FLAGS = annoClass("MemberAccessFlags");
+  private static final EnumReference MEMBER_ACCESS_PUBLIC = enumRef(MEMBER_ACCESS_FLAGS, "PUBLIC");
+  private static final EnumReference MEMBER_ACCESS_PROTECTED =
+      enumRef(MEMBER_ACCESS_FLAGS, "PROTECTED");
+  private static final EnumReference MEMBER_ACCESS_PACKAGE_PRIVATE =
+      enumRef(MEMBER_ACCESS_FLAGS, "PACKAGE_PRIVATE");
+  private static final EnumReference MEMBER_ACCESS_PRIVATE =
+      enumRef(MEMBER_ACCESS_FLAGS, "PRIVATE");
+  private static final EnumReference MEMBER_ACCESS_STATIC = enumRef(MEMBER_ACCESS_FLAGS, "STATIC");
+  private static final EnumReference MEMBER_ACCESS_FINAL = enumRef(MEMBER_ACCESS_FLAGS, "FINAL");
+  private static final EnumReference MEMBER_ACCESS_SYNTHETIC =
+      enumRef(MEMBER_ACCESS_FLAGS, "SYNTHETIC");
+  private static final List<EnumReference> MEMBER_ACCESS_VALUES =
+      ImmutableList.of(
+          MEMBER_ACCESS_PUBLIC,
+          MEMBER_ACCESS_PROTECTED,
+          MEMBER_ACCESS_PACKAGE_PRIVATE,
+          MEMBER_ACCESS_PRIVATE,
+          MEMBER_ACCESS_STATIC,
+          MEMBER_ACCESS_FINAL,
+          MEMBER_ACCESS_SYNTHETIC);
+
+  private static final ClassReference METHOD_ACCESS_FLAGS = annoClass("MethodAccessFlags");
+  private static final EnumReference METHOD_ACCESS_SYNCHRONIZED =
+      enumRef(METHOD_ACCESS_FLAGS, "SYNCHRONIZED");
+  private static final EnumReference METHOD_ACCESS_BRIDGE = enumRef(METHOD_ACCESS_FLAGS, "BRIDGE");
+  private static final EnumReference METHOD_ACCESS_NATIVE = enumRef(METHOD_ACCESS_FLAGS, "NATIVE");
+  private static final EnumReference METHOD_ACCESS_ABSTRACT =
+      enumRef(METHOD_ACCESS_FLAGS, "ABSTRACT");
+  private static final EnumReference METHOD_ACCESS_STRICT_FP =
+      enumRef(METHOD_ACCESS_FLAGS, "STRICT_FP");
+  private static final List<EnumReference> METHOD_ACCESS_VALUES =
+      ImmutableList.of(
+          METHOD_ACCESS_SYNCHRONIZED,
+          METHOD_ACCESS_BRIDGE,
+          METHOD_ACCESS_NATIVE,
+          METHOD_ACCESS_ABSTRACT,
+          METHOD_ACCESS_STRICT_FP);
+
+  private static final ClassReference FIELD_ACCESS_FLAGS = annoClass("FieldAccessFlags");
+  private static final EnumReference FIELD_ACCESS_VOLATILE =
+      enumRef(FIELD_ACCESS_FLAGS, "VOLATILE");
+  private static final EnumReference FIELD_ACCESS_TRANSIENT =
+      enumRef(FIELD_ACCESS_FLAGS, "TRANSIENT");
+  private static final List<EnumReference> FIELD_ACCESS_VALUES =
+      ImmutableList.of(FIELD_ACCESS_VOLATILE, FIELD_ACCESS_TRANSIENT);
+
   private static final String DEFAULT_INVALID_STRING_PATTERN =
-      "@" + simpleName(StringPattern.class) + "(exact = \"\")";
+      "@" + simpleName(STRING_PATTERN) + "(exact = \"\")";
   private static final String DEFAULT_INVALID_TYPE_PATTERN =
-      "@" + simpleName(TypePattern.class) + "(name = \"\")";
+      "@" + simpleName(TYPE_PATTERN) + "(name = \"\")";
   private static final String DEFAULT_INVALID_CLASS_NAME_PATTERN =
-      "@" + simpleName(ClassNamePattern.class) + "(simpleName = \"\")";
+      "@" + simpleName(CLASS_NAME_PATTERN) + "(simpleName = \"\")";
+
+  private static ClassReference astClass(String simpleName) {
+    return classFromTypeName(AST_PKG + simpleName);
+  }
+
+  private static ClassReference annoClass(String simpleName) {
+    return classFromTypeName(ANNO_PKG + simpleName);
+  }
+
+  private static EnumReference enumRef(ClassReference enumClass, String valueName) {
+    return new EnumReference(enumClass, valueName);
+  }
 
   public static String quote(String str) {
     return "\"" + str + "\"";
   }
 
-  private static String simpleName(Class<?> clazz) {
-    return clazz.getSimpleName();
+  private static String simpleName(ClassReference clazz) {
+    String binaryName = clazz.getBinaryName();
+    return binaryName.substring(binaryName.lastIndexOf('/') + 1);
   }
 
   private static class GroupMember extends DocPrinterBase<GroupMember> {
@@ -111,39 +261,39 @@
       generator.println("public static final String " + name + " = " + quote(name) + ";");
     }
 
-    public GroupMember requiredValue(Class<?> type) {
+    public GroupMember requiredValue(ClassReference type) {
       assert valueDefault == null;
       return setType(simpleName(type));
     }
 
-    public GroupMember requiredArrayValue(Class<?> type) {
+    public GroupMember requiredArrayValue(ClassReference type) {
       assert valueDefault == null;
       return setType(simpleName(type) + "[]");
     }
 
     public GroupMember requiredStringValue() {
-      return requiredValue(String.class);
+      return requiredValue(JAVA_STRING);
     }
 
-    public GroupMember defaultValue(Class<?> type, String value) {
+    public GroupMember defaultValue(ClassReference type, String value) {
       setType(simpleName(type));
       return setValue(value);
     }
 
-    public GroupMember defaultArrayValue(Class<?> type, String value) {
+    public GroupMember defaultArrayValue(ClassReference type, String value) {
       setType(simpleName(type) + "[]");
       return setValue("{" + value + "}");
     }
 
     public GroupMember defaultEmptyString() {
-      return defaultValue(String.class, quote(""));
+      return defaultValue(JAVA_STRING, quote(""));
     }
 
     public GroupMember defaultObjectClass() {
       return setType("Class<?>").setValue("Object.class");
     }
 
-    public GroupMember defaultArrayEmpty(Class<?> type) {
+    public GroupMember defaultArrayEmpty(ClassReference type) {
       return defaultArrayValue(type, "");
     }
   }
@@ -315,7 +465,7 @@
 
     private Group createBindingsGroup() {
       return new Group("bindings")
-          .addMember(new GroupMember("bindings").defaultArrayEmpty(KeepBinding.class));
+          .addMember(new GroupMember("bindings").defaultArrayEmpty(KEEP_BINDING));
     }
 
     private Group createPreconditionsGroup() {
@@ -327,7 +477,7 @@
                   .setDocReturn(
                       "The list of preconditions. "
                           + "Defaults to no conditions, thus trivially/unconditionally satisfied.")
-                  .defaultArrayEmpty(KeepCondition.class));
+                  .defaultArrayEmpty(KEEP_CONDITION));
     }
 
     private Group createConsequencesGroup() {
@@ -336,7 +486,7 @@
               new GroupMember("consequences")
                   .setDocTitle("Consequences that must be kept if the annotation is in effect.")
                   .setDocReturn("The list of target consequences.")
-                  .requiredArrayValue(KeepTarget.class));
+                  .requiredArrayValue(KEEP_TARGET));
     }
 
     private Group createConsequencesAsValueGroup() {
@@ -345,7 +495,7 @@
               new GroupMember("value")
                   .setDocTitle("Consequences that must be kept if the annotation is in effect.")
                   .setDocReturn("The list of target consequences.")
-                  .requiredArrayValue(KeepTarget.class));
+                  .requiredArrayValue(KEEP_TARGET));
     }
 
     private Group createAdditionalPreconditionsGroup() {
@@ -356,7 +506,7 @@
                   .setDocReturn(
                       "The list of additional preconditions. "
                           + "Defaults to no additional preconditions.")
-                  .defaultArrayEmpty(KeepCondition.class));
+                  .defaultArrayEmpty(KEEP_CONDITION));
     }
 
     private Group createAdditionalTargetsGroup(String docTitle) {
@@ -367,7 +517,7 @@
                   .setDocReturn(
                       "List of additional target consequences. "
                           + "Defaults to no additional target consequences.")
-                  .defaultArrayEmpty(KeepTarget.class));
+                  .defaultArrayEmpty(KEEP_TARGET));
     }
 
     private Group stringPatternExactGroup() {
@@ -417,7 +567,7 @@
           .addMember(
               new GroupMember("classNamePattern")
                   .setDocTitle("Classes matching the class-name pattern.")
-                  .defaultValue(ClassNamePattern.class, DEFAULT_INVALID_CLASS_NAME_PATTERN));
+                  .defaultValue(CLASS_NAME_PATTERN, DEFAULT_INVALID_CLASS_NAME_PATTERN));
       // TODO(b/248408342): Add more injections on type pattern variants.
       // /** Exact type name as a string to match any array with that type as member. */
       // String arrayOf() default "";
@@ -468,25 +618,25 @@
 
     private static GroupMember getKindMember() {
       return new GroupMember("kind")
-          .defaultValue(KeepItemKind.class, "KeepItemKind.DEFAULT")
+          .defaultValue(KEEP_ITEM_KIND, "KeepItemKind.DEFAULT")
           .setDocTitle("Specify the kind of this item pattern.")
           .setDocReturn("The kind for this pattern.")
           .addParagraph("Possible values are:")
           .addUnorderedList(
-              docLink(KeepItemKind.ONLY_CLASS),
-              docLink(KeepItemKind.ONLY_MEMBERS),
-              docLink(KeepItemKind.ONLY_METHODS),
-              docLink(KeepItemKind.ONLY_FIELDS),
-              docLink(KeepItemKind.CLASS_AND_MEMBERS),
-              docLink(KeepItemKind.CLASS_AND_METHODS),
-              docLink(KeepItemKind.CLASS_AND_FIELDS))
+              docEnumLink(KIND_ONLY_CLASS),
+              docEnumLink(KIND_ONLY_MEMBERS),
+              docEnumLink(KIND_ONLY_METHODS),
+              docEnumLink(KIND_ONLY_FIELDS),
+              docEnumLink(KIND_CLASS_AND_MEMBERS),
+              docEnumLink(KIND_CLASS_AND_METHODS),
+              docEnumLink(KIND_CLASS_AND_FIELDS))
           .addParagraph(
               "If unspecified the default kind for an item depends on its member patterns:")
           .addUnorderedList(
-              docLink(KeepItemKind.ONLY_CLASS) + " if no member patterns are defined",
-              docLink(KeepItemKind.ONLY_METHODS) + " if method patterns are defined",
-              docLink(KeepItemKind.ONLY_FIELDS) + " if field patterns are defined",
-              docLink(KeepItemKind.ONLY_MEMBERS) + " otherwise.");
+              docEnumLink(KIND_ONLY_CLASS) + " if no member patterns are defined",
+              docEnumLink(KIND_ONLY_METHODS) + " if method patterns are defined",
+              docEnumLink(KIND_ONLY_FIELDS) + " if field patterns are defined",
+              docEnumLink(KIND_ONLY_MEMBERS) + " otherwise.");
     }
 
     private void forEachKeepConstraintGroups(Consumer<Group> fn) {
@@ -506,17 +656,17 @@
               "The default constraints depend on the kind of the target.",
               "For all targets the default constraints include:")
           .addUnorderedList(
-              docLink(KeepConstraint.LOOKUP),
-              docLink(KeepConstraint.NAME),
-              docLink(KeepConstraint.VISIBILITY_RELAX))
+              docEnumLink(CONSTRAINT_LOOKUP),
+              docEnumLink(CONSTRAINT_NAME),
+              docEnumLink(CONSTRAINT_VISIBILITY_RELAX))
           .addParagraph("For classes the default constraints also include:")
-          .addUnorderedList(docLink(KeepConstraint.CLASS_INSTANTIATE))
+          .addUnorderedList(docEnumLink(CONSTRAINT_CLASS_INSTANTIATE))
           .addParagraph("For methods the default constraints also include:")
-          .addUnorderedList(docLink(KeepConstraint.METHOD_INVOKE))
+          .addUnorderedList(docEnumLink(CONSTRAINT_METHOD_INVOKE))
           .addParagraph("For fields the default constraints also include:")
-          .addUnorderedList(docLink(KeepConstraint.FIELD_GET), docLink(KeepConstraint.FIELD_SET))
+          .addUnorderedList(docEnumLink(CONSTRAINT_FIELD_GET), docEnumLink(CONSTRAINT_FIELD_SET))
           .setDocReturn("Usage constraints for the target.")
-          .defaultArrayEmpty(KeepConstraint.class);
+          .defaultArrayEmpty(KEEP_CONSTRAINT);
     }
 
     private static GroupMember constraintAdditions() {
@@ -527,7 +677,7 @@
               "in addition to the default constraints.")
           .addParagraph("The default constraints are documented in " + docLink(constraints()))
           .setDocReturn("Additional usage constraints for the target.")
-          .defaultArrayEmpty(KeepConstraint.class);
+          .defaultArrayEmpty(KEEP_CONSTRAINT);
     }
 
     private static GroupMember constrainAnnotations() {
@@ -545,7 +695,7 @@
               "By default no annotation patterns are defined and no annotations are required to",
               "remain.")
           .setDocReturn("Annotation patterns")
-          .defaultArrayEmpty(AnnotationPattern.class);
+          .defaultArrayEmpty(ANNOTATION_PATTERN);
     }
 
     private Group annotationNameGroup() {
@@ -582,7 +732,7 @@
                   + ANNOTATION_NAME_GROUP
                   + " pattern by reference to a class-name pattern.")
           .setDocReturn("The class-name pattern that defines the annotation.")
-          .defaultValue(ClassNamePattern.class, DEFAULT_INVALID_CLASS_NAME_PATTERN);
+          .defaultValue(CLASS_NAME_PATTERN, DEFAULT_INVALID_CLASS_NAME_PATTERN);
     }
 
     private static GroupMember annotationRetention() {
@@ -590,7 +740,7 @@
           .setDocTitle("Specify which retention policies must be set for the annotations.")
           .addParagraph("Matches annotations with matching retention policies")
           .setDocReturn("Retention policies. By default {@code RetentionPolicy.RUNTIME}.")
-          .defaultArrayValue(RetentionPolicy.class, "RetentionPolicy.RUNTIME");
+          .defaultArrayValue(JAVA_RETENTION_POLICY, "RetentionPolicy.RUNTIME");
     }
 
     private GroupMember bindingName() {
@@ -636,7 +786,7 @@
           .setDocTitle(
               "Define the " + CLASS_NAME_GROUP + " pattern by reference to a class-name pattern.")
           .setDocReturn("The class-name pattern that defines the class.")
-          .defaultValue(ClassNamePattern.class, DEFAULT_INVALID_CLASS_NAME_PATTERN);
+          .defaultValue(CLASS_NAME_PATTERN, DEFAULT_INVALID_CLASS_NAME_PATTERN);
     }
 
     private Group createClassNamePatternGroup() {
@@ -732,7 +882,7 @@
                   .setDocTitle(
                       "Define the " + groupName + " pattern by reference to a class-name pattern.")
                   .setDocReturn("The class-name pattern that defines the annotation.")
-                  .defaultValue(ClassNamePattern.class, DEFAULT_INVALID_CLASS_NAME_PATTERN));
+                  .defaultValue(CLASS_NAME_PATTERN, DEFAULT_INVALID_CLASS_NAME_PATTERN));
     }
 
     private Group createClassAnnotatedByPatternGroup() {
@@ -769,7 +919,7 @@
                   .setDocTitle("Define the member-access pattern by matching on access flags.")
                   .addParagraph(getMutuallyExclusiveForMemberProperties())
                   .setDocReturn("The member access-flag constraints that must be met.")
-                  .defaultArrayEmpty(MemberAccessFlags.class));
+                  .defaultArrayEmpty(MEMBER_ACCESS_FLAGS));
     }
 
     private String getMutuallyExclusiveForMemberProperties() {
@@ -812,7 +962,7 @@
                   .addParagraph(getMutuallyExclusiveForMethodProperties())
                   .addParagraph(getMethodDefaultDoc("any method-access flags"))
                   .setDocReturn("The method access-flag constraints that must be met.")
-                  .defaultArrayEmpty(MethodAccessFlags.class));
+                  .defaultArrayEmpty(METHOD_ACCESS_FLAGS));
     }
 
     private Group createMethodNameGroup() {
@@ -830,7 +980,7 @@
                   .addParagraph(getMutuallyExclusiveForMethodProperties())
                   .addParagraph(getMethodDefaultDoc("any method name"))
                   .setDocReturn("The string pattern of the method name.")
-                  .defaultValue(StringPattern.class, DEFAULT_INVALID_STRING_PATTERN));
+                  .defaultValue(STRING_PATTERN, DEFAULT_INVALID_STRING_PATTERN));
     }
 
     private Group createMethodReturnTypeGroup() {
@@ -856,7 +1006,7 @@
                   .addParagraph(getMutuallyExclusiveForMethodProperties())
                   .addParagraph(getMethodDefaultDoc("any return type"))
                   .setDocReturn("The pattern of the method return type.")
-                  .defaultValue(TypePattern.class, DEFAULT_INVALID_TYPE_PATTERN));
+                  .defaultValue(TYPE_PATTERN, DEFAULT_INVALID_TYPE_PATTERN));
     }
 
     private Group createMethodParametersGroup() {
@@ -868,7 +1018,7 @@
                   .addParagraph(getMutuallyExclusiveForMethodProperties())
                   .addParagraph(getMethodDefaultDoc("any parameters"))
                   .setDocReturn("The list of qualified type names of the method parameters.")
-                  .defaultArrayValue(String.class, quote("")))
+                  .defaultArrayValue(JAVA_STRING, quote("")))
           .addMember(
               new GroupMember("methodParameterTypePatterns")
                   .setDocTitle(
@@ -876,7 +1026,7 @@
                   .addParagraph(getMutuallyExclusiveForMethodProperties())
                   .addParagraph(getMethodDefaultDoc("any parameters"))
                   .setDocReturn("The list of type patterns for the method parameters.")
-                  .defaultArrayValue(TypePattern.class, DEFAULT_INVALID_TYPE_PATTERN));
+                  .defaultArrayValue(TYPE_PATTERN, DEFAULT_INVALID_TYPE_PATTERN));
     }
 
     private Group createFieldAnnotatedByGroup() {
@@ -894,7 +1044,7 @@
                   .addParagraph(getMutuallyExclusiveForFieldProperties())
                   .addParagraph(getFieldDefaultDoc("any field-access flags"))
                   .setDocReturn("The field access-flag constraints that must be met.")
-                  .defaultArrayEmpty(FieldAccessFlags.class));
+                  .defaultArrayEmpty(FIELD_ACCESS_FLAGS));
     }
 
     private Group createFieldNameGroup() {
@@ -912,7 +1062,7 @@
                   .addParagraph(getMutuallyExclusiveForFieldProperties())
                   .addParagraph(getFieldDefaultDoc("any field name"))
                   .setDocReturn("The string pattern of the field name.")
-                  .defaultValue(StringPattern.class, DEFAULT_INVALID_STRING_PATTERN));
+                  .defaultValue(STRING_PATTERN, DEFAULT_INVALID_STRING_PATTERN));
     }
 
     private Group createFieldTypeGroup() {
@@ -937,7 +1087,7 @@
                   .addParagraph(getMutuallyExclusiveForFieldProperties())
                   .addParagraph(getFieldDefaultDoc("any type"))
                   .setDocReturn("The type pattern for the field type.")
-                  .defaultValue(TypePattern.class, DEFAULT_INVALID_TYPE_PATTERN));
+                  .defaultValue(TYPE_PATTERN, DEFAULT_INVALID_TYPE_PATTERN));
     }
 
     private void generateClassAndMemberPropertiesWithClassAndMemberBinding() {
@@ -1030,7 +1180,7 @@
           .printDoc(this::println);
       println("@Target(ElementType.ANNOTATION_TYPE)");
       println("@Retention(RetentionPolicy.CLASS)");
-      println("public @interface " + simpleName(StringPattern.class) + " {");
+      println("public @interface " + simpleName(STRING_PATTERN) + " {");
       println();
       withIndent(
           () -> {
@@ -1059,7 +1209,7 @@
           .printDoc(this::println);
       println("@Target(ElementType.ANNOTATION_TYPE)");
       println("@Retention(RetentionPolicy.CLASS)");
-      println("public @interface " + simpleName(TypePattern.class) + " {");
+      println("public @interface " + simpleName(TYPE_PATTERN) + " {");
       println();
       withIndent(() -> typePatternGroup().generate(this));
       println();
@@ -1078,7 +1228,7 @@
           .printDoc(this::println);
       println("@Target(ElementType.ANNOTATION_TYPE)");
       println("@Retention(RetentionPolicy.CLASS)");
-      println("public @interface " + simpleName(ClassNamePattern.class) + " {");
+      println("public @interface " + simpleName(CLASS_NAME_PATTERN) + " {");
       println();
       withIndent(
           () -> {
@@ -1102,7 +1252,7 @@
           .printDoc(this::println);
       println("@Target(ElementType.ANNOTATION_TYPE)");
       println("@Retention(RetentionPolicy.CLASS)");
-      println("public @interface " + simpleName(AnnotationPattern.class) + " {");
+      println("public @interface " + simpleName(ANNOTATION_PATTERN) + " {");
       println();
       withIndent(
           () -> {
@@ -1231,17 +1381,17 @@
                 .clearDocLines()
                 .addParagraph(
                     "Default kind is",
-                    KeepItemKind.CLASS_AND_MEMBERS.name(),
-                    ", meaning the annotated class and/or member is to be kept.",
+                    docEnumLink(KIND_CLASS_AND_MEMBERS) + ",",
+                    "meaning the annotated class and/or member is to be kept.",
                     "When annotating a class this can be set to",
-                    KeepItemKind.ONLY_CLASS.name(),
+                    docEnumLink(KIND_ONLY_CLASS),
                     "to avoid patterns on any members.",
                     "That can be useful when the API members are themselves explicitly annotated.")
                 .addParagraph(
                     "It is not possible to use",
-                    KeepItemKind.ONLY_CLASS.name(),
+                    docEnumLink(KIND_ONLY_CLASS),
                     "if annotating a member. Also, it is never valid to use kind",
-                    KeepItemKind.ONLY_MEMBERS.name(),
+                    docEnumLink(KIND_ONLY_MEMBERS),
                     "as the API surface must keep the class if any member is to be accessible.")
                 .generate(this);
             println();
@@ -1269,18 +1419,18 @@
                   + " conditions that should be satisfied for the annotation to be in effect.")
           .addParagraph(
               "The translation of the "
-                  + docLink(UsesReflection.class)
+                  + docLink(USES_REFLECTION)
                   + " annotation into a "
-                  + docLink(KeepEdge.class)
+                  + docLink(KEEP_EDGE)
                   + " is as follows:")
           .addParagraph(
               "Assume the item of the annotation is denoted by 'CTX' and referred to as its"
                   + " context.")
           .addCodeBlock(
-              annoSimpleName(UsesReflection.class)
+              annoSimpleName(USES_REFLECTION)
                   + "(value = targets, [additionalPreconditions = preconditions])",
               "==>",
-              annoSimpleName(KeepEdge.class) + "(",
+              annoSimpleName(KEEP_EDGE) + "(",
               "  consequences = targets,",
               "  preconditions = {createConditionFromContext(CTX)} + preconditions",
               ")",
@@ -1310,7 +1460,7 @@
           "@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD,"
               + " ElementType.CONSTRUCTOR})");
       println("@Retention(RetentionPolicy.CLASS)");
-      println("public @interface " + simpleName(UsesReflection.class) + " {");
+      println("public @interface " + simpleName(USES_REFLECTION) + " {");
       println();
       withIndent(
           () -> {
@@ -1332,12 +1482,12 @@
           .addParagraph(
               "Note: Before using this annotation, consider if instead you can annotate the code"
                   + " that is doing reflection with "
-                  + docLink(UsesReflection.class)
+                  + docLink(USES_REFLECTION)
                   + ". Annotating the"
                   + " reflecting code is generally more clear and maintainable, and it also"
                   + " naturally gives rise to edges that describe just the reflected aspects of the"
                   + " program. The "
-                  + docLink(UsedByReflection.class)
+                  + docLink(USED_BY_REFLECTION)
                   + " annotation is suitable for cases where"
                   + " the reflecting code is not under user control, or in migrating away from"
                   + " rules.")
@@ -1371,19 +1521,18 @@
                 .addParagraph("If unspecified the default kind depends on the annotated item.")
                 .addParagraph("When annotating a class the default kind is:")
                 .addUnorderedList(
-                    docLink(KeepItemKind.ONLY_CLASS) + " if no member patterns are defined;",
-                    docLink(KeepItemKind.CLASS_AND_METHODS) + " if method patterns are defined;",
-                    docLink(KeepItemKind.CLASS_AND_FIELDS) + " if field patterns are defined;",
-                    docLink(KeepItemKind.CLASS_AND_MEMBERS) + "otherwise.")
+                    docEnumLink(KIND_ONLY_CLASS) + " if no member patterns are defined;",
+                    docEnumLink(KIND_CLASS_AND_METHODS) + " if method patterns are defined;",
+                    docEnumLink(KIND_CLASS_AND_FIELDS) + " if field patterns are defined;",
+                    docEnumLink(KIND_CLASS_AND_MEMBERS) + "otherwise.")
                 .addParagraph(
                     "When annotating a method the default kind is: "
-                        + docLink(KeepItemKind.ONLY_METHODS))
+                        + docEnumLink(KIND_ONLY_METHODS))
                 .addParagraph(
-                    "When annotating a field the default kind is: "
-                        + docLink(KeepItemKind.ONLY_FIELDS))
+                    "When annotating a field the default kind is: " + docEnumLink(KIND_ONLY_FIELDS))
                 .addParagraph(
                     "It is not possible to use "
-                        + docLink(KeepItemKind.ONLY_CLASS)
+                        + docEnumLink(KIND_ONLY_CLASS)
                         + " if annotating a member.")
                 .generate(this);
             println();
@@ -1398,11 +1547,11 @@
       println("}");
     }
 
-    private static String annoSimpleName(Class<?> clazz) {
+    private static String annoSimpleName(ClassReference clazz) {
       return "@" + simpleName(clazz);
     }
 
-    private static String docLink(Class<?> clazz) {
+    private static String docLink(ClassReference clazz) {
       return "{@link " + simpleName(clazz) + "}";
     }
 
@@ -1410,12 +1559,12 @@
       return "{@link #" + member.name + "}";
     }
 
-    private static String docLink(Enum<?> kind) {
-      return "{@link " + simpleName(kind.getClass()) + "#" + kind.name() + "}";
+    private static String docEnumLink(EnumReference enumRef) {
+      return "{@link " + simpleName(enumRef.enumClass) + "#" + enumRef.enumValue + "}";
     }
 
-    private static String docLinkList(Enum<?>... values) {
-      return StringUtils.join(", ", values, v -> docLink(v), BraceType.TUBORG);
+    private static String docEnumLinkList(EnumReference... values) {
+      return StringUtils.join(", ", values, v -> docEnumLink(v), BraceType.TUBORG);
     }
 
     private void generateConstants() {
@@ -1448,7 +1597,6 @@
             generateTargetConstants();
             generateKindConstants();
             generateConstraintConstants();
-            generateOptionConstants();
             generateMemberAccessConstants();
             generateMethodAccessConstants();
             generateFieldAccessConstants();
@@ -1461,8 +1609,8 @@
       println("}");
     }
 
-    private void generateAnnotationConstants(Class<?> clazz) {
-      String desc = TestBase.descriptor(clazz);
+    private void generateAnnotationConstants(ClassReference clazz) {
+      String desc = clazz.getDescriptor();
       println("public static final String DESCRIPTOR = " + quote(desc) + ";");
     }
 
@@ -1470,7 +1618,7 @@
       println("public static final class Edge {");
       withIndent(
           () -> {
-            generateAnnotationConstants(KeepEdge.class);
+            generateAnnotationConstants(KEEP_EDGE);
             createDescriptionGroup().generateConstants(this);
             createBindingsGroup().generateConstants(this);
             createPreconditionsGroup().generateConstants(this);
@@ -1484,7 +1632,7 @@
       println("public static final class ForApi {");
       withIndent(
           () -> {
-            generateAnnotationConstants(KeepForApi.class);
+            generateAnnotationConstants(KEEP_FOR_API);
             createDescriptionGroup().generateConstants(this);
             createAdditionalTargetsGroup(".").generateConstants(this);
             createMemberAccessGroup().generateConstants(this);
@@ -1497,7 +1645,7 @@
       println("public static final class UsesReflection {");
       withIndent(
           () -> {
-            generateAnnotationConstants(UsesReflection.class);
+            generateAnnotationConstants(USES_REFLECTION);
             createDescriptionGroup().generateConstants(this);
             createConsequencesAsValueGroup().generateConstants(this);
             createAdditionalPreconditionsGroup().generateConstants(this);
@@ -1510,7 +1658,7 @@
       println("public static final class UsedByReflection {");
       withIndent(
           () -> {
-            generateAnnotationConstants(UsedByReflection.class);
+            generateAnnotationConstants(USED_BY_REFLECTION);
             createDescriptionGroup().generateConstants(this);
             createPreconditionsGroup().generateConstants(this);
             createAdditionalTargetsGroup(".").generateConstants(this);
@@ -1523,8 +1671,8 @@
       println("public static final class UsedByNative {");
       withIndent(
           () -> {
-            generateAnnotationConstants(UsedByNative.class);
-            println("// Content is the same as " + simpleName(UsedByReflection.class) + ".");
+            generateAnnotationConstants(USED_BY_NATIVE);
+            println("// Content is the same as " + simpleName(USED_BY_REFLECTION) + ".");
           });
       println("}");
       println();
@@ -1534,7 +1682,7 @@
       println("public static final class CheckRemoved {");
       withIndent(
           () -> {
-            generateAnnotationConstants(CheckRemoved.class);
+            generateAnnotationConstants(CHECK_REMOVED);
           });
       println("}");
       println();
@@ -1544,7 +1692,7 @@
       println("public static final class CheckOptimizedOut {");
       withIndent(
           () -> {
-            generateAnnotationConstants(CheckOptimizedOut.class);
+            generateAnnotationConstants(CHECK_OPTIMIZED_OUT);
           });
       println("}");
       println();
@@ -1587,7 +1735,7 @@
       println("public static final class Binding {");
       withIndent(
           () -> {
-            generateAnnotationConstants(KeepBinding.class);
+            generateAnnotationConstants(KEEP_BINDING);
             bindingName().generateConstants(this);
           });
       println("}");
@@ -1598,7 +1746,7 @@
       println("public static final class Condition {");
       withIndent(
           () -> {
-            generateAnnotationConstants(KeepCondition.class);
+            generateAnnotationConstants(KEEP_CONDITION);
           });
       println("}");
       println();
@@ -1608,7 +1756,7 @@
       println("public static final class Target {");
       withIndent(
           () -> {
-            generateAnnotationConstants(KeepTarget.class);
+            generateAnnotationConstants(KEEP_TARGET);
             getKindGroup().generateConstants(this);
             forEachKeepConstraintGroups(g -> g.generateConstants(this));
           });
@@ -1620,7 +1768,7 @@
       println("public static final class Kind {");
       withIndent(
           () -> {
-            generateAnnotationConstants(KeepItemKind.class);
+            generateAnnotationConstants(KEEP_ITEM_KIND);
             for (KeepItemKind value : KeepItemKind.values()) {
               if (value != KeepItemKind.DEFAULT) {
                 println(
@@ -1640,33 +1788,27 @@
       println("public static final class Constraints {");
       withIndent(
           () -> {
-            generateAnnotationConstants(KeepConstraint.class);
-            for (KeepConstraint value : KeepConstraint.values()) {
+            generateAnnotationConstants(KEEP_CONSTRAINT);
+            for (EnumReference constraint : KEEP_CONSTRAINT_VALUES) {
               println(
-                  "public static final String " + value.name() + " = " + quote(value.name()) + ";");
+                  "public static final String "
+                      + constraint.enumValue
+                      + " = "
+                      + quote(constraint.enumValue)
+                      + ";");
             }
           });
       println("}");
       println();
     }
 
-    private void generateOptionConstants() {
-      println("public static final class Option {");
-      withIndent(
-          () -> {
-            generateAnnotationConstants(KeepOption.class);
-            for (KeepOption value : KeepOption.values()) {
-              println(
-                  "public static final String " + value.name() + " = " + quote(value.name()) + ";");
-            }
-          });
-      println("}");
-      println();
+    private boolean isAccessPropertyNegation(EnumReference enumReference) {
+      return enumReference.name().startsWith("NON_");
     }
 
-    private boolean isMemberAccessProperty(String name) {
-      for (MemberAccessFlags value : MemberAccessFlags.values()) {
-        if (value.name().equals(name)) {
+    private boolean isMemberAccessProperty(EnumReference enumReference) {
+      for (EnumReference memberAccessValue : MEMBER_ACCESS_VALUES) {
+        if (memberAccessValue.enumValue.equals(enumReference.enumValue)) {
           return true;
         }
       }
@@ -1677,17 +1819,13 @@
       println("public static final class MemberAccess {");
       withIndent(
           () -> {
-            generateAnnotationConstants(MemberAccessFlags.class);
+            generateAnnotationConstants(MEMBER_ACCESS_FLAGS);
             println("public static final String NEGATION_PREFIX = \"NON_\";");
-            for (MemberAccessFlags value : MemberAccessFlags.values()) {
-              if (!value.name().startsWith("NON_")) {
-                println(
-                    "public static final String "
-                        + value.name()
-                        + " = "
-                        + quote(value.name())
-                        + ";");
-              }
+            for (EnumReference value : MEMBER_ACCESS_VALUES) {
+              assert !isAccessPropertyNegation(value);
+              assert isMemberAccessProperty(value);
+              println(
+                  "public static final String " + value.name() + " = " + quote(value.name()) + ";");
             }
           });
       println("}");
@@ -1698,11 +1836,10 @@
       println("public static final class MethodAccess {");
       withIndent(
           () -> {
-            generateAnnotationConstants(MethodAccessFlags.class);
-            for (MethodAccessFlags value : MethodAccessFlags.values()) {
-              if (value.name().startsWith("NON_") || isMemberAccessProperty(value.name())) {
-                continue;
-              }
+            generateAnnotationConstants(METHOD_ACCESS_FLAGS);
+            for (EnumReference value : METHOD_ACCESS_VALUES) {
+              assert !isAccessPropertyNegation(value);
+              assert !isMemberAccessProperty(value);
               println(
                   "public static final String " + value.name() + " = " + quote(value.name()) + ";");
             }
@@ -1715,11 +1852,10 @@
       println("public static final class FieldAccess {");
       withIndent(
           () -> {
-            generateAnnotationConstants(FieldAccessFlags.class);
-            for (FieldAccessFlags value : FieldAccessFlags.values()) {
-              if (value.name().startsWith("NON_") || isMemberAccessProperty(value.name())) {
-                continue;
-              }
+            generateAnnotationConstants(FIELD_ACCESS_FLAGS);
+            for (EnumReference value : FIELD_ACCESS_VALUES) {
+              assert !isAccessPropertyNegation(value);
+              assert !isMemberAccessProperty(value);
               println(
                   "public static final String " + value.name() + " = " + quote(value.name()) + ";");
             }
@@ -1732,7 +1868,7 @@
       println("public static final class StringPattern {");
       withIndent(
           () -> {
-            generateAnnotationConstants(StringPattern.class);
+            generateAnnotationConstants(STRING_PATTERN);
             stringPatternExactGroup().generateConstants(this);
             stringPatternPrefixGroup().generateConstants(this);
             stringPatternSuffixGroup().generateConstants(this);
@@ -1745,7 +1881,7 @@
       println("public static final class TypePattern {");
       withIndent(
           () -> {
-            generateAnnotationConstants(TypePattern.class);
+            generateAnnotationConstants(TYPE_PATTERN);
             typePatternGroup().generateConstants(this);
           });
       println("}");
@@ -1756,7 +1892,7 @@
       println("public static final class ClassNamePattern {");
       withIndent(
           () -> {
-            generateAnnotationConstants(ClassNamePattern.class);
+            generateAnnotationConstants(CLASS_NAME_PATTERN);
             classNamePatternSimpleNameGroup().generateConstants(this);
             classNamePatternPackageGroup().generateConstants(this);
           });
@@ -1768,7 +1904,7 @@
       println("public static final class AnnotationPattern {");
       withIndent(
           () -> {
-            generateAnnotationConstants(AnnotationPattern.class);
+            generateAnnotationConstants(ANNOTATION_PATTERN);
             annotationNameGroup().generateConstants(this);
             annotationRetention().generateConstants(this);
           });
@@ -1776,7 +1912,8 @@
       println();
     }
 
-    private static void writeFile(Path file, Consumer<Generator> fn) throws IOException {
+    private static void writeFile(Path file, Consumer<Generator> fn, BiConsumer<Path, String> write)
+        throws IOException {
       ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
       PrintStream printStream = new PrintStream(byteStream);
       Generator generator = new Generator(printStream);
@@ -1785,38 +1922,39 @@
       if (file.toString().endsWith(".java")) {
         formatted = CodeGenerationBase.formatRawOutput(formatted);
       }
-      Files.write(Paths.get(ToolHelper.getProjectRoot()).resolve(file), formatted.getBytes());
+      Path resolved = Paths.get(ToolHelper.getProjectRoot()).resolve(file);
+      write.accept(resolved, formatted);
     }
 
-    public static Path source(Path pkg, Class<?> clazz) {
-      return pkg.resolve(simpleName(clazz) + ".java");
+    public static Path source(ClassReference clazz) {
+      return Paths.get("src", "keepanno", "java").resolve(clazz.getBinaryName() + ".java");
     }
 
-    public static void run() throws IOException {
-      writeFile(Paths.get("doc/keepanno-guide.md"), KeepAnnoMarkdownGenerator::generateMarkdownDoc);
-
-      Path keepAnnoRoot = Paths.get("src/keepanno/java/com/android/tools/r8/keepanno");
-
-      Path astPkg = keepAnnoRoot.resolve("ast");
-      writeFile(source(astPkg, AnnotationConstants.class), Generator::generateConstants);
-
-      Path annoPkg = Paths.get("src/keepanno/java/com/android/tools/r8/keepanno/annotations");
-      writeFile(source(annoPkg, StringPattern.class), Generator::generateStringPattern);
-      writeFile(source(annoPkg, TypePattern.class), Generator::generateTypePattern);
-      writeFile(source(annoPkg, ClassNamePattern.class), Generator::generateClassNamePattern);
-      writeFile(source(annoPkg, AnnotationPattern.class), Generator::generateAnnotationPattern);
-      writeFile(source(annoPkg, KeepBinding.class), Generator::generateKeepBinding);
-      writeFile(source(annoPkg, KeepTarget.class), Generator::generateKeepTarget);
-      writeFile(source(annoPkg, KeepCondition.class), Generator::generateKeepCondition);
-      writeFile(source(annoPkg, KeepForApi.class), Generator::generateKeepForApi);
-      writeFile(source(annoPkg, UsesReflection.class), Generator::generateUsesReflection);
+    public static void run(BiConsumer<Path, String> write) throws IOException {
+      Path projectRoot = Paths.get(ToolHelper.getProjectRoot());
       writeFile(
-          source(annoPkg, UsedByReflection.class),
-          g -> g.generateUsedByX("UsedByReflection", "accessed reflectively"));
+          Paths.get("doc/keepanno-guide.md"),
+          generator -> KeepAnnoMarkdownGenerator.generateMarkdownDoc(generator, projectRoot),
+          write);
+
+      writeFile(source(ANNOTATION_CONSTANTS), Generator::generateConstants, write);
+      writeFile(source(STRING_PATTERN), Generator::generateStringPattern, write);
+      writeFile(source(TYPE_PATTERN), Generator::generateTypePattern, write);
+      writeFile(source(CLASS_NAME_PATTERN), Generator::generateClassNamePattern, write);
+      writeFile(source(ANNOTATION_PATTERN), Generator::generateAnnotationPattern, write);
+      writeFile(source(KEEP_BINDING), Generator::generateKeepBinding, write);
+      writeFile(source(KEEP_TARGET), Generator::generateKeepTarget, write);
+      writeFile(source(KEEP_CONDITION), Generator::generateKeepCondition, write);
+      writeFile(source(KEEP_FOR_API), Generator::generateKeepForApi, write);
+      writeFile(source(USES_REFLECTION), Generator::generateUsesReflection, write);
       writeFile(
-          source(annoPkg, UsedByNative.class),
-          g -> g.generateUsedByX("UsedByNative", "accessed from native code via JNI"));
+          source(USED_BY_REFLECTION),
+          g -> g.generateUsedByX("UsedByReflection", "accessed reflectively"),
+          write);
+      writeFile(
+          source(USED_BY_NATIVE),
+          g -> g.generateUsedByX("UsedByNative", "accessed from native code via JNI"),
+          write);
     }
   }
-
 }
diff --git a/src/test/java/com/android/tools/r8/keepanno/utils/KeepItemGeneratedFilesTest.java b/src/test/java/com/android/tools/r8/keepanno/utils/KeepItemGeneratedFilesTest.java
new file mode 100644
index 0000000..28f1c57
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/keepanno/utils/KeepItemGeneratedFilesTest.java
@@ -0,0 +1,29 @@
+// Copyright (c) 2024, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.keepanno.utils;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.keepanno.utils.KeepItemAnnotationGenerator.Generator;
+import com.android.tools.r8.utils.FileUtils;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import org.junit.Test;
+
+public class KeepItemGeneratedFilesTest {
+
+  @Test
+  public void checkUpToDate() throws IOException {
+    Generator.run(
+        (file, content) -> {
+          try {
+            String expectedContent = FileUtils.readTextFile(file, StandardCharsets.UTF_8);
+            assertEquals(expectedContent, content);
+          } catch (IOException e) {
+            throw new RuntimeException(e);
+          }
+        });
+  }
+}