[KeepAnno] Introduce patterns on types

The TypePattern is the general matcher for types. It is basically a
disjoint union of the possible ways on which to match types.

Follow-up CLs will extend this with more specific patterns for classes,
packages, class names, method names, field names, etc.

Bug: b/248408342

Change-Id: Ifa933e8bd792384f45f7552410756d586e790ee6
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 c3c58c2..579d46f 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
@@ -298,11 +298,56 @@
    * <p>If none, and other properties define this item as a method, the default matches any return
    * type.
    *
+   * <p>Mutually exclusive with the following other properties defining return-type:
+   *
+   * <ul>
+   *   <li>methodReturnTypeConstant
+   *   <li>methodReturnTypePattern
+   * </ul>
+   *
    * @return The qualified type name of the method return type.
    */
   String methodReturnType() default "";
 
   /**
+   * Define the method return-type pattern by a class constant.
+   *
+   * <p>Mutually exclusive with all field properties.
+   *
+   * <p>If none, and other properties define this item as a method, the default matches any return
+   * type.
+   *
+   * <p>Mutually exclusive with the following other properties defining return-type:
+   *
+   * <ul>
+   *   <li>methodReturnType
+   *   <li>methodReturnTypePattern
+   * </ul>
+   *
+   * @return A class constant denoting the type of the method return type.
+   */
+  Class<?> methodReturnTypeConstant() default Object.class;
+
+  /**
+   * Define the method return-type pattern by a type pattern.
+   *
+   * <p>Mutually exclusive with all field properties.
+   *
+   * <p>If none, and other properties define this item as a method, the default matches any return
+   * type.
+   *
+   * <p>Mutually exclusive with the following other properties defining return-type:
+   *
+   * <ul>
+   *   <li>methodReturnType
+   *   <li>methodReturnTypeConstant
+   * </ul>
+   *
+   * @return The pattern of the method return type.
+   */
+  TypePattern methodReturnTypePattern() default @TypePattern(name = "");
+
+  /**
    * Define the method parameters pattern by a list of fully qualified types.
    *
    * <p>Mutually exclusive with all field properties.
@@ -310,9 +355,25 @@
    * <p>If none, and other properties define this item as a method, the default matches any
    * parameters.
    *
+   * <p>Mutually exclusive with the property `methodParameterTypePatterns` also defining parameters.
+   *
    * @return The list of qualified type names of the method parameters.
    */
-  String[] methodParameters() default {"<default>"};
+  String[] methodParameters() default {""};
+
+  /**
+   * Define the method parameters pattern by a list of patterns on types.
+   *
+   * <p>Mutually exclusive with all field properties.
+   *
+   * <p>If none, and other properties define this item as a method, the default matches any
+   * parameters.
+   *
+   * <p>Mutually exclusive with the property `methodParameters` also defining parameters.
+   *
+   * @return The list of type patterns for the method parameters.
+   */
+  TypePattern[] methodParameterTypePatterns() default {@TypePattern(name = "")};
 
   /**
    * Define the field-access pattern by matching on access flags.
@@ -345,7 +406,50 @@
    *
    * <p>If none, and other properties define this item as a field, the default matches any type.
    *
-   * @return The qualified type name of the field type.
+   * <p>Mutually exclusive with the following other properties defining field-type:
+   *
+   * <ul>
+   *   <li>fieldTypeConstant
+   *   <li>fieldTypePattern
+   * </ul>
+   *
+   * @return The qualified type name for the field type.
    */
   String fieldType() default "";
+
+  /**
+   * Define the field-type pattern by a class constant.
+   *
+   * <p>Mutually exclusive with all method properties.
+   *
+   * <p>If none, and other properties define this item as a field, the default matches any type.
+   *
+   * <p>Mutually exclusive with the following other properties defining field-type:
+   *
+   * <ul>
+   *   <li>fieldType
+   *   <li>fieldTypePattern
+   * </ul>
+   *
+   * @return The class constant for the field type.
+   */
+  Class<?> fieldTypeConstant() default Object.class;
+
+  /**
+   * Define the field-type pattern by a pattern on types.
+   *
+   * <p>Mutually exclusive with all method properties.
+   *
+   * <p>If none, and other properties define this item as a field, the default matches any type.
+   *
+   * <p>Mutually exclusive with the following other properties defining field-type:
+   *
+   * <ul>
+   *   <li>fieldType
+   *   <li>fieldTypeConstant
+   * </ul>
+   *
+   * @return The type pattern for the field type.
+   */
+  TypePattern fieldTypePattern() default @TypePattern(name = "");
 }
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 e539cc4..470266b 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
@@ -270,11 +270,56 @@
    * <p>If none, and other properties define this item as a method, the default matches any return
    * type.
    *
+   * <p>Mutually exclusive with the following other properties defining return-type:
+   *
+   * <ul>
+   *   <li>methodReturnTypeConstant
+   *   <li>methodReturnTypePattern
+   * </ul>
+   *
    * @return The qualified type name of the method return type.
    */
   String methodReturnType() default "";
 
   /**
+   * Define the method return-type pattern by a class constant.
+   *
+   * <p>Mutually exclusive with all field properties.
+   *
+   * <p>If none, and other properties define this item as a method, the default matches any return
+   * type.
+   *
+   * <p>Mutually exclusive with the following other properties defining return-type:
+   *
+   * <ul>
+   *   <li>methodReturnType
+   *   <li>methodReturnTypePattern
+   * </ul>
+   *
+   * @return A class constant denoting the type of the method return type.
+   */
+  Class<?> methodReturnTypeConstant() default Object.class;
+
+  /**
+   * Define the method return-type pattern by a type pattern.
+   *
+   * <p>Mutually exclusive with all field properties.
+   *
+   * <p>If none, and other properties define this item as a method, the default matches any return
+   * type.
+   *
+   * <p>Mutually exclusive with the following other properties defining return-type:
+   *
+   * <ul>
+   *   <li>methodReturnType
+   *   <li>methodReturnTypeConstant
+   * </ul>
+   *
+   * @return The pattern of the method return type.
+   */
+  TypePattern methodReturnTypePattern() default @TypePattern(name = "");
+
+  /**
    * Define the method parameters pattern by a list of fully qualified types.
    *
    * <p>Mutually exclusive with all field properties.
@@ -282,9 +327,25 @@
    * <p>If none, and other properties define this item as a method, the default matches any
    * parameters.
    *
+   * <p>Mutually exclusive with the property `methodParameterTypePatterns` also defining parameters.
+   *
    * @return The list of qualified type names of the method parameters.
    */
-  String[] methodParameters() default {"<default>"};
+  String[] methodParameters() default {""};
+
+  /**
+   * Define the method parameters pattern by a list of patterns on types.
+   *
+   * <p>Mutually exclusive with all field properties.
+   *
+   * <p>If none, and other properties define this item as a method, the default matches any
+   * parameters.
+   *
+   * <p>Mutually exclusive with the property `methodParameters` also defining parameters.
+   *
+   * @return The list of type patterns for the method parameters.
+   */
+  TypePattern[] methodParameterTypePatterns() default {@TypePattern(name = "")};
 
   /**
    * Define the field-access pattern by matching on access flags.
@@ -317,7 +378,50 @@
    *
    * <p>If none, and other properties define this item as a field, the default matches any type.
    *
-   * @return The qualified type name of the field type.
+   * <p>Mutually exclusive with the following other properties defining field-type:
+   *
+   * <ul>
+   *   <li>fieldTypeConstant
+   *   <li>fieldTypePattern
+   * </ul>
+   *
+   * @return The qualified type name for the field type.
    */
   String fieldType() default "";
