[keepanno] Add support for keeping subclasses as well

This is an experimental way of specifying this. Implementation to
allow for testing.

Change-Id: I0d87b41a9afbfb4843b8e8c24336b7610a39f70d
Bug: b/392865072
diff --git a/src/keepanno/java/androidx/annotation/keep/UsesReflectionToAccessField.kt b/src/keepanno/java/androidx/annotation/keep/UsesReflectionToAccessField.kt
index b453d72..d0f8a9a 100644
--- a/src/keepanno/java/androidx/annotation/keep/UsesReflectionToAccessField.kt
+++ b/src/keepanno/java/androidx/annotation/keep/UsesReflectionToAccessField.kt
@@ -60,6 +60,9 @@
      */
     @Suppress("KotlinDefaultParameterOrder") val className: String = "",
 
+    /** Also access field on subclasses. */
+    @Suppress("KotlinDefaultParameterOrder") val includeSubclasses: Boolean = false,
+
     /** Name (or name pattern) of field accessed by reflection. */
     val fieldName: String,
 
diff --git a/src/keepanno/java/androidx/annotation/keep/UsesReflectionToAccessMethod.kt b/src/keepanno/java/androidx/annotation/keep/UsesReflectionToAccessMethod.kt
index ddaefbf..d15fba6 100644
--- a/src/keepanno/java/androidx/annotation/keep/UsesReflectionToAccessMethod.kt
+++ b/src/keepanno/java/androidx/annotation/keep/UsesReflectionToAccessMethod.kt
@@ -60,6 +60,9 @@
      */
     @Suppress("KotlinDefaultParameterOrder") val className: String = "",
 
+    /** Also access methods on subclasses. */
+    @Suppress("KotlinDefaultParameterOrder") val includeSubclasses: Boolean = false,
+
     /** Name (or name pattern) of method accessed by reflection. */
     val methodName: String,
 
diff --git a/src/keepanno/java/androidx/annotation/keep/UsesReflectionToConstruct.kt b/src/keepanno/java/androidx/annotation/keep/UsesReflectionToConstruct.kt
index 8aabcb5..35a04cb 100644
--- a/src/keepanno/java/androidx/annotation/keep/UsesReflectionToConstruct.kt
+++ b/src/keepanno/java/androidx/annotation/keep/UsesReflectionToConstruct.kt
@@ -63,6 +63,9 @@
      */
     val className: String = "",
 
