Replace uses of reflection in keepanno documentation generator

Fixes: b/326030080
Change-Id: I9cb1bc1d0134af5254f1805f1d572f86467da3cb
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 b543b8d..f79c36e 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
@@ -4,36 +4,44 @@
 
 package com.android.tools.r8.keepanno.utils;
 
+import static com.android.tools.r8.keepanno.utils.KeepItemAnnotationGenerator.ANNOTATION_PATTERN;
+import static com.android.tools.r8.keepanno.utils.KeepItemAnnotationGenerator.FIELD_ACCESS_FLAGS;
+import static com.android.tools.r8.keepanno.utils.KeepItemAnnotationGenerator.FIELD_ACCESS_VALUES;
+import static com.android.tools.r8.keepanno.utils.KeepItemAnnotationGenerator.KEEP_BINDING;
+import static com.android.tools.r8.keepanno.utils.KeepItemAnnotationGenerator.KEEP_CONDITION;
+import static com.android.tools.r8.keepanno.utils.KeepItemAnnotationGenerator.KEEP_CONSTRAINT;
+import static com.android.tools.r8.keepanno.utils.KeepItemAnnotationGenerator.KEEP_CONSTRAINT_VALUES;
+import static com.android.tools.r8.keepanno.utils.KeepItemAnnotationGenerator.KEEP_EDGE;
+import static com.android.tools.r8.keepanno.utils.KeepItemAnnotationGenerator.KEEP_FOR_API;
+import static com.android.tools.r8.keepanno.utils.KeepItemAnnotationGenerator.KEEP_ITEM_KIND;
+import static com.android.tools.r8.keepanno.utils.KeepItemAnnotationGenerator.KEEP_ITEM_KIND_VALUES;
+import static com.android.tools.r8.keepanno.utils.KeepItemAnnotationGenerator.KEEP_TARGET;
+import static com.android.tools.r8.keepanno.utils.KeepItemAnnotationGenerator.MEMBER_ACCESS_FLAGS;
+import static com.android.tools.r8.keepanno.utils.KeepItemAnnotationGenerator.MEMBER_ACCESS_VALUES;
+import static com.android.tools.r8.keepanno.utils.KeepItemAnnotationGenerator.METHOD_ACCESS_FLAGS;
+import static com.android.tools.r8.keepanno.utils.KeepItemAnnotationGenerator.METHOD_ACCESS_VALUES;
+import static com.android.tools.r8.keepanno.utils.KeepItemAnnotationGenerator.STRING_PATTERN;
+import static com.android.tools.r8.keepanno.utils.KeepItemAnnotationGenerator.TYPE_PATTERN;
+import static com.android.tools.r8.keepanno.utils.KeepItemAnnotationGenerator.USED_BY_NATIVE;
+import static com.android.tools.r8.keepanno.utils.KeepItemAnnotationGenerator.USED_BY_REFLECTION;
+import static com.android.tools.r8.keepanno.utils.KeepItemAnnotationGenerator.USES_REFLECTION;
 import static com.android.tools.r8.keepanno.utils.KeepItemAnnotationGenerator.quote;
+import static com.android.tools.r8.keepanno.utils.KeepItemAnnotationGenerator.simpleName;
 
 import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.keepanno.annotations.AnnotationPattern;
-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.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.doctests.ForApiDocumentationTest;
 import com.android.tools.r8.keepanno.doctests.MainMethodsDocumentationTest;
 import com.android.tools.r8.keepanno.doctests.UsesReflectionAnnotationsDocumentationTest;
 import com.android.tools.r8.keepanno.doctests.UsesReflectionDocumentationTest;
+import com.android.tools.r8.keepanno.utils.KeepItemAnnotationGenerator.EnumReference;
 import com.android.tools.r8.keepanno.utils.KeepItemAnnotationGenerator.Generator;
+import com.android.tools.r8.keepanno.utils.KeepItemAnnotationGenerator.Group;
+import com.android.tools.r8.keepanno.utils.KeepItemAnnotationGenerator.GroupMember;
+import com.android.tools.r8.references.ClassReference;
 import com.android.tools.r8.utils.FileUtils;
 import com.android.tools.r8.utils.StringUtils;
 import com.google.common.collect.ImmutableMap;
 import java.io.IOException;