+
+  /**
+   * Define the field-type pattern by a class constant.
+   *
+   * <p>Mutually exclusive with all method properties.
+   *
+   * <p>If none, and other properties define this item as a field, the default matches any type.
+   *
+   * <p>Mutually exclusive with the following other properties defining field-type:
+   *
+   * <ul>
+   *   <li>fieldType
+   *   <li>fieldTypePattern
+   * </ul>
+   *
+   * @return The class constant for the field type.
+   */
+  Class<?> fieldTypeConstant() default Object.class;
+
+  /**
+   * Define the field-type pattern by a pattern on types.
+   *
+   * <p>Mutually exclusive with all method properties.
+   *
+   * <p>If none, and other properties define this item as a field, the default matches any type.
+   *
+   * <p>Mutually exclusive with the following other properties defining field-type:
+   *
+   * <ul>
+   *   <li>fieldType
+   *   <li>fieldTypeConstant
+   * </ul>
+   *
+   * @return The type pattern for the field type.
+   */
+  TypePattern fieldTypePattern() default @TypePattern(name = "");
 }
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 cd3559c..5ece578 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
@@ -97,11 +97,56 @@
    * <p>If none, and other properties define this item as a method, the default matches any return
    * type.
    *
+   * <p>Mutually exclusive with the following other properties defining return-type:
+   *
+   * <ul>
+   *   <li>methodReturnTypeConstant
+   *   <li>methodReturnTypePattern
+   * </ul>
+   *
    * @return The qualified type name of the method return type.
    */
   String methodReturnType() default "";
 
   /**
+   * Define the method return-type pattern by a class constant.
+   *
+   * <p>Mutually exclusive with all field properties.
+   *
+   * <p>If none, and other properties define this item as a method, the default matches any return
+   * type.
+   *
+   * <p>Mutually exclusive with the following other properties defining return-type:
+   *
+   * <ul>
+   *   <li>methodReturnType
+   *   <li>methodReturnTypePattern
+   * </ul>
+   *
+   * @return A class constant denoting the type of the method return type.
+   */
+  Class<?> methodReturnTypeConstant() default Object.class;
+
+  /**
+   * Define the method return-type pattern by a type pattern.
+   *
+   * <p>Mutually exclusive with all field properties.
+   *
+   * <p>If none, and other properties define this item as a method, the default matches any return
+   * type.
+   *
+   * <p>Mutually exclusive with the following other properties defining return-type:
+   *
+   * <ul>
+   *   <li>methodReturnType
+   *   <li>methodReturnTypeConstant
+   * </ul>
+   *
+   * @return The pattern of the method return type.
+   */
+  TypePattern methodReturnTypePattern() default @TypePattern(name = "");
+
+  /**
    * Define the method parameters pattern by a list of fully qualified types.
    *
    * <p>Mutually exclusive with all field properties.
@@ -109,9 +154,25 @@
    * <p>If none, and other properties define this item as a method, the default matches any
    * parameters.
    *
+   * <p>Mutually exclusive with the property `methodParameterTypePatterns` also defining parameters.
+   *
    * @return The list of qualified type names of the method parameters.
    */
-  String[] methodParameters() default {"<default>"};
+  String[] methodParameters() default {""};
+
+  /**
+   * Define the method parameters pattern by a list of patterns on types.
+   *
+   * <p>Mutually exclusive with all field properties.
+   *
+   * <p>If none, and other properties define this item as a method, the default matches any
+   * parameters.
+   *
+   * <p>Mutually exclusive with the property `methodParameters` also defining parameters.
+   *
+   * @return The list of type patterns for the method parameters.
+   */
+  TypePattern[] methodParameterTypePatterns() default {@TypePattern(name = "")};
 
   /**
    * Define the field-access pattern by matching on access flags.
@@ -144,7 +205,50 @@
    *
    * <p>If none, and other properties define this item as a field, the default matches any type.
    *
-   * @return The qualified type name of the field type.
+   * <p>Mutually exclusive with the following other properties defining field-type:
+   *
+   * <ul>
+   *   <li>fieldTypeConstant
+   *   <li>fieldTypePattern
+   * </ul>
+   *
+   * @return The qualified type name for the field type.
    */
   String fieldType() default "";
+
+  /**
+   * Define the field-type pattern by a class constant.
+   *
+   * <p>Mutually exclusive with all method properties.
+   *
+   * <p>If none, and other properties define this item as a field, the default matches any type.
+   *
+   * <p>Mutually exclusive with the following other properties defining field-type:
+   *
+   * <ul>
+   *   <li>fieldType
+   *   <li>fieldTypePattern
+   * </ul>
+   *
+   * @return The class constant for the field type.
+   */
+  Class<?> fieldTypeConstant() default Object.class;
+
+  /**
+   * Define the field-type pattern by a pattern on types.
+   *
+   * <p>Mutually exclusive with all method properties.
+   *
+   * <p>If none, and other properties define this item as a field, the default matches any type.
+   *
+   * <p>Mutually exclusive with the following other properties defining field-type:
+   *
+   * <ul>
+   *   <li>fieldType
+   *   <li>fieldTypeConstant
+   * </ul>
+   *
+   * @return The type pattern for the field type.
+   */
+  TypePattern fieldTypePattern() default @TypePattern(name = "");
 }
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 f080454..7bbc2f0 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
@@ -367,11 +367,56 @@
    * <p>If none, and other properties define this item as a method, the default matches any return
    * type.
    *
+   * <p>Mutually exclusive with the following other properties defining return-type:
+   *
+   * <ul>
+   *   <li>methodReturnTypeConstant
+   *   <li>methodReturnTypePattern
+   * </ul>
+   *
    * @return The qualified type name of the method return type.
    */
   String methodReturnType() default "";
 
   /**
+   * Define the method return-type pattern by a class constant.
+   *
+   * <p>Mutually exclusive with all field properties.
+   *
+   * <p>If none, and other properties define this item as a method, the default matches any return
+   * type.
+   *
+   * <p>Mutually exclusive with the following other properties defining return-type:
+   *
+   * <ul>
+   *   <li>methodReturnType
+   *   <li>methodReturnTypePattern
+   * </ul>
+   *
+   * @return A class constant denoting the type of the method return type.
+   */
+  Class<?> methodReturnTypeConstant() default Object.class;
+
+  /**
+   * Define the method return-type pattern by a type pattern.
+   *
+   * <p>Mutually exclusive with all field properties.
+   *
+   * <p>If none, and other properties define this item as a method, the default matches any return
+   * type.
+   *
+   * <p>Mutually exclusive with the following other properties defining return-type:
+   *
+   * <ul>
+   *   <li>methodReturnType
+   *   <li>methodReturnTypeConstant
+   * </ul>
+   *
+   * @return The pattern of the method return type.
+   */
+  TypePattern methodReturnTypePattern() default @TypePattern(name = "");
+
+  /**
    * Define the method parameters pattern by a list of fully qualified types.
    *
    * <p>Mutually exclusive with all field properties.
@@ -379,9 +424,25 @@
    * <p>If none, and other properties define this item as a method, the default matches any
    * parameters.
    *
+   * <p>Mutually exclusive with the property `methodParameterTypePatterns` also defining parameters.
+   *
    * @return The list of qualified type names of the method parameters.
    */
-  String[] methodParameters() default {"<default>"};
+  String[] methodParameters() default {""};
+
+  /**
+   * Define the method parameters pattern by a list of patterns on types.
+   *
+   * <p>Mutually exclusive with all field properties.
+   *
+   * <p>If none, and other properties define this item as a method, the default matches any
+   * parameters.
+   *
+   * <p>Mutually exclusive with the property `methodParameters` also defining parameters.
+   *
+   * @return The list of type patterns for the method parameters.
+   */
+  TypePattern[] methodParameterTypePatterns() default {@TypePattern(name = "")};
 
   /**
    * Define the field-access pattern by matching on access flags.
@@ -414,7 +475,50 @@
    *
    * <p>If none, and other properties define this item as a field, the default matches any type.
    *
-   * @return The qualified type name of the field type.
+   * <p>Mutually exclusive with the following other properties defining field-type:
+   *
+   * <ul>
+   *   <li>fieldTypeConstant
+   *   <li>fieldTypePattern
+   * </ul>
+   *
+   * @return The qualified type name for the field type.
    */
   String fieldType() default "";
