[KeepAnno] Support annotated-by member patterns

Bug: b/248408342
Change-Id: Ic51685de7588e9688fde354e2062009cc5f03798
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepBinding.java b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepBinding.java
index 408abf2..2f8ff1c 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepBinding.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepBinding.java
@@ -334,6 +334,66 @@
   ClassNamePattern classAnnotatedByClassNamePattern() default @ClassNamePattern(simpleName = "");
 
   /**
+   * Define the member-annotated-by pattern by fully qualified class name.
+   *
+   * <p>Mutually exclusive with the following other properties defining member-annotated-by:
+   *
+   * <ul>
+   *   <li>memberAnnotatedByClassConstant
+   *   <li>memberAnnotatedByClassNamePattern
+   * </ul>
+   *
+   * <p>Mutually exclusive with all field and method properties as use restricts the match to both
+   * types of members.
+   *
+   * <p>If none are specified the default is to match any member regardless of what the member is
+   * annotated by.
+   *
+   * @return The qualified class name that defines the annotation.
+   */
+  String memberAnnotatedByClassName() default "";
+
+  /**
+   * Define the member-annotated-by pattern by reference to a Class constant.
+   *
+   * <p>Mutually exclusive with the following other properties defining member-annotated-by:
+   *
+   * <ul>
+   *   <li>memberAnnotatedByClassName
+   *   <li>memberAnnotatedByClassNamePattern
+   * </ul>
+   *
+   * <p>Mutually exclusive with all field and method properties as use restricts the match to both
+   * types of members.
+   *
+   * <p>If none are specified the default is to match any member regardless of what the member is
+   * annotated by.
+   *
+   * @return The class-constant that defines the annotation.
+   */
+  Class<?> memberAnnotatedByClassConstant() default Object.class;
+
+  /**
+   * Define the member-annotated-by pattern by reference to a class-name pattern.
+   *
+   * <p>Mutually exclusive with the following other properties defining member-annotated-by:
+   *
+   * <ul>
+   *   <li>memberAnnotatedByClassName
+   *   <li>memberAnnotatedByClassConstant
+   * </ul>
+   *
+   * <p>Mutually exclusive with all field and method properties as use restricts the match to both
+   * types of members.
+   *
+   * <p>If none are specified the default is to match any member regardless of what the member is
+   * annotated by.
+   *
+   * @return The class-name pattern that defines the annotation.
+   */
+  ClassNamePattern memberAnnotatedByClassNamePattern() default @ClassNamePattern(simpleName = "");
+
+  /**
    * Define the member-access pattern by matching on access flags.
    *
    * <p>Mutually exclusive with all field and method properties as use restricts the match to both
@@ -344,6 +404,63 @@
   MemberAccessFlags[] memberAccess() default {};
 
   /**
+   * Define the method-annotated-by pattern by fully qualified class name.
+   *
+   * <p>Mutually exclusive with the following other properties defining method-annotated-by:
+   *
+   * <ul>
+   *   <li>methodAnnotatedByClassConstant
+   *   <li>methodAnnotatedByClassNamePattern
+   * </ul>
+   *
+   * <p>Mutually exclusive with all field properties.
+   *
+   * <p>If none are specified the default is to match any method regardless of what the method is
+   * annotated by.
+   *
+   * @return The qualified class name that defines the annotation.
+   */
+  String methodAnnotatedByClassName() default "";
+
+  /**
+   * Define the method-annotated-by pattern by reference to a Class constant.
+   *
+   * <p>Mutually exclusive with the following other properties defining method-annotated-by:
+   *
+   * <ul>
+   *   <li>methodAnnotatedByClassName
+   *   <li>methodAnnotatedByClassNamePattern
+   * </ul>
+   *
+   * <p>Mutually exclusive with all field properties.
+   *
+   * <p>If none are specified the default is to match any method regardless of what the method is
+   * annotated by.
+   *
+   * @return The class-constant that defines the annotation.
+   */
+  Class<?> methodAnnotatedByClassConstant() default Object.class;
+
+  /**
+   * Define the method-annotated-by pattern by reference to a class-name pattern.
+   *
+   * <p>Mutually exclusive with the following other properties defining method-annotated-by:
+   *
+   * <ul>
+   *   <li>methodAnnotatedByClassName
+   *   <li>methodAnnotatedByClassConstant
+   * </ul>
+   *
+   * <p>Mutually exclusive with all field properties.
+   *
+   * <p>If none are specified the default is to match any method regardless of what the method is
+   * annotated by.
+   *
+   * @return The class-name pattern that defines the annotation.
+   */
+  ClassNamePattern methodAnnotatedByClassNamePattern() default @ClassNamePattern(simpleName = "");
+
+  /**
    * Define the method-access pattern by matching on access flags.
    *
    * <p>Mutually exclusive with all field properties.
@@ -469,6 +586,63 @@
   TypePattern[] methodParameterTypePatterns() default {@TypePattern(name = "")};
 
   /**
+   * Define the field-annotated-by pattern by fully qualified class name.
+   *
+   * <p>Mutually exclusive with the following other properties defining field-annotated-by:
+   *
+   * <ul>
+   *   <li>fieldAnnotatedByClassConstant
+   *   <li>fieldAnnotatedByClassNamePattern
+   * </ul>
+   *
+   * <p>Mutually exclusive with all method properties.
+   *
+   * <p>If none are specified the default is to match any field regardless of what the field is
+   * annotated by.
+   *
+   * @return The qualified class name that defines the annotation.
+   */
+  String fieldAnnotatedByClassName() default "";
+
+  /**
+   * Define the field-annotated-by pattern by reference to a Class constant.
+   *
+   * <p>Mutually exclusive with the following other properties defining field-annotated-by:
+   *
+   * <ul>
+   *   <li>fieldAnnotatedByClassName
+   *   <li>fieldAnnotatedByClassNamePattern
+   * </ul>
+   *
+   * <p>Mutually exclusive with all method properties.
+   *
+   * <p>If none are specified the default is to match any field regardless of what the field is
+   * annotated by.
+   *
+   * @return The class-constant that defines the annotation.
+   */
+  Class<?> fieldAnnotatedByClassConstant() default Object.class;
+
+  /**
+   * Define the field-annotated-by pattern by reference to a class-name pattern.
+   *
+   * <p>Mutually exclusive with the following other properties defining field-annotated-by:
+   *
+   * <ul>
+   *   <li>fieldAnnotatedByClassName
+   *   <li>fieldAnnotatedByClassConstant
+   * </ul>
+   *
+   * <p>Mutually exclusive with all method properties.
+   *
+   * <p>If none are specified the default is to match any field regardless of what the field is
+   * annotated by.
+   *
+   * @return The class-name pattern that defines the annotation.
+   */
+  ClassNamePattern fieldAnnotatedByClassNamePattern() default @ClassNamePattern(simpleName = "");
+
+  /**
    * Define the field-access pattern by matching on access flags.
    *
    * <p>Mutually exclusive with all method properties.
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepCondition.java b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepCondition.java
index 5737edf..4a39ca8 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepCondition.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepCondition.java
@@ -306,6 +306,69 @@
   String memberFromBinding() default "";
 
   /**
+   * Define the member-annotated-by pattern by fully qualified class name.
+   *
+   * <p>Mutually exclusive with the following other properties defining member-annotated-by:
+   *
+   * <ul>
+   *   <li>memberAnnotatedByClassConstant
+   *   <li>memberAnnotatedByClassNamePattern
+   *   <li>memberFromBinding
+   * </ul>
+   *
+   * <p>Mutually exclusive with all field and method properties as use restricts the match to both
+   * types of members.
+   *
+   * <p>If none are specified the default is to match any member regardless of what the member is
+   * annotated by.
+   *
+   * @return The qualified class name that defines the annotation.
+   */
+  String memberAnnotatedByClassName() default "";
+
+  /**
+   * Define the member-annotated-by pattern by reference to a Class constant.
+   *
+   * <p>Mutually exclusive with the following other properties defining member-annotated-by:
+   *
+   * <ul>
+   *   <li>memberAnnotatedByClassName
+   *   <li>memberAnnotatedByClassNamePattern
+   *   <li>memberFromBinding
+   * </ul>
+   *
+   * <p>Mutually exclusive with all field and method properties as use restricts the match to both
+   * types of members.
+   *
+   * <p>If none are specified the default is to match any member regardless of what the member is
+   * annotated by.
+   *
+   * @return The class-constant that defines the annotation.
+   */
+  Class<?> memberAnnotatedByClassConstant() default Object.class;
+
+  /**
+   * Define the member-annotated-by pattern by reference to a class-name pattern.
+   *
+   * <p>Mutually exclusive with the following other properties defining member-annotated-by:
+   *
+   * <ul>
+   *   <li>memberAnnotatedByClassName
+   *   <li>memberAnnotatedByClassConstant
+   *   <li>memberFromBinding
+   * </ul>
+   *
+   * <p>Mutually exclusive with all field and method properties as use restricts the match to both
+   * types of members.
+   *
+   * <p>If none are specified the default is to match any member regardless of what the member is
+   * annotated by.
+   *
+   * @return The class-name pattern that defines the annotation.
+   */
+  ClassNamePattern memberAnnotatedByClassNamePattern() default @ClassNamePattern(simpleName = "");
+
+  /**
    * Define the member-access pattern by matching on access flags.
    *
    * <p>Mutually exclusive with all field and method properties as use restricts the match to both
@@ -318,6 +381,66 @@
   MemberAccessFlags[] memberAccess() default {};
 
   /**
+   * Define the method-annotated-by pattern by fully qualified class name.
+   *
+   * <p>Mutually exclusive with the following other properties defining method-annotated-by:
+   *
+   * <ul>
+   *   <li>methodAnnotatedByClassConstant
+   *   <li>methodAnnotatedByClassNamePattern
+   *   <li>memberFromBinding
+   * </ul>
+   *
+   * <p>Mutually exclusive with all field properties.
+   *
+   * <p>If none are specified the default is to match any method regardless of what the method is
+   * annotated by.
+   *
+   * @return The qualified class name that defines the annotation.
+   */
+  String methodAnnotatedByClassName() default "";
+
+  /**
+   * Define the method-annotated-by pattern by reference to a Class constant.
+   *
+   * <p>Mutually exclusive with the following other properties defining method-annotated-by:
+   *
+   * <ul>
+   *   <li>methodAnnotatedByClassName
+   *   <li>methodAnnotatedByClassNamePattern
+   *   <li>memberFromBinding
+   * </ul>
+   *
+   * <p>Mutually exclusive with all field properties.
+   *
+   * <p>If none are specified the default is to match any method regardless of what the method is
+   * annotated by.
+   *
+   * @return The class-constant that defines the annotation.
+   */
+  Class<?> methodAnnotatedByClassConstant() default Object.class;
+
+  /**
+   * Define the method-annotated-by pattern by reference to a class-name pattern.
+   *
+   * <p>Mutually exclusive with the following other properties defining method-annotated-by:
+   *
+   * <ul>
+   *   <li>methodAnnotatedByClassName
+   *   <li>methodAnnotatedByClassConstant
+   *   <li>memberFromBinding
+   * </ul>
+   *
+   * <p>Mutually exclusive with all field properties.
+   *
+   * <p>If none are specified the default is to match any method regardless of what the method is
+   * annotated by.
+   *
+   * @return The class-name pattern that defines the annotation.
+   */
+  ClassNamePattern methodAnnotatedByClassNamePattern() default @ClassNamePattern(simpleName = "");
+
+  /**
    * Define the method-access pattern by matching on access flags.
    *
    * <p>Mutually exclusive with all field properties.
@@ -468,6 +591,66 @@
   TypePattern[] methodParameterTypePatterns() default {@TypePattern(name = "")};
 
   /**
+   * Define the field-annotated-by pattern by fully qualified class name.
+   *
+   * <p>Mutually exclusive with the following other properties defining field-annotated-by:
+   *
+   * <ul>
+   *   <li>fieldAnnotatedByClassConstant
+   *   <li>fieldAnnotatedByClassNamePattern
+   *   <li>memberFromBinding
+   * </ul>
+   *
+   * <p>Mutually exclusive with all method properties.
+   *
+   * <p>If none are specified the default is to match any field regardless of what the field is
+   * annotated by.
+   *
+   * @return The qualified class name that defines the annotation.
+   */
+  String fieldAnnotatedByClassName() default "";
+
+  /**
+   * Define the field-annotated-by pattern by reference to a Class constant.
+   *
+   * <p>Mutually exclusive with the following other properties defining field-annotated-by:
+   *
+   * <ul>
+   *   <li>fieldAnnotatedByClassName
+   *   <li>fieldAnnotatedByClassNamePattern
+   *   <li>memberFromBinding
+   * </ul>
+   *
+   * <p>Mutually exclusive with all method properties.
+   *
+   * <p>If none are specified the default is to match any field regardless of what the field is
+   * annotated by.
+   *
+   * @return The class-constant that defines the annotation.
+   */
+  Class<?> fieldAnnotatedByClassConstant() default Object.class;
+
+  /**
+   * Define the field-annotated-by pattern by reference to a class-name pattern.
+   *
+   * <p>Mutually exclusive with the following other properties defining field-annotated-by:
+   *
+   * <ul>
+   *   <li>fieldAnnotatedByClassName
+   *   <li>fieldAnnotatedByClassConstant
+   *   <li>memberFromBinding
+   * </ul>
+   *
+   * <p>Mutually exclusive with all method properties.
+   *
+   * <p>If none are specified the default is to match any field regardless of what the field is
+   * annotated by.
+   *
+   * @return The class-name pattern that defines the annotation.
+   */
+  ClassNamePattern fieldAnnotatedByClassNamePattern() default @ClassNamePattern(simpleName = "");
+
+  /**
    * Define the field-access pattern by matching on access flags.
    *
    * <p>Mutually exclusive with all method properties.
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 4b56b2e..91934e0 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
@@ -56,6 +56,66 @@
   KeepItemKind kind() default KeepItemKind.DEFAULT;
 
   /**
+   * Define the member-annotated-by pattern by fully qualified class name.
+   *
+   * <p>Mutually exclusive with the following other properties defining member-annotated-by:
+   *
+   * <ul>
+   *   <li>memberAnnotatedByClassConstant
+   *   <li>memberAnnotatedByClassNamePattern
+   * </ul>
+   *
+   * <p>Mutually exclusive with all field and method properties as use restricts the match to both
+   * types of members.
+   *
+   * <p>If none are specified the default is to match any member regardless of what the member is
+   * annotated by.
+   *
+   * @return The qualified class name that defines the annotation.
+   */
+  String memberAnnotatedByClassName() default "";
+
+  /**
+   * Define the member-annotated-by pattern by reference to a Class constant.
+   *
+   * <p>Mutually exclusive with the following other properties defining member-annotated-by:
+   *
+   * <ul>
+   *   <li>memberAnnotatedByClassName
+   *   <li>memberAnnotatedByClassNamePattern
+   * </ul>
+   *
+   * <p>Mutually exclusive with all field and method properties as use restricts the match to both
+   * types of members.
+   *
+   * <p>If none are specified the default is to match any member regardless of what the member is
+   * annotated by.
+   *
+   * @return The class-constant that defines the annotation.
+   */
+  Class<?> memberAnnotatedByClassConstant() default Object.class;
+
+  /**
+   * Define the member-annotated-by pattern by reference to a class-name pattern.
+   *
+   * <p>Mutually exclusive with the following other properties defining member-annotated-by:
+   *
+   * <ul>
+   *   <li>memberAnnotatedByClassName
+   *   <li>memberAnnotatedByClassConstant
+   * </ul>
+   *
+   * <p>Mutually exclusive with all field and method properties as use restricts the match to both
+   * types of members.
+   *
+   * <p>If none are specified the default is to match any member regardless of what the member is
+   * annotated by.
+   *
+   * @return The class-name pattern that defines the annotation.
+   */
+  ClassNamePattern memberAnnotatedByClassNamePattern() default @ClassNamePattern(simpleName = "");
+
+  /**
    * Define the member-access pattern by matching on access flags.
    *
    * <p>Mutually exclusive with all field and method properties as use restricts the match to both
@@ -66,6 +126,63 @@
   MemberAccessFlags[] memberAccess() default {};
 
   /**
+   * Define the method-annotated-by pattern by fully qualified class name.
+   *
+   * <p>Mutually exclusive with the following other properties defining method-annotated-by:
+   *
+   * <ul>
+   *   <li>methodAnnotatedByClassConstant
+   *   <li>methodAnnotatedByClassNamePattern
+   * </ul>
+   *
+   * <p>Mutually exclusive with all field properties.
+   *
+   * <p>If none are specified the default is to match any method regardless of what the method is
+   * annotated by.
+   *
+   * @return The qualified class name that defines the annotation.
+   */
+  String methodAnnotatedByClassName() default "";
+
+  /**
+   * Define the method-annotated-by pattern by reference to a Class constant.
+   *
+   * <p>Mutually exclusive with the following other properties defining method-annotated-by:
+   *
+   * <ul>
+   *   <li>methodAnnotatedByClassName
+   *   <li>methodAnnotatedByClassNamePattern
+   * </ul>
+   *
+   * <p>Mutually exclusive with all field properties.
+   *
+   * <p>If none are specified the default is to match any method regardless of what the method is
+   * annotated by.
+   *
+   * @return The class-constant that defines the annotation.
+   */
+  Class<?> methodAnnotatedByClassConstant() default Object.class;
+
+  /**
+   * Define the method-annotated-by pattern by reference to a class-name pattern.
+   *
+   * <p>Mutually exclusive with the following other properties defining method-annotated-by:
+   *
+   * <ul>
+   *   <li>methodAnnotatedByClassName
+   *   <li>methodAnnotatedByClassConstant
+   * </ul>
+   *
+   * <p>Mutually exclusive with all field properties.
+   *
+   * <p>If none are specified the default is to match any method regardless of what the method is
+   * annotated by.
+   *
+   * @return The class-name pattern that defines the annotation.
+   */
+  ClassNamePattern methodAnnotatedByClassNamePattern() default @ClassNamePattern(simpleName = "");
+
+  /**
    * Define the method-access pattern by matching on access flags.
    *
    * <p>Mutually exclusive with all field properties.
@@ -191,6 +308,63 @@
   TypePattern[] methodParameterTypePatterns() default {@TypePattern(name = "")};
 
   /**
+   * Define the field-annotated-by pattern by fully qualified class name.
+   *
+   * <p>Mutually exclusive with the following other properties defining field-annotated-by:
+   *
+   * <ul>
+   *   <li>fieldAnnotatedByClassConstant
+   *   <li>fieldAnnotatedByClassNamePattern
+   * </ul>
+   *
+   * <p>Mutually exclusive with all method properties.
+   *
+   * <p>If none are specified the default is to match any field regardless of what the field is
+   * annotated by.
+   *
+   * @return The qualified class name that defines the annotation.
+   */
+  String fieldAnnotatedByClassName() default "";
+
+  /**
+   * Define the field-annotated-by pattern by reference to a Class constant.
+   *
+   * <p>Mutually exclusive with the following other properties defining field-annotated-by:
+   *
+   * <ul>
+   *   <li>fieldAnnotatedByClassName
+   *   <li>fieldAnnotatedByClassNamePattern
+   * </ul>
+   *
+   * <p>Mutually exclusive with all method properties.
+   *
+   * <p>If none are specified the default is to match any field regardless of what the field is
+   * annotated by.
+   *
+   * @return The class-constant that defines the annotation.
+   */
+  Class<?> fieldAnnotatedByClassConstant() default Object.class;
+
+  /**
+   * Define the field-annotated-by pattern by reference to a class-name pattern.
+   *
+   * <p>Mutually exclusive with the following other properties defining field-annotated-by:
+   *
+   * <ul>
+   *   <li>fieldAnnotatedByClassName
+   *   <li>fieldAnnotatedByClassConstant
+   * </ul>
+   *
+   * <p>Mutually exclusive with all method properties.
+   *
+   * <p>If none are specified the default is to match any field regardless of what the field is
+   * annotated by.
+   *
+   * @return The class-name pattern that defines the annotation.
+   */
+  ClassNamePattern fieldAnnotatedByClassNamePattern() default @ClassNamePattern(simpleName = "");
+
+  /**
    * Define the field-access pattern by matching on access flags.
    *
    * <p>Mutually exclusive with all method properties.
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepTarget.java b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepTarget.java
index ed5e0ce..d18f91d 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepTarget.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepTarget.java
@@ -403,6 +403,69 @@
   String memberFromBinding() default "";
 
   /**
+   * Define the member-annotated-by pattern by fully qualified class name.
+   *
+   * <p>Mutually exclusive with the following other properties defining member-annotated-by:
+   *
+   * <ul>
+   *   <li>memberAnnotatedByClassConstant
+   *   <li>memberAnnotatedByClassNamePattern
+   *   <li>memberFromBinding
+   * </ul>
+   *
+   * <p>Mutually exclusive with all field and method properties as use restricts the match to both
+   * types of members.
+   *
+   * <p>If none are specified the default is to match any member regardless of what the member is
+   * annotated by.
+   *
+   * @return The qualified class name that defines the annotation.
+   */
+  String memberAnnotatedByClassName() default "";
+
+  /**
+   * Define the member-annotated-by pattern by reference to a Class constant.
+   *
+   * <p>Mutually exclusive with the following other properties defining member-annotated-by:
+   *
+   * <ul>
+   *   <li>memberAnnotatedByClassName
+   *   <li>memberAnnotatedByClassNamePattern
+   *   <li>memberFromBinding
+   * </ul>
+   *
+   * <p>Mutually exclusive with all field and method properties as use restricts the match to both
+   * types of members.
+   *
+   * <p>If none are specified the default is to match any member regardless of what the member is
+   * annotated by.
+   *
+   * @return The class-constant that defines the annotation.
+   */
+  Class<?> memberAnnotatedByClassConstant() default Object.class;
+
+  /**
+   * Define the member-annotated-by pattern by reference to a class-name pattern.
+   *
+   * <p>Mutually exclusive with the following other properties defining member-annotated-by:
+   *
+   * <ul>
+   *   <li>memberAnnotatedByClassName
+   *   <li>memberAnnotatedByClassConstant
+   *   <li>memberFromBinding
+   * </ul>
+   *
+   * <p>Mutually exclusive with all field and method properties as use restricts the match to both
+   * types of members.
+   *
+   * <p>If none are specified the default is to match any member regardless of what the member is
+   * annotated by.
+   *
+   * @return The class-name pattern that defines the annotation.
+   */
+  ClassNamePattern memberAnnotatedByClassNamePattern() default @ClassNamePattern(simpleName = "");
+
+  /**
    * Define the member-access pattern by matching on access flags.
    *
    * <p>Mutually exclusive with all field and method properties as use restricts the match to both
@@ -415,6 +478,66 @@
   MemberAccessFlags[] memberAccess() default {};
 
   /**
+   * Define the method-annotated-by pattern by fully qualified class name.
+   *
+   * <p>Mutually exclusive with the following other properties defining method-annotated-by:
+   *
+   * <ul>
+   *   <li>methodAnnotatedByClassConstant
+   *   <li>methodAnnotatedByClassNamePattern
+   *   <li>memberFromBinding
+   * </ul>
+   *
+   * <p>Mutually exclusive with all field properties.
+   *
+   * <p>If none are specified the default is to match any method regardless of what the method is
+   * annotated by.
+   *
+   * @return The qualified class name that defines the annotation.
+   */
+  String methodAnnotatedByClassName() default "";
+
+  /**
+   * Define the method-annotated-by pattern by reference to a Class constant.
+   *
+   * <p>Mutually exclusive with the following other properties defining method-annotated-by:
+   *
+   * <ul>
+   *   <li>methodAnnotatedByClassName
+   *   <li>methodAnnotatedByClassNamePattern
+   *   <li>memberFromBinding
+   * </ul>
+   *
+   * <p>Mutually exclusive with all field properties.
+   *
+   * <p>If none are specified the default is to match any method regardless of what the method is
+   * annotated by.
+   *
+   * @return The class-constant that defines the annotation.
+   */
+  Class<?> methodAnnotatedByClassConstant() default Object.class;
+
+  /**
+   * Define the method-annotated-by pattern by reference to a class-name pattern.
+   *
+   * <p>Mutually exclusive with the following other properties defining method-annotated-by:
+   *
+   * <ul>
+   *   <li>methodAnnotatedByClassName
+   *   <li>methodAnnotatedByClassConstant
+   *   <li>memberFromBinding
+   * </ul>
+   *
+   * <p>Mutually exclusive with all field properties.
+   *
+   * <p>If none are specified the default is to match any method regardless of what the method is
+   * annotated by.
+   *
+   * @return The class-name pattern that defines the annotation.
+   */
+  ClassNamePattern methodAnnotatedByClassNamePattern() default @ClassNamePattern(simpleName = "");
+
+  /**
    * Define the method-access pattern by matching on access flags.
    *
    * <p>Mutually exclusive with all field properties.
@@ -565,6 +688,66 @@
   TypePattern[] methodParameterTypePatterns() default {@TypePattern(name = "")};
 
   /**
+   * Define the field-annotated-by pattern by fully qualified class name.
+   *
+   * <p>Mutually exclusive with the following other properties defining field-annotated-by:
+   *
+   * <ul>
+   *   <li>fieldAnnotatedByClassConstant
+   *   <li>fieldAnnotatedByClassNamePattern
+   *   <li>memberFromBinding
+   * </ul>
+   *
+   * <p>Mutually exclusive with all method properties.
+   *
+   * <p>If none are specified the default is to match any field regardless of what the field is
+   * annotated by.
+   *
+   * @return The qualified class name that defines the annotation.
+   */
+  String fieldAnnotatedByClassName() default "";
+
+  /**
+   * Define the field-annotated-by pattern by reference to a Class constant.
+   *
+   * <p>Mutually exclusive with the following other properties defining field-annotated-by:
+   *
+   * <ul>
+   *   <li>fieldAnnotatedByClassName
+   *   <li>fieldAnnotatedByClassNamePattern
+   *   <li>memberFromBinding
+   * </ul>
+   *
+   * <p>Mutually exclusive with all method properties.
+   *
+   * <p>If none are specified the default is to match any field regardless of what the field is
+   * annotated by.
+   *
+   * @return The class-constant that defines the annotation.
+   */
+  Class<?> fieldAnnotatedByClassConstant() default Object.class;
+
+  /**
+   * Define the field-annotated-by pattern by reference to a class-name pattern.
+   *
+   * <p>Mutually exclusive with the following other properties defining field-annotated-by:
+   *
+   * <ul>
+   *   <li>fieldAnnotatedByClassName
+   *   <li>fieldAnnotatedByClassConstant
+   *   <li>memberFromBinding
+   * </ul>
+   *
+   * <p>Mutually exclusive with all method properties.
+   *
+   * <p>If none are specified the default is to match any field regardless of what the field is
+   * annotated by.
+   *
+   * @return The class-name pattern that defines the annotation.
+   */
+  ClassNamePattern fieldAnnotatedByClassNamePattern() default @ClassNamePattern(simpleName = "");
+
+  /**
    * Define the field-access pattern by matching on access flags.
    *
    * <p>Mutually exclusive with all method properties.
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/UsedByNative.java b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/UsedByNative.java
index f025c0e..b547778 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/UsedByNative.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/UsedByNative.java
@@ -99,6 +99,66 @@
   KeepConstraint[] constraints() default {};
 
   /**
+   * Define the member-annotated-by pattern by fully qualified class name.
+   *
+   * <p>Mutually exclusive with the following other properties defining member-annotated-by:
+   *
+   * <ul>
+   *   <li>memberAnnotatedByClassConstant
+   *   <li>memberAnnotatedByClassNamePattern
+   * </ul>
+   *
+   * <p>Mutually exclusive with all field and method properties as use restricts the match to both
+   * types of members.
+   *
+   * <p>If none are specified the default is to match any member regardless of what the member is
+   * annotated by.
+   *
+   * @return The qualified class name that defines the annotation.
+   */
+  String memberAnnotatedByClassName() default "";
+
+  /**
+   * Define the member-annotated-by pattern by reference to a Class constant.
+   *
+   * <p>Mutually exclusive with the following other properties defining member-annotated-by:
+   *
+   * <ul>
+   *   <li>memberAnnotatedByClassName
+   *   <li>memberAnnotatedByClassNamePattern
+   * </ul>
+   *
+   * <p>Mutually exclusive with all field and method properties as use restricts the match to both
+   * types of members.
+   *
+   * <p>If none are specified the default is to match any member regardless of what the member is
+   * annotated by.
+   *
+   * @return The class-constant that defines the annotation.
+   */
+  Class<?> memberAnnotatedByClassConstant() default Object.class;
+
+  /**
+   * Define the member-annotated-by pattern by reference to a class-name pattern.
+   *
+   * <p>Mutually exclusive with the following other properties defining member-annotated-by:
+   *
+   * <ul>
+   *   <li>memberAnnotatedByClassName
+   *   <li>memberAnnotatedByClassConstant
+   * </ul>
+   *
+   * <p>Mutually exclusive with all field and method properties as use restricts the match to both
+   * types of members.
+   *
+   * <p>If none are specified the default is to match any member regardless of what the member is
+   * annotated by.
+   *
+   * @return The class-name pattern that defines the annotation.
+   */
+  ClassNamePattern memberAnnotatedByClassNamePattern() default @ClassNamePattern(simpleName = "");
+
+  /**
    * Define the member-access pattern by matching on access flags.
    *
    * <p>Mutually exclusive with all field and method properties as use restricts the match to both
@@ -109,6 +169,63 @@
   MemberAccessFlags[] memberAccess() default {};
 
   /**
+   * Define the method-annotated-by pattern by fully qualified class name.
+   *
+   * <p>Mutually exclusive with the following other properties defining method-annotated-by:
+   *
+   * <ul>
+   *   <li>methodAnnotatedByClassConstant
+   *   <li>methodAnnotatedByClassNamePattern
+   * </ul>
+   *
+   * <p>Mutually exclusive with all field properties.
+   *
+   * <p>If none are specified the default is to match any method regardless of what the method is
+   * annotated by.
+   *
+   * @return The qualified class name that defines the annotation.
+   */
+  String methodAnnotatedByClassName() default "";
+
+  /**
+   * Define the method-annotated-by pattern by reference to a Class constant.
+   *
+   * <p>Mutually exclusive with the following other properties defining method-annotated-by:
+   *
+   * <ul>
+   *   <li>methodAnnotatedByClassName
+   *   <li>methodAnnotatedByClassNamePattern
+   * </ul>
+   *
+   * <p>Mutually exclusive with all field properties.
+   *
+   * <p>If none are specified the default is to match any method regardless of what the method is
+   * annotated by.
+   *
+   * @return The class-constant that defines the annotation.
+   */
+  Class<?> methodAnnotatedByClassConstant() default Object.class;
+
+  /**
+   * Define the method-annotated-by pattern by reference to a class-name pattern.
+   *
+   * <p>Mutually exclusive with the following other properties defining method-annotated-by:
+   *
+   * <ul>
+   *   <li>methodAnnotatedByClassName
+   *   <li>methodAnnotatedByClassConstant
+   * </ul>
+   *
+   * <p>Mutually exclusive with all field properties.
+   *
+   * <p>If none are specified the default is to match any method regardless of what the method is
+   * annotated by.
+   *
+   * @return The class-name pattern that defines the annotation.
+   */
+  ClassNamePattern methodAnnotatedByClassNamePattern() default @ClassNamePattern(simpleName = "");
+
+  /**
    * Define the method-access pattern by matching on access flags.
    *
    * <p>Mutually exclusive with all field properties.
@@ -234,6 +351,63 @@
   TypePattern[] methodParameterTypePatterns() default {@TypePattern(name = "")};
 
   /**
+   * Define the field-annotated-by pattern by fully qualified class name.
+   *
+   * <p>Mutually exclusive with the following other properties defining field-annotated-by:
+   *
+   * <ul>
+   *   <li>fieldAnnotatedByClassConstant
+   *   <li>fieldAnnotatedByClassNamePattern
+   * </ul>
+   *
+   * <p>Mutually exclusive with all method properties.
+   *
+   * <p>If none are specified the default is to match any field regardless of what the field is
+   * annotated by.
+   *
+   * @return The qualified class name that defines the annotation.
+   */
+  String fieldAnnotatedByClassName() default "";
+
+  /**
+   * Define the field-annotated-by pattern by reference to a Class constant.
+   *
+   * <p>Mutually exclusive with the following other properties defining field-annotated-by:
+   *
+   * <ul>
+   *   <li>fieldAnnotatedByClassName
+   *   <li>fieldAnnotatedByClassNamePattern
+   * </ul>
+   *
+   * <p>Mutually exclusive with all method properties.
+   *
+   * <p>If none are specified the default is to match any field regardless of what the field is
+   * annotated by.
+   *
+   * @return The class-constant that defines the annotation.
+   */
+  Class<?> fieldAnnotatedByClassConstant() default Object.class;
+
+  /**
+   * Define the field-annotated-by pattern by reference to a class-name pattern.
+   *
+   * <p>Mutually exclusive with the following other properties defining field-annotated-by:
+   *
+   * <ul>
+   *   <li>fieldAnnotatedByClassName
+   *   <li>fieldAnnotatedByClassConstant
+   * </ul>
+   *
+   * <p>Mutually exclusive with all method properties.
+   *
+   * <p>If none are specified the default is to match any field regardless of what the field is
+   * annotated by.
+   *
+   * @return The class-name pattern that defines the annotation.
+   */
+  ClassNamePattern fieldAnnotatedByClassNamePattern() default @ClassNamePattern(simpleName = "");
+
+  /**
    * Define the field-access pattern by matching on access flags.
    *
    * <p>Mutually exclusive with all method properties.
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/UsedByReflection.java b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/UsedByReflection.java
index 8076e51..a139965 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/UsedByReflection.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/UsedByReflection.java
@@ -99,6 +99,66 @@
   KeepConstraint[] constraints() default {};
 
   /**
+   * Define the member-annotated-by pattern by fully qualified class name.
+   *
+   * <p>Mutually exclusive with the following other properties defining member-annotated-by:
+   *
+   * <ul>
+   *   <li>memberAnnotatedByClassConstant
+   *   <li>memberAnnotatedByClassNamePattern
+   * </ul>
+   *
+   * <p>Mutually exclusive with all field and method properties as use restricts the match to both
+   * types of members.
+   *
+   * <p>If none are specified the default is to match any member regardless of what the member is
+   * annotated by.
+   *
+   * @return The qualified class name that defines the annotation.
+   */
+  String memberAnnotatedByClassName() default "";
+
+  /**
+   * Define the member-annotated-by pattern by reference to a Class constant.
+   *
+   * <p>Mutually exclusive with the following other properties defining member-annotated-by:
+   *
+   * <ul>
+   *   <li>memberAnnotatedByClassName
+   *   <li>memberAnnotatedByClassNamePattern
+   * </ul>
+   *
+   * <p>Mutually exclusive with all field and method properties as use restricts the match to both
+   * types of members.
+   *
+   * <p>If none are specified the default is to match any member regardless of what the member is
+   * annotated by.
+   *
+   * @return The class-constant that defines the annotation.
+   */
+  Class<?> memberAnnotatedByClassConstant() default Object.class;
+
+  /**
+   * Define the member-annotated-by pattern by reference to a class-name pattern.
+   *
+   * <p>Mutually exclusive with the following other properties defining member-annotated-by:
+   *
+   * <ul>
+   *   <li>memberAnnotatedByClassName
+   *   <li>memberAnnotatedByClassConstant
+   * </ul>
+   *
+   * <p>Mutually exclusive with all field and method properties as use restricts the match to both
+   * types of members.
+   *
+   * <p>If none are specified the default is to match any member regardless of what the member is
+   * annotated by.
+   *
+   * @return The class-name pattern that defines the annotation.
+   */
+  ClassNamePattern memberAnnotatedByClassNamePattern() default @ClassNamePattern(simpleName = "");
+
+  /**
    * Define the member-access pattern by matching on access flags.
    *
    * <p>Mutually exclusive with all field and method properties as use restricts the match to both
@@ -109,6 +169,63 @@
   MemberAccessFlags[] memberAccess() default {};
 
   /**
+   * Define the method-annotated-by pattern by fully qualified class name.
+   *
+   * <p>Mutually exclusive with the following other properties defining method-annotated-by:
+   *
+   * <ul>
+   *   <li>methodAnnotatedByClassConstant
+   *   <li>methodAnnotatedByClassNamePattern
+   * </ul>
+   *
+   * <p>Mutually exclusive with all field properties.
+   *
+   * <p>If none are specified the default is to match any method regardless of what the method is
+   * annotated by.
+   *
+   * @return The qualified class name that defines the annotation.
+   */
+  String methodAnnotatedByClassName() default "";
+
+  /**
+   * Define the method-annotated-by pattern by reference to a Class constant.
+   *
+   * <p>Mutually exclusive with the following other properties defining method-annotated-by:
+   *
+   * <ul>
+   *   <li>methodAnnotatedByClassName
+   *   <li>methodAnnotatedByClassNamePattern
+   * </ul>
+   *
+   * <p>Mutually exclusive with all field properties.
+   *
+   * <p>If none are specified the default is to match any method regardless of what the method is
+   * annotated by.
+   *
+   * @return The class-constant that defines the annotation.
+   */
+  Class<?> methodAnnotatedByClassConstant() default Object.class;
+
+  /**
+   * Define the method-annotated-by pattern by reference to a class-name pattern.
+   *
+   * <p>Mutually exclusive with the following other properties defining method-annotated-by:
+   *
+   * <ul>
+   *   <li>methodAnnotatedByClassName
+   *   <li>methodAnnotatedByClassConstant
+   * </ul>
+   *
+   * <p>Mutually exclusive with all field properties.
+   *
+   * <p>If none are specified the default is to match any method regardless of what the method is
+   * annotated by.
+   *
+   * @return The class-name pattern that defines the annotation.
+   */
+  ClassNamePattern methodAnnotatedByClassNamePattern() default @ClassNamePattern(simpleName = "");
+
+  /**
    * Define the method-access pattern by matching on access flags.
    *
    * <p>Mutually exclusive with all field properties.
@@ -234,6 +351,63 @@
   TypePattern[] methodParameterTypePatterns() default {@TypePattern(name = "")};
 
   /**
+   * Define the field-annotated-by pattern by fully qualified class name.
+   *
+   * <p>Mutually exclusive with the following other properties defining field-annotated-by:
+   *
+   * <ul>
+   *   <li>fieldAnnotatedByClassConstant
+   *   <li>fieldAnnotatedByClassNamePattern
+   * </ul>
+   *
+   * <p>Mutually exclusive with all method properties.
+   *
+   * <p>If none are specified the default is to match any field regardless of what the field is
+   * annotated by.
+   *
+   * @return The qualified class name that defines the annotation.
+   */
+  String fieldAnnotatedByClassName() default "";
+
+  /**
+   * Define the field-annotated-by pattern by reference to a Class constant.
+   *
+   * <p>Mutually exclusive with the following other properties defining field-annotated-by:
+   *
+   * <ul>
+   *   <li>fieldAnnotatedByClassName
+   *   <li>fieldAnnotatedByClassNamePattern
+   * </ul>
+   *
+   * <p>Mutually exclusive with all method properties.
+   *
+   * <p>If none are specified the default is to match any field regardless of what the field is
+   * annotated by.
+   *
+   * @return The class-constant that defines the annotation.
+   */
+  Class<?> fieldAnnotatedByClassConstant() default Object.class;
+
+  /**
+   * Define the field-annotated-by pattern by reference to a class-name pattern.
+   *
+   * <p>Mutually exclusive with the following other properties defining field-annotated-by:
+   *
+   * <ul>
+   *   <li>fieldAnnotatedByClassName
+   *   <li>fieldAnnotatedByClassConstant
+   * </ul>
+   *
+   * <p>Mutually exclusive with all method properties.
+   *
+   * <p>If none are specified the default is to match any field regardless of what the field is
+   * annotated by.
+   *
+   * @return The class-name pattern that defines the annotation.
+   */
+  ClassNamePattern fieldAnnotatedByClassNamePattern() default @ClassNamePattern(simpleName = "");
+
+  /**
    * Define the field-access pattern by matching on access flags.
    *
    * <p>Mutually exclusive with all method properties.
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 6da12d5..b8b81ae 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
@@ -1297,6 +1297,7 @@
     private final ParsingContext parsingContext;
     private KeepMethodAccessPattern.Builder accessBuilder = null;
     private KeepMethodPattern.Builder builder = null;
+    private final ClassNameParser annotatedByParser;
     private final StringPatternParser nameParser;
     private final MethodReturnTypeParser returnTypeParser;
     private final MethodParametersParser parametersParser;
@@ -1306,6 +1307,13 @@
     private MethodDeclarationParser(ParsingContext parsingContext) {
       this.parsingContext = parsingContext;
 
+      annotatedByParser = new ClassNameParser(parsingContext.group(Item.methodAnnotatedByGroup));
+      annotatedByParser.setProperty(Item.methodAnnotatedByClassName, ClassNameProperty.NAME);
+      annotatedByParser.setProperty(
+          Item.methodAnnotatedByClassConstant, ClassNameProperty.CONSTANT);
+      annotatedByParser.setProperty(
+          Item.methodAnnotatedByClassNamePattern, ClassNameProperty.PATTERN);
+
       nameParser = new StringPatternParser(parsingContext.group(Item.methodNameGroup));
       nameParser.setProperty(Item.methodName, StringProperty.EXACT);
       nameParser.setProperty(Item.methodNamePattern, StringProperty.PATTERN);
@@ -1319,7 +1327,7 @@
       parametersParser.setProperty(Item.methodParameters, TypeProperty.TYPE_NAME);
       parametersParser.setProperty(Item.methodParameterTypePatterns, TypeProperty.TYPE_PATTERN);
 
-      parsers = ImmutableList.of(nameParser, returnTypeParser, parametersParser);
+      parsers = ImmutableList.of(annotatedByParser, nameParser, returnTypeParser, parametersParser);
     }
 
     @Override
@@ -1343,14 +1351,17 @@
       if (accessBuilder != null) {
         getBuilder().setAccessPattern(accessBuilder.build());
       }
-      if (!nameParser.isDefault()) {
+      if (annotatedByParser.isDeclared()) {
+        getBuilder().setAnnotatedByPattern(OptionalPattern.of(annotatedByParser.getValue()));
+      }
+      if (nameParser.isDeclared()) {
         KeepStringPattern namePattern = nameParser.getValue();
         getBuilder().setNamePattern(KeepMethodNamePattern.fromStringPattern(namePattern));
       }
-      if (!returnTypeParser.isDefault()) {
+      if (returnTypeParser.isDeclared()) {
         getBuilder().setReturnTypePattern(returnTypeParser.getValue());
       }
-      if (!parametersParser.isDefault()) {
+      if (parametersParser.isDeclared()) {
         getBuilder().setParametersPattern(parametersParser.getValue());
       }
       return builder != null ? builder.build() : null;
@@ -1369,6 +1380,7 @@
   private static class FieldDeclarationParser extends DeclarationParser<KeepFieldPattern> {
 
     private final ParsingContext parsingContext;
+    private final ClassNameParser annotatedByParser;
     private final StringPatternParser nameParser;
     private final FieldTypeParser typeParser;
     private KeepFieldAccessPattern.Builder accessBuilder = null;
@@ -1377,6 +1389,12 @@
 
     public FieldDeclarationParser(ParsingContext parsingContext) {
       this.parsingContext = parsingContext;
+      annotatedByParser = new ClassNameParser(parsingContext.group(Item.fieldAnnotatedByGroup));
+      annotatedByParser.setProperty(Item.fieldAnnotatedByClassName, ClassNameProperty.NAME);
+      annotatedByParser.setProperty(Item.fieldAnnotatedByClassConstant, ClassNameProperty.CONSTANT);
+      annotatedByParser.setProperty(
+          Item.fieldAnnotatedByClassNamePattern, ClassNameProperty.PATTERN);
+
       nameParser = new StringPatternParser(parsingContext.group(Item.fieldNameGroup));
       nameParser.setProperty(Item.fieldName, StringProperty.EXACT);
       nameParser.setProperty(Item.fieldNamePattern, StringProperty.PATTERN);
@@ -1386,7 +1404,7 @@
       typeParser.setProperty(Item.fieldType, TypeProperty.TYPE_NAME);
       typeParser.setProperty(Item.fieldTypeConstant, TypeProperty.TYPE_CONSTANT);
 
-      parsers = ImmutableList.of(nameParser, typeParser);
+      parsers = ImmutableList.of(annotatedByParser, nameParser, typeParser);
     }
 
     @Override
@@ -1403,17 +1421,20 @@
 
     @Override
     public boolean isDeclared() {
-      return accessBuilder != null || builder != null;
+      return accessBuilder != null || builder != null || super.isDeclared();
     }
 
     public KeepFieldPattern getValue() {
       if (accessBuilder != null) {
         getBuilder().setAccessPattern(accessBuilder.build());
       }
-      if (!nameParser.isDefault()) {
+      if (annotatedByParser.isDeclared()) {
+        getBuilder().setAnnotatedByPattern(OptionalPattern.of(annotatedByParser.getValue()));
+      }
+      if (nameParser.isDeclared()) {
         getBuilder().setNamePattern(KeepFieldNamePattern.fromStringPattern(nameParser.getValue()));
       }
-      if (!typeParser.isDefault()) {
+      if (typeParser.isDeclared()) {
         getBuilder().setTypePattern(typeParser.getValue());
       }
       return builder != null ? builder.build() : null;
@@ -1433,15 +1454,25 @@
 
     private final ParsingContext parsingContext;
     private KeepMemberAccessPattern.Builder accessBuilder = null;
+    private final ClassNameParser annotatedByParser;
+
     private final MethodDeclarationParser methodDeclaration;
     private final FieldDeclarationParser fieldDeclaration;
     private final List<Parser<?>> parsers;
 
     MemberDeclarationParser(ParsingContext parsingContext) {
       this.parsingContext = parsingContext.group(Item.memberGroup);
+
+      annotatedByParser = new ClassNameParser(parsingContext.group(Item.memberAnnotatedByGroup));
+      annotatedByParser.setProperty(Item.memberAnnotatedByClassName, ClassNameProperty.NAME);
+      annotatedByParser.setProperty(
+          Item.memberAnnotatedByClassConstant, ClassNameProperty.CONSTANT);
+      annotatedByParser.setProperty(
+          Item.memberAnnotatedByClassNamePattern, ClassNameProperty.PATTERN);
+
       methodDeclaration = new MethodDeclarationParser(parsingContext);
       fieldDeclaration = new FieldDeclarationParser(parsingContext);
-      parsers = ImmutableList.of(methodDeclaration, fieldDeclaration);
+      parsers = ImmutableList.of(annotatedByParser, methodDeclaration, fieldDeclaration);
     }
 
     @Override
@@ -1451,20 +1482,23 @@
 
     @Override
     public boolean isDeclared() {
-      return accessBuilder != null
-          || methodDeclaration.isDeclared()
-          || fieldDeclaration.isDeclared();
+      return accessBuilder != null || super.isDeclared();
     }
 
     public KeepMemberPattern getValue() {
       KeepMethodPattern method = methodDeclaration.getValue();
       KeepFieldPattern field = fieldDeclaration.getValue();
-      if (accessBuilder != null) {
+      if (accessBuilder != null || annotatedByParser.isDeclared()) {
         if (method != null || field != null) {
           throw parsingContext.error(
               "Cannot define common member access as well as field or method pattern");
         }
-        return KeepMemberPattern.memberBuilder().setAccessPattern(accessBuilder.build()).build();
+        KeepMemberPattern.Builder builder = KeepMemberPattern.memberBuilder();
+        if (accessBuilder != null) {
+          builder.setAccessPattern(accessBuilder.build());
+        }
+        builder.setAnnotatedByPattern(OptionalPattern.ofNullable(annotatedByParser.getValue()));
+        return builder.build();
       }
       if (method != null && field != null) {
         throw parsingContext.error("Cannot define both a field and a method 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 40010dc..2ac416c 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
@@ -86,7 +86,17 @@
     public static final String classAnnotatedByClassConstant = "classAnnotatedByClassConstant";
     public static final String classAnnotatedByClassNamePattern =
         "classAnnotatedByClassNamePattern";
+    public static final String memberAnnotatedByGroup = "member-annotated-by";
+    public static final String memberAnnotatedByClassName = "memberAnnotatedByClassName";
+    public static final String memberAnnotatedByClassConstant = "memberAnnotatedByClassConstant";
+    public static final String memberAnnotatedByClassNamePattern =
+        "memberAnnotatedByClassNamePattern";
     public static final String memberAccess = "memberAccess";
+    public static final String methodAnnotatedByGroup = "method-annotated-by";
+    public static final String methodAnnotatedByClassName = "methodAnnotatedByClassName";
+    public static final String methodAnnotatedByClassConstant = "methodAnnotatedByClassConstant";
+    public static final String methodAnnotatedByClassNamePattern =
+        "methodAnnotatedByClassNamePattern";
     public static final String methodAccess = "methodAccess";
     public static final String methodNameGroup = "method-name";
     public static final String methodName = "methodName";
@@ -98,6 +108,11 @@
     public static final String parametersGroup = "parameters";
     public static final String methodParameters = "methodParameters";
     public static final String methodParameterTypePatterns = "methodParameterTypePatterns";
+    public static final String fieldAnnotatedByGroup = "field-annotated-by";
+    public static final String fieldAnnotatedByClassName = "fieldAnnotatedByClassName";
+    public static final String fieldAnnotatedByClassConstant = "fieldAnnotatedByClassConstant";
+    public static final String fieldAnnotatedByClassNamePattern =
+        "fieldAnnotatedByClassNamePattern";
     public static final String fieldAccess = "fieldAccess";
     public static final String fieldNameGroup = "field-name";
     public static final String fieldName = "fieldName";
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepEdge.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepEdge.java
index 992cf1e..713686a 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepEdge.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepEdge.java
@@ -44,7 +44,7 @@
  *
  *   ITEM_PATTERN ::= CLASS_ITEM_PATTERN | MEMBER_ITEM_PATTERN
  *   CLASS_ITEM_PATTERN ::= class QUALIFIED_CLASS_NAME_PATTERN
- *                              annotated-by OPT(ANNOTATED_BY_PATTERN)
+ *                              annotated-by ANNOTATED_BY_PATTERN
  *                              instance-of INSTANCE_OF_PATTERN
  *   MEMBER_ITEM_PATTERN ::= CLASS_ITEM_REFERENCE { MEMBER_PATTERN }
  *
@@ -69,19 +69,24 @@
  *   INSTANCE_OF_PATTERN_INCLUSIVE ::= QUALIFIED_CLASS_NAME_PATTERN
  *   INSTANCE_OF_PATTERN_EXCLUSIVE ::= QUALIFIED_CLASS_NAME_PATTERN
  *
- *   ANNOTATED_BY_PATTERN ::= QUALIFIED_CLASS_NAME_PATTERN
+ *   ANNOTATED_BY_PATTERN ::= OPT(QUALIFIED_CLASS_NAME_PATTERN)
  *
  *   MEMBER_PATTERN ::= GENERAL_MEMBER_PATTERN | FIELD_PATTERN | METHOD_PATTERN
  *
- *   GENERAL_MEMBER_PATTERN ::= MEMBER_ACCESS_PATTERN any
+ *   GENERAL_MEMBER_PATTERN
+ *     ::= ANNOTATED_BY_PATTERN
+ *           MEMBER_ACCESS_PATTERN
+ *           any
  *
  *   FIELD_PATTERN
- *     ::= FIELD_ACCESS_PATTERN
+ *     ::= ANNOTATED_BY_PATTERN
+ *           FIELD_ACCESS_PATTERN
  *           FIELD_TYPE_PATTERN
  *           FIELD_NAME_PATTERN;
  *
  *   METHOD_PATTERN
- *     ::= METHOD_ACCESS_PATTERN
+ *     ::= ANNOTATED_BY_PATTERN
+ *           METHOD_ACCESS_PATTERN
  *           METHOD_RETURN_TYPE_PATTERN
  *           METHOD_NAME_PATTERN
  *           METHOD_PARAMETERS_PATTERN
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepFieldPattern.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepFieldPattern.java
index b5b028d..64b8ed8 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepFieldPattern.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepFieldPattern.java
@@ -17,6 +17,8 @@
 
   public static class Builder {
 
+    private OptionalPattern<KeepQualifiedClassNamePattern> annotatedByPattern =
+        OptionalPattern.absent();
     private KeepFieldAccessPattern accessPattern = KeepFieldAccessPattern.anyFieldAccess();
     private KeepFieldNamePattern namePattern = KeepFieldNamePattern.any();
     private KeepFieldTypePattern typePattern = KeepFieldTypePattern.any();
@@ -35,6 +37,12 @@
               .build());
     }
 
+    public Builder setAnnotatedByPattern(
+        OptionalPattern<KeepQualifiedClassNamePattern> annotatedByPattern) {
+      this.annotatedByPattern = annotatedByPattern;
+      return this;
+    }
+
     public Builder setAccessPattern(KeepFieldAccessPattern accessPattern) {
       this.accessPattern = accessPattern;
       return self();
@@ -51,21 +59,25 @@
     }
 
     public KeepFieldPattern build() {
-      return new KeepFieldPattern(accessPattern, namePattern, typePattern);
+      return new KeepFieldPattern(annotatedByPattern, accessPattern, namePattern, typePattern);
     }
   }
 
+  private final OptionalPattern<KeepQualifiedClassNamePattern> annotatedByPattern;
   private final KeepFieldAccessPattern accessPattern;
   private final KeepFieldNamePattern namePattern;
   private final KeepFieldTypePattern typePattern;
 
   private KeepFieldPattern(
+      OptionalPattern<KeepQualifiedClassNamePattern> annotatedByPattern,
       KeepFieldAccessPattern accessPattern,
       KeepFieldNamePattern namePattern,
       KeepFieldTypePattern typePattern) {
+    assert annotatedByPattern != null;
     assert accessPattern != null;
     assert namePattern != null;
     assert typePattern != null;
+    this.annotatedByPattern = annotatedByPattern;
     this.accessPattern = accessPattern;
     this.namePattern = namePattern;
     this.typePattern = typePattern;
@@ -77,7 +89,15 @@
   }
 
   public boolean isAnyField() {
-    return accessPattern.isAny() && namePattern.isAny() && typePattern.isAny();
+    return annotatedByPattern.isAbsent()
+        && accessPattern.isAny()
+        && namePattern.isAny()
+        && typePattern.isAny();
+  }
+
+  @Override
+  public OptionalPattern<KeepQualifiedClassNamePattern> getAnnotatedByPattern() {
+    return annotatedByPattern;
   }
 
   @Override
@@ -102,19 +122,22 @@
       return false;
     }
     KeepFieldPattern that = (KeepFieldPattern) o;
-    return accessPattern.equals(that.accessPattern)
+    return annotatedByPattern.equals(that.annotatedByPattern)
+        && accessPattern.equals(that.accessPattern)
         && namePattern.equals(that.namePattern)
         && typePattern.equals(that.typePattern);
   }
 
   @Override
   public int hashCode() {
-    return Objects.hash(accessPattern, namePattern, typePattern);
+    return Objects.hash(annotatedByPattern, accessPattern, namePattern, typePattern);
   }
 
   @Override
   public String toString() {
     return "KeepFieldPattern{"
+        + "annotated-by="
+        + annotatedByPattern
         + "access="
         + accessPattern
         + ", name="
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMemberPattern.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMemberPattern.java
index 5cbf53d..fd9cf63 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMemberPattern.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMemberPattern.java
@@ -3,6 +3,8 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.keepanno.ast;
 
+import java.util.Objects;
+
 public abstract class KeepMemberPattern {
 
   public static KeepMemberPattern allMembers() {
@@ -14,32 +16,48 @@
   }
 
   public static class Builder {
+    private OptionalPattern<KeepQualifiedClassNamePattern> annotatedByPattern;
     private KeepMemberAccessPattern accessPattern = KeepMemberAccessPattern.anyMemberAccess();
 
+    public Builder setAnnotatedByPattern(
+        OptionalPattern<KeepQualifiedClassNamePattern> annotatedByPattern) {
+      this.annotatedByPattern = annotatedByPattern;
+      return this;
+    }
+
     public Builder setAccessPattern(KeepMemberAccessPattern accessPattern) {
       this.accessPattern = accessPattern;
       return this;
     }
 
     public KeepMemberPattern build() {
-      if (accessPattern.isAny()) {
+      if (annotatedByPattern.isAbsent() && accessPattern.isAny()) {
         return allMembers();
       }
-      return new Some(accessPattern);
+      return new Some(annotatedByPattern, accessPattern);
     }
   }
 
   private static class Some extends KeepMemberPattern {
     private static final KeepMemberPattern ANY =
-        new Some(KeepMemberAccessPattern.anyMemberAccess());
+        new Some(OptionalPattern.absent(), KeepMemberAccessPattern.anyMemberAccess());
 
+    private final OptionalPattern<KeepQualifiedClassNamePattern> annotatedByPattern;
     private final KeepMemberAccessPattern accessPattern;
 
-    public Some(KeepMemberAccessPattern accessPattern) {
+    public Some(
+        OptionalPattern<KeepQualifiedClassNamePattern> annotatedByPattern,
+        KeepMemberAccessPattern accessPattern) {
+      this.annotatedByPattern = annotatedByPattern;
       this.accessPattern = accessPattern;
     }
 
     @Override
+    public OptionalPattern<KeepQualifiedClassNamePattern> getAnnotatedByPattern() {
+      return annotatedByPattern;
+    }
+
+    @Override
     public KeepMemberAccessPattern getAccessPattern() {
       return accessPattern;
     }
@@ -53,17 +71,18 @@
         return false;
       }
       Some some = (Some) o;
-      return accessPattern.equals(some.accessPattern);
+      return annotatedByPattern.equals(some.annotatedByPattern)
+          && accessPattern.equals(some.accessPattern);
     }
 
     @Override
     public int hashCode() {
-      return accessPattern.hashCode();
+      return Objects.hash(annotatedByPattern, accessPattern);
     }
 
     @Override
     public String toString() {
-      return "Member{" + "access=" + accessPattern + '}';
+      return "Member{" + "annotated-by=" + annotatedByPattern + ", access=" + accessPattern + '}';
     }
   }
 
@@ -94,4 +113,6 @@
   }
 
   public abstract KeepMemberAccessPattern getAccessPattern();
+
+  public abstract OptionalPattern<KeepQualifiedClassNamePattern> getAnnotatedByPattern();
 }
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMethodPattern.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMethodPattern.java
index 0e30b11..27128ba 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMethodPattern.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMethodPattern.java
@@ -17,6 +17,8 @@
 
   public static class Builder {
 
+    private OptionalPattern<KeepQualifiedClassNamePattern> annotatedByPattern =
+        OptionalPattern.absent();
     private KeepMethodAccessPattern accessPattern = KeepMethodAccessPattern.anyMethodAccess();
     private KeepMethodNamePattern namePattern = KeepMethodNamePattern.any();
     private KeepMethodReturnTypePattern returnTypePattern = KeepMethodReturnTypePattern.any();
@@ -36,6 +38,12 @@
               .build());
     }
 
+    public Builder setAnnotatedByPattern(
+        OptionalPattern<KeepQualifiedClassNamePattern> annotatedByPattern) {
+      this.annotatedByPattern = annotatedByPattern;
+      return this;
+    }
+
     public Builder setAccessPattern(KeepMethodAccessPattern accessPattern) {
       this.accessPattern = accessPattern;
       return self();
@@ -69,24 +77,28 @@
         returnTypePattern = KeepMethodReturnTypePattern.voidType();
       }
       return new KeepMethodPattern(
-          accessPattern, namePattern, returnTypePattern, parametersPattern);
+          annotatedByPattern, accessPattern, namePattern, returnTypePattern, parametersPattern);
     }
   }
 
+  private final OptionalPattern<KeepQualifiedClassNamePattern> annotatedByPattern;
   private final KeepMethodAccessPattern accessPattern;
   private final KeepMethodNamePattern namePattern;
   private final KeepMethodReturnTypePattern returnTypePattern;
   private final KeepMethodParametersPattern parametersPattern;
 
   private KeepMethodPattern(
+      OptionalPattern<KeepQualifiedClassNamePattern> annotatedByPattern,
       KeepMethodAccessPattern accessPattern,
       KeepMethodNamePattern namePattern,
       KeepMethodReturnTypePattern returnTypePattern,
       KeepMethodParametersPattern parametersPattern) {
+    assert annotatedByPattern != null;
     assert accessPattern != null;
     assert namePattern != null;
     assert returnTypePattern != null;
     assert parametersPattern != null;
+    this.annotatedByPattern = annotatedByPattern;
     this.accessPattern = accessPattern;
     this.namePattern = namePattern;
     this.returnTypePattern = returnTypePattern;
@@ -99,13 +111,19 @@
   }
 
   public boolean isAnyMethod() {
-    return accessPattern.isAny()
+    return annotatedByPattern.isAbsent()
+        && accessPattern.isAny()
         && namePattern.isAny()
         && returnTypePattern.isAny()
         && parametersPattern.isAny();
   }
 
   @Override
+  public OptionalPattern<KeepQualifiedClassNamePattern> getAnnotatedByPattern() {
+    return annotatedByPattern;
+  }
+
+  @Override
   public KeepMethodAccessPattern getAccessPattern() {
     return accessPattern;
   }
@@ -131,7 +149,8 @@
       return false;
     }
     KeepMethodPattern that = (KeepMethodPattern) o;
-    return accessPattern.equals(that.accessPattern)
+    return annotatedByPattern.equals(that.annotatedByPattern)
+        && accessPattern.equals(that.accessPattern)
         && namePattern.equals(that.namePattern)
         && returnTypePattern.equals(that.returnTypePattern)
         && parametersPattern.equals(that.parametersPattern);
@@ -139,12 +158,15 @@
 
   @Override
   public int hashCode() {
-    return Objects.hash(accessPattern, namePattern, returnTypePattern, parametersPattern);
+    return Objects.hash(
+        annotatedByPattern, accessPattern, namePattern, returnTypePattern, parametersPattern);
   }
 
   @Override
   public String toString() {
     return "KeepMethodPattern{"
+        + "annotated-by="
+        + annotatedByPattern
         + "access="
         + accessPattern
         + ", name="
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/keeprules/RulePrintingUtils.java b/src/keepanno/java/com/android/tools/r8/keepanno/keeprules/RulePrintingUtils.java
index 3e78d23..7eddae7 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/keeprules/RulePrintingUtils.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/keeprules/RulePrintingUtils.java
@@ -123,6 +123,11 @@
       // members via a binding must be split in two up front: one for methods and one for fields.
       return printer.appendWithoutBackReferenceAssert("*").append(";");
     }
+    if (member.getAnnotatedByPattern().isPresent()) {
+      printer.append("@");
+      printClassName(member.getAnnotatedByPattern().get(), printer);
+      printer.append(" ");
+    }
     if (member.isMethod()) {
       return printMethod(member.asMethod(), printer);
     }
@@ -130,27 +135,27 @@
       return printField(member.asField(), printer);
     }
     // The pattern is a restricted member pattern, e.g., it must apply to fields and methods
-    // without any specifics not common to both. For now that is just the access pattern.
-    assert !member.getAccessPattern().isAny();
+    // without any specifics not common to both. For now that is annotated-by and access patterns.
+    assert !member.getAccessPattern().isAny() || member.getAnnotatedByPattern().isPresent();
     printMemberAccess(printer, member.getAccessPattern());
     return printer.appendWithoutBackReferenceAssert("*").append(";");
   }
 
-  private static RulePrinter printField(KeepFieldPattern fieldPattern, RulePrinter builder) {
-    printFieldAccess(builder, fieldPattern.getAccessPattern());
-    printType(builder, fieldPattern.getTypePattern().asType());
-    builder.append(" ");
-    printFieldName(builder, fieldPattern.getNamePattern());
-    return builder.append(";");
+  private static RulePrinter printField(KeepFieldPattern fieldPattern, RulePrinter printer) {
+    printFieldAccess(printer, fieldPattern.getAccessPattern());
+    printType(printer, fieldPattern.getTypePattern().asType());
+    printer.append(" ");
+    printFieldName(printer, fieldPattern.getNamePattern());
+    return printer.append(";");
   }
 
-  private static RulePrinter printMethod(KeepMethodPattern methodPattern, RulePrinter builder) {
-    printMethodAccess(builder, methodPattern.getAccessPattern());
-    printReturnType(builder, methodPattern.getReturnTypePattern());
-    builder.append(" ");
-    printMethodName(builder, methodPattern.getNamePattern());
-    printParameters(builder, methodPattern.getParametersPattern());
-    return builder.append(";");
+  private static RulePrinter printMethod(KeepMethodPattern methodPattern, RulePrinter printer) {
+    printMethodAccess(printer, methodPattern.getAccessPattern());
+    printReturnType(printer, methodPattern.getReturnTypePattern());
+    printer.append(" ");
+    printMethodName(printer, methodPattern.getNamePattern());
+    printParameters(printer, methodPattern.getParametersPattern());
+    return printer.append(";");
   }
 
   private static RulePrinter printParameters(
diff --git a/src/test/java/com/android/tools/r8/keepanno/MembersAnnotatedByPatternsTest.java b/src/test/java/com/android/tools/r8/keepanno/MembersAnnotatedByPatternsTest.java
new file mode 100644
index 0000000..9bbcd59
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/keepanno/MembersAnnotatedByPatternsTest.java
@@ -0,0 +1,196 @@
+// 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;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.keepanno.annotations.ClassNamePattern;
+import com.android.tools.r8.keepanno.annotations.KeepConstraint;
+import com.android.tools.r8.keepanno.annotations.KeepItemKind;
+import com.android.tools.r8.keepanno.annotations.KeepTarget;
+import com.android.tools.r8.keepanno.annotations.UsedByReflection;
+import com.android.tools.r8.keepanno.annotations.UsesReflection;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.google.common.collect.ImmutableList;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class MembersAnnotatedByPatternsTest extends TestBase {
+
+  static final String EXPECTED = StringUtils.lines("b", "bar", "a", "foo");
+
+  private final TestParameters parameters;
+
+  @Parameterized.Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withDefaultRuntimes().withApiLevel(AndroidApiLevel.B).build();
+  }
+
+  public MembersAnnotatedByPatternsTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void testReference() throws Exception {
+    testForRuntime(parameters)
+        .addProgramClasses(getInputClasses())
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutput(EXPECTED);
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    testForR8(parameters.getBackend())
+        .enableExperimentalKeepAnnotations()
+        .addProgramClasses(getInputClasses())
+        .setMinApi(parameters)
+        // TODO(b/248408342): Make this implicit when annotations are kept by the keep-annotation.
+        .addKeepRuntimeVisibleAnnotations()
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutput(EXPECTED)
+        .inspect(this::checkOutput);
+  }
+
+  public List<Class<?>> getInputClasses() {
+    return ImmutableList.of(
+        TestClass.class,
+        Reflector.class,
+        A.class,
+        B.class,
+        C.class,
+        OnMembers.class,
+        OnFields.class,
+        OnMethods.class);
+  }
+
+  private void checkOutput(CodeInspector inspector) {
+    // The class constant use will ensure the annotation remains.
+    assertThat(inspector.clazz(A.class), isPresentAndRenamed());
+
+    ClassSubject onMembers = inspector.clazz(OnMembers.class);
+    assertThat(onMembers.uniqueFieldWithOriginalName("a"), isAbsent());
+    assertThat(onMembers.uniqueFieldWithOriginalName("b"), isPresentAndNotRenamed());
+    assertThat(onMembers.uniqueMethodWithOriginalName("foo"), isAbsent());
+    assertThat(onMembers.uniqueMethodWithOriginalName("bar"), isPresentAndNotRenamed());
+
+    ClassSubject onFields = inspector.clazz(OnFields.class);
+    assertThat(onFields.uniqueFieldWithOriginalName("a"), isPresentAndNotRenamed());
+    assertThat(onFields.uniqueFieldWithOriginalName("b"), isAbsent());
+    assertThat(onFields.uniqueMethodWithOriginalName("foo"), isAbsent());
+    assertThat(onFields.uniqueMethodWithOriginalName("bar"), isAbsent());
+
+    ClassSubject onMethods = inspector.clazz(OnMethods.class);
+    assertThat(onMethods.uniqueFieldWithOriginalName("a"), isAbsent());
+    assertThat(onMethods.uniqueFieldWithOriginalName("b"), isAbsent());
+    assertThat(onMethods.uniqueMethodWithOriginalName("foo"), isPresentAndNotRenamed());
+    assertThat(onMethods.uniqueMethodWithOriginalName("bar"), isAbsent());
+  }
+
+  @Target({ElementType.FIELD, ElementType.METHOD})
+  @Retention(RetentionPolicy.RUNTIME)
+  @interface A {}
+
+  @Target({ElementType.FIELD})
+  @Retention(RetentionPolicy.RUNTIME)
+  @interface B {}
+
+  @Target({ElementType.METHOD})
+  @Retention(RetentionPolicy.RUNTIME)
+  @interface C {}
+
+  static class Reflector {
+
+    @UsesReflection({
+      @KeepTarget(
+          classConstant = OnMembers.class,
+          kind = KeepItemKind.CLASS_AND_MEMBERS,
+          memberAnnotatedByClassConstant = A.class,
+          constraints = {KeepConstraint.LOOKUP, KeepConstraint.ANNOTATIONS, KeepConstraint.NAME}),
+      @KeepTarget(
+          classConstant = OnFields.class,
+          kind = KeepItemKind.CLASS_AND_FIELDS,
+          fieldAnnotatedByClassName =
+              "com.android.tools.r8.keepanno.MembersAnnotatedByPatternsTest$B",
+          constraints = {KeepConstraint.LOOKUP, KeepConstraint.ANNOTATIONS, KeepConstraint.NAME}),
+      @KeepTarget(
+          classConstant = OnMethods.class,
+          kind = KeepItemKind.CLASS_AND_METHODS,
+          methodAnnotatedByClassNamePattern =
+              @ClassNamePattern(simpleName = "MembersAnnotatedByPatternsTest$C"),
+          constraints = {KeepConstraint.LOOKUP, KeepConstraint.ANNOTATIONS, KeepConstraint.NAME})
+    })
+    public void foo(Class<?> clazz) throws Exception {
+      for (Field field : clazz.getDeclaredFields()) {
+        if (field.isAnnotationPresent(A.class) || field.isAnnotationPresent(B.class)) {
+          System.out.println(field.getName());
+        }
+      }
+      for (Method method : clazz.getDeclaredMethods()) {
+        if (method.isAnnotationPresent(A.class) || method.isAnnotationPresent(C.class)) {
+          System.out.println(method.getName());
+        }
+      }
+    }
+  }
+
+  static class OnMembers {
+    int a;
+
+    @A int b;
+
+    void foo() {}
+
+    @A
+    void bar() {}
+  }
+
+  static class OnFields {
+    @B int a;
+
+    int b;
+
+    void foo() {}
+
+    void bar() {}
+  }
+
+  static class OnMethods {
+    int a;
+
+    int b;
+
+    @C
+    void foo() {}
+
+    void bar() {}
+  }
+
+  static class TestClass {
+
+    @UsedByReflection(kind = KeepItemKind.CLASS_AND_METHODS)
+    public static void main(String[] args) throws Exception {
+      new Reflector().foo(OnMembers.class);
+      new Reflector().foo(OnFields.class);
+      new Reflector().foo(OnMethods.class);
+    }
+  }
+}
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 4b7c0c4..b12f190 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
@@ -298,6 +298,9 @@
     private static String CLASS_NAME_GROUP = "class-name";
     private static String INSTANCE_OF_GROUP = "instance-of";
     private static String CLASS_ANNOTATED_BY_GROUP = "class-annotated-by";
+    private static String MEMBER_ANNOTATED_BY_GROUP = "member-annotated-by";
+    private static String METHOD_ANNOTATED_BY_GROUP = "method-annotated-by";
+    private static String FIELD_ANNOTATED_BY_GROUP = "field-annotated-by";
 
     private Group createDescriptionGroup() {
       return new Group("description")
@@ -682,42 +685,40 @@
               "If none are specified the default is to match any class instance.");
     }
 
-    private GroupMember classAnnotatedByClassName() {
-      return new GroupMember("classAnnotatedByClassName")
-          .setDocTitle(
-              "Define the " + CLASS_ANNOTATED_BY_GROUP + " pattern by fully qualified class name.")
-          .setDocReturn("The qualified class name that defines the annotation.")
-          .defaultEmptyString();
+    private String annotatedByDefaultDocFooter(String name) {
+      return "If none are specified the default is to match any "
+          + name
+          + " regardless of what the "
+          + name
+          + " is annotated by.";
     }
 
-    private GroupMember classAnnotatedByClassConstant() {
-      return new GroupMember("classAnnotatedByClassConstant")
-          .setDocTitle(
-              "Define the "
-                  + CLASS_ANNOTATED_BY_GROUP
-                  + " pattern by reference to a Class constant.")
-          .setDocReturn("The class-constant that defines the annotation.")
-          .defaultObjectClass();
-    }
-
-    private GroupMember classAnnotatedByClassNamePattern() {
-      return new GroupMember("classAnnotatedByClassNamePattern")
-          .setDocTitle(
-              "Define the "
-                  + CLASS_ANNOTATED_BY_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);
+    private Group createAnnotatedByPatternGroup(String name, String groupName) {
+      return new Group(groupName)
+          .addMember(
+              new GroupMember(name + "AnnotatedByClassName")
+                  .setDocTitle(
+                      "Define the " + groupName + " pattern by fully qualified class name.")
+                  .setDocReturn("The qualified class name that defines the annotation.")
+                  .defaultEmptyString())
+          .addMember(
+              new GroupMember(name + "AnnotatedByClassConstant")
+                  .setDocTitle(
+                      "Define the " + groupName + " pattern by reference to a Class constant.")
+                  .setDocReturn("The class-constant that defines the annotation.")
+                  .defaultObjectClass())
+          .addMember(
+              new GroupMember(name + "AnnotatedByClassNamePattern")
+                  .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));
     }
 
     private Group createClassAnnotatedByPatternGroup() {
-      return new Group(CLASS_ANNOTATED_BY_GROUP)
-          .addMember(classAnnotatedByClassName())
-          .addMember(classAnnotatedByClassConstant())
-          .addMember(classAnnotatedByClassNamePattern())
-          .addDocFooterParagraph(
-              "If none are specified the default is to match any class "
-                  + "regardless of what the class is annotated by.");
+      String name = "class";
+      return createAnnotatedByPatternGroup(name, CLASS_ANNOTATED_BY_GROUP)
+          .addDocFooterParagraph(annotatedByDefaultDocFooter(name));
     }
 
     private Group createMemberBindingGroup() {
@@ -734,18 +735,28 @@
                   .defaultEmptyString());
     }
 
+    private Group createMemberAnnotatedByGroup() {
+      String name = "member";
+      return createAnnotatedByPatternGroup(name, MEMBER_ANNOTATED_BY_GROUP)
+          .addDocFooterParagraph(getMutuallyExclusiveForMemberProperties())
+          .addDocFooterParagraph(annotatedByDefaultDocFooter(name));
+    }
+
     private Group createMemberAccessGroup() {
       return new Group("member-access")
           .addMember(
               new GroupMember("memberAccess")
                   .setDocTitle("Define the member-access pattern by matching on access flags.")
-                  .addParagraph(
-                      "Mutually exclusive with all field and method properties",
-                      "as use restricts the match to both types of members.")
+                  .addParagraph(getMutuallyExclusiveForMemberProperties())
                   .setDocReturn("The member access-flag constraints that must be met.")
                   .defaultArrayEmpty(MemberAccessFlags.class));
     }
 
+    private String getMutuallyExclusiveForMemberProperties() {
+      return "Mutually exclusive with all field and method properties "
+          + "as use restricts the match to both types of members.";
+    }
+
     private String getMutuallyExclusiveForMethodProperties() {
       return "Mutually exclusive with all field properties.";
     }
@@ -766,6 +777,13 @@
           + ".";
     }
 
+    private Group createMethodAnnotatedByGroup() {
+      String name = "method";
+      return createAnnotatedByPatternGroup(name, METHOD_ANNOTATED_BY_GROUP)
+          .addDocFooterParagraph(getMutuallyExclusiveForMethodProperties())
+          .addDocFooterParagraph(annotatedByDefaultDocFooter(name));
+    }
+
     private Group createMethodAccessGroup() {
       return new Group("method-access")
           .addMember(
@@ -841,6 +859,13 @@
                   .defaultArrayValue(TypePattern.class, DEFAULT_INVALID_TYPE_PATTERN));
     }
 
+    private Group createFieldAnnotatedByGroup() {
+      String name = "field";
+      return createAnnotatedByPatternGroup(name, FIELD_ANNOTATED_BY_GROUP)
+          .addDocFooterParagraph(getMutuallyExclusiveForFieldProperties())
+          .addDocFooterParagraph(annotatedByDefaultDocFooter(name));
+    }
+
     private Group createFieldAccessGroup() {
       return new Group("field-access")
           .addMember(
@@ -948,10 +973,14 @@
 
     private void internalGenerateMemberPropertiesNoBinding(Group memberBindingGroup) {
       // General member properties.
+      maybeLink(createMemberAnnotatedByGroup(), memberBindingGroup).generate(this);
+      println();
       maybeLink(createMemberAccessGroup(), memberBindingGroup).generate(this);
       println();
 
       // Method properties.
+      maybeLink(createMethodAnnotatedByGroup(), memberBindingGroup).generate(this);
+      println();
       maybeLink(createMethodAccessGroup(), memberBindingGroup).generate(this);
       println();
       maybeLink(createMethodNameGroup(), memberBindingGroup).generate(this);
@@ -962,6 +991,8 @@
       println();
 
       // Field properties.
+      maybeLink(createFieldAnnotatedByGroup(), memberBindingGroup).generate(this);
+      println();
       maybeLink(createFieldAccessGroup(), memberBindingGroup).generate(this);
       println();
       maybeLink(createFieldNameGroup(), memberBindingGroup).generate(this);
@@ -1479,13 +1510,16 @@
             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);