-import java.lang.reflect.Field;
-import java.lang.reflect.Method;
 import java.nio.charset.StandardCharsets;
 import java.nio.file.Path;
 import java.util.ArrayList;
@@ -78,26 +86,28 @@
 
   public KeepAnnoMarkdownGenerator(Generator generator) {
     this.generator = generator;
-    typeLinkReplacements =
-        getTypeLinkReplacements(
-            // Annotations.
-            KeepEdge.class,
-            KeepBinding.class,
-            KeepTarget.class,
-            KeepCondition.class,
-            UsesReflection.class,
-            UsedByReflection.class,
-            UsedByNative.class,
-            KeepForApi.class,
-            StringPattern.class,
-            TypePattern.class,
-            AnnotationPattern.class,
-            // Enums.
-            KeepConstraint.class,
-            KeepItemKind.class,
-            MemberAccessFlags.class,
-            MethodAccessFlags.class,
-            FieldAccessFlags.class);
+    ImmutableMap.Builder<String, String> builder = ImmutableMap.builder();
+    // Annotations.
+    addAnnotationReplacements(KEEP_EDGE, builder, generator.getKeepEdgeGroups());
+    addAnnotationReplacements(KEEP_BINDING, builder, generator.getBindingGroups());
+    addAnnotationReplacements(KEEP_TARGET, builder, generator.getTargetGroups());
+    addAnnotationReplacements(KEEP_CONDITION, builder, generator.getConditionGroups());
+    addAnnotationReplacements(USES_REFLECTION, builder, generator.getUsesReflectionGroups());
+    addAnnotationReplacements(USED_BY_REFLECTION, builder, generator.getUsedByReflectionGroups());
+    addAnnotationReplacements(USED_BY_NATIVE, builder, generator.getUsedByNativeGroups());
+    addAnnotationReplacements(KEEP_FOR_API, builder, generator.getKeepForApiGroups());
+    addAnnotationReplacements(STRING_PATTERN, builder, generator.getStringPatternGroups());
+    addAnnotationReplacements(TYPE_PATTERN, builder, generator.getTypePatternGroups());
+    addAnnotationReplacements(ANNOTATION_PATTERN, builder, generator.getAnnotationPatternGroups());
+
+    // Enums.
+    addEnumReplacements(KEEP_ITEM_KIND, KEEP_ITEM_KIND_VALUES, builder);
+    addEnumReplacements(KEEP_CONSTRAINT, KEEP_CONSTRAINT_VALUES, builder);
+    addEnumReplacements(MEMBER_ACCESS_FLAGS, MEMBER_ACCESS_VALUES, builder);
+    addEnumReplacements(METHOD_ACCESS_FLAGS, METHOD_ACCESS_VALUES, builder);
+    addEnumReplacements(FIELD_ACCESS_FLAGS, FIELD_ACCESS_VALUES, builder);
+
+    typeLinkReplacements = builder.build();
     populateCodeAndDocReplacements(
         UsesReflectionDocumentationTest.class,
         UsesReflectionAnnotationsDocumentationTest.class,
@@ -105,37 +115,51 @@
         MainMethodsDocumentationTest.class);
   }
 