+
+  /**
+   * Define the field-type pattern by a class constant.
+   *
+   * <p>Mutually exclusive with all method properties.
+   *
+   * <p>If none, and other properties define this item as a field, the default matches any type.
+   *
+   * <p>Mutually exclusive with the following other properties defining field-type:
+   *
+   * <ul>
+   *   <li>fieldType
+   *   <li>fieldTypePattern
+   * </ul>
+   *
+   * @return The class constant for the field type.
+   */
+  Class<?> fieldTypeConstant() default Object.class;
+
+  /**
+   * Define the field-type pattern by a pattern on types.
+   *
+   * <p>Mutually exclusive with all method properties.
+   *
+   * <p>If none, and other properties define this item as a field, the default matches any type.
+   *
+   * <p>Mutually exclusive with the following other properties defining field-type:
+   *
+   * <ul>
+   *   <li>fieldType
+   *   <li>fieldTypeConstant
+   * </ul>
+   *
+   * @return The type pattern for the field type.
+   */
+  TypePattern fieldTypePattern() default @TypePattern(name = "");
 }
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/TypePattern.java b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/TypePattern.java
new file mode 100644
index 0000000..1d7e6c6
--- /dev/null
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/TypePattern.java
@@ -0,0 +1,44 @@
+// Copyright (c) 2023, 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.
+
+// ***********************************************************************************
+// GENERATED FILE. DO NOT EDIT! See KeepItemAnnotationGenerator.java.
+// ***********************************************************************************
+
+package com.android.tools.r8.keepanno.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * A pattern structure for matching types.
+ *
+ * <p>If no properties are set, the default pattern matches any type.
+ *
+ * <p>All properties on this annotation are mutually exclusive.
+ */
+@Target(ElementType.ANNOTATION_TYPE)
+@Retention(RetentionPolicy.CLASS)
+public @interface TypePattern {
+
+  /**
+   * Exact type name as a string.
+   *
+   * <p>For example, {@code "long"} or {@code "java.lang.String"}.
+   *
+   * <p>Mutually exclusive with the property `constant` also defining type-pattern.
+   */
+  String name() default "";
+
+  /**
+   * Exact type from a class constant.
+   *
+   * <p>For example, {@code String.class}.
+   *
+   * <p>Mutually exclusive with the property `name` also defining type-pattern.
+   */
+  Class<?> constant() default Object.class;
+}
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 fa41767..149e51c 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
@@ -140,11 +140,56 @@
    * <p>If none, and other properties define this item as a method, the default matches any return
    * type.
    *
+   * <p>Mutually exclusive with the following other properties defining return-type:
+   *
+   * <ul>
+   *   <li>methodReturnTypeConstant
+   *   <li>methodReturnTypePattern
+   * </ul>
+   *
    * @return The qualified type name of the method return type.
    */
   String methodReturnType() default "";
 
   /**
+   * Define the method return-type pattern by a class constant.
+   *
+   * <p>Mutually exclusive with all field properties.
+   *
+   * <p>If none, and other properties define this item as a method, the default matches any return
+   * type.
+   *
+   * <p>Mutually exclusive with the following other properties defining return-type:
+   *
+   * <ul>
+   *   <li>methodReturnType
+   *   <li>methodReturnTypePattern
+   * </ul>
+   *
+   * @return A class constant denoting the type of the method return type.
+   */
+  Class<?> methodReturnTypeConstant() default Object.class;
+
+  /**
+   * Define the method return-type pattern by a type pattern.
+   *
+   * <p>Mutually exclusive with all field properties.
+   *
+   * <p>If none, and other properties define this item as a method, the default matches any return
+   * type.
+   *
+   * <p>Mutually exclusive with the following other properties defining return-type:
+   *
+   * <ul>
+   *   <li>methodReturnType
+   *   <li>methodReturnTypeConstant
+   * </ul>
+   *
+   * @return The pattern of the method return type.
+   */
+  TypePattern methodReturnTypePattern() default @TypePattern(name = "");
+
+  /**
    * Define the method parameters pattern by a list of fully qualified types.
    *
    * <p>Mutually exclusive with all field properties.
@@ -152,9 +197,25 @@
    * <p>If none, and other properties define this item as a method, the default matches any
    * parameters.
    *
+   * <p>Mutually exclusive with the property `methodParameterTypePatterns` also defining parameters.
+   *
    * @return The list of qualified type names of the method parameters.
    */
-  String[] methodParameters() default {"<default>"};
+  String[] methodParameters() default {""};
+
+  /**
+   * Define the method parameters pattern by a list of patterns on types.
+   *
+   * <p>Mutually exclusive with all field properties.
+   *
+   * <p>If none, and other properties define this item as a method, the default matches any
+   * parameters.
+   *
+   * <p>Mutually exclusive with the property `methodParameters` also defining parameters.
+   *
+   * @return The list of type patterns for the method parameters.
+   */
+  TypePattern[] methodParameterTypePatterns() default {@TypePattern(name = "")};
 
   /**
    * Define the field-access pattern by matching on access flags.
@@ -187,7 +248,50 @@
    *
    * <p>If none, and other properties define this item as a field, the default matches any type.
    *
-   * @return The qualified type name of the field type.
+   * <p>Mutually exclusive with the following other properties defining field-type:
+   *
+   * <ul>
+   *   <li>fieldTypeConstant
+   *   <li>fieldTypePattern
+   * </ul>
+   *
+   * @return The qualified type name for the field type.
    */
   String fieldType() default "";
+
+  /**
+   * Define the field-type pattern by a class constant.
+   *
+   * <p>Mutually exclusive with all method properties.
+   *
+   * <p>If none, and other properties define this item as a field, the default matches any type.
+   *
+   * <p>Mutually exclusive with the following other properties defining field-type:
+   *
+   * <ul>
+   *   <li>fieldType
+   *   <li>fieldTypePattern
+   * </ul>
+   *
+   * @return The class constant for the field type.
+   */
+  Class<?> fieldTypeConstant() default Object.class;
+
+  /**
+   * Define the field-type pattern by a pattern on types.
+   *
+   * <p>Mutually exclusive with all method properties.
+   *
+   * <p>If none, and other properties define this item as a field, the default matches any type.
+   *
+   * <p>Mutually exclusive with the following other properties defining field-type:
+   *
+   * <ul>
+   *   <li>fieldType
+   *   <li>fieldTypeConstant
+   * </ul>
+   *
+   * @return The type pattern for the field type.
+   */
+  TypePattern fieldTypePattern() default @TypePattern(name = "");
 }
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 83425ad..24073af 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
@@ -140,11 +140,56 @@
    * <p>If none, and other properties define this item as a method, the default matches any return
    * type.
    *
+   * <p>Mutually exclusive with the following other properties defining return-type:
+   *
+   * <ul>
+   *   <li>methodReturnTypeConstant
+   *   <li>methodReturnTypePattern
+   * </ul>
+   *
    * @return The qualified type name of the method return type.
    */
   String methodReturnType() default "";
 
   /**
+   * Define the method return-type pattern by a class constant.
+   *
+   * <p>Mutually exclusive with all field properties.
+   *
+   * <p>If none, and other properties define this item as a method, the default matches any return
+   * type.
+   *
+   * <p>Mutually exclusive with the following other properties defining return-type:
+   *
+   * <ul>
+   *   <li>methodReturnType
+   *   <li>methodReturnTypePattern
+   * </ul>
+   *
+   * @return A class constant denoting the type of the method return type.
+   */
+  Class<?> methodReturnTypeConstant() default Object.class;
+
+  /**
+   * Define the method return-type pattern by a type pattern.
+   *
+   * <p>Mutually exclusive with all field properties.
+   *
+   * <p>If none, and other properties define this item as a method, the default matches any return
+   * type.
+   *
+   * <p>Mutually exclusive with the following other properties defining return-type:
+   *
+   * <ul>
+   *   <li>methodReturnType
+   *   <li>methodReturnTypeConstant
+   * </ul>
+   *
+   * @return The pattern of the method return type.
+   */
+  TypePattern methodReturnTypePattern() default @TypePattern(name = "");
+
+  /**
    * Define the method parameters pattern by a list of fully qualified types.
    *
    * <p>Mutually exclusive with all field properties.
@@ -152,9 +197,25 @@
    * <p>If none, and other properties define this item as a method, the default matches any
    * parameters.
    *
+   * <p>Mutually exclusive with the property `methodParameterTypePatterns` also defining parameters.
+   *
    * @return The list of qualified type names of the method parameters.
    */
