[KeepAnno] Allow only singular member patterns in items.

Bug: b/248408342
Change-Id: Id8e297a4a2b86d19cba6d98d8c3aa31567c8addd
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 3ba3b8b..37c4087 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
@@ -10,7 +10,6 @@
 import com.android.tools.r8.keepanno.ast.KeepEdgeException;
 import com.android.tools.r8.keepanno.ast.KeepItemPattern;
 import com.android.tools.r8.keepanno.ast.KeepItemPattern.Builder;
-import com.android.tools.r8.keepanno.ast.KeepMembersPattern;
 import com.android.tools.r8.keepanno.ast.KeepMethodNamePattern;
 import com.android.tools.r8.keepanno.ast.KeepMethodPattern;
 import com.android.tools.r8.keepanno.ast.KeepPreconditions;
@@ -170,9 +169,7 @@
       }
       if (methodName != null) {
         itemBuilder.setMembersPattern(
-            KeepMembersPattern.builder()
-                .addMethodPattern(KeepMethodPattern.builder().setNamePattern(methodName).build())
-                .build());
+            KeepMethodPattern.builder().setNamePattern(methodName).build());
       }
       KeepTarget target = KeepTarget.builder().setItem(itemBuilder.build()).build();
       parent.accept(target);
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeWriter.java b/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeWriter.java
index ee6d1f5..373853f 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeWriter.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeWriter.java
@@ -8,7 +8,9 @@
 import com.android.tools.r8.keepanno.ast.KeepConsequences;
 import com.android.tools.r8.keepanno.ast.KeepEdge;
 import com.android.tools.r8.keepanno.ast.KeepItemPattern;
+import com.android.tools.r8.keepanno.ast.KeepMembersPattern;
 import com.android.tools.r8.keepanno.ast.KeepMethodNamePattern;
+import com.android.tools.r8.keepanno.ast.KeepMethodPattern;
 import com.android.tools.r8.keepanno.ast.KeepPreconditions;
 import com.android.tools.r8.keepanno.ast.KeepQualifiedClassNamePattern;
 import com.android.tools.r8.keepanno.utils.Unimplemented;
@@ -68,39 +70,38 @@
           if (!item.getExtendsPattern().isAny()) {
             throw new Unimplemented();
           }
-          if (item.getMembersPattern().isNone()) {
-            // Default is "no methods".
-          } else if (item.getMembersPattern().isAll()) {
-            throw new Unimplemented();
-          } else {
-            item.getMembersPattern()
-                .forEach(
-                    field -> {
-                      throw new Unimplemented();
-                    },
-                    method -> {
-                      KeepMethodNamePattern methodNamePattern = method.getNamePattern();
-                      methodNamePattern.match(
-                          () -> {
-                            throw new Unimplemented();
-                          },
-                          exactMethodName -> {
-                            targetVisitor.visit(Target.methodName, exactMethodName);
-                            return null;
-                          });
-                      if (!method.getAccessPattern().isAny()) {
-                        throw new Unimplemented();
-                      }
-                      if (!method.getReturnTypePattern().isAny()) {
-                        throw new Unimplemented();
-                      }
-                      if (!method.getParametersPattern().isAny()) {
-                        throw new Unimplemented();
-                      }
-                    });
-          }
+          writeMembers(item.getMembersPattern(), targetVisitor);
           targetVisitor.visitEnd();
         });
     arrayVisitor.visitEnd();
   }
+
+  private void writeMembers(KeepMembersPattern membersPattern, AnnotationVisitor targetVisitor) {
+    if (membersPattern.isNone()) {
+      // Default is "no methods".
+      return;
+    }
+    if (membersPattern.isAll()) {
+      throw new Unimplemented();
+    }
+    KeepMethodPattern method = membersPattern.asMethod();
+    KeepMethodNamePattern methodNamePattern = method.getNamePattern();
+    methodNamePattern.match(
+        () -> {
+          throw new Unimplemented();
+        },
+        exactMethodName -> {
+          targetVisitor.visit(Target.methodName, exactMethodName);
+          return null;
+        });
+    if (!method.getAccessPattern().isAny()) {
+      throw new Unimplemented();
+    }
+    if (!method.getReturnTypePattern().isAny()) {
+      throw new Unimplemented();
+    }
+    if (!method.getParametersPattern().isAny()) {
+      throw new Unimplemented();
+    }
+  }
 }
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepEdge.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepEdge.java
index 2865b8f..5271837 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepEdge.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepEdge.java
@@ -25,7 +25,7 @@
  *   CONDITION ::= ITEM_PATTERN
  *
  *   CONSEQUENCES ::= TARGET+
