[KeepAnno] Add parsing context structure for error reporting

Bug: b/248408342
Change-Id: If845574089b98b9cfa0201f9dd0692af523cdc0a
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/ClassNamePattern.java b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/ClassNamePattern.java
index 1d40ae5..a175b3d 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/ClassNamePattern.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/ClassNamePattern.java
@@ -25,7 +25,7 @@
   /**
    * Exact simple name of the class or interface.
    *
-   * <p>For example, the simple name {@code com.example.MyClass} is {@code MyClass}.
+   * <p>For example, the simple name of {@code com.example.MyClass} is {@code MyClass}.
    *
    * <p>The default matches any simple name.
    */
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/asm/AnnotationVisitorBase.java b/src/keepanno/java/com/android/tools/r8/keepanno/asm/AnnotationVisitorBase.java
index e15d4e7..777e99b 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/asm/AnnotationVisitorBase.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/asm/AnnotationVisitorBase.java
@@ -4,20 +4,17 @@
 
 package com.android.tools.r8.keepanno.asm;
 
-import com.android.tools.r8.keepanno.ast.KeepEdgeException;
+import com.android.tools.r8.keepanno.ast.ParsingContext;
 import org.objectweb.asm.AnnotationVisitor;
 import org.objectweb.asm.Type;
 
 public abstract class AnnotationVisitorBase extends AnnotationVisitor {
 
-  AnnotationVisitorBase() {
+  private final ParsingContext parsingContext;
+
+  AnnotationVisitorBase(ParsingContext parsingContext) {
     super(KeepEdgeReader.ASM_VERSION);
-  }
-
-  public abstract String getAnnotationName();
-
-  private String errorMessagePrefix() {
-    return "@" + getAnnotationName() + ": ";
+    this.parsingContext = parsingContext;
   }
 
   private String getTypeName(String descriptor) {
@@ -26,34 +23,31 @@
 
   @Override
   public void visit(String name, Object value) {
-    throw new KeepEdgeException(
-        "Unexpected value in " + errorMessagePrefix() + name + " = " + value);
+    throw parsingContext.error("Unexpected value for property " + name + " with value " + value);
   }
 
   @Override
   public AnnotationVisitor visitAnnotation(String name, String descriptor) {
-    throw new KeepEdgeException(
-        "Unexpected annotation in "
-            + errorMessagePrefix()
+    throw parsingContext.error(
+        "Unexpected annotation for property "
             + name
-            + " for annotation: "
+            + " of annotation type "
             + getTypeName(descriptor));
   }
 
   @Override
   public void visitEnum(String name, String descriptor, String value) {
-    throw new KeepEdgeException(
-        "Unexpected enum in "
-            + errorMessagePrefix()
+    throw parsingContext.error(
+        "Unexpected enum for property "
             + name
-            + " for enum: "
+            + " of enum type "
             + getTypeName(descriptor)
-            + " with value: "
+            + " with value "
             + value);
   }
 
   @Override
   public AnnotationVisitor visitArray(String name) {
-    throw new KeepEdgeException("Unexpected array in " + errorMessagePrefix() + name);
+    throw parsingContext.error("Unexpected array for property " + name);
   }
 }
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/asm/ClassNameParser.java b/src/keepanno/java/com/android/tools/r8/keepanno/asm/ClassNameParser.java
index c1e5655..d6c41ac 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/asm/ClassNameParser.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/asm/ClassNameParser.java
@@ -11,6 +11,8 @@
 import com.android.tools.r8.keepanno.ast.KeepPackagePattern;
 import com.android.tools.r8.keepanno.ast.KeepQualifiedClassNamePattern;
 import com.android.tools.r8.keepanno.ast.KeepUnqualfiedClassNamePattern;
+import com.android.tools.r8.keepanno.ast.ParsingContext;
+import com.android.tools.r8.keepanno.ast.ParsingContext.AnnotationParsingContext;
 import com.google.common.collect.ImmutableList;
 import java.util.function.Consumer;
 import org.objectweb.asm.AnnotationVisitor;
@@ -18,6 +20,10 @@
 public class ClassNameParser
     extends PropertyParserBase<KeepQualifiedClassNamePattern, ClassNameProperty, ClassNameParser> {
 
+  public ClassNameParser(ParsingContext parsingContext) {
+    super(parsingContext);
+  }
+
   public enum ClassNameProperty {
     PATTERN
   }
@@ -36,13 +42,16 @@
     switch (property) {
       case PATTERN:
         {
+          AnnotationParsingContext parsingContext =
+              new AnnotationParsingContext(getParsingContext(), descriptor);
           PackageNameParser packageParser =
-              new PackageNameParser()
+              new PackageNameParser(parsingContext)
                   .setProperty(PackageNameProperty.NAME, ClassNamePattern.packageName);
           ClassSimpleNameParser nameParser =
-              new ClassSimpleNameParser()
+              new ClassSimpleNameParser(parsingContext)
                   .setProperty(ClassSimpleNameProperty.NAME, ClassNamePattern.simpleName);
           return new ParserVisitor(
+              parsingContext,
               descriptor,
               ImmutableList.of(packageParser, nameParser),
               () ->
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/asm/ClassSimpleNameParser.java b/src/keepanno/java/com/android/tools/r8/keepanno/asm/ClassSimpleNameParser.java
index 17296bc..d349898 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/asm/ClassSimpleNameParser.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/asm/ClassSimpleNameParser.java
@@ -6,12 +6,17 @@
 
 import com.android.tools.r8.keepanno.asm.ClassSimpleNameParser.ClassSimpleNameProperty;
 import com.android.tools.r8.keepanno.ast.KeepUnqualfiedClassNamePattern;
+import com.android.tools.r8.keepanno.ast.ParsingContext;
 import java.util.function.Consumer;
 
 public class ClassSimpleNameParser
     extends PropertyParserBase<
         KeepUnqualfiedClassNamePattern, ClassSimpleNameProperty, ClassSimpleNameParser> {
 
+  public ClassSimpleNameParser(ParsingContext parsingContext) {
+    super(parsingContext);
+  }
+
   public enum ClassSimpleNameProperty {
     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 4513464..00729cb 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
@@ -20,7 +20,7 @@
 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.KeepAnnotationParserException;
 import com.android.tools.r8.keepanno.ast.KeepBindingReference;
 import com.android.tools.r8.keepanno.ast.KeepBindings;
 import com.android.tools.r8.keepanno.ast.KeepBindings.KeepBindingSymbol;
@@ -32,7 +32,6 @@
 import com.android.tools.r8.keepanno.ast.KeepConsequences;
 import com.android.tools.r8.keepanno.ast.KeepDeclaration;
 import com.android.tools.r8.keepanno.ast.KeepEdge;
-import com.android.tools.r8.keepanno.ast.KeepEdgeException;
 import com.android.tools.r8.keepanno.ast.KeepEdgeMetaInfo;
 import com.android.tools.r8.keepanno.ast.KeepFieldAccessPattern;
 import com.android.tools.r8.keepanno.ast.KeepFieldNamePattern;
@@ -42,6 +41,7 @@
 import com.android.tools.r8.keepanno.ast.KeepItemPattern;
 import com.android.tools.r8.keepanno.ast.KeepItemReference;
 import com.android.tools.r8.keepanno.ast.KeepMemberAccessPattern;
+import com.android.tools.r8.keepanno.ast.KeepMemberAccessPattern.BuilderBase;
 import com.android.tools.r8.keepanno.ast.KeepMemberItemPattern;
 import com.android.tools.r8.keepanno.ast.KeepMemberPattern;
 import com.android.tools.r8.keepanno.ast.KeepMethodAccessPattern;
@@ -57,6 +57,11 @@
 import com.android.tools.r8.keepanno.ast.KeepTarget;
 import com.android.tools.r8.keepanno.ast.KeepTypePattern;
 import com.android.tools.r8.keepanno.ast.KeepUnqualfiedClassNamePattern;
+import com.android.tools.r8.keepanno.ast.ParsingContext;
+import com.android.tools.r8.keepanno.ast.ParsingContext.AnnotationParsingContext;
+import com.android.tools.r8.keepanno.ast.ParsingContext.ClassParsingContext;
+import com.android.tools.r8.keepanno.ast.ParsingContext.FieldParsingContext;
+import com.android.tools.r8.keepanno.ast.ParsingContext.MethodParsingContext;
 import com.android.tools.r8.keepanno.utils.Unimplemented;
 import com.google.common.collect.ImmutableList;
 import java.util.ArrayList;
@@ -193,6 +198,7 @@
   private static class KeepEdgeClassVisitor extends ClassVisitor {
     private final Parent<KeepDeclaration> parent;
     private String className;
+    private ClassParsingContext parsingContext;
 
     KeepEdgeClassVisitor(Parent<KeepDeclaration> parent) {
       super(ASM_VERSION);
@@ -212,7 +218,12 @@
         String superName,
         String[] interfaces) {
       super.visit(version, access, name, signature, superName, interfaces);
-      this.className = binaryNameToTypeName(name);
+      className = binaryNameToTypeName(name);
+      parsingContext = new ClassParsingContext(className);
+    }
+
+    private AnnotationParsingContext annotationParsingContext(String descriptor) {
+      return new AnnotationParsingContext(parsingContext, descriptor);
     }
 
     @Override
@@ -222,48 +233,66 @@
         return null;
       }
       if (descriptor.equals(Edge.DESCRIPTOR)) {
-        return new KeepEdgeVisitor(parent::accept, this::setContext);
+        return new KeepEdgeVisitor(
+            annotationParsingContext(descriptor), parent::accept, this::setContext);
       }
       if (descriptor.equals(AnnotationConstants.UsesReflection.DESCRIPTOR)) {
         KeepClassItemPattern classItem =
             KeepClassItemPattern.builder()
                 .setClassNamePattern(KeepQualifiedClassNamePattern.exact(className))
                 .build();
-        return new UsesReflectionVisitor(parent::accept, this::setContext, classItem);
+        return new UsesReflectionVisitor(
+            annotationParsingContext(descriptor), parent::accept, this::setContext, classItem);
       }
       if (descriptor.equals(AnnotationConstants.ForApi.DESCRIPTOR)) {
-        return new ForApiClassVisitor(parent::accept, this::setContext, className);
+        return new ForApiClassVisitor(
+            annotationParsingContext(descriptor), parent::accept, this::setContext, className);
       }
       if (descriptor.equals(AnnotationConstants.UsedByReflection.DESCRIPTOR)
           || descriptor.equals(AnnotationConstants.UsedByNative.DESCRIPTOR)) {
         return new UsedByReflectionClassVisitor(
-            descriptor, parent::accept, this::setContext, className);
+            annotationParsingContext(descriptor),
+            descriptor,
+            parent::accept,
+            this::setContext,
+            className);
       }
       if (descriptor.equals(AnnotationConstants.CheckRemoved.DESCRIPTOR)) {
         return new CheckRemovedClassVisitor(
-            descriptor, parent::accept, this::setContext, className, KeepCheckKind.REMOVED);
+            annotationParsingContext(descriptor),
+            descriptor,
+            parent::accept,
+            this::setContext,
+            className,
+            KeepCheckKind.REMOVED);
       }
       if (descriptor.equals(AnnotationConstants.CheckOptimizedOut.DESCRIPTOR)) {
         return new CheckRemovedClassVisitor(
-            descriptor, parent::accept, this::setContext, className, KeepCheckKind.OPTIMIZED_OUT);
+            annotationParsingContext(descriptor),
+            descriptor,
+            parent::accept,
+            this::setContext,
+            className,
+            KeepCheckKind.OPTIMIZED_OUT);
       }
       return null;
     }
 
     private void setContext(KeepEdgeMetaInfo.Builder builder) {
-      builder.setContextFromClassDescriptor(KeepEdgeReaderUtils.javaTypeToDescriptor(className));
+      builder.setContextFromClassDescriptor(
+          KeepEdgeReaderUtils.getDescriptorFromJavaType(className));
     }
 
     @Override
     public MethodVisitor visitMethod(
         int access, String name, String descriptor, String signature, String[] exceptions) {
-      return new KeepEdgeMethodVisitor(parent::accept, className, name, descriptor);
+      return new KeepEdgeMethodVisitor(parsingContext, parent::accept, className, name, descriptor);
     }
 
     @Override
     public FieldVisitor visitField(
         int access, String name, String descriptor, String signature, Object value) {
-      return new KeepEdgeFieldVisitor(parent::accept, className, name, descriptor);
+      return new KeepEdgeFieldVisitor(parsingContext, parent::accept, className, name, descriptor);
     }
   }
 
@@ -272,8 +301,10 @@
     private final String className;
     private final String methodName;
     private final String methodDescriptor;
+    private final MethodParsingContext parsingContext;
 
     KeepEdgeMethodVisitor(
+        ClassParsingContext classParsingContext,
         Parent<KeepDeclaration> parent,
         String className,
         String methodName,
@@ -283,6 +314,8 @@
       this.className = className;
       this.methodName = methodName;
       this.methodDescriptor = methodDescriptor;
+      this.parsingContext =
+          new MethodParsingContext(classParsingContext, methodName, methodDescriptor);
     }
 
     private KeepMemberItemPattern createMethodItemContext() {
@@ -308,6 +341,10 @@
           .build();
     }
 
+    private AnnotationParsingContext annotationParsingContext(String descriptor) {
+      return new AnnotationParsingContext(parsingContext, descriptor);
+    }
+
     @Override
     public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
       // Skip any visible annotations as @KeepEdge is not runtime visible.
@@ -315,22 +352,35 @@
         return null;
       }
       if (descriptor.equals(Edge.DESCRIPTOR)) {
-        return new KeepEdgeVisitor(parent::accept, this::setContext);
+        return new KeepEdgeVisitor(
+            annotationParsingContext(descriptor), parent::accept, this::setContext);
       }
       if (descriptor.equals(AnnotationConstants.UsesReflection.DESCRIPTOR)) {
         return new UsesReflectionVisitor(
-            parent::accept, this::setContext, createMethodItemContext());
+            annotationParsingContext(descriptor),
+            parent::accept,
+            this::setContext,
+            createMethodItemContext());
       }
       if (descriptor.equals(AnnotationConstants.ForApi.DESCRIPTOR)) {
-        return new ForApiMemberVisitor(parent::accept, this::setContext, createMethodItemContext());
+        return new ForApiMemberVisitor(
+            annotationParsingContext(descriptor),
+            parent::accept,
+            this::setContext,
+            createMethodItemContext());
       }
       if (descriptor.equals(AnnotationConstants.UsedByReflection.DESCRIPTOR)
           || descriptor.equals(AnnotationConstants.UsedByNative.DESCRIPTOR)) {
         return new UsedByReflectionMemberVisitor(
-            descriptor, parent::accept, this::setContext, createMethodItemContext());
+            annotationParsingContext(descriptor),
+            descriptor,
+            parent::accept,
+            this::setContext,
+            createMethodItemContext());
       }
       if (descriptor.equals(AnnotationConstants.CheckRemoved.DESCRIPTOR)) {
         return new CheckRemovedMemberVisitor(
+            annotationParsingContext(descriptor),
             descriptor,
             parent::accept,
             this::setContext,
@@ -339,6 +389,7 @@
       }
       if (descriptor.equals(AnnotationConstants.CheckOptimizedOut.DESCRIPTOR)) {
         return new CheckRemovedMemberVisitor(
+            annotationParsingContext(descriptor),
             descriptor,
             parent::accept,
             this::setContext,
@@ -350,7 +401,7 @@
 
     private void setContext(KeepEdgeMetaInfo.Builder builder) {
       builder.setContextFromMethodDescriptor(
-          KeepEdgeReaderUtils.javaTypeToDescriptor(className), methodName, methodDescriptor);
+          KeepEdgeReaderUtils.getDescriptorFromJavaType(className), methodName, methodDescriptor);
     }
   }
 
@@ -359,14 +410,25 @@
     private final String className;
     private final String fieldName;
     private final String fieldDescriptor;
+    private final FieldParsingContext parsingContext;
 
     KeepEdgeFieldVisitor(
-        Parent<KeepEdge> parent, String className, String fieldName, String fieldDescriptor) {
+        ClassParsingContext classParsingContext,
+        Parent<KeepEdge> parent,
+        String className,
+        String fieldName,
+        String fieldDescriptor) {
       super(ASM_VERSION);
       this.parent = parent;
       this.className = className;
       this.fieldName = fieldName;
       this.fieldDescriptor = fieldDescriptor;
+      this.parsingContext =
+          new FieldParsingContext(classParsingContext, fieldName, fieldDescriptor);
+    }
+
+    private AnnotationParsingContext annotationParsingContext(String descriptor) {
+      return new AnnotationParsingContext(parsingContext, descriptor);
     }
 
     private KeepMemberItemPattern createMemberItemContext() {
@@ -384,7 +446,7 @@
 
     private void setContext(KeepEdgeMetaInfo.Builder builder) {
       builder.setContextFromFieldDescriptor(
-          KeepEdgeReaderUtils.javaTypeToDescriptor(className), fieldName, fieldDescriptor);
+          KeepEdgeReaderUtils.getDescriptorFromJavaType(className), fieldName, fieldDescriptor);
     }
 
     @Override
@@ -394,18 +456,30 @@
         return null;
       }
       if (descriptor.equals(Edge.DESCRIPTOR)) {
-        return new KeepEdgeVisitor(parent, this::setContext);
+        return new KeepEdgeVisitor(annotationParsingContext(descriptor), parent, this::setContext);
       }
       if (descriptor.equals(AnnotationConstants.UsesReflection.DESCRIPTOR)) {
-        return new UsesReflectionVisitor(parent, this::setContext, createMemberItemContext());
+        return new UsesReflectionVisitor(
+            annotationParsingContext(descriptor),
+            parent,
+            this::setContext,
+            createMemberItemContext());
       }
       if (descriptor.equals(AnnotationConstants.ForApi.DESCRIPTOR)) {
-        return new ForApiMemberVisitor(parent, this::setContext, createMemberItemContext());
+        return new ForApiMemberVisitor(
+            annotationParsingContext(descriptor),
+            parent,
+            this::setContext,
+            createMemberItemContext());
       }
       if (descriptor.equals(AnnotationConstants.UsedByReflection.DESCRIPTOR)
           || descriptor.equals(AnnotationConstants.UsedByNative.DESCRIPTOR)) {
         return new UsedByReflectionMemberVisitor(
-            descriptor, parent, this::setContext, createMemberItemContext());
+            annotationParsingContext(descriptor),
+            descriptor,
+            parent,
+            this::setContext,
+            createMemberItemContext());
       }
       return null;
     }