-  String[] methodParameters() default {"<default>"};
+  String[] methodParameters() default {""};
+
+  /**
+   * Define the method parameters pattern by a list of patterns on types.
+   *
+   * <p>Mutually exclusive with all field properties.
+   *
+   * <p>If none, and other properties define this item as a method, the default matches any
+   * parameters.
+   *
+   * <p>Mutually exclusive with the property `methodParameters` also defining parameters.
+   *
+   * @return The list of type patterns for the method parameters.
+   */
+  TypePattern[] methodParameterTypePatterns() default {@TypePattern(name = "")};
 
   /**
    * Define the field-access pattern by matching on access flags.
@@ -187,7 +248,50 @@
    *
    * <p>If none, and other properties define this item as a field, the default matches any type.
    *
-   * @return The qualified type name of the field type.
+   * <p>Mutually exclusive with the following other properties defining field-type:
+   *
+   * <ul>
+   *   <li>fieldTypeConstant
+   *   <li>fieldTypePattern
+   * </ul>
+   *
+   * @return The qualified type name for the field type.
    */
   String fieldType() default "";
+
+  /**
+   * Define the field-type pattern by a class constant.
+   *
+   * <p>Mutually exclusive with all method properties.
+   *
+   * <p>If none, and other properties define this item as a field, the default matches any type.
+   *
+   * <p>Mutually exclusive with the following other properties defining field-type:
+   *
+   * <ul>
+   *   <li>fieldType
+   *   <li>fieldTypePattern
+   * </ul>
+   *
+   * @return The class constant for the field type.
+   */
+  Class<?> fieldTypeConstant() default Object.class;
+
+  /**
+   * Define the field-type pattern by a pattern on types.
+   *
+   * <p>Mutually exclusive with all method properties.
+   *
+   * <p>If none, and other properties define this item as a field, the default matches any type.
+   *
+   * <p>Mutually exclusive with the following other properties defining field-type:
+   *
+   * <ul>
+   *   <li>fieldType
+   *   <li>fieldTypeConstant
+   * </ul>
+   *
+   * @return The type pattern for the field type.
+   */
+  TypePattern fieldTypePattern() default @TypePattern(name = "");
 }
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 98223c7..35beddf 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
@@ -17,6 +17,7 @@
 import com.android.tools.r8.keepanno.ast.AnnotationConstants.MethodAccess;
 import com.android.tools.r8.keepanno.ast.AnnotationConstants.Option;
 import com.android.tools.r8.keepanno.ast.AnnotationConstants.Target;
+import com.android.tools.r8.keepanno.ast.AnnotationConstants.TypePattern;
 import com.android.tools.r8.keepanno.ast.AnnotationConstants.UsedByReflection;
 import com.android.tools.r8.keepanno.ast.AnnotationConstants.UsesReflection;
 import com.android.tools.r8.keepanno.ast.KeepBindingReference;
@@ -756,7 +757,7 @@
             },
             bindingsHelper);
       }
-      AnnotationVisitor visitor = optionsDeclaration.tryParseArray(name, v -> {});
+      AnnotationVisitor visitor = optionsDeclaration.tryParseArray(name);
       if (visitor != null) {
         return visitor;
       }
@@ -879,7 +880,7 @@
             },
             bindingsHelper);
       }
-      AnnotationVisitor visitor = optionsDeclaration.tryParseArray(name, v -> {});
+      AnnotationVisitor visitor = optionsDeclaration.tryParseArray(name);
       if (visitor != null) {
         return visitor;
       }
@@ -1186,15 +1187,48 @@
   abstract static class Declaration<T> {
     abstract String kind();
 
-    abstract boolean isDefault();
+    boolean isDefault() {
+      for (Declaration<?> declaration : declarations()) {
+        if (!declaration.isDefault()) {
+          return false;
+        }
+      }
+      return true;
+    }
+    ;
 
     abstract T getValue();
 
+    List<Declaration<?>> declarations() {
+      return Collections.emptyList();
+    }
+
     boolean tryParse(String name, Object value) {
+      for (Declaration<?> declaration : declarations()) {
+        if (declaration.tryParse(name, value)) {
+          return true;
+        }
+      }
       return false;
     }
 
-    AnnotationVisitor tryParseArray(String name, Consumer<T> onValue) {
+    AnnotationVisitor tryParseArray(String name) {
+      for (Declaration<?> declaration : declarations()) {
+        AnnotationVisitor visitor = declaration.tryParseArray(name);
+        if (visitor != null) {
+          return visitor;
+        }
+      }
+      return null;
+    }
+
+    AnnotationVisitor tryParseAnnotation(String name, String descriptor) {
+      for (Declaration<?> declaration : declarations()) {
+        AnnotationVisitor visitor = declaration.tryParseAnnotation(name, descriptor);
+        if (visitor != null) {
+          return visitor;
+        }
+      }
       return null;
     }
   }
@@ -1212,6 +1246,10 @@
       return null;
     }
 
+    AnnotationVisitor parseAnnotation(String name, String descriptor, Consumer<T> setValue) {
+      return null;
+    }
+
     @Override
     boolean isDefault() {
       return !hasDeclaration();
@@ -1252,8 +1290,22 @@
     }
 
     @Override