- *   TARGET ::= any | OPTIONS ITEM_PATTERN
+ *   TARGET ::= any | OPTIONS ITEM_PATTERN // TODO(b/248408342): What options are on target 'any'?
  *   OPTIONS ::= keep-all | OPTION+
  *   OPTION ::= shrinking | optimizing | obfuscating | access-modifying
  *
@@ -38,7 +38,7 @@
  *   UNQUALIFIED_CLASS_NAME_PATTERN ::= any | exact simple-class-name
  *   EXTENDS_PATTERN ::= any | QUALIFIED_CLASS_NAME_PATTERN
  *
- *   MEMBERS_PATTERN ::= none | all | METHOD_PATTERN*
+ *   MEMBERS_PATTERN ::= none | all | METHOD_PATTERN
  *
  *   METHOD_PATTERN
  *     ::= METHOD_ACCESS_PATTERN
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepFieldPattern.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepFieldPattern.java
deleted file mode 100644
index 8de46e1..0000000
--- a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepFieldPattern.java
+++ /dev/null
@@ -1,13 +0,0 @@
-// Copyright (c) 2022, 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 KeepFieldPattern extends KeepMemberPattern {
-
-  private KeepFieldPattern() {}
-
-  public boolean isAnyField() {
-    return false;
-  }
-}
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMemberPattern.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMemberPattern.java
deleted file mode 100644
index 51884e1..0000000
--- a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMemberPattern.java
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright (c) 2022, 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 abstract class KeepMemberPattern {
-
-  public static KeepMemberPattern anyMember() {
-    return Any.getInstance();
-  }
-
-  private static class Any extends KeepMemberPattern {
-    private static final Any INSTANCE = new Any();
-
-    public static Any getInstance() {
-      return INSTANCE;
-    }
-
-    @Override
-    public boolean isAnyMember() {
-      return true;
-    }
-  }
-
-  public boolean isAnyMember() {
-    return false;
-  }
-
-  abstract static class Builder<T extends Builder<T>> {
-
-    public abstract T self();
-
-    Builder() {}
-  }
-}
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMembersPattern.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMembersPattern.java
index 9571329..64e1b25 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMembersPattern.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMembersPattern.java
@@ -3,19 +3,9 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.keepanno.ast;
 
-import com.android.tools.r8.keepanno.utils.Unimplemented;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Objects;
-import java.util.function.Consumer;
-import java.util.stream.Collectors;
 
 public abstract class KeepMembersPattern {
 
-  public static Builder builder() {
-    return new Builder();
-  }
-
   public static KeepMembersPattern none() {
     return None.getInstance();
   }
@@ -24,48 +14,6 @@
     return All.getInstance();
   }
 
-  public static class Builder {
-
-    private boolean anyMethod = false;
-    private boolean anyField = false;
-    private List<KeepMethodPattern> methods = new ArrayList<>();
-    private List<KeepFieldPattern> fields = new ArrayList<>();
-
-    public Builder addMethodPattern(KeepMethodPattern methodPattern) {
-      if (anyMethod) {
-        return this;
-      }
-      if (methodPattern.isAnyMethod()) {
-        methods.clear();
-        anyMethod = true;
-      }
-      methods.add(methodPattern);
-      return this;
-    }
-
-    public Builder addFieldPattern(KeepFieldPattern fieldPattern) {
-      if (anyField) {
-        return this;
-      }
-      if (fieldPattern.isAnyField()) {
-        fields.clear();
-        anyField = true;
-      }
-      fields.add(fieldPattern);
-      return this;
-    }
-
-    public KeepMembersPattern build() {
-      if (methods.isEmpty() && fields.isEmpty()) {
-        return KeepMembersPattern.none();
-      }
-      if (anyMethod && anyField) {
-        return KeepMembersPattern.all();
-      }
-      return new Some(methods, fields);
-    }
-  }
-
   private static class All extends KeepMembersPattern {
 
     private static final All INSTANCE = new All();
@@ -80,16 +28,6 @@
     }
 
     @Override
-    public boolean isNone() {
-      return true;
-    }
-
-    @Override
-    public void forEach(Consumer<KeepFieldPattern> onField, Consumer<KeepMethodPattern> onMethod) {
-      throw new Unimplemented("Should this include all and none?");
-    }
-
-    @Override
     public boolean equals(Object obj) {
       return this == obj;
     }
@@ -114,21 +52,11 @@
     }
 
     @Override
-    public boolean isAll() {
-      return false;
-    }
-
-    @Override
     public boolean isNone() {
       return true;
     }
 
     @Override
-    public void forEach(Consumer<KeepFieldPattern> onField, Consumer<KeepMethodPattern> onMethod) {
-      throw new Unimplemented("Should this include all and none?");
-    }
-
-    @Override
     public boolean equals(Object obj) {
       return this == obj;
     }