@@ -444,22 +518,24 @@
   }
 
   private static class KeepEdgeVisitor extends AnnotationVisitorBase {
+
+    private final AnnotationParsingContext parsingContext;
     private final Parent<KeepEdge> parent;
     private final KeepEdge.Builder builder = KeepEdge.builder();
     private final KeepEdgeMetaInfo.Builder metaInfoBuilder = KeepEdgeMetaInfo.builder();
     private final UserBindingsHelper bindingsHelper = new UserBindingsHelper();
 
-    KeepEdgeVisitor(Parent<KeepEdge> parent, Consumer<KeepEdgeMetaInfo.Builder> addContext) {
+    KeepEdgeVisitor(
+        AnnotationParsingContext parsingContext,
+        Parent<KeepEdge> parent,
+        Consumer<KeepEdgeMetaInfo.Builder> addContext) {
+      super(parsingContext);
+      this.parsingContext = parsingContext;
       this.parent = parent;
       addContext.accept(metaInfoBuilder);
     }
 
     @Override
-    public String getAnnotationName() {
-      return "KeepEdge";
-    }
-
-    @Override
     public void visit(String name, Object value) {
       if (name.equals(Edge.description) && value instanceof String) {
         metaInfoBuilder.setDescription((String) value);
@@ -471,15 +547,15 @@
     @Override
     public AnnotationVisitor visitArray(String name) {
       if (name.equals(Edge.bindings)) {
-        return new KeepBindingsVisitor(getAnnotationName(), bindingsHelper);
+        return new KeepBindingsVisitor(parsingContext, bindingsHelper);
       }
       if (name.equals(Edge.preconditions)) {
         return new KeepPreconditionsVisitor(
-            getAnnotationName(), builder::setPreconditions, bindingsHelper);
+            parsingContext, builder::setPreconditions, bindingsHelper);
       }
       if (name.equals(Edge.consequences)) {
         return new KeepConsequencesVisitor(
-            getAnnotationName(), builder::setConsequences, bindingsHelper);
+            parsingContext, builder::setConsequences, bindingsHelper);
       }
       return super.visitArray(name);
     }
@@ -499,6 +575,8 @@
    * properties are encountered.
    */
   private static class ForApiClassVisitor extends KeepItemVisitorBase {
+
+    private final AnnotationParsingContext parsingContext;
     private final String className;
     private final Parent<KeepEdge> parent;
     private final KeepEdge.Builder builder = KeepEdge.builder();
@@ -507,7 +585,12 @@
     private final UserBindingsHelper bindingsHelper = new UserBindingsHelper();
 
     ForApiClassVisitor(
-        Parent<KeepEdge> parent, Consumer<KeepEdgeMetaInfo.Builder> addContext, String className) {
+        AnnotationParsingContext parsingContext,
+        Parent<KeepEdge> parent,
+        Consumer<KeepEdgeMetaInfo.Builder> addContext,
+        String className) {
+      super(parsingContext);
+      this.parsingContext = parsingContext;
       this.className = className;
       this.parent = parent;
       addContext.accept(metaInfoBuilder);
@@ -523,11 +606,6 @@
     }
 
     @Override
-    public String getAnnotationName() {
-      return ForApi.SIMPLE_NAME;
-    }
-
-    @Override
     public void visit(String name, Object value) {
       if (name.equals(Edge.description) && value instanceof String) {
         metaInfoBuilder.setDescription((String) value);
@@ -540,7 +618,7 @@
     public AnnotationVisitor visitArray(String name) {
       if (name.equals(ForApi.additionalTargets)) {
         return new KeepConsequencesVisitor(
-            getAnnotationName(),
+            parsingContext,
             additionalConsequences -> {
               additionalConsequences.forEachTarget(consequences::addTarget);
             },
@@ -561,7 +639,7 @@
       Collection<KeepItemReference> items = getItemsWithoutBinding();
       for (KeepItemReference item : items) {
         if (item.isBindingReference()) {
-          throw new KeepEdgeException("@KeepForApi cannot reference bindings");
+          throw parsingContext.error("cannot reference bindings");
         }
         KeepClassItemPattern classItemPattern = item.asClassItemPattern();
         if (classItemPattern == null) {
@@ -571,13 +649,13 @@
         String descriptor = KeepEdgeReaderUtils.getDescriptorFromClassTypeName(className);
         String itemDescriptor = classItemPattern.getClassNamePattern().getExactDescriptor();
         if (!descriptor.equals(itemDescriptor)) {
-          throw new KeepEdgeException("@KeepForApi must reference its class context " + className);
+          throw parsingContext.error("must reference its class context " + className);
         }
         if (classItemPattern.isMemberItemPattern() && items.size() == 1) {
-            throw new KeepEdgeException("@KeepForApi kind must include its class");
+          throw parsingContext.error("kind must include its class");
         }
         if (!classItemPattern.getInstanceOfPattern().isAny()) {
-          throw new KeepEdgeException("@KeepForApi cannot define an 'extends' pattern.");
+          throw parsingContext.error("cannot define an 'extends' pattern.");
         }
         consequences.addTarget(KeepTarget.builder().setItemReference(item).build());
       }
@@ -596,6 +674,8 @@
    * <p>When used on a member context the annotation does not allow member related patterns.
    */
   private static class ForApiMemberVisitor extends AnnotationVisitorBase {
+
+    private final AnnotationParsingContext parsingContext;
     private final Parent<KeepEdge> parent;
     private final KeepEdge.Builder builder = KeepEdge.builder();
     private final KeepEdgeMetaInfo.Builder metaInfoBuilder = KeepEdgeMetaInfo.builder();
@@ -603,9 +683,12 @@
     private final KeepConsequences.Builder consequences = KeepConsequences.builder();
 
     ForApiMemberVisitor(
+        AnnotationParsingContext parsingContext,
         Parent<KeepEdge> parent,
         Consumer<KeepEdgeMetaInfo.Builder> addContext,
         KeepMemberItemPattern context) {
+      super(parsingContext);
+      this.parsingContext = parsingContext;
       this.parent = parent;
       addContext.accept(metaInfoBuilder);
       // Create a binding for the context such that the class and member are shared.
@@ -625,11 +708,6 @@
     }
 
     @Override
-    public String getAnnotationName() {
-      return ForApi.SIMPLE_NAME;
-    }
-
-    @Override
     public void visit(String name, Object value) {
       if (name.equals(Edge.description) && value instanceof String) {
         metaInfoBuilder.setDescription((String) value);
@@ -642,7 +720,7 @@
     public AnnotationVisitor visitArray(String name) {
       if (name.equals(ForApi.additionalTargets)) {
         return new KeepConsequencesVisitor(
-            getAnnotationName(),
+            parsingContext,
             additionalConsequences -> {
               additionalConsequences.forEachTarget(consequences::addTarget);
             },
@@ -670,6 +748,8 @@
    * properties are encountered.
    */
   private static class UsedByReflectionClassVisitor extends KeepItemVisitorBase {
+
+    private final AnnotationParsingContext parsingContext;
     private final String annotationDescriptor;
     private final String className;
     private final Parent<KeepEdge> parent;
@@ -680,17 +760,20 @@
     private final OptionsDeclaration optionsDeclaration;
 
     UsedByReflectionClassVisitor(
+        AnnotationParsingContext parsingContext,
         String annotationDescriptor,
         Parent<KeepEdge> parent,
         Consumer<KeepEdgeMetaInfo.Builder> addContext,
         String className) {
+      super(parsingContext);
+      this.parsingContext = parsingContext;
       this.annotationDescriptor = annotationDescriptor;
       this.className = className;
       this.parent = parent;
       addContext.accept(metaInfoBuilder);
       // The class context/holder is the annotated class.
       visit(Item.className, className);
-      optionsDeclaration = new OptionsDeclaration(getAnnotationName());
+      optionsDeclaration = new OptionsDeclaration(parsingContext);
     }
 
     @Override
@@ -699,12 +782,6 @@
     }
 
     @Override
-    public String getAnnotationName() {
-      int sep = annotationDescriptor.lastIndexOf('/');
-      return annotationDescriptor.substring(sep + 1, annotationDescriptor.length() - 1);
-    }
-
-    @Override
     public void visit(String name, Object value) {
       if (name.equals(Edge.description) && value instanceof String) {
         metaInfoBuilder.setDescription((String) value);
@@ -717,11 +794,11 @@
     public AnnotationVisitor visitArray(String name) {
       if (name.equals(Edge.preconditions)) {
         return new KeepPreconditionsVisitor(
-            getAnnotationName(), builder::setPreconditions, bindingsHelper);
+            parsingContext, builder::setPreconditions, bindingsHelper);
       }
       if (name.equals(UsedByReflection.additionalTargets)) {
         return new KeepConsequencesVisitor(
-            getAnnotationName(),
+            parsingContext,
             additionalConsequences -> {
               additionalConsequences.forEachTarget(consequences::addTarget);
             },
@@ -745,7 +822,7 @@
       for (KeepItemReference item : items) {
         if (item.isBindingReference()) {
           // TODO(b/248408342): The edge can have preconditions so it should support bindings!
-          throw new KeepEdgeException("@" + getAnnotationName() + " cannot reference bindings");
+          throw parsingContext.error("cannot reference bindings");
         }
         KeepItemPattern itemPattern = item.asItemPattern();
         KeepClassItemPattern holderPattern =
@@ -755,15 +832,13 @@
         String descriptor = KeepEdgeReaderUtils.getDescriptorFromClassTypeName(className);
         String itemDescriptor = holderPattern.getClassNamePattern().getExactDescriptor();
         if (!descriptor.equals(itemDescriptor)) {
-          throw new KeepEdgeException(
-              "@" + getAnnotationName() + " must reference its class context " + className);
+          throw parsingContext.error("must reference its class context " + className);
         }
         if (itemPattern.isMemberItemPattern() && items.size() == 1) {
-          throw new KeepEdgeException("@" + getAnnotationName() + " kind must include its class");
+          throw parsingContext.error("kind must include its class");
         }
         if (!holderPattern.getInstanceOfPattern().isAny()) {
-          throw new KeepEdgeException(
-              "@" + getAnnotationName() + " cannot define an 'extends' pattern.");
+          throw parsingContext.error("cannot define an 'extends' pattern.");
         }
         consequences.addTarget(
             KeepTarget.builder()
@@ -786,6 +861,8 @@
    * <p>When used on a member context the annotation does not allow member related patterns.
    */
   private static class UsedByReflectionMemberVisitor extends AnnotationVisitorBase {
+
+    private final AnnotationParsingContext parsingContext;
     private final String annotationDescriptor;
     private final Parent<KeepEdge> parent;
     private final KeepItemPattern context;
@@ -797,21 +874,18 @@
     private final OptionsDeclaration optionsDeclaration;
 
     UsedByReflectionMemberVisitor(
+        AnnotationParsingContext parsingContext,
         String annotationDescriptor,
         Parent<KeepEdge> parent,
         Consumer<KeepEdgeMetaInfo.Builder> addContext,
         KeepItemPattern context) {
+      super(parsingContext);
+      this.parsingContext = parsingContext;
       this.annotationDescriptor = annotationDescriptor;
       this.parent = parent;
       this.context = context;
       addContext.accept(metaInfoBuilder);
-      optionsDeclaration = new OptionsDeclaration(getAnnotationName());
-    }
-
-    @Override
-    public String getAnnotationName() {
-      int sep = annotationDescriptor.lastIndexOf('/');
-      return annotationDescriptor.substring(sep + 1, annotationDescriptor.length() - 1);
+      optionsDeclaration = new OptionsDeclaration(parsingContext);
     }
 
     @Override
@@ -840,11 +914,11 @@
     public AnnotationVisitor visitArray(String name) {
       if (name.equals(Edge.preconditions)) {
         return new KeepPreconditionsVisitor(
-            getAnnotationName(), builder::setPreconditions, bindingsHelper);
+            parsingContext, builder::setPreconditions, bindingsHelper);
       }
       if (name.equals(UsedByReflection.additionalTargets)) {
         return new KeepConsequencesVisitor(
-            getAnnotationName(),
+            parsingContext,
             additionalConsequences -> {
               additionalConsequences.forEachTarget(consequences::addTarget);
             },
@@ -860,7 +934,7 @@
     @Override
     public void visitEnd() {
       if (kind.isOnlyClass()) {
-        throw new KeepEdgeException("@" + getAnnotationName() + " kind must include its member");
+        throw parsingContext.error("kind must include its member");
       }
       assert context.isMemberItemPattern();
       KeepMemberItemPattern memberContext = context.asMemberItemPattern();
@@ -884,18 +958,20 @@
 
     private void validateConsistentKind(KeepMemberPattern memberPattern) {
       if (memberPattern.isGeneralMember()) {
-        throw new KeepEdgeException("Unexpected general pattern for context.");
+        throw parsingContext.error("Unexpected general pattern for context.");
       }
       if (memberPattern.isMethod() && !kind.includesMethod()) {
-        throw new KeepEdgeException("Kind " + kind + " cannot be use when annotating a method");
+        throw parsingContext.error("Kind " + kind + " cannot be use when annotating a method");
       }
       if (memberPattern.isField() && !kind.includesField()) {
-        throw new KeepEdgeException("Kind " + kind + " cannot be use when annotating a field");
+        throw parsingContext.error("Kind " + kind + " cannot be use when annotating a field");
       }
     }
   }
 
   private static class UsesReflectionVisitor extends AnnotationVisitorBase {
+
+    private final AnnotationParsingContext parsingContext;
     private final Parent<KeepEdge> parent;
     private final KeepEdge.Builder builder = KeepEdge.builder();
     private final KeepPreconditions.Builder preconditions = KeepPreconditions.builder();
@@ -903,20 +979,18 @@
     private final UserBindingsHelper bindingsHelper = new UserBindingsHelper();
 
     UsesReflectionVisitor(
+        AnnotationParsingContext parsingContext,
         Parent<KeepEdge> parent,
         Consumer<KeepEdgeMetaInfo.Builder> addContext,
         KeepItemPattern context) {
+      super(parsingContext);
+      this.parsingContext = parsingContext;
       this.parent = parent;
       preconditions.addCondition(KeepCondition.builder().setItemPattern(context).build());
       addContext.accept(metaInfoBuilder);
     }
 
     @Override
-    public String getAnnotationName() {
-      return UsesReflection.SIMPLE_NAME;
-    }
-
-    @Override
     public void visit(String name, Object value) {
       if (name.equals(Edge.description) && value instanceof String) {
         metaInfoBuilder.setDescription((String) value);
@@ -929,11 +1003,11 @@
     public AnnotationVisitor visitArray(String name) {
       if (name.equals(AnnotationConstants.UsesReflection.value)) {
         return new KeepConsequencesVisitor(
-            getAnnotationName(), builder::setConsequences, bindingsHelper);
+            parsingContext, builder::setConsequences, bindingsHelper);
       }
       if (name.equals(AnnotationConstants.UsesReflection.additionalPreconditions)) {
         return new KeepPreconditionsVisitor(
-            getAnnotationName(),
+            parsingContext,
             additionalPreconditions -> {
               additionalPreconditions.forEach(preconditions::addCondition);
             },
@@ -954,52 +1028,44 @@
   }
 
   private static class KeepBindingsVisitor extends AnnotationVisitorBase {
-    private final String annotationName;
+    private final AnnotationParsingContext parsingContext;
     private final UserBindingsHelper helper;
 
-    public KeepBindingsVisitor(String annotationName, UserBindingsHelper helper) {
-      this.annotationName = annotationName;
+    public KeepBindingsVisitor(AnnotationParsingContext parsingContext, UserBindingsHelper helper) {
+      super(parsingContext);
+      this.parsingContext = parsingContext;
       this.helper = helper;
     }
 
     @Override
-    public String getAnnotationName() {
-      return annotationName;
-    }
-
-    @Override
     public AnnotationVisitor visitAnnotation(String name, String descriptor) {
       if (descriptor.equals(AnnotationConstants.Binding.DESCRIPTOR)) {
-        return new KeepBindingVisitor(helper);
+        return new KeepBindingVisitor(parsingContext, helper);
       }
       return super.visitAnnotation(name, descriptor);
     }
   }
 
   private static class KeepPreconditionsVisitor extends AnnotationVisitorBase {
-    private final String annotationName;
+    private final AnnotationParsingContext parsingContext;
     private final Parent<KeepPreconditions> parent;
     private final KeepPreconditions.Builder builder = KeepPreconditions.builder();
     private final UserBindingsHelper bindingsHelper;
 
     public KeepPreconditionsVisitor(
-        String annotationName,
+        AnnotationParsingContext parsingContext,
         Parent<KeepPreconditions> parent,
         UserBindingsHelper bindingsHelper) {
-      this.annotationName = annotationName;
+      super(parsingContext);
+      this.parsingContext = parsingContext;
       this.parent = parent;
       this.bindingsHelper = bindingsHelper;
     }
 
     @Override
-    public String getAnnotationName() {
-      return annotationName;
-    }
-
-    @Override
     public AnnotationVisitor visitAnnotation(String name, String descriptor) {
       if (descriptor.equals(Condition.DESCRIPTOR)) {
-        return new KeepConditionVisitor(builder::addCondition, bindingsHelper);
+        return new KeepConditionVisitor(parsingContext, builder::addCondition, bindingsHelper);
       }
       return super.visitAnnotation(name, descriptor);
     }
@@ -1011,27 +1077,25 @@
   }
 
   private static class KeepConsequencesVisitor extends AnnotationVisitorBase {
-    private final String annotationName;
+    private final AnnotationParsingContext parsingContext;
     private final Parent<KeepConsequences> parent;
     private final KeepConsequences.Builder builder = KeepConsequences.builder();
     private final UserBindingsHelper bindingsHelper;
 
     public KeepConsequencesVisitor(
-        String annotationName, Parent<KeepConsequences> parent, UserBindingsHelper bindingsHelper) {
-      this.annotationName = annotationName;
+        AnnotationParsingContext parsingContext,
+        Parent<KeepConsequences> parent,
+        UserBindingsHelper bindingsHelper) {
+      super(parsingContext);
+      this.parsingContext = parsingContext;
       this.parent = parent;
       this.bindingsHelper = bindingsHelper;
     }
 
     @Override
-    public String getAnnotationName() {
-      return annotationName;
-    }
-
-    @Override
     public AnnotationVisitor visitAnnotation(String name, String descriptor) {
       if (descriptor.equals(Target.DESCRIPTOR)) {
-        return KeepTargetVisitor.create(builder::addTarget, bindingsHelper);
+        return KeepTargetVisitor.create(parsingContext, builder::addTarget, bindingsHelper);
       }
       return super.visitAnnotation(name, descriptor);
     }
@@ -1045,6 +1109,7 @@
   /** Parsing of @CheckRemoved and @CheckOptimizedOut on a class context. */
   private static class CheckRemovedClassVisitor extends AnnotationVisitorBase {
 
+    private final AnnotationParsingContext parsingContext;
     private final String annotationDescriptor;
     private final Parent<KeepCheck> parent;
     private final KeepEdgeMetaInfo.Builder metaInfoBuilder = KeepEdgeMetaInfo.builder();
@@ -1052,11 +1117,14 @@
     private final KeepCheckKind kind;
 
     public CheckRemovedClassVisitor(
+        AnnotationParsingContext parsingContext,
         String annotationDescriptor,
         Parent<KeepCheck> parent,
         Consumer<KeepEdgeMetaInfo.Builder> addContext,
         String className,
         KeepCheckKind kind) {
+      super(parsingContext);
+      this.parsingContext = parsingContext;
       this.annotationDescriptor = annotationDescriptor;
       this.parent = parent;
       this.className = className;
@@ -1065,12 +1133,6 @@
     }
 
     @Override
-    public String getAnnotationName() {
-      int sep = annotationDescriptor.lastIndexOf('/');
-      return annotationDescriptor.substring(sep + 1, annotationDescriptor.length() - 1);
-    }
-
-    @Override
     public void visit(String name, Object value) {
       if (name.equals(Edge.description) && value instanceof String) {
         metaInfoBuilder.setDescription((String) value);
@@ -1083,15 +1145,10 @@
     public void visitEnd() {
       CheckRemovedClassVisitor superVisitor = this;
       KeepItemVisitorBase itemVisitor =
-          new KeepItemVisitorBase() {
+          new KeepItemVisitorBase(parsingContext) {
             @Override
             public UserBindingsHelper getBindingsHelper() {
-              throw new KeepEdgeException("Bindings not supported in @" + getAnnotationName());
-            }
-
-            @Override
-            public String getAnnotationName() {
-              return superVisitor.getAnnotationName();
+              throw parsingContext.error("bindings not supported");
             }
           };
       itemVisitor.visit(Item.className, className);
@@ -1115,11 +1172,13 @@
     private final KeepCheckKind kind;
 
     CheckRemovedMemberVisitor(
+        AnnotationParsingContext parsingContext,
         String annotationDescriptor,
         Parent<KeepDeclaration> parent,
         Consumer<KeepEdgeMetaInfo.Builder> addContext,
         KeepItemPattern context,
         KeepCheckKind kind) {
+      super(parsingContext);
       this.annotationDescriptor = annotationDescriptor;
       this.parent = parent;
       this.context = context;
@@ -1128,12 +1187,6 @@
     }
 
     @Override
-    public String getAnnotationName() {
-      int sep = annotationDescriptor.lastIndexOf('/');
-      return annotationDescriptor.substring(sep + 1, annotationDescriptor.length() - 1);
-    }
-
-    @Override
     public void visit(String name, Object value) {
       if (name.equals(Edge.description) && value instanceof String) {
         metaInfoBuilder.setDescription((String) value);
@@ -1203,10 +1256,19 @@
   }
 
   private abstract static class SingleDeclaration<T> extends Declaration<T> {
+    private final ParsingContext parsingContext;
     private String declarationName = null;
     private T declarationValue = null;
     private AnnotationVisitor declarationVisitor = null;
 
+    private SingleDeclaration(ParsingContext parsingContext) {
+      this.parsingContext = parsingContext;
+    }
+
+    public ParsingContext getParsingContext() {
+      return parsingContext;
+    }
+
     abstract T getDefaultValue();
 
     abstract T parse(String name, Object value);
@@ -1229,7 +1291,8 @@
     }
 
     private void error(String name) {
-      throw new KeepEdgeException(
+      throw new KeepAnnotationParserException(
+          parsingContext,
           "Multiple declarations defining "
               + kind()
               + ": '"
@@ -1290,6 +1353,10 @@
   private static class ClassNameDeclaration
       extends SingleDeclaration<KeepQualifiedClassNamePattern> {
 
+    private ClassNameDeclaration(ParsingContext parsingContext) {
+      super(parsingContext);
+    }
+
     @Override
     String kind() {
       return "class-name";
@@ -1315,7 +1382,8 @@
     AnnotationVisitor parseAnnotation(
         String name, String descriptor, Consumer<KeepQualifiedClassNamePattern> setValue) {
       if (name.equals(Item.classNamePattern) && descriptor.equals(ClassNamePattern.DESCRIPTOR)) {
-        return new ClassNamePatternVisitor(setValue);
+        return new ClassNamePatternVisitor(
+            new AnnotationParsingContext(getParsingContext(), descriptor), setValue);
       }
       return super.parseAnnotation(name, descriptor, setValue);
     }
@@ -1323,6 +1391,10 @@
 
   private static class InstanceOfDeclaration extends SingleDeclaration<KeepInstanceOfPattern> {
 
+    private InstanceOfDeclaration(ParsingContext parsingContext) {
+      super(parsingContext);
+    }
+
     @Override
     String kind() {
       return "instance-of";
@@ -1375,16 +1447,21 @@
 
   private static class ClassDeclaration extends Declaration<KeepClassItemReference> {
 
+    private final ParsingContext parsingContext;
     private final Supplier<UserBindingsHelper> getBindingsHelper;
 
     private KeepClassItemReference boundClassItemReference = null;
-    private final ClassNameDeclaration classNameDeclaration = new ClassNameDeclaration();
-    private final InstanceOfDeclaration instanceOfDeclaration = new InstanceOfDeclaration();
-    private final List<Declaration<?>> declarations =
-        ImmutableList.of(classNameDeclaration, instanceOfDeclaration);
+    private final ClassNameDeclaration classNameDeclaration;
+    private final InstanceOfDeclaration instanceOfDeclaration;
+    private final List<Declaration<?>> declarations;
 
-    public ClassDeclaration(Supplier<UserBindingsHelper> getBindingsHelper) {
+    public ClassDeclaration(
+        ParsingContext parsingContext, Supplier<UserBindingsHelper> getBindingsHelper) {
+      this.parsingContext = parsingContext;
       this.getBindingsHelper = getBindingsHelper;
+      classNameDeclaration = new ClassNameDeclaration(parsingContext);
+      instanceOfDeclaration = new InstanceOfDeclaration(parsingContext);
+      declarations = ImmutableList.of(classNameDeclaration, instanceOfDeclaration);
     }
 
     @Override
@@ -1402,7 +1479,7 @@
 
     private void checkAllowedDefinitions() {
       if (isBindingReferenceDefined() && classPatternsAreDefined()) {
-        throw new KeepEdgeException(
+        throw parsingContext.error(
             "Cannot reference a class binding and class patterns for a single class item");
       }
     }
@@ -1436,7 +1513,7 @@
 
     public void setBindingReference(KeepClassItemReference bindingReference) {
       if (isBindingReferenceDefined()) {
-        throw new KeepEdgeException(
+        throw parsingContext.error(
             "Cannot reference multiple class bindings for a single class item");
       }
       this.boundClassItemReference = bindingReference;
@@ -1456,13 +1533,12 @@
   private static class MethodReturnTypeDeclaration
       extends SingleDeclaration<KeepMethodReturnTypePattern> {
 
-    private final Supplier<String> annotationName;
     private final TypeParser typeParser;
 
-    private MethodReturnTypeDeclaration(Supplier<String> annotationName) {
-      this.annotationName = annotationName;
+    private MethodReturnTypeDeclaration(ParsingContext parsingContext) {
+      super(parsingContext);
       typeParser =
-          new TypeParser()
+          new TypeParser(parsingContext)
               .setKind("return type")
               .enableTypePattern(Item.methodReturnTypePattern)
               .enableTypeName(Item.methodReturnType)
@@ -1506,18 +1582,19 @@
   private static class MethodParametersDeclaration
       extends SingleDeclaration<KeepMethodParametersPattern> {
 
-    private final Supplier<String> annotationName;
+    private final ParsingContext parsingContext;
     private KeepMethodParametersPattern pattern = null;
 
-    public MethodParametersDeclaration(Supplier<String> annotationName) {
-      this.annotationName = annotationName;
+    public MethodParametersDeclaration(ParsingContext parsingContext) {
+      super(parsingContext);
+      this.parsingContext = parsingContext;
     }
 
     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");
+        throw parsingContext.error("Cannot declare multiple patterns for the parameter list");
       }
       setValue.accept(pattern);
       this.pattern = pattern;
@@ -1542,7 +1619,7 @@
     AnnotationVisitor parseArray(String name, Consumer<KeepMethodParametersPattern> setValue) {
       if (name.equals(Item.methodParameters)) {
         return new StringArrayVisitor(
-            annotationName,
+            getParsingContext(),
             params -> {
               KeepMethodParametersPattern.Builder builder = KeepMethodParametersPattern.builder();
               for (String param : params) {
@@ -1553,7 +1630,7 @@
       }
       if (name.equals(Item.methodParameterTypePatterns)) {
         return new TypePatternsArrayVisitor(
-            annotationName,
+            getParsingContext(),
             params -> {
               KeepMethodParametersPattern.Builder builder = KeepMethodParametersPattern.builder();
               for (KeepTypePattern param : params) {
@@ -1567,7 +1644,8 @@
   }
 
   private static class MethodDeclaration extends Declaration<KeepMethodPattern> {
-    private final Supplier<String> annotationName;
+
+    private final ParsingContext parsingContext;
     private KeepMethodAccessPattern.Builder accessBuilder = null;
     private KeepMethodPattern.Builder builder = null;
     private final MethodReturnTypeDeclaration returnTypeDeclaration;
@@ -1575,10 +1653,10 @@
 
     private final List<Declaration<?>> declarations;
 
-    private MethodDeclaration(Supplier<String> annotationName) {
-      this.annotationName = annotationName;
-      returnTypeDeclaration = new MethodReturnTypeDeclaration(annotationName);
-      parametersDeclaration = new MethodParametersDeclaration(annotationName);
+    private MethodDeclaration(ParsingContext parsingContext) {
+      this.parsingContext = parsingContext;
+      returnTypeDeclaration = new MethodReturnTypeDeclaration(parsingContext);
+      parametersDeclaration = new MethodParametersDeclaration(parsingContext);
       declarations = ImmutableList.of(returnTypeDeclaration, parametersDeclaration);
     }
 
@@ -1631,7 +1709,7 @@
     AnnotationVisitor tryParseArray(String name) {
       if (name.equals(Item.methodAccess)) {
         accessBuilder = KeepMethodAccessPattern.builder();
-        return new MethodAccessVisitor(annotationName, accessBuilder);
+        return new MethodAccessVisitor(parsingContext, accessBuilder);
       }
       return super.tryParseArray(name);
     }
@@ -1639,13 +1717,12 @@
 
   private static class FieldTypeDeclaration extends SingleDeclaration<KeepFieldTypePattern> {
 
-    private final Supplier<String> annotationName;
     private final TypeParser typeParser;
 
-    private FieldTypeDeclaration(Supplier<String> annotationName) {
-      this.annotationName = annotationName;
+    private FieldTypeDeclaration(ParsingContext parsingContext) {
+      super(parsingContext);
       this.typeParser =
-          new TypeParser()
+          new TypeParser(parsingContext)
               .setKind("field type")
               .enableTypePattern(Item.fieldTypePattern)
               .enableTypeName(Item.fieldType)
@@ -1680,15 +1757,16 @@
   }
 
   private static class FieldDeclaration extends Declaration<KeepFieldPattern> {
-    private final Supplier<String> annotationName;
+
+    private final ParsingContext parsingContext;
     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);
+    public FieldDeclaration(ParsingContext parsingContext) {
+      this.parsingContext = parsingContext;
+      typeDeclaration = new FieldTypeDeclaration(parsingContext);
       declarations = Collections.singletonList(typeDeclaration);
     }
 
@@ -1738,23 +1816,24 @@
     AnnotationVisitor tryParseArray(String name) {
       if (name.equals(Item.fieldAccess)) {
         accessBuilder = KeepFieldAccessPattern.builder();
-        return new FieldAccessVisitor(annotationName, accessBuilder);
+        return new FieldAccessVisitor(parsingContext, accessBuilder);
       }
       return super.tryParseArray(name);
     }
   }
 
   private static class MemberDeclaration extends Declaration<KeepMemberPattern> {
-    private final Supplier<String> annotationName;
+
+    private final ParsingContext parsingContext;
     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);
+    MemberDeclaration(ParsingContext parsingContext) {
+      this.parsingContext = parsingContext;
+      methodDeclaration = new MethodDeclaration(parsingContext);
+      fieldDeclaration = new FieldDeclaration(parsingContext);
       declarations = ImmutableList.of(methodDeclaration, fieldDeclaration);
     }
 
@@ -1779,13 +1858,13 @@
       KeepFieldPattern field = fieldDeclaration.getValue();
       if (accessBuilder != null) {
         if (method != null || field != null) {
-          throw new KeepEdgeException(
+          throw parsingContext.error(
               "Cannot define common member access as well as field or method pattern");
         }
         return KeepMemberPattern.memberBuilder().setAccessPattern(accessBuilder.build()).build();
       }
       if (method != null && field != null) {
-        throw new KeepEdgeException("Cannot define both a field and a method pattern");
+        throw parsingContext.error("Cannot define both a field and a method pattern");
       }
       if (method != null) {
         return method;
@@ -1800,37 +1879,42 @@
     AnnotationVisitor tryParseArray(String name) {
       if (name.equals(Item.memberAccess)) {
         accessBuilder = KeepMemberAccessPattern.memberBuilder();
-        return new MemberAccessVisitor(annotationName, accessBuilder);
+        return new MemberAccessVisitor(parsingContext, accessBuilder);
       }
       return super.tryParseArray(name);
     }
   }
 
   private abstract static class KeepItemVisitorBase extends AnnotationVisitorBase {
+    private final ParsingContext parsingContext;
     private String memberBindingReference = null;
     private ItemKind kind = null;
-    private final ClassDeclaration classDeclaration = new ClassDeclaration(this::getBindingsHelper);
+    private final ClassDeclaration classDeclaration;
     private final MemberDeclaration memberDeclaration;
 
+
     public abstract UserBindingsHelper getBindingsHelper();
 
     // Constructed item available once visitEnd has been called.
     private KeepItemReference itemReference = null;
 
-    KeepItemVisitorBase() {
-      memberDeclaration = new MemberDeclaration(this::getAnnotationName);
+    KeepItemVisitorBase(ParsingContext parsingContext) {
+      super(parsingContext);
+      this.parsingContext = parsingContext;
+      classDeclaration = new ClassDeclaration(parsingContext, this::getBindingsHelper);
+      memberDeclaration = new MemberDeclaration(parsingContext);
     }
 
     public Collection<KeepItemReference> getItemsWithoutBinding() {
       if (itemReference == null) {
-        throw new KeepEdgeException("Item reference not finalized. Missing call to visitEnd()");
+        throw parsingContext.error("Item reference not finalized. Missing call to visitEnd()");
       }
       if (itemReference.isBindingReference()) {
         return Collections.singletonList(itemReference);
       }
       // Kind is only null if item is a "binding reference".
       if (kind == null) {
-        throw new KeepEdgeException("Unexpected state: unknown kind for an item pattern");
+        throw parsingContext.error("Unexpected state: unknown kind for an item pattern");
       }
       if (kind.includesClassAndMembers()) {
         assert !itemReference.isBindingReference();
@@ -1856,14 +1940,14 @@
 
     public Collection<KeepItemReference> getItemsWithBinding() {
       if (itemReference == null) {
-        throw new KeepEdgeException("Item reference not finalized. Missing call to visitEnd()");
+        throw parsingContext.error("Item reference not finalized. Missing call to visitEnd()");
       }
       if (itemReference.isBindingReference()) {
         return Collections.singletonList(itemReference);
       }
       // Kind is only null if item is a "binding reference".
       if (kind == null) {
-        throw new KeepEdgeException("Unexpected state: unknown kind for an item pattern");
+        throw parsingContext.error("Unexpected state: unknown kind for an item pattern");
       }
       if (kind.includesClassAndMembers()) {
         KeepItemPattern itemPattern = itemReference.asItemPattern();
@@ -1900,7 +1984,7 @@
 
     public KeepItemReference getItemReference() {
       if (itemReference == null) {
-        throw new KeepEdgeException("Item reference not finalized. Missing call to visitEnd()");
+        throw parsingContext.error("Item reference not finalized. Missing call to visitEnd()");
       }
       return itemReference;
     }
@@ -1967,7 +2051,7 @@
         if (!classDeclaration.isDefault()
             || !memberDeclaration.getValue().isNone()
             || kind != null) {
-          throw new KeepEdgeException(
+          throw parsingContext.error(
               "Cannot define an item explicitly and via a member-binding reference");
         }
         KeepBindingSymbol symbol = getBindingsHelper().resolveUserBinding(memberBindingReference);
@@ -1989,7 +2073,7 @@
         }
 
         if (kind.isOnlyClass() && !memberPattern.isNone()) {
-          throw new KeepEdgeException("Item pattern for members is incompatible with kind " + kind);
+          throw parsingContext.error("Item pattern for members is incompatible with kind " + kind);
         }
 
         // Refine the member pattern to be as precise as the specified kind.
@@ -2006,8 +2090,7 @@
             memberPattern = KeepMethodPattern.allMethods();
           } else {
             assert memberPattern.isField();
-            throw new KeepEdgeException(
-                "Item pattern for fields is incompatible with kind " + kind);
+            throw parsingContext.error("Item pattern for fields is incompatible with kind " + kind);
           }
         }
 
@@ -2024,7 +2107,7 @@
             memberPattern = KeepFieldPattern.allFields();
           } else {
             assert memberPattern.isMethod();
-            throw new KeepEdgeException(
+            throw parsingContext.error(
                 "Item pattern for methods is incompatible with kind " + kind);
           }
         }
@@ -2050,10 +2133,13 @@
 
   private static class KeepBindingVisitor extends KeepItemVisitorBase {
 
+    private final AnnotationParsingContext parsingContext;
     private final UserBindingsHelper helper;
     private String bindingName;
 
-    public KeepBindingVisitor(UserBindingsHelper helper) {
+    public KeepBindingVisitor(AnnotationParsingContext parsingContext, UserBindingsHelper helper) {
+      super(parsingContext);
+      this.parsingContext = parsingContext;
       this.helper = helper;
     }
 
@@ -2063,11 +2149,6 @@
     }
 
     @Override
-    public String getAnnotationName() {
-      return Binding.SIMPLE_NAME;
-    }
-
-    @Override
     public void visit(String name, Object value) {
       if (name.equals(Binding.bindingName) && value instanceof String) {
         bindingName = (String) value;
@@ -2083,7 +2164,7 @@
       // The language currently disallows aliasing bindings, thus a binding cannot directly be
       // defined by a reference to another binding.
       if (item.isBindingReference()) {
-        throw new KeepEdgeException(
+        throw parsingContext.error(
             "Invalid binding reference to '"
                 + item.asBindingReference()
                 + "' in binding definition of '"
@@ -2095,21 +2176,15 @@
   }
 
   private static class StringArrayVisitor extends AnnotationVisitorBase {
-    private final Supplier<String> annotationName;
     private final Consumer<List<String>> fn;
     private final List<String> strings = new ArrayList<>();
 
-    public StringArrayVisitor(Supplier<String> annotationName, Consumer<List<String>> fn) {
-      this.annotationName = annotationName;
+    public StringArrayVisitor(ParsingContext parsingContext, Consumer<List<String>> fn) {
+      super(parsingContext);
       this.fn = fn;
     }
 
     @Override
-    public String getAnnotationName() {
-      return annotationName.get();
-    }
-
-    @Override
     public void visit(String name, Object value) {
       if (value instanceof String) {
         strings.add((String) value);
@@ -2128,6 +2203,10 @@
   private static class ClassSimpleNameDeclaration
       extends SingleDeclaration<KeepUnqualfiedClassNamePattern> {
 
+    private ClassSimpleNameDeclaration(ParsingContext parsingContext) {
+      super(parsingContext);
+    }
+
     @Override
     String kind() {
       return "class-simple-name";
@@ -2149,6 +2228,10 @@
 
   private static class PackageDeclaration extends SingleDeclaration<KeepPackagePattern> {
 
+    private PackageDeclaration(ParsingContext parsingContext) {
+      super(parsingContext);
+    }
+
     @Override
     String kind() {
       return "package";
@@ -2171,10 +2254,15 @@
   private static class ClassNamePatternDeclaration
       extends Declaration<KeepQualifiedClassNamePattern> {
 
-    private final ClassSimpleNameDeclaration nameDeclaration = new ClassSimpleNameDeclaration();
-    private final PackageDeclaration packageDeclaration = new PackageDeclaration();
-    private final List<Declaration<?>> declarations =
-        ImmutableList.of(nameDeclaration, packageDeclaration);
+    private final ClassSimpleNameDeclaration nameDeclaration;
+    private final PackageDeclaration packageDeclaration;
+    private final List<Declaration<?>> declarations;
+
+    public ClassNamePatternDeclaration(ParsingContext parsingContext) {
+      nameDeclaration = new ClassSimpleNameDeclaration(parsingContext);
+      packageDeclaration = new PackageDeclaration(parsingContext);
+      declarations = ImmutableList.of(nameDeclaration, packageDeclaration);
+    }
 
     @Override
     String kind() {
@@ -2200,16 +2288,14 @@
 
   private static class ClassNamePatternVisitor extends AnnotationVisitorBase {
 
-    private final ClassNamePatternDeclaration declaration = new ClassNamePatternDeclaration();
+    private final ClassNamePatternDeclaration declaration;
     private final Consumer<KeepQualifiedClassNamePattern> setValue;
 
-    public ClassNamePatternVisitor(Consumer<KeepQualifiedClassNamePattern> setValue) {
+    public ClassNamePatternVisitor(
+        AnnotationParsingContext parsingContext, Consumer<KeepQualifiedClassNamePattern> setValue) {
+      super(parsingContext);
       this.setValue = setValue;
-    }
-
-    @Override
-    public String getAnnotationName() {
-      return ClassNamePattern.SIMPLE_NAME;
+      declaration = new ClassNamePatternDeclaration(parsingContext);
     }
 
     @Override
@@ -2229,24 +2315,19 @@
   }
 
   private static class TypePatternVisitor extends AnnotationVisitorBase {
-    private final Supplier<String> annotationName;
+    private final ParsingContext parsingContext;
     private final Consumer<KeepTypePattern> consumer;
     private KeepTypePattern result = null;
 
-    private TypePatternVisitor(
-        Supplier<String> annotationName, Consumer<KeepTypePattern> consumer) {
-      this.annotationName = annotationName;
+    private TypePatternVisitor(ParsingContext parsingContext, Consumer<KeepTypePattern> consumer) {
+      super(parsingContext);
+      this.parsingContext = parsingContext;
       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.");
+        throw parsingContext.error("Invalid type annotation defining multiple properties.");
       }
       this.result = result;
     }
@@ -2270,6 +2351,7 @@
       if (TypePattern.classNamePattern.equals(name)
           && descriptor.equals(ClassNamePattern.DESCRIPTOR)) {
         return new ClassNamePatternVisitor(
+            new AnnotationParsingContext(parsingContext, descriptor),
             p -> {
               if (p.isExact()) {
                 setResult(KeepTypePattern.fromDescriptor(p.getExactDescriptor()));
@@ -2289,25 +2371,21 @@
   }
 
   private static class TypePatternsArrayVisitor extends AnnotationVisitorBase {
-    private final Supplier<String> annotationName;
+    private final ParsingContext parsingContext;
     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;
+        ParsingContext parsingContext, Consumer<List<KeepTypePattern>> fn) {
+      super(parsingContext);
+      this.parsingContext = parsingContext;
       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 new TypePatternVisitor(parsingContext, patterns::add);
       }
       return null;
     }
@@ -2321,10 +2399,8 @@
 
   private static class OptionsDeclaration extends SingleDeclaration<KeepOptions> {
 
-    private final String annotationName;
-
-    public OptionsDeclaration(String annotationName) {
-      this.annotationName = annotationName;
+    public OptionsDeclaration(ParsingContext parsingContext) {
+      super(parsingContext);
     }
 
     @Override
@@ -2346,17 +2422,17 @@
     AnnotationVisitor parseArray(String name, Consumer<KeepOptions> setValue) {
       if (name.equals(AnnotationConstants.Target.constraints)) {
         return new KeepConstraintsVisitor(
-            annotationName,
+            getParsingContext(),
             options -> setValue.accept(KeepOptions.disallowBuilder().addAll(options).build()));
       }
       if (name.equals(AnnotationConstants.Target.disallow)) {
         return new KeepOptionsVisitor(
-            annotationName,
+            getParsingContext(),
             options -> setValue.accept(KeepOptions.disallowBuilder().addAll(options).build()));
       }
       if (name.equals(AnnotationConstants.Target.allow)) {
         return new KeepOptionsVisitor(
-            annotationName,
+            getParsingContext(),
             options -> setValue.accept(KeepOptions.allowBuilder().addAll(options).build()));
       }
       return null;
@@ -2366,18 +2442,25 @@
   private static class KeepTargetVisitor extends KeepItemVisitorBase {
 
     private final Parent<KeepTarget> parent;
-    private final KeepTarget.Builder builder = KeepTarget.builder();
-    private final OptionsDeclaration optionsDeclaration =
-        new OptionsDeclaration(getAnnotationName());
     private final UserBindingsHelper bindingsHelper;
+    private final OptionsDeclaration optionsDeclaration;
+    private final KeepTarget.Builder builder = KeepTarget.builder();
 
-    static KeepTargetVisitor create(Parent<KeepTarget> parent, UserBindingsHelper bindingsHelper) {
-      return new KeepTargetVisitor(parent, bindingsHelper);
+    static KeepTargetVisitor create(
+        ParsingContext parsingContext,
+        Parent<KeepTarget> parent,
+        UserBindingsHelper bindingsHelper) {
+      return new KeepTargetVisitor(parsingContext, parent, bindingsHelper);
     }
 
-    private KeepTargetVisitor(Parent<KeepTarget> parent, UserBindingsHelper bindingsHelper) {
+    private KeepTargetVisitor(
+        ParsingContext parsingContext,
+        Parent<KeepTarget> parent,
+        UserBindingsHelper bindingsHelper) {
+      super(parsingContext);
       this.parent = parent;
       this.bindingsHelper = bindingsHelper;
+      optionsDeclaration = new OptionsDeclaration(parsingContext);
     }
 
     @Override
@@ -2386,11 +2469,6 @@
     }
 
     @Override
-    public String getAnnotationName() {
-      return Target.SIMPLE_NAME;
-    }
-
-    @Override
     public AnnotationVisitor visitArray(String name) {
       AnnotationVisitor visitor = optionsDeclaration.tryParseArray(name);
       if (visitor != null) {
@@ -2414,7 +2492,11 @@
     private final Parent<KeepCondition> parent;
     private final UserBindingsHelper bindingsHelper;
 
-    public KeepConditionVisitor(Parent<KeepCondition> parent, UserBindingsHelper bindingsHelper) {
+    public KeepConditionVisitor(
+        ParsingContext parsingContext,
+        Parent<KeepCondition> parent,
+        UserBindingsHelper bindingsHelper) {
+      super(parsingContext);
       this.parent = parent;
       this.bindingsHelper = bindingsHelper;
     }
@@ -2425,11 +2507,6 @@
     }
 
     @Override
-    public String getAnnotationName() {
-      return Condition.SIMPLE_NAME;
-    }
-
-    @Override
     public void visitEnd() {
       super.visitEnd();
       parent.accept(KeepCondition.builder().setItemReference(getItemReference()).build());
@@ -2438,21 +2515,16 @@
 
   private static class KeepConstraintsVisitor extends AnnotationVisitorBase {
 
-    private final String annotationName;
     private final Parent<Collection<KeepOption>> parent;
     private final Set<KeepOption> options = new HashSet<>();
 
-    public KeepConstraintsVisitor(String annotationName, Parent<Collection<KeepOption>> parent) {
-      this.annotationName = annotationName;
+    public KeepConstraintsVisitor(
+        ParsingContext parsingContext, Parent<Collection<KeepOption>> parent) {
+      super(parsingContext);
       this.parent = parent;
     }
 
     @Override
-    public String getAnnotationName() {
-      return annotationName;
-    }
-
-    @Override
     public void visitEnum(String ignore, String descriptor, String value) {
       if (!descriptor.equals(AnnotationConstants.Constraints.DESCRIPTOR)) {
         super.visitEnum(ignore, descriptor, value);
@@ -2506,21 +2578,16 @@
 
   private static class KeepOptionsVisitor extends AnnotationVisitorBase {
 
-    private final String annotationName;
     private final Parent<Collection<KeepOption>> parent;
     private final Set<KeepOption> options = new HashSet<>();
 
-    public KeepOptionsVisitor(String annotationName, Parent<Collection<KeepOption>> parent) {
-      this.annotationName = annotationName;
+    public KeepOptionsVisitor(
+        ParsingContext parsingContext, Parent<Collection<KeepOption>> parent) {
+      super(parsingContext);
       this.parent = parent;
     }
 
     @Override
-    public String getAnnotationName() {
-      return annotationName;
-    }
-
-    @Override
     public void visitEnum(String ignore, String descriptor, String value) {
       if (!descriptor.equals(AnnotationConstants.Option.DESCRIPTOR)) {
         super.visitEnum(ignore, descriptor, value);
@@ -2557,20 +2624,13 @@
   }
 
   private static class MemberAccessVisitor extends AnnotationVisitorBase {
-    private final Supplier<String> annotationName;
-    KeepMemberAccessPattern.BuilderBase<?, ?> builder;
+    private KeepMemberAccessPattern.BuilderBase<?, ?> builder;
 
-    public MemberAccessVisitor(
-        Supplier<String> annotationName, KeepMemberAccessPattern.BuilderBase<?, ?> builder) {
-      this.annotationName = annotationName;
+    public MemberAccessVisitor(ParsingContext parsingContext, BuilderBase<?, ?> builder) {
+      super(parsingContext);
       this.builder = builder;
     }
 
-    @Override
-    public String getAnnotationName() {
-      return annotationName.get();
-    }
-
     static boolean withNormalizedAccessFlag(String flag, BiPredicate<String, Boolean> fn) {
       boolean allow = !flag.startsWith(MemberAccess.NEGATION_PREFIX);
       return allow
@@ -2629,13 +2689,12 @@
 
   private static class MethodAccessVisitor extends MemberAccessVisitor {
 
-    @SuppressWarnings("HidingField")
-    KeepMethodAccessPattern.Builder builder;
+    private KeepMethodAccessPattern.Builder methodAccessBuilder;
 
     public MethodAccessVisitor(
-        Supplier<String> annotationName, KeepMethodAccessPattern.Builder builder) {
-      super(annotationName, builder);
-      this.builder = builder;
+        ParsingContext parsingContext, KeepMethodAccessPattern.Builder builder) {
+      super(parsingContext, builder);
+      this.methodAccessBuilder = builder;
     }
 
     @Override
@@ -2649,19 +2708,19 @@
               (flag, allow) -> {
                 switch (flag) {
                   case MethodAccess.SYNCHRONIZED:
-                    builder.setSynchronized(allow);
+                    methodAccessBuilder.setSynchronized(allow);
                     return true;
                   case MethodAccess.BRIDGE:
-                    builder.setBridge(allow);
+                    methodAccessBuilder.setBridge(allow);
                     return true;
                   case MethodAccess.NATIVE:
-                    builder.setNative(allow);
+                    methodAccessBuilder.setNative(allow);
                     return true;
                   case MethodAccess.ABSTRACT:
-                    builder.setAbstract(allow);
+                    methodAccessBuilder.setAbstract(allow);
                     return true;
                   case MethodAccess.STRICT_FP:
-                    builder.setStrictFp(allow);
+                    methodAccessBuilder.setStrictFp(allow);
                     return true;
                   default:
                     return false;
@@ -2676,13 +2735,12 @@
 
   private static class FieldAccessVisitor extends MemberAccessVisitor {
 
-    @SuppressWarnings("HidingField")
-    KeepFieldAccessPattern.Builder builder;
+    private KeepFieldAccessPattern.Builder fieldAccessBuilder;
 
     public FieldAccessVisitor(
-        Supplier<String> annotationName, KeepFieldAccessPattern.Builder builder) {
-      super(annotationName, builder);
-      this.builder = builder;
+        ParsingContext parsingContext, KeepFieldAccessPattern.Builder builder) {
+      super(parsingContext, builder);
+      this.fieldAccessBuilder = builder;
     }
 
     @Override
@@ -2696,10 +2754,10 @@
               (flag, allow) -> {
                 switch (flag) {
                   case FieldAccess.VOLATILE:
-                    builder.setVolatile(allow);
+                    fieldAccessBuilder.setVolatile(allow);
                     return true;
                   case FieldAccess.TRANSIENT:
-                    builder.setTransient(allow);
+                    fieldAccessBuilder.setTransient(allow);
                     return true;
                   default:
                     return false;
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 d88b010..18e5c02 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
@@ -23,19 +23,55 @@
     return "L" + getBinaryNameFromClassTypeName(classTypeName) + ";";
   }
 
+  public static String getJavaTypeFromDescriptor(String descriptor) {
+    if (descriptor.length() == 1) {
+      switch (descriptor.charAt(0)) {
+        case 'Z':
+          return "boolean";
+        case 'B':
+          return "byte";
+        case 'C':
+          return "char";
+        case 'S':
+          return "short";
+        case 'I':
+          return "int";
+        case 'J':
+          return "long";
+        case 'F':
+          return "float";
+        case 'D':
+          return "double";
+        case 'V':
+          return "void";
+        default:
+          throw new IllegalStateException("Unexpected descriptor: " + descriptor);
+      }
+    }
+    if (descriptor.charAt(0) == '[') {
+      return getJavaTypeFromDescriptor(descriptor.substring(1)) + "[]";
+    }
+    if (descriptor.charAt(0) == 'L') {
+      return descriptor.substring(1, descriptor.length() - 1).replace('/', '.');
+    }
+    throw new IllegalStateException("Unexpected descriptor: " + descriptor);
+  }
+
   public static KeepTypePattern typePatternFromString(String string) {
     if (string.equals("<any>")) {
       return KeepTypePattern.any();
     }
-    return KeepTypePattern.fromDescriptor(javaTypeToDescriptor(string));
+    return KeepTypePattern.fromDescriptor(getDescriptorFromJavaType(string));
   }
 
-  public static String javaTypeToDescriptor(String type) {
+  public static String getDescriptorFromJavaType(String type) {
     switch (type) {
       case "boolean":
         return "Z";
       case "byte":
         return "B";
+      case "char":
+        return "C";
       case "short":
         return "S";
       case "int":
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/asm/PackageNameParser.java b/src/keepanno/java/com/android/tools/r8/keepanno/asm/PackageNameParser.java
index adef04f..dadb8de 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/asm/PackageNameParser.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/asm/PackageNameParser.java
@@ -6,11 +6,16 @@
 
 import com.android.tools.r8.keepanno.asm.PackageNameParser.PackageNameProperty;
 import com.android.tools.r8.keepanno.ast.KeepPackagePattern;
+import com.android.tools.r8.keepanno.ast.ParsingContext;
 import java.util.function.Consumer;
 
 public class PackageNameParser
     extends PropertyParserBase<KeepPackagePattern, PackageNameProperty, PackageNameParser> {
 
+  public PackageNameParser(ParsingContext parsingContext) {
+    super(parsingContext);
+  }
+
   public enum PackageNameProperty {
     NAME
   }
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/asm/ParserVisitor.java b/src/keepanno/java/com/android/tools/r8/keepanno/asm/ParserVisitor.java
index 5c8ea6f..77d6087 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/asm/ParserVisitor.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/asm/ParserVisitor.java
@@ -4,6 +4,7 @@
 
 package com.android.tools.r8.keepanno.asm;
 
+import com.android.tools.r8.keepanno.ast.ParsingContext.AnnotationParsingContext;
 import java.util.Collections;
 import java.util.List;
 import org.objectweb.asm.AnnotationVisitor;
@@ -11,32 +12,31 @@
 /** Convert parser(s) into an annotation visitor. */
 public class ParserVisitor extends AnnotationVisitorBase {
 
-  private final String annotationDescriptor;
   private final List<PropertyParser<?, ?, ?>> parsers;
   private final Runnable onVisitEnd;
 
   public ParserVisitor(
-      String annotationDescriptor, List<PropertyParser<?, ?, ?>> parsers, Runnable onVisitEnd) {
-    this.annotationDescriptor = annotationDescriptor;
+      AnnotationParsingContext parsingContext,
+      String annotationDescriptor,
+      List<PropertyParser<?, ?, ?>> parsers,
+      Runnable onVisitEnd) {
+    super(parsingContext);
     this.parsers = parsers;
     this.onVisitEnd = onVisitEnd;
+    assert annotationDescriptor.equals(parsingContext.getAnnotationDescriptor());
   }
 
   public ParserVisitor(
-      String annotationDescriptor, PropertyParser<?, ?, ?> declaration, Runnable onVisitEnd) {
-    this(annotationDescriptor, Collections.singletonList(declaration), onVisitEnd);
+      AnnotationParsingContext parsingContext,
+      String annotationDescriptor,
+      PropertyParser<?, ?, ?> declaration,
+      Runnable onVisitEnd) {
+    this(parsingContext, annotationDescriptor, Collections.singletonList(declaration), onVisitEnd);
   }
 
   private <T> void ignore(T unused) {}
 
   @Override
-  public String getAnnotationName() {
-    int start = annotationDescriptor.lastIndexOf('/') + 1;
-    int end = annotationDescriptor.length() - 1;
-    return annotationDescriptor.substring(start, end);
-  }
-
-  @Override
   public void visit(String name, Object value) {
     for (PropertyParser<?, ?, ?> parser : parsers) {
       if (parser.tryParse(name, value, this::ignore)) {
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/asm/PropertyParserBase.java b/src/keepanno/java/com/android/tools/r8/keepanno/asm/PropertyParserBase.java
index 027b6c2..108f086 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/asm/PropertyParserBase.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/asm/PropertyParserBase.java
@@ -5,6 +5,7 @@
 package com.android.tools.r8.keepanno.asm;
 
 import com.android.tools.r8.keepanno.ast.KeepEdgeException;
+import com.android.tools.r8.keepanno.ast.ParsingContext;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.function.Consumer;
@@ -13,11 +14,21 @@
 /** Special case of a property parser allowing only a single value callback. */
 public abstract class PropertyParserBase<T, P, S> implements PropertyParser<T, P, S> {
 
+  private final ParsingContext parsingContext;
+
   private String kind;
   private final Map<String, P> mapping = new HashMap<>();
   private String resultPropertyName = null;
   private T resultValue = null;
 
+  protected PropertyParserBase(ParsingContext parsingContext) {
+    this.parsingContext = parsingContext;
+  }
+
+  public ParsingContext getParsingContext() {
+    return parsingContext;
+  }
+
   boolean tryProperty(P property, String name, Object value, Consumer<T> setValue) {
     return false;
   }
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/asm/TypeParser.java b/src/keepanno/java/com/android/tools/r8/keepanno/asm/TypeParser.java
index f9c6739..6b11f70 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/asm/TypeParser.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/asm/TypeParser.java
@@ -8,6 +8,8 @@
 import com.android.tools.r8.keepanno.asm.TypeParser.TypeProperty;
 import com.android.tools.r8.keepanno.ast.AnnotationConstants.TypePattern;
 import com.android.tools.r8.keepanno.ast.KeepTypePattern;
+import com.android.tools.r8.keepanno.ast.ParsingContext;
+import com.android.tools.r8.keepanno.ast.ParsingContext.AnnotationParsingContext;
 import com.android.tools.r8.keepanno.utils.Unimplemented;
 import java.util.function.Consumer;
 import org.objectweb.asm.AnnotationVisitor;
@@ -15,6 +17,10 @@
 
 public class TypeParser extends PropertyParserBase<KeepTypePattern, TypeProperty, TypeParser> {
 
+  public TypeParser(ParsingContext parsingContext) {
+    super(parsingContext);
+  }
+
   public enum TypeProperty {
     SELF_PATTERN,
     TYPE_NAME,
@@ -63,32 +69,39 @@
       TypeProperty property, String name, String descriptor, Consumer<KeepTypePattern> setValue) {
     switch (property) {
       case SELF_PATTERN:
-        TypeParser typeParser =
-            new TypeParser()
-                .setKind(kind())
-                .enableTypeName(TypePattern.name)
-                .enableTypeConstant(TypePattern.constant)
-                .enableTypeClassNamePattern(TypePattern.classNamePattern);
-        return new ParserVisitor(
-            descriptor,
-            typeParser,
-            () -> setValue.accept(typeParser.getValueOrDefault(KeepTypePattern.any())));
+        {
+          AnnotationParsingContext parsingContext =
+              new AnnotationParsingContext(getParsingContext(), descriptor);
+          TypeParser typeParser =
+              new TypeParser(parsingContext)
+                  .setKind(kind())
+                  .enableTypeName(TypePattern.name)
+                  .enableTypeConstant(TypePattern.constant)
+                  .enableTypeClassNamePattern(TypePattern.classNamePattern);
+          return new ParserVisitor(
+              parsingContext,
+              descriptor,
+              typeParser,
+              () -> setValue.accept(typeParser.getValueOrDefault(KeepTypePattern.any())));
+        }
       case CLASS_NAME_PATTERN:
-        return new ClassNameParser()
-            .setKind(kind())
-            .tryPropertyAnnotation(
-                ClassNameProperty.PATTERN,
-                name,
-                descriptor,
-                classNamePattern -> {
-                  if (classNamePattern.isExact()) {
-                    setValue.accept(
-                        KeepTypePattern.fromDescriptor(classNamePattern.getExactDescriptor()));
-                  } else {
-                    // TODO(b/248408342): Extend the AST type patterns.
-                    throw new Unimplemented("Non-exact class patterns are not implemented yet");
-                  }
-                });
+        {
+          return new ClassNameParser(getParsingContext())
+              .setKind(kind())
+              .tryPropertyAnnotation(
+                  ClassNameProperty.PATTERN,
+                  name,
+                  descriptor,
+                  classNamePattern -> {
+                    if (classNamePattern.isExact()) {
+                      setValue.accept(
+                          KeepTypePattern.fromDescriptor(classNamePattern.getExactDescriptor()));
+                    } else {
+                      // TODO(b/248408342): Extend the AST type patterns.
+                      throw new Unimplemented("Non-exact class patterns are not implemented yet");
+                    }
+                  });
+        }
       default:
         return null;
     }
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 cae779e..13a228b 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
@@ -16,7 +16,6 @@
  */
 public final class AnnotationConstants {
   public static final class Edge {
-    public static final String SIMPLE_NAME = "KeepEdge";
     public static final String DESCRIPTOR = "Lcom/android/tools/r8/keepanno/annotations/KeepEdge;";
     public static final String description = "description";
     public static final String bindings = "bindings";
@@ -25,7 +24,6 @@
   }
 
   public static final class ForApi {
-    public static final String SIMPLE_NAME = "KeepForApi";
     public static final String DESCRIPTOR =
         "Lcom/android/tools/r8/keepanno/annotations/KeepForApi;";
     public static final String description = "description";
@@ -34,7 +32,6 @@
   }
 
   public static final class UsesReflection {
-    public static final String SIMPLE_NAME = "UsesReflection";
     public static final String DESCRIPTOR =
         "Lcom/android/tools/r8/keepanno/annotations/UsesReflection;";
     public static final String description = "description";
@@ -43,7 +40,6 @@
   }
 
   public static final class UsedByReflection {
-    public static final String SIMPLE_NAME = "UsedByReflection";
     public static final String DESCRIPTOR =
         "Lcom/android/tools/r8/keepanno/annotations/UsedByReflection;";
     public static final String description = "description";
@@ -52,20 +48,17 @@
   }
 
   public static final class UsedByNative {
-    public static final String SIMPLE_NAME = "UsedByNative";
     public static final String DESCRIPTOR =
         "Lcom/android/tools/r8/keepanno/annotations/UsedByNative;";
     // Content is the same as UsedByReflection.
   }
 
   public static final class CheckRemoved {
-    public static final String SIMPLE_NAME = "CheckRemoved";
     public static final String DESCRIPTOR =
         "Lcom/android/tools/r8/keepanno/annotations/CheckRemoved;";
   }
 
   public static final class CheckOptimizedOut {
-    public static final String SIMPLE_NAME = "CheckOptimizedOut";
     public static final String DESCRIPTOR =
         "Lcom/android/tools/r8/keepanno/annotations/CheckOptimizedOut;";
   }
@@ -100,20 +93,17 @@
   }
 
   public static final class Binding {
-    public static final String SIMPLE_NAME = "KeepBinding";
     public static final String DESCRIPTOR =
         "Lcom/android/tools/r8/keepanno/annotations/KeepBinding;";
     public static final String bindingName = "bindingName";
   }
 
   public static final class Condition {
-    public static final String SIMPLE_NAME = "KeepCondition";
     public static final String DESCRIPTOR =
         "Lcom/android/tools/r8/keepanno/annotations/KeepCondition;";
   }
 
   public static final class Target {
-    public static final String SIMPLE_NAME = "KeepTarget";
     public static final String DESCRIPTOR =
         "Lcom/android/tools/r8/keepanno/annotations/KeepTarget;";
     public static final String kind = "kind";
@@ -123,7 +113,6 @@
   }
 
   public static final class Kind {
-    public static final String SIMPLE_NAME = "KeepItemKind";
     public static final String DESCRIPTOR =
         "Lcom/android/tools/r8/keepanno/annotations/KeepItemKind;";
     public static final String ONLY_CLASS = "ONLY_CLASS";
@@ -136,7 +125,6 @@
   }
 
   public static final class Constraints {
-    public static final String SIMPLE_NAME = "KeepConstraint";
     public static final String DESCRIPTOR =
         "Lcom/android/tools/r8/keepanno/annotations/KeepConstraint;";
     public static final String LOOKUP = "LOOKUP";
@@ -155,7 +143,6 @@
   }
 
   public static final class Option {
-    public static final String SIMPLE_NAME = "KeepOption";
     public static final String DESCRIPTOR =
         "Lcom/android/tools/r8/keepanno/annotations/KeepOption;";
     public static final String SHRINKING = "SHRINKING";
@@ -166,7 +153,6 @@
   }
 
   public static final class MemberAccess {
-    public static final String SIMPLE_NAME = "MemberAccessFlags";
     public static final String DESCRIPTOR =
         "Lcom/android/tools/r8/keepanno/annotations/MemberAccessFlags;";
     public static final String NEGATION_PREFIX = "NON_";
@@ -180,7 +166,6 @@
   }
 
   public static final class MethodAccess {
-    public static final String SIMPLE_NAME = "MethodAccessFlags";
     public static final String DESCRIPTOR =
         "Lcom/android/tools/r8/keepanno/annotations/MethodAccessFlags;";
     public static final String SYNCHRONIZED = "SYNCHRONIZED";
@@ -191,7 +176,6 @@
   }
 
   public static final class FieldAccess {
-    public static final String SIMPLE_NAME = "FieldAccessFlags";
     public static final String DESCRIPTOR =
         "Lcom/android/tools/r8/keepanno/annotations/FieldAccessFlags;";
     public static final String VOLATILE = "VOLATILE";
@@ -199,7 +183,6 @@
   }
 
   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";
@@ -208,7 +191,6 @@
   }
 
   public static final class ClassNamePattern {
-    public static final String SIMPLE_NAME = "ClassNamePattern";
     public static final String DESCRIPTOR =
         "Lcom/android/tools/r8/keepanno/annotations/ClassNamePattern;";
     public static final String simpleName = "simpleName";
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepAnnotationParserException.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepAnnotationParserException.java
new file mode 100644
index 0000000..d98d410
--- /dev/null
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepAnnotationParserException.java
@@ -0,0 +1,30 @@
+// 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.ast;
+
+public class KeepAnnotationParserException extends KeepEdgeException {
+
+  private final ParsingContext context;
+
+  public KeepAnnotationParserException(ParsingContext context, String message) {
+    super(message);
+    this.context = context;
+  }
+
+  @Override
+  public String getMessage() {
+    return super.getMessage() + getContextAsString();
+  }
+
+  private String getContextAsString() {
+    StringBuilder builder = new StringBuilder();
+    ParsingContext current = context;
+    while (current != null) {
+      builder.append("\n  in ").append(current.getContextFrameAsString());
+      current = current.getParentContext();
+    }
+    return builder.toString();
+  }
+}
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepEdgeMetaInfo.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepEdgeMetaInfo.java
index 75094be..4ec248b 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepEdgeMetaInfo.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepEdgeMetaInfo.java
@@ -115,12 +115,11 @@
     }
 
     @Override
-    @SuppressWarnings("EqualsGetClass")
     public boolean equals(Object o) {
       if (this == o) {
         return true;
       }
-      if (o == null || getClass() != o.getClass()) {
+      if (!(o instanceof KeepEdgeClassContext)) {
         return false;
       }
       KeepEdgeClassContext that = (KeepEdgeClassContext) o;
@@ -154,12 +153,11 @@
     }
 
     @Override
-    @SuppressWarnings("EqualsGetClass")
     public boolean equals(Object o) {
       if (this == o) {
         return true;
       }
-      if (o == null || getClass() != o.getClass()) {
+      if (!(o instanceof KeepEdgeMethodContext)) {
         return false;
       }
       KeepEdgeMethodContext that = (KeepEdgeMethodContext) o;
@@ -191,12 +189,11 @@
     }
 
     @Override
-    @SuppressWarnings("EqualsGetClass")
     public boolean equals(Object o) {
       if (this == o) {
         return true;
       }
-      if (o == null || getClass() != o.getClass()) {
+      if (!(o instanceof KeepEdgeFieldContext)) {
         return false;
       }
       KeepEdgeFieldContext that = (KeepEdgeFieldContext) o;
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/ParsingContext.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/ParsingContext.java
new file mode 100644
index 0000000..d7ce9a4
--- /dev/null
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/ParsingContext.java
@@ -0,0 +1,147 @@
+// 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.ast;
+
+import static com.android.tools.r8.keepanno.asm.KeepEdgeReaderUtils.getJavaTypeFromDescriptor;
+
+import org.objectweb.asm.Type;
+
+public abstract class ParsingContext {
+
+  public KeepAnnotationParserException error(String message) {
+    throw new KeepAnnotationParserException(this, message);
+  }
+
+  public abstract String getHolderName();
+
+  public ParsingContext getParentContext() {
+    return null;
+  }
+
+  public abstract String getContextFrameAsString();
+
+  public static class ClassParsingContext extends ParsingContext {
+    private final String className;
+
+    public ClassParsingContext(String className) {
+      this.className = className;
+    }
+
+    @Override
+    public String getHolderName() {
+      return className;
+    }
+
+    @Override
+    public String getContextFrameAsString() {
+      return className;
+    }
+  }
+
+  public abstract static class MemberParsingContext extends ParsingContext {
+    private final ClassParsingContext classContext;
+
+    public MemberParsingContext(ClassParsingContext classContext) {
+      this.classContext = classContext;
+    }
+
+    @Override
+    public String getHolderName() {
+      return classContext.getHolderName();
+    }
+
+    @Override
+    public ParsingContext getParentContext() {
+      return classContext;
+    }
+  }
+
+  public static class MethodParsingContext extends MemberParsingContext {
+
+    private final String methodName;
+    private final String methodDescriptor;
+
+    public MethodParsingContext(
+        ClassParsingContext classContext, String methodName, String methodDescriptor) {
+      super(classContext);
+      this.methodName = methodName;
+      this.methodDescriptor = methodDescriptor;
+    }
+
+    @Override
+    public String getContextFrameAsString() {
+      Type methodType = Type.getMethodType(methodDescriptor);
+      StringBuilder builder = new StringBuilder();
+      builder
+          .append("method ")
+          .append(getJavaTypeFromDescriptor(methodType.getReturnType().getDescriptor()))
+          .append(' ')
+          .append(methodName)
+          .append('(');
+      boolean first = true;
+      for (Type argument : methodType.getArgumentTypes()) {
+        if (first) {
+          first = false;
+        } else {
+          builder.append(", ");
+        }
+        builder.append(getJavaTypeFromDescriptor(argument.getDescriptor()));
+      }
+      return builder.append(')').toString();
+    }
+  }
+
+  public static class FieldParsingContext extends MemberParsingContext {
+
+    private final String fieldName;
+    private final String fieldDescriptor;
+
+    public FieldParsingContext(
+        ClassParsingContext classContext, String fieldName, String fieldDescriptor) {
+      super(classContext);
+      this.fieldName = fieldName;
+      this.fieldDescriptor = fieldDescriptor;
+    }
+
+    @Override
+    public String getContextFrameAsString() {
+      return "field " + getJavaTypeFromDescriptor(fieldDescriptor) + " " + fieldName;
+    }
+  }
+
+  public static class AnnotationParsingContext extends ParsingContext {
+    private final ParsingContext parentContext;
+    private final String annotationDescriptor;
+
+    public AnnotationParsingContext(ParsingContext parentContext, String annotationDescriptor) {
+      this.parentContext = parentContext;
+      this.annotationDescriptor = annotationDescriptor;
+    }
+
+    public String getAnnotationDescriptor() {
+      return annotationDescriptor;
+    }
+
+    private String getSimpleAnnotationName() {
+      int i = annotationDescriptor.lastIndexOf('/') + 1;
+      return annotationDescriptor.substring(Math.max(i, 1), annotationDescriptor.length() - 1);
+    }
+
+    @Override
+    public String getHolderName() {
+      return parentContext.getHolderName();
+    }
+
+    @Override
+    public ParsingContext getParentContext() {
+      return parentContext;
+    }
+
+    @Override
+    public String getContextFrameAsString() {
+      return "@" + getSimpleAnnotationName();
+    }
+  }
+}
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 bee5471..312e8bb 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
@@ -1220,9 +1220,7 @@
     }
 
     private void generateAnnotationConstants(Class<?> clazz) {
-      String name = simpleName(clazz);
       String desc = TestBase.descriptor(clazz);
-      println("public static final String SIMPLE_NAME = " + quote(name) + ";");
       println("public static final String DESCRIPTOR = " + quote(desc) + ";");
     }