-    AnnotationVisitor tryParseArray(String name, Consumer<T> setValue) {
-      AnnotationVisitor visitor = parseArray(name, setValue.andThen(v -> declarationValue = v));
+    final AnnotationVisitor tryParseArray(String name) {
+      AnnotationVisitor visitor = parseArray(name, v -> declarationValue = v);
+      if (visitor != null) {
+        if (hasDeclaration()) {
+          error(name);
+        }
+        declarationName = name;
+        declarationVisitor = visitor;
+        return visitor;
+      }
+      return null;
+    }
+
+    @Override
+    final AnnotationVisitor tryParseAnnotation(String name, String descriptor) {
+      AnnotationVisitor visitor = parseAnnotation(name, descriptor, v -> declarationValue = v);
       if (visitor != null) {
         if (hasDeclaration()) {
           error(name);
@@ -1423,13 +1475,130 @@
     }
   }
 
+  private static class MethodReturnTypeDeclaration
+      extends SingleDeclaration<KeepMethodReturnTypePattern> {
+
+    private final Supplier<String> annotationName;
+
+    private MethodReturnTypeDeclaration(Supplier<String> annotationName) {
+      this.annotationName = annotationName;
+    }
+
+    @Override
+    String kind() {
+      return "return type";
+    }
+
+    @Override
+    KeepMethodReturnTypePattern getDefaultValue() {
+      return KeepMethodReturnTypePattern.any();
+    }
+
+    @Override
+    KeepMethodReturnTypePattern parse(String name, Object value) {
+      if (name.equals(Item.methodReturnType) && value instanceof String) {
+        return KeepEdgeReaderUtils.methodReturnTypeFromTypeName((String) value);
+      }
+      if (name.equals(Item.methodReturnTypeConstant) && value instanceof Type) {
+        Type type = (Type) value;
+        return KeepEdgeReaderUtils.methodReturnTypeFromTypeDescriptor(type.getDescriptor());
+      }
+      return null;
+    }
+
+    @Override
+    AnnotationVisitor parseAnnotation(
+        String name, String descriptor, Consumer<KeepMethodReturnTypePattern> setValue) {
+      if (name.equals(Item.methodReturnTypePattern) && descriptor.equals(TypePattern.DESCRIPTOR)) {
+        return new TypePatternVisitor(
+            annotationName, t -> setValue.accept(KeepMethodReturnTypePattern.fromType(t)));
+      }
+      return super.parseAnnotation(name, descriptor, setValue);
+    }
+  }
+
+  private static class MethodParametersDeclaration
+      extends SingleDeclaration<KeepMethodParametersPattern> {
+
+    private final Supplier<String> annotationName;
+    private KeepMethodParametersPattern pattern = null;
+
+    public MethodParametersDeclaration(Supplier<String> annotationName) {
+      this.annotationName = annotationName;
+    }
+
+    private void setPattern(
+        KeepMethodParametersPattern pattern, Consumer<KeepMethodParametersPattern> setValue) {
+      assert setValue != null;
+      if (this.pattern != null) {
+        throw new KeepEdgeException("Cannot declare multiple patterns for the parameter list");
+      }
+      setValue.accept(pattern);
+      this.pattern = pattern;
+    }
+
+    @Override
+    String kind() {
+      return "parameters";
+    }
+
+    @Override
+    KeepMethodParametersPattern getDefaultValue() {
+      return KeepMethodParametersPattern.any();
+    }
+
+    @Override
+    KeepMethodParametersPattern parse(String name, Object value) {
+      return null;
+    }
+
+    @Override
+    AnnotationVisitor parseArray(String name, Consumer<KeepMethodParametersPattern> setValue) {
+      if (name.equals(Item.methodParameters)) {
+        return new StringArrayVisitor(
+            annotationName,
+            params -> {
+              KeepMethodParametersPattern.Builder builder = KeepMethodParametersPattern.builder();
+              for (String param : params) {
+                builder.addParameterTypePattern(KeepEdgeReaderUtils.typePatternFromString(param));
+              }
+              setPattern(builder.build(), setValue);
+            });
+      }
+      if (name.equals(Item.methodParameterTypePatterns)) {
+        return new TypePatternsArrayVisitor(
+            annotationName,
+            params -> {
+              KeepMethodParametersPattern.Builder builder = KeepMethodParametersPattern.builder();
+              for (KeepTypePattern param : params) {
+                builder.addParameterTypePattern(param);
+              }
+              setPattern(builder.build(), setValue);
+            });
+      }
+      return super.parseArray(name, setValue);
+    }
+  }
+
   private static class MethodDeclaration extends Declaration<KeepMethodPattern> {
     private final Supplier<String> annotationName;
     private KeepMethodAccessPattern.Builder accessBuilder = null;
     private KeepMethodPattern.Builder builder = null;
+    private final MethodReturnTypeDeclaration returnTypeDeclaration;
+    private final MethodParametersDeclaration parametersDeclaration;
+
+    private final List<Declaration<?>> declarations;
 
     private MethodDeclaration(Supplier<String> annotationName) {
       this.annotationName = annotationName;
+      returnTypeDeclaration = new MethodReturnTypeDeclaration(annotationName);
+      parametersDeclaration = new MethodParametersDeclaration(annotationName);
+      declarations = ImmutableList.of(returnTypeDeclaration, parametersDeclaration);
+    }
+
+    @Override
+    List<Declaration<?>> declarations() {
+      return declarations;
     }
 
     private KeepMethodPattern.Builder getBuilder() {
@@ -1446,7 +1615,7 @@
 
     @Override
     boolean isDefault() {
-      return accessBuilder == null && builder == null;
+      return accessBuilder == null && builder == null && super.isDefault();
     }
 
     @Override
@@ -1454,6 +1623,12 @@
       if (accessBuilder != null) {
         getBuilder().setAccessPattern(accessBuilder.build());
       }
+      if (!returnTypeDeclaration.isDefault()) {
+        getBuilder().setReturnTypePattern(returnTypeDeclaration.getValue());
+      }
+      if (!parametersDeclaration.isDefault()) {
+        getBuilder().setParametersPattern(parametersDeclaration.getValue());
+      }
       return builder != null ? builder.build() : null;
     }
 
@@ -1463,43 +1638,77 @@
         getBuilder().setNamePattern(KeepMethodNamePattern.exact((String) value));
         return true;
       }
-      if (name.equals(Item.methodReturnType) && value instanceof String) {
-        getBuilder()
-            .setReturnTypePattern(KeepEdgeReaderUtils.methodReturnTypeFromString((String) value));
-        return true;
-      }
-      return false;
+      return super.tryParse(name, value);
     }
 
     @Override
-    AnnotationVisitor tryParseArray(String name, Consumer<KeepMethodPattern> ignored) {
+    AnnotationVisitor tryParseArray(String name) {
       if (name.equals(Item.methodAccess)) {
         accessBuilder = KeepMethodAccessPattern.builder();
         return new MethodAccessVisitor(annotationName, accessBuilder);
       }
-      if (name.equals(Item.methodParameters)) {
-        return new StringArrayVisitor(
-            annotationName,
-            params -> {
-              KeepMethodParametersPattern.Builder builder = KeepMethodParametersPattern.builder();
-              for (String param : params) {
-                builder.addParameterTypePattern(KeepEdgeReaderUtils.typePatternFromString(param));
-              }
-              KeepMethodParametersPattern result = builder.build();
-              getBuilder().setParametersPattern(result);
-            });
+      return super.tryParseArray(name);
+    }
+  }
+
+  private static class FieldTypeDeclaration extends SingleDeclaration<KeepFieldTypePattern> {
+
+    private final Supplier<String> annotationName;
+
+    private FieldTypeDeclaration(Supplier<String> annotationName) {
+      this.annotationName = annotationName;
+    }
+
+    @Override
+    String kind() {
+      return "field type";
+    }
+
+    @Override
+    KeepFieldTypePattern getDefaultValue() {
+      return KeepFieldTypePattern.any();
+    }
+
+    @Override
+    KeepFieldTypePattern parse(String name, Object value) {
+      if (name.equals(Item.fieldType) && value instanceof String) {
+        return KeepFieldTypePattern.fromType(
+            KeepEdgeReaderUtils.typePatternFromString((String) value));
+      }
+      if (name.equals(Item.fieldTypeConstant) && value instanceof Type) {
+        String descriptor = ((Type) value).getDescriptor();
+        return KeepFieldTypePattern.fromType(KeepTypePattern.fromDescriptor(descriptor));
       }
       return null;
     }
+
+    @Override
+    AnnotationVisitor parseAnnotation(
+        String name, String descriptor, Consumer<KeepFieldTypePattern> setValue) {
+      if (name.equals(Item.fieldTypePattern) && descriptor.equals(TypePattern.DESCRIPTOR)) {
+        return new TypePatternVisitor(
+            annotationName, t -> setValue.accept(KeepFieldTypePattern.fromType(t)));
+      }
+      return super.parseAnnotation(name, descriptor, setValue);
+    }
   }
 
   private static class FieldDeclaration extends Declaration<KeepFieldPattern> {
     private final Supplier<String> annotationName;
+    private final FieldTypeDeclaration typeDeclaration;
     private KeepFieldAccessPattern.Builder accessBuilder = null;
     private KeepFieldPattern.Builder builder = null;
+    private final List<Declaration<?>> declarations;
 
     public FieldDeclaration(Supplier<String> annotationName) {
       this.annotationName = annotationName;
+      typeDeclaration = new FieldTypeDeclaration(annotationName);
+      declarations = Collections.singletonList(typeDeclaration);
+    }
+
+    @Override
+    List<Declaration<?>> declarations() {
+      return declarations;
     }
 
     private KeepFieldPattern.Builder getBuilder() {
@@ -1524,6 +1733,9 @@
       if (accessBuilder != null) {
         getBuilder().setAccessPattern(accessBuilder.build());
       }
+      if (!typeDeclaration.isDefault()) {
+        getBuilder().setTypePattern(typeDeclaration.getValue());
+      }
       return builder != null ? builder.build() : null;
     }
 
@@ -1533,23 +1745,16 @@
         getBuilder().setNamePattern(KeepFieldNamePattern.exact((String) value));
         return true;
       }
-      if (name.equals(Item.fieldType) && value instanceof String) {
-        getBuilder()
-            .setTypePattern(
-                KeepFieldTypePattern.fromType(
-                    KeepEdgeReaderUtils.typePatternFromString((String) value)));
-        return true;
-      }
-      return false;
+      return super.tryParse(name, value);
     }
 
     @Override
-    AnnotationVisitor tryParseArray(String name, Consumer<KeepFieldPattern> onValue) {
+    AnnotationVisitor tryParseArray(String name) {
       if (name.equals(Item.fieldAccess)) {
         accessBuilder = KeepFieldAccessPattern.builder();
         return new FieldAccessVisitor(annotationName, accessBuilder);
       }
-      return super.tryParseArray(name, onValue);
+      return super.tryParseArray(name);
     }
   }
 
@@ -1558,11 +1763,18 @@
     private KeepMemberAccessPattern.Builder accessBuilder = null;
     private final MethodDeclaration methodDeclaration;
     private final FieldDeclaration fieldDeclaration;
+    private final List<Declaration<?>> declarations;
 
     MemberDeclaration(Supplier<String> annotationName) {
       this.annotationName = annotationName;
       methodDeclaration = new MethodDeclaration(annotationName);
       fieldDeclaration = new FieldDeclaration(annotationName);
+      declarations = ImmutableList.of(methodDeclaration, fieldDeclaration);
+    }
+
+    @Override
+    List<Declaration<?>> declarations() {
+      return declarations;
     }
 
     @Override
@@ -1599,21 +1811,12 @@
     }
 
     @Override
