[KeepAnno] Add bindings to check declarations

This change aligns checks with edges and allows more uniform
parsing. Follow-up CLs will start normalizing the AST to only
allow references in the precondition and consequence sets.

Bug: b/343389186
Change-Id: I541709dd01717633f4d2024945a7e7ec1d23523e
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 c6b6f64..f28d5c8 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
@@ -49,6 +49,7 @@
 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.KeepMemberItemReference;
 import com.android.tools.r8.keepanno.ast.KeepMemberPattern;
 import com.android.tools.r8.keepanno.ast.KeepMethodAccessPattern;
 import com.android.tools.r8.keepanno.ast.KeepMethodNamePattern;
@@ -829,15 +830,30 @@
                 "Invalid extracted annotation, must specify either an edge, a removed check, or an"
                     + " optimized-out check.");
       }
+      KeepEdgeMetaInfo metaInfo = context.applyToMetadata(KeepEdgeMetaInfo.builder()).build();
       KeepCheckKind kind = isCheckRemoved ? KeepCheckKind.REMOVED : KeepCheckKind.OPTIMIZED_OUT;
-      parent.accept(
-          KeepCheck.builder()
-              .setMetaInfo(context.applyToMetadata(KeepEdgeMetaInfo.builder()).build())
-              .setKind(kind)
-              .setItemPattern(context.toItemPattern())
-              .build());
+      KeepItemPattern itemPattern = context.toItemPattern();
+      parent.accept(buildKeepCheckFromItem(metaInfo, kind, itemPattern));
       super.visitEnd();
     }
+
+    private static KeepCheck buildKeepCheckFromItem(
+        KeepEdgeMetaInfo metaInfo, KeepCheckKind kind, KeepItemPattern itemPattern) {
+      KeepBindings.Builder bindingsBuilder = KeepBindings.builder();
+      KeepBindingSymbol symbol = bindingsBuilder.generateFreshSymbol("CHECK");
+      bindingsBuilder.addBinding(symbol, itemPattern);
+      KeepItemReference itemReference =
+          itemPattern.isClassItemPattern()
+              ? KeepClassItemReference.fromBindingReference(KeepBindingReference.forClass(symbol))
+              : KeepMemberItemReference.fromBindingReference(
+                  KeepBindingReference.forMember(symbol));
+      return KeepCheck.builder()
+          .setMetaInfo(metaInfo)
+          .setKind(kind)
+          .setBindings(bindingsBuilder.build())
+          .setItemReference(itemReference)
+          .build();
+    }
   }
 
   private static class KeepEdgeVisitor extends AnnotationVisitorBase {
@@ -1531,11 +1547,12 @@
 
     @Override
     public void visitEnd() {
+      UserBindingsHelper bindingsHelper = new UserBindingsHelper();
       KeepItemVisitorBase itemVisitor =
           new KeepItemVisitorBase(parsingContext) {
             @Override
             public UserBindingsHelper getBindingsHelper() {
-              throw parsingContext.error("bindings not supported");
+              return bindingsHelper;
             }
           };
       itemVisitor.visit(Item.className, className);
@@ -1544,7 +1561,8 @@
           KeepCheck.builder()
               .setMetaInfo(metaInfoBuilder.build())
               .setKind(kind)
-              .setItemPattern(itemVisitor.getItemReference().asItemPattern())
+              .setBindings(itemVisitor.getBindingsHelper().build())
+              .setItemReference(itemVisitor.getItemReference())
               .build());
     }
   }
@@ -1583,11 +1601,8 @@
     public void visitEnd() {
       super.visitEnd();
       parent.accept(
-          KeepCheck.builder()
-              .setMetaInfo(metaInfoBuilder.build())
-              .setKind(kind)
-              .setItemPattern(context)
-              .build());
+          ExtractedAnnotationVisitor.buildKeepCheckFromItem(
+              metaInfoBuilder.build(), kind, context));
     }
   }
 
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepBindings.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepBindings.java
index df6a224..fbc429a 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepBindings.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepBindings.java
@@ -3,6 +3,8 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.keepanno.ast;
 