+    /** Also instantiate subclasses. */
+    @Suppress("KotlinDefaultParameterOrder") val includeSubclasses: Boolean = false,
+
     /**
      * Defines which constructor to keep by specifying the parameter list types.
      *
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeReader.java b/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeReader.java
index 70c0c9e..7d4245c 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeReader.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeReader.java
@@ -1569,10 +1569,12 @@
 
     private static final String UsesReflectionToXXXClassConstant = "classConstant";
     private static final String UsesReflectionToXXXClassName = "className";
+    private static final String UsesReflectionToXXXIncludeSubclasses = "includeSubclasses";
 
     protected final ParsingContext parsingContext;
 
     protected KeepQualifiedClassNamePattern qualifiedName;
+    protected boolean includeSubclasses = false;
 
     UsesReflectionToXXXVisitor(AnnotationParsingContext parsingContext) {
       super(parsingContext);
@@ -1583,6 +1585,12 @@
       assert UsesReflectionToXXXClassName.equals(UsesReflectionToConstruct.className);
       assert UsesReflectionToXXXClassName.equals(UsesReflectionToAccessMethod.className);
       assert UsesReflectionToXXXClassName.equals(UsesReflectionToAccessField.className);
+      assert UsesReflectionToXXXIncludeSubclasses.equals(
+          UsesReflectionToConstruct.includeSubclasses);
+      assert UsesReflectionToXXXIncludeSubclasses.equals(
+          UsesReflectionToAccessMethod.includeSubclasses);
+      assert UsesReflectionToXXXIncludeSubclasses.equals(
+          UsesReflectionToAccessField.includeSubclasses);
     }
 
     protected boolean maybeVisitQualifiedName(String name, Object value) {
@@ -1597,6 +1605,29 @@
       }
       return false;
     }
+
+    protected boolean maybeVisitIncludeSubclasses(String name, Object value) {
+      if (name.equals(UsesReflectionToXXXIncludeSubclasses) && value instanceof Boolean) {
+        includeSubclasses = (Boolean) value;
+        return true;
+      }
+      return false;
+    }
+
+    protected KeepClassItemPattern getKeepClassItemPattern() {
+      return includeSubclasses
+          ? KeepClassItemPattern.builder()
+              .setInstanceOfPattern(
+                  KeepInstanceOfPattern.builder()
+                      .classPattern(qualifiedName)
+                      .setInclusive(true)
+                      .build())
+              .build()
+          : KeepClassItemPattern.builder()
+              .setClassPattern(
+                  KeepClassPattern.builder().setClassNamePattern(qualifiedName).build())
+              .build();
+    }
   }
 
   private static class UsesReflectionToConstructVisitor extends UsesReflectionToXXXVisitor {
@@ -1627,6 +1658,9 @@
       if (maybeVisitQualifiedName(name, value)) {
         return;
       }
+      if (maybeVisitIncludeSubclasses(name, value)) {
+        return;
+      }
       super.visit(name, value);
     }
 
@@ -1649,11 +1683,7 @@
       String kotlinMetadataDescriptor = "Lkotlin/Metadata;";
 
       KeepClassBindingReference classBinding =
-          bindingsHelper.defineFreshClassBinding(
-              KeepClassItemPattern.builder()
-                  .setClassPattern(
-                      KeepClassPattern.builder().setClassNamePattern(qualifiedName).build())
-                  .build());
+          bindingsHelper.defineFreshClassBinding(getKeepClassItemPattern());
       KeepMemberBindingReference memberBinding =
           bindingsHelper.defineFreshMemberBinding(
               KeepMemberItemPattern.builder()
@@ -1799,6 +1829,7 @@
     private final UserBindingsHelper bindingsHelper = new UserBindingsHelper();
 
     private KeepQualifiedClassNamePattern qualifiedName;
+    private boolean includeSubclasses = false;
     private KeepMethodNamePattern methodName;
     private KeepMethodNamePattern methodNameKotlinDefault;
     private KeepMethodReturnTypePattern returnType = KeepMethodReturnTypePattern.any();
@@ -1822,6 +1853,9 @@
       if (maybeVisitQualifiedName(name, value)) {
         return;
       }
+      if (maybeVisitIncludeSubclasses(name, value)) {
+        return;
+      }
       if (name.equals(UsesReflectionToAccessMethod.methodName) && value instanceof String) {
         methodName = KeepMethodNamePattern.exact((String) value);
         methodNameKotlinDefault = KeepMethodNamePattern.exact(value + "$default");
@@ -1863,11 +1897,7 @@
       String kotlinMetadataDescriptor = "Lkotlin/Metadata;";
 
       KeepClassBindingReference classBinding =
-          bindingsHelper.defineFreshClassBinding(
-              KeepClassItemPattern.builder()
-                  .setClassPattern(
-                      KeepClassPattern.builder().setClassNamePattern(qualifiedName).build())
-                  .build());
+          bindingsHelper.defineFreshClassBinding(getKeepClassItemPattern());
       KeepMemberBindingReference memberBinding =
           bindingsHelper.defineFreshMemberBinding(
               KeepMemberItemPattern.builder()
@@ -1886,13 +1916,7 @@
                   // method will be part of the required structure of the class matched. See test
                   // KeepConjunctiveBindingsTest.
                   .setClassReference(
-                      bindingsHelper.defineFreshClassBinding(
-                          KeepClassItemPattern.builder()
-                              .setClassPattern(
-                                  KeepClassPattern.builder()
-                                      .setClassNamePattern(qualifiedName)
-                                      .build())
-                              .build()))
+                      bindingsHelper.defineFreshClassBinding(getKeepClassItemPattern()))
                   .setMemberPattern(
                       KeepMethodPattern.builder()
                           .setNamePattern(methodNameKotlinDefault)
@@ -2039,6 +2063,7 @@
     private final UserBindingsHelper bindingsHelper = new UserBindingsHelper();
 
     private KeepQualifiedClassNamePattern qualifiedName;
+    private boolean includeSubclasses = false;
     private KeepFieldNamePattern fieldName;
     private KeepFieldTypePattern fieldType = KeepFieldTypePattern.any();
 
@@ -2061,6 +2086,9 @@
       if (maybeVisitQualifiedName(name, value)) {
         return;
       }
+      if (maybeVisitIncludeSubclasses(name, value)) {
+        return;
+      }
       if (name.equals(UsesReflectionToAccessField.fieldName) && value instanceof String) {
         fieldName = KeepFieldNamePattern.exact((String) value);
         return;
@@ -2087,11 +2115,7 @@
       String kotlinMetadataDescriptor = "Lkotlin/Metadata;";
 
       KeepClassBindingReference classBinding =
-          bindingsHelper.defineFreshClassBinding(
-              KeepClassItemPattern.builder()
-                  .setClassPattern(
-                      KeepClassPattern.builder().setClassNamePattern(qualifiedName).build())
-                  .build());
+          bindingsHelper.defineFreshClassBinding(getKeepClassItemPattern());
       KeepMemberBindingReference memberBinding =
           bindingsHelper.defineFreshMemberBinding(
               KeepMemberItemPattern.builder()
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 4d813f5..83843e0 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
@@ -153,6 +153,7 @@
     public static final String classSelectionGroup = "class-selection";
     public static final String classConstant = "classConstant";
     public static final String className = "className";
+    public static final String includeSubclasses = "includeSubclasses";
     public static final String constructorParametersGroup = "constructor-parameters";
     public static final String parameterTypes = "parameterTypes";
     public static final String parameterTypeNames = "parameterTypeNames";
@@ -181,6 +182,7 @@
     public static final String classSelectionGroup = "class-selection";
     public static final String classConstant = "classConstant";
     public static final String className = "className";
+    public static final String includeSubclasses = "includeSubclasses";
     public static final String methodName = "methodName";
     public static final String constructorParametersGroup = "constructor-parameters";
     public static final String parameterTypes = "parameterTypes";
@@ -213,6 +215,7 @@
     public static final String classSelectionGroup = "class-selection";
     public static final String classConstant = "classConstant";
     public static final String className = "className";
+    public static final String includeSubclasses = "includeSubclasses";
     public static final String fieldName = "fieldName";
     public static final String fieldTypeSelectionGroup = "field-type-selection";
     public static final String fieldType = "fieldType";
diff --git a/src/test/java/com/android/tools/r8/keepanno/androidx/KeepAnnoTestExtractedRulesBase.java b/src/test/java/com/android/tools/r8/keepanno/androidx/KeepAnnoTestExtractedRulesBase.java
index 699009a..0add3e3 100644
--- a/src/test/java/com/android/tools/r8/keepanno/androidx/KeepAnnoTestExtractedRulesBase.java
+++ b/src/test/java/com/android/tools/r8/keepanno/androidx/KeepAnnoTestExtractedRulesBase.java
@@ -143,6 +143,7 @@
     private final String conditionClass;
     private final String conditionMembers;
     private final String consequentClass;
+    private final boolean extendsConsequentClass;
     private final String consequentMembers;
 
     private ExpectedKeepRule(Builder builder) {
@@ -150,6 +151,7 @@
       this.conditionClass = builder.conditionClass;
       this.conditionMembers = builder.conditionMembers;
       this.consequentClass = builder.consequentClass;
+      this.extendsConsequentClass = builder.extendsConsequentClass;
       this.consequentMembers = builder.consequentMembers;
     }
 
@@ -162,6 +164,7 @@
           + keepVariant
           + (r8 ? ",allowaccessmodification" : "")
           + " class "
+          + (extendsConsequentClass ? "** extends " : "")
           + consequentClass
           + " "
           + consequentMembers;
@@ -177,6 +180,7 @@
       private String conditionClass;
       private String conditionMembers;
       private String consequentClass;
+      private boolean extendsConsequentClass = false;
       private String consequentMembers;
 
       private Builder() {}
@@ -203,6 +207,13 @@
 
       public Builder setConsequentClass(Class<?> consequentClass) {
         this.consequentClass = consequentClass.getTypeName();
+        this.extendsConsequentClass = false;
+        return this;
+      }
+
+      public Builder setConsequentExtendsClass(Class<?> consequentClass) {
+        this.consequentClass = consequentClass.getTypeName();
+        this.extendsConsequentClass = true;
         return this;
       }
 
diff --git a/src/test/java/com/android/tools/r8/keepanno/androidx/KeepUsesReflectionForInstantiationAnyArgsConstructorTest.java b/src/test/java/com/android/tools/r8/keepanno/androidx/KeepUsesReflectionForInstantiationAnyArgsConstructorTest.java
index 7418781..a6adf1b 100644
--- a/src/test/java/com/android/tools/r8/keepanno/androidx/KeepUsesReflectionForInstantiationAnyArgsConstructorTest.java
+++ b/src/test/java/com/android/tools/r8/keepanno/androidx/KeepUsesReflectionForInstantiationAnyArgsConstructorTest.java
@@ -75,7 +75,7 @@
   }
 
   private static ExpectedRules getExpectedRulesJava(
-      Class<?> conditionClass, String conditionMembers) {
+      Class<?> conditionClass, boolean includeSubclasses, String conditionMembers) {
     Consumer<ExpectedKeepRule.Builder> setCondition =
         b -> b.setConditionClass(conditionClass).setConditionMembers(conditionMembers);
     ExpectedRules.Builder builder =
@@ -86,12 +86,29 @@
                     .setConsequentClass(KeptClass.class)
                     .setConsequentMembers("{ void <init>(...); }")
                     .build());
+    if (includeSubclasses) {
+      builder.add(
+          ExpectedKeepRule.builder()
+              .apply(setCondition)
+              .setConsequentExtendsClass(KeptClass.class)
+              .setConsequentMembers("{ void <init>(...); }")
+              .build());
+    }
     addConsequentKotlinMetadata(builder, b -> b.apply(setCondition));
     addDefaultInitWorkaround(
         builder, b -> b.apply(setCondition).setConsequentClass(KeptClass.class));
+    if (includeSubclasses) {
+      addDefaultInitWorkaround(
+          builder, b -> b.apply(setCondition).setConsequentExtendsClass(KeptClass.class));
+    }
     return builder.build();
   }
 
+  private static ExpectedRules getExpectedRulesJava(
+      Class<?> conditionClass, String conditionMembers) {
+    return getExpectedRulesJava(conditionClass, false, conditionMembers);
+  }
+
   private static ExpectedRules getExpectedRulesKotlin(String conditionClass) {
     return getExpectedRulesKotlin(conditionClass, null);
   }
@@ -157,6 +174,22 @@
   }
 
   @Test
+  public void testIncludeSubclasses() throws Exception {
+    testExtractedRules(
+        ImmutableList.of(
+            setAnnotationOnMethod(
+                AnyConstructor.class,
+                MethodPredicate.onName("foo"),
+                builder ->
+                    builder
+                        .setAnnotationClass(
+                            Reference.classFromClass(UsesReflectionToConstruct.class))
+                        .setField("classConstant", KeptClass.class)
+                        .setField("includeSubclasses", true))),
+        getExpectedRulesJava(AnyConstructor.class, true, "{ void foo(java.lang.Class); }"));
+  }
+
+  @Test
   public void testAnyConstructorKotlin() throws Exception {
     testExtractedRulesAndRunKotlin(
         compilationResults,
diff --git a/src/test/java/com/android/tools/r8/keepanno/androidx/KeepUsesReflectionForInstantiationNoArgsConstructorTest.java b/src/test/java/com/android/tools/r8/keepanno/androidx/KeepUsesReflectionForInstantiationNoArgsConstructorTest.java
index 80e4d35..146385b 100644
--- a/src/test/java/com/android/tools/r8/keepanno/androidx/KeepUsesReflectionForInstantiationNoArgsConstructorTest.java
+++ b/src/test/java/com/android/tools/r8/keepanno/androidx/KeepUsesReflectionForInstantiationNoArgsConstructorTest.java
@@ -114,7 +114,7 @@
   }
 
   private static ExpectedRules getExpectedRulesJava(
-      Class<?> conditionClass, String contitionMembers) {
+      Class<?> conditionClass, boolean includeSubclasses, String contitionMembers) {
     Consumer<ExpectedKeepRule.Builder> setCondition =
         b -> b.setConditionClass(conditionClass).setConditionMembers(contitionMembers);
 
@@ -126,12 +126,29 @@
                     .setConsequentClass(KeptClass.class)
                     .setConsequentMembers("{ void <init>(); }")
                     .build());
+    if (includeSubclasses) {
+      builder.add(
+          ExpectedKeepRule.builder()
+              .apply(setCondition)
+              .setConsequentExtendsClass(KeptClass.class)
+              .setConsequentMembers("{ void <init>(); }")
+              .build());
+    }
     addConsequentKotlinMetadata(builder, b -> b.apply(setCondition));
     addDefaultInitWorkaround(
         builder, b -> b.apply(setCondition).setConsequentClass(KeptClass.class));
+    if (includeSubclasses) {
+      addDefaultInitWorkaround(
+          builder, b -> b.apply(setCondition).setConsequentExtendsClass(KeptClass.class));
+    }
     return builder.build();
   }
 
+  private static ExpectedRules getExpectedRulesJava(
+      Class<?> conditionClass, String contitionMembers) {
+    return getExpectedRulesJava(conditionClass, false, contitionMembers);
+  }
+
   private static ExpectedRules getExpectedRulesKotlin(String conditionClass) {
     Consumer<ExpectedKeepRule.Builder> setCondition =
         b ->
@@ -292,6 +309,24 @@
   }
 
   @Test
+  public void testIncludeSubclasses() throws Exception {
+    testExtractedRules(
+        ImmutableList.of(
+            setAnnotationOnMethod(
+                OnlyNoArgsConstructorWithoutAnnotation.class,
+                MethodPredicate.onName("foo"),
+                builder ->
+                    builder
+                        .setAnnotationClass(
+                            Reference.classFromClass(UsesReflectionToConstruct.class))
+                        .setField("classConstant", KeptClass.class)
+                        .setArray("parameterTypes")
+                        .setField("includeSubclasses", true))),
+        getExpectedRulesJava(
+            OnlyNoArgsConstructorWithoutAnnotation.class, true, "{ void foo(java.lang.Class); }"));
+  }
+
+  @Test
   public void testOnlyNoArgsConstructorKotlinUsingTransformer() throws Exception {
     testExtractedRulesAndRunKotlin(
         compilationResultsWithoutAnnotation,
diff --git a/src/test/java/com/android/tools/r8/keepanno/androidx/KeepUsesReflectionToAccessFieldTest.java b/src/test/java/com/android/tools/r8/keepanno/androidx/KeepUsesReflectionToAccessFieldTest.java
index 5ddca5d..a0446cd 100644
--- a/src/test/java/com/android/tools/r8/keepanno/androidx/KeepUsesReflectionToAccessFieldTest.java
+++ b/src/test/java/com/android/tools/r8/keepanno/androidx/KeepUsesReflectionToAccessFieldTest.java
@@ -65,7 +65,7 @@
   }
 
   private static ExpectedRules getExpectedRulesJava(
-      Class<?> conditionClass, String... consequentMembers) {
+      Class<?> conditionClass, boolean includeSubclasses, String... consequentMembers) {
     java.util.function.Consumer<ExpectedKeepRule.Builder> setCondition =
         b ->
             b.setConditionClass(conditionClass)
@@ -78,13 +78,30 @@
               .setConsequentClass(KeptClass.class)
               .setConsequentMembers(consequentMembers[i])
               .build());
+      if (includeSubclasses) {
+        builder.add(
+            ExpectedKeepRule.builder()
+                .apply(setCondition)
+                .setConsequentExtendsClass(KeptClass.class)
+                .setConsequentMembers(consequentMembers[i])
+                .build());
+      }
     }
     addConsequentKotlinMetadata(builder, b -> b.apply(setCondition));
     addDefaultInitWorkaround(
         builder, b -> b.apply(setCondition).setConsequentClass(KeptClass.class));
+    if (includeSubclasses) {
+      addDefaultInitWorkaround(
+          builder, b -> b.apply(setCondition).setConsequentExtendsClass(KeptClass.class));
+    }
     return builder.build();
   }
 
+  private static ExpectedRules getExpectedRulesJava(
+      Class<?> conditionClass, String... consequentMembers) {
+    return getExpectedRulesJava(conditionClass, false, consequentMembers);
+  }
+
   private static ExpectedRules getExpectedRulesKotlin(
       String conditionClass,
       String conditionMembers,
@@ -199,6 +216,24 @@
   }
 
   @Test
+  public void testIncludeSubclasses() throws Exception {
+    testExtractedRules(
+        ImmutableList.of(
+            setAnnotationOnMethod(
+                ClassWithAnnotation.class,
+                MethodPredicate.onName("foo"),
+                builder -> {
+                  builder
+                      .setAnnotationClass(
+                          Reference.classFromClass(UsesReflectionToAccessField.class))
+                      .setField("classConstant", KeptClass.class)
+                      .setField("fieldName", "x")
+                      .setField("includeSubclasses", true);
+                })),
+        getExpectedRulesJava(ClassWithAnnotation.class, true, "{ *** x; }"));
+  }
+
+  @Test
   public void testAnyFieldTypeKotlin() throws Exception {
     testExtractedRulesAndRunKotlin(
         compilationResults,
diff --git a/src/test/java/com/android/tools/r8/keepanno/androidx/KeepUsesReflectionToAccessMethodTest.java b/src/test/java/com/android/tools/r8/keepanno/androidx/KeepUsesReflectionToAccessMethodTest.java
index 4e122d4..8755cca 100644
--- a/src/test/java/com/android/tools/r8/keepanno/androidx/KeepUsesReflectionToAccessMethodTest.java
+++ b/src/test/java/com/android/tools/r8/keepanno/androidx/KeepUsesReflectionToAccessMethodTest.java
@@ -66,7 +66,7 @@
   }
 
   private static ExpectedRules getExpectedRulesJava(
-      Class<?> conditionClass, String... consequentMembers) {
+      Class<?> conditionClass, boolean includeSubclasses, String... consequentMembers) {
     Consumer<ExpectedKeepRule.Builder> setCondition =
         b ->
             b.setConditionClass(conditionClass)
@@ -79,13 +79,30 @@
               .setConsequentClass(KeptClass.class)
               .setConsequentMembers(consequentMembers[i])
               .build());
+      if (includeSubclasses) {
+        builder.add(
+            ExpectedKeepRule.builder()
+                .apply(setCondition)
+                .setConsequentExtendsClass(KeptClass.class)
+                .setConsequentMembers(consequentMembers[i])
+                .build());
+      }
     }
     addConsequentKotlinMetadata(builder, b -> b.apply(setCondition));
     addDefaultInitWorkaround(
         builder, b -> b.apply(setCondition).setConsequentClass(KeptClass.class));
+    if (includeSubclasses) {
+      addDefaultInitWorkaround(
+          builder, b -> b.apply(setCondition).setConsequentExtendsClass(KeptClass.class));
+    }
     return builder.build();
   }
 
+  private static ExpectedRules getExpectedRulesJava(
+      Class<?> conditionClass, String... consequentMembers) {
+    return getExpectedRulesJava(conditionClass, false, consequentMembers);
+  }
+
   private static ExpectedRules getExpectedRulesKotlin(
       String conditionClass,
       String conditionMembers,
@@ -204,6 +221,24 @@
   }
 
   @Test
+  public void testIncludeSubclasses() throws Exception {
+    testExtractedRules(
+        ImmutableList.of(
+            setAnnotationOnMethod(
+                ClassWithAnnotation.class,
+                MethodPredicate.onName("foo"),
+                builder ->
+                    builder
+                        .setAnnotationClass(
+                            Reference.classFromClass(UsesReflectionToAccessMethod.class))
+                        .setField("classConstant", KeptClass.class)
+                        .setField("methodName", "m")
+                        .setField("includeSubclasses", true))),
+        getExpectedRulesJava(
+            ClassWithAnnotation.class, true, "{ *** m(...); }", "{ *** m$default(...); }"));
+  }
+
+  @Test
   public void testAnyReturnTypeAndAnyParametersKotlin() throws Exception {
     testExtractedRulesAndRunKotlin(
         compilationResults,
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 e060bbf..dc4e7a6 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
@@ -1878,6 +1878,13 @@
           .addMember(className);
     }
 
+    private Group createAndroidXSubclassSelection(Consumer<GroupMember> includeSubclassesConsumer) {
+      GroupMember includeSubclasses =
+          new GroupMember("includeSubclasses").defaultBooleanValue(false);
+      includeSubclassesConsumer.accept(includeSubclasses);
+      return new Group("subclass-selection").forAndroidX().addMember(includeSubclasses);
+    }
+
     private Group createAndroidXParameterSelection(
         Consumer<GroupMember> paramsConsumer, Consumer<GroupMember> paramTypeNamesConsumer) {
       GroupMember params = new GroupMember("parameterTypes").defaultUnspecifiedArray();
@@ -1969,6 +1976,12 @@
                     g -> g.setDocTitle("Class to be instantiated."))
                 .generate(this);
             println();
+            createAndroidXSubclassSelection(
+                    g ->
+                        g.setSuppressKotlinDefaultParameterOrder()
+                            .setDocTitle("Also instantiate subclasses."))
+                .generate(this);
+            println();
             createAndroidXParameterSelection(
                     g ->
                         g.setDocTitle(
@@ -2022,6 +2035,12 @@
                                     + " by reflection."))
                 .generate(this);
             println();
+            createAndroidXSubclassSelection(
+                    g ->
+                        g.setSuppressKotlinDefaultParameterOrder()
+                            .setDocTitle("Also access methods on subclasses."))
+                .generate(this);
+            println();
             createMethodNameSelection().generate(this);
             println();
             createAndroidXParameterSelection(
@@ -2077,6 +2096,12 @@
                                     + " by reflection."))
                 .generate(this);
             println();
+            createAndroidXSubclassSelection(
+                    g ->
+                        g.setSuppressKotlinDefaultParameterOrder()
+                            .setDocTitle("Also access field on subclasses."))
+                .generate(this);
+            println();
             createAndroidXFieldNameSelection().generate(this);
             println();
             createAndroidXFieldTypeSelection().generate(this);
@@ -2400,11 +2425,13 @@
 
     private void forEachUsesReflectionToConstructGroup(Consumer<Group> fn) {
       fn.accept(createAndroidXClassSelection(g -> {}, g -> {}));
+      fn.accept(createAndroidXSubclassSelection(g -> {}));
       fn.accept(createAndroidXParameterSelection(g -> {}, g -> {}));
     }
 
     private void forEachUsesReflectionToAccessMethodGroup(Consumer<Group> fn) {
       fn.accept(createAndroidXClassSelection(g -> {}, g -> {}));
+      fn.accept(createAndroidXSubclassSelection(g -> {}));
       fn.accept(createMethodNameSelection());
       fn.accept(createAndroidXParameterSelection(g -> {}, g -> {}));
       fn.accept(createAndroidXReturnTypeSelection());
@@ -2412,6 +2439,7 @@
 
     private void forEachUsesReflectionToAccessFieldGroup(Consumer<Group> fn) {
       fn.accept(createAndroidXClassSelection(g -> {}, g -> {}));
+      fn.accept(createAndroidXSubclassSelection(g -> {}));
       fn.accept(createAndroidXFieldNameSelection());
       fn.accept(createAndroidXFieldTypeSelection());
     }