-    boolean tryParse(String name, Object value) {
-      return methodDeclaration.tryParse(name, value) || fieldDeclaration.tryParse(name, value);
-    }
-
-    @Override
-    AnnotationVisitor tryParseArray(String name, Consumer<KeepMemberPattern> ignored) {
+    AnnotationVisitor tryParseArray(String name) {
       if (name.equals(Item.memberAccess)) {
         accessBuilder = KeepMemberAccessPattern.memberBuilder();
         return new MemberAccessVisitor(annotationName, accessBuilder);
       }
-      AnnotationVisitor visitor = methodDeclaration.tryParseArray(name, v -> {});
-      if (visitor != null) {
-        return visitor;
-      }
-      return fieldDeclaration.tryParseArray(name, v -> {});
+      return super.tryParseArray(name);
     }
   }
 
@@ -1751,8 +1954,21 @@
     }
 
     @Override
+    public AnnotationVisitor visitAnnotation(String name, String descriptor) {
+      AnnotationVisitor visitor = classDeclaration.tryParseAnnotation(name, descriptor);
+      if (visitor != null) {
+        return visitor;
+      }
+      visitor = memberDeclaration.tryParseAnnotation(name, descriptor);
+      if (visitor != null) {
+        return visitor;
+      }
+      return super.visitAnnotation(name, descriptor);
+    }
+
+    @Override
     public AnnotationVisitor visitArray(String name) {
-      AnnotationVisitor visitor = memberDeclaration.tryParseArray(name, v -> {});
+      AnnotationVisitor visitor = memberDeclaration.tryParseArray(name);
       if (visitor != null) {
         return visitor;
       }
@@ -1923,6 +2139,80 @@
     }
   }
 
+  private static class TypePatternVisitor extends AnnotationVisitorBase {
+    private final Supplier<String> annotationName;
+    private final Consumer<KeepTypePattern> consumer;
+    private KeepTypePattern result = null;
+
+    private TypePatternVisitor(
+        Supplier<String> annotationName, Consumer<KeepTypePattern> consumer) {
+      this.annotationName = annotationName;
+      this.consumer = consumer;
+    }
+
+    @Override
+    public String getAnnotationName() {
+      return annotationName.get();
+    }
+
+    private void setResult(KeepTypePattern result) {
+      if (this.result != null) {
+        throw new KeepEdgeException("Invalid type annotation defining multiple properties.");
+      }
+      this.result = result;
+    }
+
+    @Override
+    public void visit(String name, Object value) {
+      if (TypePattern.name.equals(name) && value instanceof String) {
+        setResult(KeepEdgeReaderUtils.typePatternFromString((String) value));
+        return;
+      }
+      if (TypePattern.constant.equals(name) && value instanceof Type) {
+        Type type = (Type) value;
+        setResult(KeepTypePattern.fromDescriptor(type.getDescriptor()));
+        return;
+      }
+      super.visit(name, value);
+    }
+
+    @Override
+    public void visitEnd() {
+      consumer.accept(result != null ? result : KeepTypePattern.any());
+    }
+  }
+
+  private static class TypePatternsArrayVisitor extends AnnotationVisitorBase {
+    private final Supplier<String> annotationName;
+    private final Consumer<List<KeepTypePattern>> fn;
+    private final List<KeepTypePattern> patterns = new ArrayList<>();
+
+    public TypePatternsArrayVisitor(
+        Supplier<String> annotationName, Consumer<List<KeepTypePattern>> fn) {
+      this.annotationName = annotationName;
+      this.fn = fn;
+    }
+
+    @Override
+    public String getAnnotationName() {
+      return annotationName.get();
+    }
+
+    @Override
+    public AnnotationVisitor visitAnnotation(String unusedName, String descriptor) {
+      if (TypePattern.DESCRIPTOR.equals(descriptor)) {
+        return new TypePatternVisitor(annotationName, patterns::add);
+      }
+      return null;
+    }
+
+    @Override
+    public void visitEnd() {
+      super.visitEnd();
+      fn.accept(patterns);
+    }
+  }
+
   private static class OptionsDeclaration extends SingleDeclaration<KeepOptions> {
 
     private final String annotationName;
@@ -1996,7 +2286,7 @@
 
     @Override
     public AnnotationVisitor visitArray(String name) {
-      AnnotationVisitor visitor = optionsDeclaration.tryParseArray(name, v -> {});
+      AnnotationVisitor visitor = optionsDeclaration.tryParseArray(name);
       if (visitor != null) {
         return visitor;
       }
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeReaderUtils.java b/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeReaderUtils.java
index 7b0cf28..d88b010 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeReaderUtils.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeReaderUtils.java
@@ -68,10 +68,18 @@
     }
   }
 
-  public static KeepMethodReturnTypePattern methodReturnTypeFromString(String returnType) {
+  public static KeepMethodReturnTypePattern methodReturnTypeFromTypeName(String returnType) {
     if ("void".equals(returnType)) {
       return KeepMethodReturnTypePattern.voidType();
     }
     return KeepMethodReturnTypePattern.fromType(typePatternFromString(returnType));
   }
+
+  public static KeepMethodReturnTypePattern methodReturnTypeFromTypeDescriptor(
+      String returnTypeDesc) {
+    if ("V".equals(returnTypeDesc)) {
+      return KeepMethodReturnTypePattern.voidType();
+    }
+    return KeepMethodReturnTypePattern.fromType(KeepTypePattern.fromDescriptor(returnTypeDesc));
+  }
 }
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 6ae281b..1ab1561 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
@@ -87,10 +87,15 @@
     public static final String methodAccess = "methodAccess";
     public static final String methodName = "methodName";
     public static final String methodReturnType = "methodReturnType";
+    public static final String methodReturnTypeConstant = "methodReturnTypeConstant";
+    public static final String methodReturnTypePattern = "methodReturnTypePattern";
     public static final String methodParameters = "methodParameters";
+    public static final String methodParameterTypePatterns = "methodParameterTypePatterns";
     public static final String fieldAccess = "fieldAccess";
     public static final String fieldName = "fieldName";
     public static final String fieldType = "fieldType";
+    public static final String fieldTypeConstant = "fieldTypeConstant";
+    public static final String fieldTypePattern = "fieldTypePattern";
   }
 
   public static final class Binding {
@@ -191,4 +196,12 @@
     public static final String VOLATILE = "VOLATILE";
     public static final String TRANSIENT = "TRANSIENT";
   }
+
+  public static final class TypePattern {
+    public static final String SIMPLE_NAME = "TypePattern";
+    public static final String DESCRIPTOR =
+        "Lcom/android/tools/r8/keepanno/annotations/TypePattern;";
+    public static final String name = "name";
+    public static final String constant = "constant";
+  }
 }
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMethodParametersPattern.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMethodParametersPattern.java
index aec5fbf..2a7061d 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMethodParametersPattern.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMethodParametersPattern.java
@@ -72,12 +72,11 @@
     }
 
     @Override