+import java.util.Arrays;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.IdentityHashMap;
@@ -57,6 +59,28 @@
             .collect(Collectors.joining(", "));
   }
 
+  public void verify(KeepItemReference... references) {
+    verify(Arrays.asList(references));
+  }
+
+  public void verify(Collection<KeepItemReference> references) {
+    for (KeepItemReference reference : references) {
+      if (reference.isBindingReference()) {
+        KeepBindingReference bindingReference = reference.asBindingReference();
+        if (!bindings.containsKey(bindingReference.getName())) {
+          throw new KeepEdgeException("Unbound reference to " + bindingReference);
+        }
+      } else {
+        KeepItemPattern itemPattern = reference.asItemPattern();
+        for (KeepBindingReference bindingReference : itemPattern.getBindingReferences()) {
+          if (!bindings.containsKey(bindingReference.getName())) {
+            throw new KeepEdgeException("Unbound reference to " + bindingReference);
+          }
+        }
+      }
+    }
+  }
+
   /**
    * A unique binding.
    *
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepCheck.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepCheck.java
index 1b2e42d..50f5c7c 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepCheck.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepCheck.java
@@ -14,7 +14,8 @@
 
     private KeepEdgeMetaInfo metaInfo = KeepEdgeMetaInfo.none();
     private KeepCheckKind kind = KeepCheckKind.REMOVED;
-    private KeepItemPattern itemPattern;
+    private KeepBindings bindings = KeepBindings.none();
+    private KeepItemReference itemReference;
 
     public Builder setMetaInfo(KeepEdgeMetaInfo metaInfo) {
       this.metaInfo = metaInfo;
@@ -26,16 +27,22 @@
       return this;
     }
 
-    public Builder setItemPattern(KeepItemPattern itemPattern) {
-      this.itemPattern = itemPattern;
+    public Builder setBindings(KeepBindings bindings) {
+      this.bindings = bindings;
+      return this;
+    }
+
+    public Builder setItemReference(KeepItemReference itemReference) {
+      this.itemReference = itemReference;
       return this;
     }
 
     public KeepCheck build() {
-      if (itemPattern == null) {
+      if (itemReference == null) {
         throw new KeepEdgeException("KeepCheck must have an item pattern.");
       }
-      return new KeepCheck(metaInfo, kind, itemPattern);
+      bindings.verify(itemReference);
+      return new KeepCheck(metaInfo, kind, bindings, itemReference);
     }
   }
 
@@ -45,12 +52,18 @@
 
   private final KeepEdgeMetaInfo metaInfo;
   private final KeepCheckKind kind;
-  private final KeepItemPattern itemPattern;
+  private final KeepBindings bindings;
+  private final KeepItemReference itemReference;
 
-  private KeepCheck(KeepEdgeMetaInfo metaInfo, KeepCheckKind kind, KeepItemPattern itemPattern) {
+  private KeepCheck(
+      KeepEdgeMetaInfo metaInfo,
+      KeepCheckKind kind,
+      KeepBindings bindings,
+      KeepItemReference itemReference) {
     this.metaInfo = metaInfo;
     this.kind = kind;
-    this.itemPattern = itemPattern;
+    this.bindings = bindings;
+    this.itemReference = itemReference;
   }
 
   @Override
@@ -66,12 +79,23 @@
     return kind;
   }
 
+  public KeepBindings getBindings() {
+    return bindings;
+  }
+
+  public KeepItemReference getItemReference() {
+    return itemReference;
+  }
+
   public KeepItemPattern getItemPattern() {
-    return itemPattern;
+    if (itemReference.isBindingReference()) {
+      return bindings.get(itemReference.asBindingReference()).getItem();
+    }
+    return itemReference.asItemPattern();
   }
 
   @Override
   public String toString() {
-    return "KeepCheck{kind=" + kind + ", item=" + itemPattern + "}";
+    return "KeepCheck{kind=" + kind + ", item=" + itemReference + "}";
   }
 }