-  private Map<String, String> getTypeLinkReplacements(Class<?>... classes) {
-    ImmutableMap.Builder<String, String> builder = ImmutableMap.builder();
-    for (Class<?> clazz : classes) {
-      String prefix = "`@" + clazz.getSimpleName();
-      String suffix = "`";
-      if (clazz.isAnnotation()) {
-        builder.put(prefix + suffix, getMdAnnotationLink(clazz));
-        for (Method method : clazz.getDeclaredMethods()) {
-          builder.put(
-              prefix + "#" + method.getName() + suffix, getMdAnnotationPropertyLink(method));
-        }
-      } else if (clazz.isEnum()) {
-        builder.put(prefix + suffix, getMdEnumLink(clazz));
-        for (Field field : clazz.getDeclaredFields()) {
-          builder.put(prefix + "#" + field.getName() + suffix, getMdEnumFieldLink(field));
-        }
-      } else {
-        throw new RuntimeException("Unexpected type of class for doc links");
+  private static String getPrefix(ClassReference annoType) {
+    return "`@" + simpleName(annoType);
+  }
+
+  private static String getSuffix() {
+    return "`";
+  }
+
+  private void addAnnotationReplacements(
+      ClassReference annoType, ImmutableMap.Builder<String, String> builder, List<Group> groups) {
+    String prefix = getPrefix(annoType);
+    String suffix = getSuffix();
+    builder.put(prefix + suffix, getMdAnnotationLink(annoType));
+    for (Group group : groups) {
+      for (GroupMember member : group.members) {
+        builder.put(
+            prefix + "#" + member.name + suffix, getMdAnnotationPropertyLink(annoType, member));
       }
     }
-    return builder.build();
+  }
+
+  private void addEnumReplacements(
+      ClassReference enumType,
+      List<EnumReference> enumMembers,
+      ImmutableMap.Builder<String, String> builder) {
+    String prefix = getPrefix(enumType);
+    String suffix = getSuffix();
+    builder.put(prefix + suffix, getMdEnumLink(enumType));
+    for (EnumReference enumMember : enumMembers) {
+      builder.put(prefix + "#" + enumMember.name() + suffix, getMdEnumMemberLink(enumMember));
+    }
   }
 
   private void populateCodeAndDocReplacements(Class<?>... classes) {
+    for (Class<?> clazz : classes) {
+      Path sourceFile = ToolHelper.getSourceFileForTestClass(clazz);
+      extractMarkers(sourceFile);
+    }
+  }
+
+  private void extractMarkers(Path sourceFile) {
     try {
-      for (Class<?> clazz : classes) {
-        Path sourceFile = ToolHelper.getSourceFileForTestClass(clazz);
-        String text = FileUtils.readTextFile(sourceFile, StandardCharsets.UTF_8);
-        extractMarkers(text, INCLUDE_DOC_START, INCLUDE_DOC_END, docReplacements);
-        extractMarkers(text, INCLUDE_CODE_START, INCLUDE_CODE_END, codeReplacements);
-      }
+      String text = FileUtils.readTextFile(sourceFile, StandardCharsets.UTF_8);
+      extractMarkers(text, INCLUDE_DOC_START, INCLUDE_DOC_END, docReplacements);
+      extractMarkers(text, INCLUDE_CODE_START, INCLUDE_CODE_END, codeReplacements);
     } catch (IOException e) {
       throw new RuntimeException(e);
     }
@@ -167,30 +191,29 @@
     }
   }
 
-  private static String getClassJavaDocUrl(Class<?> clazz) {
+  private static String getClassJavaDocUrl(ClassReference clazz) {
     return JAVADOC_URL + clazz.getTypeName().replace('.', '/') + ".html";
   }
 
-  private String getMdAnnotationLink(Class<?> clazz) {
-    return "[@" + clazz.getSimpleName() + "](" + getClassJavaDocUrl(clazz) + ")";
+  private String getMdAnnotationLink(ClassReference clazz) {
+    return "[@" + simpleName(clazz) + "](" + getClassJavaDocUrl(clazz) + ")";
   }
 
-  private String getMdAnnotationPropertyLink(Method method) {
-    Class<?> clazz = method.getDeclaringClass();
-    String methodName = method.getName();
+  private String getMdAnnotationPropertyLink(ClassReference clazz, GroupMember method) {
+    String methodName = method.name;
     String url = getClassJavaDocUrl(clazz) + "#" + methodName + "()";
-    return "[@" + clazz.getSimpleName() + "." + methodName + "](" + url + ")";
+    return "[@" + simpleName(clazz) + "." + methodName + "](" + url + ")";
   }
 
-  private String getMdEnumLink(Class<?> clazz) {
-    return "[" + clazz.getSimpleName() + "](" + getClassJavaDocUrl(clazz) + ")";
+  private String getMdEnumLink(ClassReference clazz) {
+    return "[" + simpleName(clazz) + "](" + getClassJavaDocUrl(clazz) + ")";
   }
 
-  private String getMdEnumFieldLink(Field field) {
-    Class<?> clazz = field.getDeclaringClass();
-    String fieldName = field.getName();
-    String url = getClassJavaDocUrl(clazz) + "#" + fieldName;
-    return "[" + clazz.getSimpleName() + "." + fieldName + "](" + url + ")";
+  private String getMdEnumMemberLink(EnumReference enumMember) {
+    ClassReference clazz = enumMember.enumClass;
+    String enumName = enumMember.name();
+    String url = getClassJavaDocUrl(clazz) + "#" + enumName;
+    return "[" + simpleName(clazz) + "." + enumName + "](" + url + ")";
   }
 
   private void println() {
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 325960e..bb5de23 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
@@ -47,7 +47,7 @@
         });
   }
 
-  private static class EnumReference {
+  public static class EnumReference {
     public final ClassReference enumClass;
     public final String enumValue;
 
@@ -70,27 +70,25 @@
 
   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 INSTANCE_OF_PATTERN = annoClass("InstanceOfPattern");
-  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 EXTRACTED_KEEP_ANNOTATIONS =
-      annoClass("ExtractedKeepAnnotations");
-  private static final ClassReference EXTRACTED_KEEP_ANNOTATION =
-      annoClass("ExtractedKeepAnnotation");
-  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");
+  static final ClassReference STRING_PATTERN = annoClass("StringPattern");
+  static final ClassReference TYPE_PATTERN = annoClass("TypePattern");
+  static final ClassReference CLASS_NAME_PATTERN = annoClass("ClassNamePattern");
+  static final ClassReference INSTANCE_OF_PATTERN = annoClass("InstanceOfPattern");
+  static final ClassReference ANNOTATION_PATTERN = annoClass("AnnotationPattern");
+  static final ClassReference USES_REFLECTION = annoClass("UsesReflection");
+  static final ClassReference USED_BY_REFLECTION = annoClass("UsedByReflection");
+  static final ClassReference USED_BY_NATIVE = annoClass("UsedByNative");
+  static final ClassReference CHECK_REMOVED = annoClass("CheckRemoved");
+  static final ClassReference CHECK_OPTIMIZED_OUT = annoClass("CheckOptimizedOut");
+  static final ClassReference EXTRACTED_KEEP_ANNOTATIONS = annoClass("ExtractedKeepAnnotations");
+  static final ClassReference EXTRACTED_KEEP_ANNOTATION = annoClass("ExtractedKeepAnnotation");
+  static final ClassReference KEEP_EDGE = annoClass("KeepEdge");
+  static final ClassReference KEEP_BINDING = annoClass("KeepBinding");
+  static final ClassReference KEEP_TARGET = annoClass("KeepTarget");
+  static final ClassReference KEEP_CONDITION = annoClass("KeepCondition");
+  static final ClassReference KEEP_FOR_API = annoClass("KeepForApi");
 
-  private static final ClassReference KEEP_ITEM_KIND = annoClass("KeepItemKind");
+  public 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");
@@ -101,7 +99,7 @@
       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 =
+  public static final List<EnumReference> KEEP_ITEM_KIND_VALUES =
       ImmutableList.of(
           KIND_ONLY_CLASS,
           KIND_ONLY_MEMBERS,
@@ -111,7 +109,7 @@
           KIND_CLASS_AND_METHODS,
           KIND_CLASS_AND_FIELDS);
 
-  private static final ClassReference KEEP_CONSTRAINT = annoClass("KeepConstraint");
+  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 =
@@ -134,7 +132,7 @@
       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 =
+  static final List<EnumReference> KEEP_CONSTRAINT_VALUES =
       ImmutableList.of(
           CONSTRAINT_LOOKUP,
           CONSTRAINT_NAME,
@@ -150,7 +148,7 @@
           CONSTRAINT_NEVER_INLINE,
           CONSTRAINT_CLASS_OPEN_HIERARCHY);
 
-  private static final ClassReference MEMBER_ACCESS_FLAGS = annoClass("MemberAccessFlags");
+  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");
@@ -162,7 +160,7 @@
   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 =
+  static final List<EnumReference> MEMBER_ACCESS_VALUES =
       ImmutableList.of(
           MEMBER_ACCESS_PUBLIC,
           MEMBER_ACCESS_PROTECTED,
@@ -172,7 +170,7 @@
           MEMBER_ACCESS_FINAL,
           MEMBER_ACCESS_SYNTHETIC);
 
-  private static final ClassReference METHOD_ACCESS_FLAGS = annoClass("MethodAccessFlags");
+  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");
@@ -181,7 +179,7 @@
       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 =
+  static final List<EnumReference> METHOD_ACCESS_VALUES =
       ImmutableList.of(
           METHOD_ACCESS_SYNCHRONIZED,
           METHOD_ACCESS_BRIDGE,
@@ -189,12 +187,12 @@
           METHOD_ACCESS_ABSTRACT,
           METHOD_ACCESS_STRICT_FP);
 
-  private static final ClassReference FIELD_ACCESS_FLAGS = annoClass("FieldAccessFlags");
+  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 =
+  static final List<EnumReference> FIELD_ACCESS_VALUES =
       ImmutableList.of(FIELD_ACCESS_VOLATILE, FIELD_ACCESS_TRANSIENT);
 
   private static final String DEFAULT_INVALID_STRING_PATTERN =
@@ -222,12 +220,12 @@
     return "\"" + str + "\"";
   }
 
-  private static String simpleName(ClassReference clazz) {
+  public static String simpleName(ClassReference clazz) {
     String binaryName = clazz.getBinaryName();
     return binaryName.substring(binaryName.lastIndexOf('/') + 1);
   }
 
-  private static class GroupMember extends DocPrinterBase<GroupMember> {
+  public static class GroupMember extends DocPrinterBase<GroupMember> {
 
     final String name;
     String valueType = null;
@@ -310,7 +308,7 @@
     }
   }
 
-  private static class Group {
+  public static class Group {
 
     final String name;
     final List<GroupMember> members = new ArrayList<>();
@@ -565,7 +563,7 @@
                   .defaultEmptyString());
     }
 
-    private Group typePatternGroup() {
+    public Group typePatternGroup() {
       return new Group("type-pattern")
           .addMember(
               new GroupMember("name")
@@ -1699,6 +1697,11 @@
       println("}");
     }
 
+    void generateGroupConstants(ClassReference annoType, List<Group> groups) {
+      generateAnnotationConstants(annoType);
+      groups.forEach(g -> g.generateConstants(this));
+    }
+
     private void generateAnnotationConstants(ClassReference clazz) {
       String desc = clazz.getDescriptor();
       println("public static final String DESCRIPTOR = " + quote(desc) + ";");
@@ -1747,59 +1750,77 @@
       println();
     }
 
+    List<Group> getKeepEdgeGroups() {
+      return ImmutableList.of(
+          createDescriptionGroup(),
+          createBindingsGroup(),
+          createPreconditionsGroup(),
+          createConsequencesGroup());
+    }
+
     private void generateKeepEdgeConstants() {
       println("public static final class Edge {");
-      withIndent(
-          () -> {
-            generateAnnotationConstants(KEEP_EDGE);
-            createDescriptionGroup().generateConstants(this);
-            createBindingsGroup().generateConstants(this);
-            createPreconditionsGroup().generateConstants(this);
-            createConsequencesGroup().generateConstants(this);
-          });
+      withIndent(() -> generateGroupConstants(KEEP_EDGE, getKeepEdgeGroups()));
       println("}");
       println();
     }
 
+    List<Group> getKeepForApiGroups() {
+      return ImmutableList.of(
+          createDescriptionGroup(), createAdditionalTargetsGroup("."), createMemberAccessGroup());
+    }
+
     private void generateKeepForApiConstants() {
       println("public static final class ForApi {");
-      withIndent(
-          () -> {
-            generateAnnotationConstants(KEEP_FOR_API);
-            createDescriptionGroup().generateConstants(this);
-            createAdditionalTargetsGroup(".").generateConstants(this);
-            createMemberAccessGroup().generateConstants(this);
-          });
+      withIndent(() -> generateGroupConstants(KEEP_FOR_API, getKeepForApiGroups()));
       println("}");
       println();
     }
 
+    List<Group> getUsesReflectionGroups() {
+      return ImmutableList.of(
+          createDescriptionGroup(),
+          createConsequencesAsValueGroup(),
+          createAdditionalPreconditionsGroup());
+    }
+
     private void generateUsesReflectionConstants() {
       println("public static final class UsesReflection {");
-      withIndent(
-          () -> {
-            generateAnnotationConstants(USES_REFLECTION);
-            createDescriptionGroup().generateConstants(this);
-            createConsequencesAsValueGroup().generateConstants(this);
-            createAdditionalPreconditionsGroup().generateConstants(this);
-          });
+      withIndent(() -> generateGroupConstants(USES_REFLECTION, getUsesReflectionGroups()));
       println("}");
       println();
     }
 
+    List<Group> getUsedByReflectionGroups() {
+      ImmutableList.Builder<Group> builder = ImmutableList.builder();
+      builder.addAll(getItemGroups());
+      builder.add(getKindGroup());
+      forEachExtraUsedByReflectionGroup(builder::add);
+      forEachKeepConstraintGroups(builder::add);
+      return builder.build();
+    }
+
+    private void forEachExtraUsedByReflectionGroup(Consumer<Group> fn) {
+      fn.accept(createDescriptionGroup());
+      fn.accept(createPreconditionsGroup());
+      fn.accept(createAdditionalTargetsGroup("."));
+    }
+
     private void generateUsedByReflectionConstants() {
       println("public static final class UsedByReflection {");
       withIndent(
           () -> {
             generateAnnotationConstants(USED_BY_REFLECTION);
-            createDescriptionGroup().generateConstants(this);
-            createPreconditionsGroup().generateConstants(this);
-            createAdditionalTargetsGroup(".").generateConstants(this);
+            forEachExtraUsedByReflectionGroup(g -> g.generateConstants(this));
           });
       println("}");
       println();
     }
 
+    List<Group> getUsedByNativeGroups() {
+      return getUsedByReflectionGroups();
+    }
+
     private void generateUsedByNativeConstants() {
       println("public static final class UsedByNative {");
       withIndent(
@@ -1831,39 +1852,48 @@
       println();
     }
 
+    private List<Group> getItemGroups() {
+      return ImmutableList.of(
+          // Bindings.
+          createClassBindingGroup(),
+          createMemberBindingGroup(),
+          // Classes.
+          createClassNamePatternGroup(),
+          createClassInstanceOfPatternGroup(),
+          createClassAnnotatedByPatternGroup(),
+          // Members.
+          createMemberAnnotatedByGroup(),
+          createMemberAccessGroup(),
+          // Methods.
+          createMethodAnnotatedByGroup(),
+          createMethodAccessGroup(),
+          createMethodNameGroup(),
+          createMethodReturnTypeGroup(),
+          createMethodParametersGroup(),
+          // Fields.
+          createFieldAnnotatedByGroup(),
+          createFieldAccessGroup(),
+          createFieldNameGroup(),
+          createFieldTypeGroup());
+    }
+
     private void generateItemConstants() {
       DocPrinter.printer()
           .setDocTitle("Item properties common to binding items, conditions and targets.")
           .printDoc(this::println);
       println("public static final class Item {");
-      withIndent(
-          () -> {
-            // Bindings.
-            createClassBindingGroup().generateConstants(this);
-            createMemberBindingGroup().generateConstants(this);
-            // Classes.
-            createClassNamePatternGroup().generateConstants(this);
-            createClassInstanceOfPatternGroup().generateConstants(this);
-            createClassAnnotatedByPatternGroup().generateConstants(this);
-            // Members.
-            createMemberAnnotatedByGroup().generateConstants(this);
-            createMemberAccessGroup().generateConstants(this);
-            // Methods.
-            createMethodAnnotatedByGroup().generateConstants(this);
-            createMethodAccessGroup().generateConstants(this);
-            createMethodNameGroup().generateConstants(this);
-            createMethodReturnTypeGroup().generateConstants(this);
-            createMethodParametersGroup().generateConstants(this);
-            // Fields.
-            createFieldAnnotatedByGroup().generateConstants(this);
-            createFieldAccessGroup().generateConstants(this);
-            createFieldNameGroup().generateConstants(this);
-            createFieldTypeGroup().generateConstants(this);
-          });
+      withIndent(() -> getItemGroups().forEach(g -> g.generateConstants(this)));
       println("}");
       println();
     }
 
+    List<Group> getBindingGroups() {
+      return ImmutableList.<Group>builder()
+          .addAll(getItemGroups())
+          .add(new Group("binding-name").addMember(bindingName()))
+          .build();
+    }
+
     private void generateBindingConstants() {
       println("public static final class Binding {");
       withIndent(
@@ -1875,6 +1905,10 @@
       println();
     }
 
+    List<Group> getConditionGroups() {
+      return getItemGroups();
+    }
+
     private void generateConditionConstants() {
       println("public static final class Condition {");
       withIndent(
@@ -1885,13 +1919,24 @@
       println();
     }
 
+    List<Group> getTargetGroups() {
+      ImmutableList.Builder<Group> builder = ImmutableList.builder();
+      builder.addAll(getItemGroups());
+      forEachExtraTargetGroup(builder::add);
+      return builder.build();
+    }
+
+    private void forEachExtraTargetGroup(Consumer<Group> fn) {
+      fn.accept(getKindGroup());
+      forEachKeepConstraintGroups(fn);
+    }
+
     private void generateTargetConstants() {
       println("public static final class Target {");
       withIndent(
           () -> {
             generateAnnotationConstants(KEEP_TARGET);
-            getKindGroup().generateConstants(this);
-            forEachKeepConstraintGroups(g -> g.generateConstants(this));
+            forEachExtraTargetGroup(g -> g.generateConstants(this));
           });
       println("}");
       println();
@@ -1997,25 +2042,32 @@
       println();
     }
 
+    List<Group> getStringPatternGroups() {
+      return ImmutableList.of(
+          stringPatternExactGroup(), stringPatternPrefixGroup(), stringPatternSuffixGroup());
+    }
+
     private void generateStringPatternConstants() {
       println("public static final class StringPattern {");
       withIndent(
           () -> {
             generateAnnotationConstants(STRING_PATTERN);
-            stringPatternExactGroup().generateConstants(this);
-            stringPatternPrefixGroup().generateConstants(this);
-            stringPatternSuffixGroup().generateConstants(this);
+            getStringPatternGroups().forEach(g -> g.generateConstants(this));
           });
       println("}");
       println();
     }
 
+    List<Group> getTypePatternGroups() {
+      return ImmutableList.of(typePatternGroup());
+    }
+
     private void generateTypePatternConstants() {
       println("public static final class TypePattern {");
       withIndent(
           () -> {
             generateAnnotationConstants(TYPE_PATTERN);
-            typePatternGroup().generateConstants(this);
+            getTypePatternGroups().forEach(g -> g.generateConstants((this)));
           });
       println("}");
       println();
@@ -2046,13 +2098,17 @@
       println();
     }
 
+    List<Group> getAnnotationPatternGroups() {
+      return ImmutableList.of(
+          annotationNameGroup(), new Group("retention").addMember(annotationRetention()));
+    }
+
     private void generateAnnotationPatternConstants() {
       println("public static final class AnnotationPattern {");
       withIndent(
           () -> {
             generateAnnotationConstants(ANNOTATION_PATTERN);
-            annotationNameGroup().generateConstants(this);
-            annotationRetention().generateConstants(this);
+            getAnnotationPatternGroups().forEach(g -> g.generateConstants(this));
           });
       println("}");
       println();
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
index 99e7645..28f1c57 100644
--- a/src/test/java/com/android/tools/r8/keepanno/utils/KeepItemGeneratedFilesTest.java
+++ b/src/test/java/com/android/tools/r8/keepanno/utils/KeepItemGeneratedFilesTest.java
@@ -10,12 +10,10 @@
 import com.android.tools.r8.utils.FileUtils;
 import java.io.IOException;
 import java.nio.charset.StandardCharsets;
-import org.junit.Ignore;
 import org.junit.Test;
 
 public class KeepItemGeneratedFilesTest {
 
-  @Ignore
   @Test
   public void checkUpToDate() throws IOException {
     Generator.run(