@@ -144,69 +72,21 @@
     }
   }
 
-  private static class Some extends KeepMembersPattern {
+  KeepMembersPattern() {}
 
-    private final List<KeepMethodPattern> methods;
-    private final List<KeepFieldPattern> fields;
-
-    private Some(List<KeepMethodPattern> methods, List<KeepFieldPattern> fields) {
-      assert !methods.isEmpty() || !fields.isEmpty();
-      this.methods = methods;
-      this.fields = fields;
-    }
-
-    @Override
-    public boolean isAll() {
-      // Since there is at least one none-all field or method this is not a match all.
-      return false;
-    }
-
-    @Override
-    public boolean isNone() {
-      // Since there is at least one field or method this is not a match none.
-      return false;
-    }
-
-    @Override
-    public void forEach(Consumer<KeepFieldPattern> onField, Consumer<KeepMethodPattern> onMethod) {
-      fields.forEach(onField);
-      methods.forEach(onMethod);
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-      if (this == obj) {
-        return true;
-      }
-      if (obj == null || getClass() != obj.getClass()) {
-        return false;
-      }
-      Some that = (Some) obj;
-      return methods.equals(that.methods) && fields.equals(that.fields);
-    }
-
-    @Override
-    public int hashCode() {
-      return Objects.hash(methods, fields);
-    }
-
-    @Override
-    public String toString() {
-      return "KeepMembersSomePattern{"
-          + "methods={"
-          + methods.stream().map(Object::toString).collect(Collectors.joining(", "))
-          + "}, fields={"
-          + fields.stream().map(Object::toString).collect(Collectors.joining(", "))
-          + "}}";
-    }
+  public boolean isAll() {
+    return false;
   }
 
-  private KeepMembersPattern() {}
+  public boolean isNone() {
+    return false;
+  }
 
-  public abstract boolean isAll();
+  public final boolean isMethod() {
+    return asMethod() != null;
+  }
 
-  public abstract boolean isNone();
-
-  public abstract void forEach(
-      Consumer<KeepFieldPattern> onField, Consumer<KeepMethodPattern> onMethod);
+  public KeepMethodPattern asMethod() {
+    return null;
+  }
 }
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMethodPattern.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMethodPattern.java
index b3ec418..bec8102 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMethodPattern.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMethodPattern.java
@@ -5,13 +5,13 @@
 
 import java.util.Objects;
 