-    @SuppressWarnings("EqualsGetClass")
     public boolean equals(Object o) {
       if (this == o) {
         return true;
       }
-      if (o == null || getClass() != o.getClass()) {
+      if (!(o instanceof Some)) {
         return false;
       }
 
diff --git a/src/test/java/com/android/tools/r8/keepanno/FieldPatternsTest.java b/src/test/java/com/android/tools/r8/keepanno/FieldPatternsTest.java
new file mode 100644
index 0000000..4f26412
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/keepanno/FieldPatternsTest.java
@@ -0,0 +1,114 @@
+// Copyright (c) 2023, 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.isPresent;
+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.KeepConstraint;
+import com.android.tools.r8.keepanno.annotations.KeepTarget;
+import com.android.tools.r8.keepanno.annotations.TypePattern;
+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.CodeInspector;
+import com.google.common.collect.ImmutableList;
+import java.lang.reflect.Field;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class FieldPatternsTest extends TestBase {
+
+  static final String EXPECTED = StringUtils.lines("Hello, world", "42", A.class.toString());
+
+  private final TestParameters parameters;
+
+  @Parameterized.Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withDefaultRuntimes().withApiLevel(AndroidApiLevel.B).build();
+  }
+
+  public FieldPatternsTest(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 testWithRuleExtraction() throws Exception {
+    testForR8(parameters.getBackend())
+        .enableExperimentalKeepAnnotations()
+        .addProgramClasses(getInputClasses())
+        .addKeepMainRule(TestClass.class)
+        .setMinApi(parameters)
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutput(EXPECTED)
+        .inspect(this::checkOutput);
+  }
+
+  public List<Class<?>> getInputClasses() {
+    return ImmutableList.of(TestClass.class, A.class);
+  }
+
+  private void checkOutput(CodeInspector inspector) {
+    assertThat(inspector.clazz(A.class), isPresent());
+    assertThat(inspector.clazz(A.class).uniqueFieldWithOriginalName("fieldA"), isPresent());
+    assertThat(inspector.clazz(A.class).uniqueFieldWithOriginalName("fieldB"), isAbsent());
+    assertThat(inspector.clazz(A.class).uniqueFieldWithOriginalName("fieldC"), isPresent());
+    assertThat(inspector.clazz(A.class).uniqueFieldWithOriginalName("fieldD"), isPresent());
+  }
+
+  static class A {
+
+    public String fieldA = "Hello, world";
+    public Integer fieldB = 42; // Not used or kept.
+    public int fieldC = 42;
+    public Object fieldD = A.class;
+
+    @UsesReflection({
+      @KeepTarget(classConstant = A.class),
+      @KeepTarget(
+          classConstant = A.class,
+          fieldTypePattern = @TypePattern(name = "java.lang.String"),
+          constraints = {KeepConstraint.LOOKUP, KeepConstraint.FIELD_GET}),
+      @KeepTarget(
+          classConstant = A.class,
+          fieldTypeConstant = Object.class,
+          constraints = {KeepConstraint.LOOKUP, KeepConstraint.FIELD_GET}),
+      @KeepTarget(
+          classConstant = A.class,
+          fieldTypePattern = @TypePattern(constant = int.class),
+          constraints = {KeepConstraint.LOOKUP, KeepConstraint.FIELD_GET})
+    })
+    public void foo() throws Exception {
+      for (Field field : getClass().getDeclaredFields()) {
+        if (field.getType().equals(String.class)
+            || field.getType().equals(Object.class)
+            || field.getType().equals(int.class)) {
+          System.out.println(field.get(this));
+        }
+      }
+    }
+  }
+
+  static class TestClass {
+
+    public static void main(String[] args) throws Exception {
+      new A().foo();
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/keepanno/MethodPatternsTest.java b/src/test/java/com/android/tools/r8/keepanno/MethodPatternsTest.java
new file mode 100644
index 0000000..86f2f9c
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/keepanno/MethodPatternsTest.java
@@ -0,0 +1,156 @@
+// Copyright (c) 2023, 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.isPresent;
+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.KeepItemKind;
+import com.android.tools.r8.keepanno.annotations.KeepTarget;
+import com.android.tools.r8.keepanno.annotations.TypePattern;
+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.CodeInspector;
+import com.google.common.collect.ImmutableList;
+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 MethodPatternsTest extends TestBase {
+
+  static final String EXPECTED =
+      StringUtils.lines("Hello 42", "Hello 12", "int", "long", "class java.lang.Integer");
+
+  private final TestParameters parameters;
+
+  @Parameterized.Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withDefaultRuntimes().withApiLevel(AndroidApiLevel.B).build();
+  }
+
+  public MethodPatternsTest(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)
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutput(EXPECTED)
+        .inspect(this::checkOutput);
+  }
+
+  public List<Class<?>> getInputClasses() {
+    return ImmutableList.of(TestClass.class, A.class, B.class);
+  }
+
+  private void checkOutput(CodeInspector inspector) {
+    assertThat(inspector.clazz(B.class), isPresentAndRenamed());
+    assertThat(inspector.clazz(B.class).method("void", "bar"), isAbsent());
+    assertThat(inspector.clazz(B.class).method("void", "bar", "int"), isPresent());
+    assertThat(inspector.clazz(B.class).method("void", "bar", "int", "int"), isPresent());
+    assertThat(
+        inspector.clazz(B.class).method("void", "bar", "java.lang.Object", "java.lang.Object"),
+        isAbsent());
+    assertThat(
+        inspector.clazz(B.class).method("int", "bar", "int", "long", "java.lang.Integer"),
+        isPresent());
+    assertThat(
+        inspector.clazz(B.class).method("int", "bar", "int", "long", "java.lang.Integer", "int"),
+        isAbsent());
+  }
+
+  static class A {
+
+    @UsesReflection({
+      @KeepTarget(
+          classConstant = B.class,
+          methodName = "bar",
+          methodReturnTypePattern = @TypePattern(name = "void"),
+          methodParameterTypePatterns = {@TypePattern(constant = int.class)}),
+      @KeepTarget(
+          classConstant = B.class,
+          methodName = "bar",
+          methodReturnTypeConstant = void.class,
+          methodParameterTypePatterns = {
+            @TypePattern(constant = int.class),
+            @TypePattern(name = "int")
+          }),
+      @KeepTarget(
+          classConstant = B.class,
+          methodName = "bar",
+          methodReturnTypeConstant = int.class,
+          methodParameterTypePatterns = {@TypePattern, @TypePattern, @TypePattern}),
+    })
+    public void foo() throws Exception {
+      // Invoke the first and second method.
+      B.class.getDeclaredMethod("bar", int.class).invoke(null, 42);
+      B.class.getDeclaredMethod("bar", int.class, int.class).invoke(null, 1, 2);
+      // Print args of third method.
+      for (Method method : B.class.getDeclaredMethods()) {
+        if (method.getReturnType().equals(int.class) && method.getParameterCount() == 3) {
+          for (Class<?> type : method.getParameterTypes()) {
+            System.out.println(type);
+          }
+        }
+      }
+    }
+  }
+
+  static class B {
+    public static void bar() {
+      throw new RuntimeException("UNUSED");
+    }
+
+    public static void bar(int value) {
+      System.out.println("Hello " + value);
+    }
+
+    public static void bar(int value1, int value2) {
+      System.out.println("Hello " + value1 + value2);
+    }
+
+    public static void bar(Object value1, Object value2) {
+      throw new RuntimeException("UNUSED");
+    }
+
+    public static int bar(int value1, long value2, Integer value3) {
+      System.out.println("Hello " + value1 + value2 + value3);
+      return value1 + (int) value2 + value3;
+    }
+
+    public static int bar(int value1, long value2, Integer value3, int value4) {
+      throw new RuntimeException("UNUSED");
+    }
+  }
+
+  static class TestClass {
+
+    @UsedByReflection(kind = KeepItemKind.CLASS_AND_METHODS)
+    public static void main(String[] args) throws Exception {
+      new A().foo();
+    }
+  }
+}
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 3e334a3..bd3d82f 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
@@ -20,6 +20,7 @@
 import com.android.tools.r8.keepanno.annotations.KeepTarget;
 import com.android.tools.r8.keepanno.annotations.MemberAccessFlags;
 import com.android.tools.r8.keepanno.annotations.MethodAccessFlags;
+import com.android.tools.r8.keepanno.annotations.TypePattern;
 import com.android.tools.r8.keepanno.annotations.UsedByNative;
 import com.android.tools.r8.keepanno.annotations.UsedByReflection;
 import com.android.tools.r8.keepanno.annotations.UsesReflection;
@@ -42,7 +43,6 @@
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.function.Consumer;
-import org.jetbrains.annotations.NotNull;
 
 public class KeepItemAnnotationGenerator {
 
@@ -334,6 +334,38 @@
                   .defaultEmptyArray("KeepTarget"));
     }
 
+    private Group typePatternGroup() {
+      return new Group("type-pattern")
+          .addMember(
+              new GroupMember("name")
+                  .setDocTitle("Exact type name as a string.")
+                  .addParagraph("For example, {@code \"long\"} or {@code \"java.lang.String\"}.")
+                  .defaultEmptyString())
+          .addMember(
+              new GroupMember("constant")
+                  .setDocTitle("Exact type from a class constant.")
+                  .addParagraph("For example, {@code String.class}.")
+                  .defaultObjectClass());
+      // TODO(b/248408342): Add more injections on type pattern variants.
+      // /** Exact type name as a string to match any array with that type as member. */
+      // String arrayOf() default "";
+      //
+      // /** Exact type as a class constant to match any array with that type as member. */
+      // Class<?> arrayOfConstant() default TypePattern.class;
+      //
+      // /** If true, the pattern matches any primitive type. Such as, boolean, int, etc. */
+      // boolean anyPrimitive() default false;
+      //
+      // /** If true, the pattern matches any array type. */
+      // boolean anyArray() default false;
+      //
+      // /** If true, the pattern matches any class type. */
+      // boolean anyClass() default false;
+      //
+      // /** If true, the pattern matches any reference type, namely: arrays or classes. */
+      // boolean anyReference() default false;
+    }
+
     private Group getKindGroup() {
       return new Group(KIND_GROUP).addMember(getKindMember());
     }
@@ -396,7 +428,6 @@
       return StringUtils.join(", ", values, v -> docLink(v), BraceType.TUBORG);
     }
 
-    @NotNull
     private static GroupMember constraints() {
       return new GroupMember("constraints")
           .setDocTitle("Define the usage constraints of the target.")
@@ -627,7 +658,22 @@
                   .addParagraph(getMutuallyExclusiveForMethodProperties())
                   .addParagraph(getMethodDefaultDoc("any return type"))
                   .setDocReturn("The qualified type name of the method return type.")
-                  .defaultEmptyString());
+                  .defaultEmptyString())
+          .addMember(
+              new GroupMember("methodReturnTypeConstant")
+                  .setDocTitle("Define the method return-type pattern by a class constant.")
+                  .addParagraph(getMutuallyExclusiveForMethodProperties())
+                  .addParagraph(getMethodDefaultDoc("any return type"))
+                  .setDocReturn("A class constant denoting the type of the method return type.")
+                  .defaultObjectClass())
+          .addMember(
+              new GroupMember("methodReturnTypePattern")
+                  .setDocTitle("Define the method return-type pattern by a type pattern.")
+                  .addParagraph(getMutuallyExclusiveForMethodProperties())
+                  .addParagraph(getMethodDefaultDoc("any return type"))
+                  .setDocReturn("The pattern of the method return type.")
+                  .defaultType("TypePattern")
+                  .defaultValue("@TypePattern(name = \"\")"));
     }
 
     private Group createMethodParametersGroup() {
@@ -640,7 +686,16 @@
                   .addParagraph(getMethodDefaultDoc("any parameters"))
                   .setDocReturn("The list of qualified type names of the method parameters.")
                   .defaultType("String[]")
-                  .defaultValue("{\"<default>\"}"));
+                  .defaultValue("{\"\"}"))
+          .addMember(
+              new GroupMember("methodParameterTypePatterns")
+                  .setDocTitle(
+                      "Define the method parameters pattern by a list of patterns on types.")
+                  .addParagraph(getMutuallyExclusiveForMethodProperties())
+                  .addParagraph(getMethodDefaultDoc("any parameters"))
+                  .setDocReturn("The list of type patterns for the method parameters.")
+                  .defaultType("TypePattern[]")
+                  .defaultValue("{@TypePattern(name = \"\")}"));
     }
 
     private Group createFieldAccessGroup() {
@@ -672,8 +727,23 @@
                   .setDocTitle("Define the field-type pattern by a fully qualified type.")
                   .addParagraph(getMutuallyExclusiveForFieldProperties())
                   .addParagraph(getFieldDefaultDoc("any type"))
-                  .setDocReturn("The qualified type name of the field type.")
-                  .defaultEmptyString());
+                  .setDocReturn("The qualified type name for the field type.")
+                  .defaultEmptyString())
+          .addMember(
+              new GroupMember("fieldTypeConstant")
+                  .setDocTitle("Define the field-type pattern by a class constant.")
+                  .addParagraph(getMutuallyExclusiveForFieldProperties())
+                  .addParagraph(getFieldDefaultDoc("any type"))
+                  .setDocReturn("The class constant for the field type.")
+                  .defaultObjectClass())
+          .addMember(
+              new GroupMember("fieldTypePattern")
+                  .setDocTitle("Define the field-type pattern by a pattern on types.")
+                  .addParagraph(getMutuallyExclusiveForFieldProperties())
+                  .addParagraph(getFieldDefaultDoc("any type"))
+                  .setDocReturn("The type pattern for the field type.")
+                  .defaultType("TypePattern")
+                  .defaultValue("@TypePattern(name = \"\")"));
     }
 
     private void generateClassAndMemberPropertiesWithClassAndMemberBinding() {
@@ -733,6 +803,24 @@
       createFieldTypeGroup().generate(this);
     }
 