-public final class KeepMethodPattern extends KeepMemberPattern {
+public final class KeepMethodPattern extends KeepMembersPattern {
 
   public static Builder builder() {
     return new Builder();
   }
 
-  public static class Builder extends KeepMemberPattern.Builder<Builder> {
+  public static class Builder {
 
     private KeepMethodAccessPattern accessPattern = KeepMethodAccessPattern.any();
     private KeepMethodNamePattern namePattern = null;
@@ -20,7 +20,6 @@
 
     private Builder() {}
 
-    @Override
     public Builder self() {
       return this;
     }
@@ -74,8 +73,16 @@
     this.parametersPattern = parametersPattern;
   }
 
+  @Override
+  public KeepMethodPattern asMethod() {
+    return this;
+  }
+
   public boolean isAnyMethod() {
-    return false;
+    return accessPattern.isAny()
+        && namePattern.isAny()
+        && returnTypePattern.isAny()
+        && parametersPattern.isAny();
   }
 
   public KeepMethodAccessPattern getAccessPattern() {
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/keeprules/KeepRuleExtractor.java b/src/keepanno/java/com/android/tools/r8/keepanno/keeprules/KeepRuleExtractor.java
index 735c6de..2d0df1b 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/keeprules/KeepRuleExtractor.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/keeprules/KeepRuleExtractor.java
@@ -5,7 +5,6 @@
 
 import com.android.tools.r8.keepanno.ast.KeepConsequences;
 import com.android.tools.r8.keepanno.ast.KeepEdge;
-import com.android.tools.r8.keepanno.ast.KeepFieldPattern;
 import com.android.tools.r8.keepanno.ast.KeepItemPattern;
 import com.android.tools.r8.keepanno.ast.KeepMembersPattern;
 import com.android.tools.r8.keepanno.ast.KeepMethodAccessPattern;
@@ -110,19 +109,12 @@
     if (members.isAll()) {
       return builder.append(" { *; }");
     }
-    builder.append(" {");
-    members.forEach(
-        field -> printField(builder.append(' '), field),
-        method -> printMethod(builder.append(' '), method));
-    return builder.append(" }");
-  }
-
-  private static StringBuilder printField(StringBuilder builder, KeepFieldPattern field) {
-    if (field.isAnyField()) {
-      return builder.append("<fields>;");
-    } else {
-      throw new Unimplemented();
+    if (members.isMethod()) {
+      builder.append(" {");
+      printMethod(builder.append(' '), members.asMethod());
+      return builder.append(" }");
     }
+    throw new Unimplemented();
   }
 
   private static StringBuilder printMethod(StringBuilder builder, KeepMethodPattern methodPattern) {
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/processor/KeepEdgeProcessor.java b/src/keepanno/java/com/android/tools/r8/keepanno/processor/KeepEdgeProcessor.java
index 36bbaeb..58601a6 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/processor/KeepEdgeProcessor.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/processor/KeepEdgeProcessor.java
@@ -16,7 +16,6 @@
 import com.android.tools.r8.keepanno.ast.KeepEdge;
 import com.android.tools.r8.keepanno.ast.KeepEdge.Builder;
 import com.android.tools.r8.keepanno.ast.KeepItemPattern;
-import com.android.tools.r8.keepanno.ast.KeepMembersPattern;
 import com.android.tools.r8.keepanno.ast.KeepMethodNamePattern;
 import com.android.tools.r8.keepanno.ast.KeepMethodPattern;
 import com.android.tools.r8.keepanno.ast.KeepQualifiedClassNamePattern;
@@ -137,11 +136,8 @@
     if (methodNameValue != null) {
       String methodName = AnnotationStringValueVisitor.getString(methodNameValue);
       itemBuilder.setMembersPattern(
-          KeepMembersPattern.builder()
-              .addMethodPattern(
-                  KeepMethodPattern.builder()
-                      .setNamePattern(KeepMethodNamePattern.exact(methodName))
-                      .build())
+          KeepMethodPattern.builder()
+              .setNamePattern(KeepMethodNamePattern.exact(methodName))
               .build());
     }
 
diff --git a/src/test/java/com/android/tools/r8/keepanno/ast/KeepEdgeAstTest.java b/src/test/java/com/android/tools/r8/keepanno/ast/KeepEdgeAstTest.java
index bcdb8ed..0f07d76 100644
--- a/src/test/java/com/android/tools/r8/keepanno/ast/KeepEdgeAstTest.java
+++ b/src/test/java/com/android/tools/r8/keepanno/ast/KeepEdgeAstTest.java
@@ -170,13 +170,10 @@
   }
 
   private KeepMembersPattern defaultInitializerPattern() {
-    return KeepMembersPattern.builder()
-        .addMethodPattern(
-            KeepMethodPattern.builder()
-                .setNamePattern(KeepMethodNamePattern.initializer())
-                .setParametersPattern(KeepMethodParametersPattern.none())
-                .setReturnTypeVoid()
-                .build())
+    return KeepMethodPattern.builder()
+        .setNamePattern(KeepMethodNamePattern.initializer())
+        .setParametersPattern(KeepMethodParametersPattern.none())
+        .setReturnTypeVoid()
         .build();
   }
 }
diff --git a/src/test/java/com/android/tools/r8/keepanno/testsource/KeepSourceEdges.java b/src/test/java/com/android/tools/r8/keepanno/testsource/KeepSourceEdges.java
index 187a019..b6369de 100644
--- a/src/test/java/com/android/tools/r8/keepanno/testsource/KeepSourceEdges.java
+++ b/src/test/java/com/android/tools/r8/keepanno/testsource/KeepSourceEdges.java
@@ -6,7 +6,6 @@
 import com.android.tools.r8.keepanno.ast.KeepConsequences;
 import com.android.tools.r8.keepanno.ast.KeepEdge;
 import com.android.tools.r8.keepanno.ast.KeepItemPattern;
-import com.android.tools.r8.keepanno.ast.KeepMembersPattern;
 import com.android.tools.r8.keepanno.ast.KeepMethodNamePattern;
 import com.android.tools.r8.keepanno.ast.KeepMethodPattern;
 import com.android.tools.r8.keepanno.ast.KeepQualifiedClassNamePattern;
@@ -37,12 +36,10 @@
     // Build the constructor target.
     KeepMethodPattern constructorMethod =
         KeepMethodPattern.builder().setNamePattern(KeepMethodNamePattern.exact("<init>")).build();
-    KeepMembersPattern constructorMembers =
-        KeepMembersPattern.builder().addMethodPattern(constructorMethod).build();
     KeepItemPattern constructorItem =
         KeepItemPattern.builder()
             .setClassPattern(name)
-            .setMembersPattern(constructorMembers)
+            .setMembersPattern(constructorMethod)
             .build();
     KeepTarget constructorTarget = KeepTarget.builder().setItem(constructorItem).build();
     // The consequet set is the class an its constructor.