+    private void generateTypePattern() {
+      printCopyRight(2023);
+      printPackage("annotations");
+      printImports(ANNOTATION_IMPORTS);
+      DocPrinter.printer()
+          .setDocTitle("A pattern structure for matching types.")
+          .addParagraph("If no properties are set, the default pattern matches any type.")
+          .addParagraph("All properties on this annotation are mutually exclusive.")
+          .printDoc(this::println);
+      println("@Target(ElementType.ANNOTATION_TYPE)");
+      println("@Retention(RetentionPolicy.CLASS)");
+      println("public @interface TypePattern {");
+      println();
+      withIndent(() -> typePatternGroup().generate(this));
+      println();
+      println("}");
+    }
+
     private void generateKeepBinding() {
       printCopyRight(2022);
       printPackage("annotations");
@@ -1061,6 +1149,8 @@
             generateMemberAccessConstants();
             generateMethodAccessConstants();
             generateFieldAccessConstants();
+
+            generateTypePatternConstants();
           });
       println("}");
     }
@@ -1330,6 +1420,17 @@
       println();
     }
 
+    private void generateTypePatternConstants() {
+      println("public static final class TypePattern {");
+      withIndent(
+          () -> {
+            generateAnnotationConstants(TypePattern.class);
+            typePatternGroup().generateConstants(this);
+          });
+      println("}");
+      println();
+    }
+
     private static void writeFile(Path file, Consumer<Generator> fn) throws IOException {
       ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
       PrintStream printStream = new PrintStream(byteStream);
@@ -1351,6 +1452,7 @@
       writeFile(astPkg.resolve("AnnotationConstants.java"), Generator::generateConstants);
 
       Path annoPkg = Paths.get("src/keepanno/java/com/android/tools/r8/keepanno/annotations");
+      writeFile(annoPkg.resolve("TypePattern.java"), Generator::generateTypePattern);
       writeFile(annoPkg.resolve("KeepBinding.java"), Generator::generateKeepBinding);
       writeFile(annoPkg.resolve("KeepTarget.java"), Generator::generateKeepTarget);
       writeFile(annoPkg.resolve("KeepCondition.java"), Generator::generateKeepCondition);